Skip to content

Commit

Permalink
Merge c225cc0 into 92258d7
Browse files Browse the repository at this point in the history
  • Loading branch information
soyarielruiz committed Sep 25, 2019
2 parents 92258d7 + c225cc0 commit 52ecd79
Show file tree
Hide file tree
Showing 6 changed files with 582 additions and 60 deletions.
91 changes: 89 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,98 @@ Parameters example:
filters: {
itemField: 'foobar',
otherItemField: {
$in: ['foo', 'bar']
},
'value': ['foo', 'bar'],
'type' : 'in'
}
}
}
```
##### Filters

The filters has a structure to apply, by default uses `equal`.
For example:
```js
{
filters: {
'aField': 'valueToFilter'
}
}
```
Transforms to:
```js
{
<aField>: { $eq: <valueToFilter> }
}
```
If you want to apply different filters it should be as follows:
```js
{
filters: {
'aField': {
value: 'valueToFilter', // required(string or array)
type: 'aTypeChoosen' //optional
}
}
}
```

The possible types to use are:

| Filter | Mongo filter |
|---------------|-----------------------|
| equal | $eq |
| not | $ne |
| greater | $gt |
| greaterOrEqual| $gte |
| lesser | $lt |
| lesserOrEqual | $lte |
| in | $in |
| notIn | $nin |
| reg | $reg |
| all | $all |


You can also add filters in the model defining the `fields` function as follow:
```js
static get fields() {
return {
'aFieldWithName': {
type: 'aTypeDefined',
field: 'fieldInMongoDB'
},
'anotherFieldName': {
type: 'aTypeDefined',
field: 'fieldInMongoDB'
},
{
...
}
}
}
```
In which we have:

`afieldWithName` and `anotherFieldName` as name default in Model <br/>
`aTypeDefined` as a possible type to use as filter<br/>
`fieldInMongoDB` as the field in MongoDB to compare<br/>

For example:

```js
static get fields() {
return {
date_from: {
type: 'greaterOrEqual',
field: 'date'
},
date_from_g: {
type: 'greater',
field: 'date'
}
}
}
```


### ***async*** `getTotals(model)`
Get the totals of the items from the latest get operation with pagination.
Expand Down
4 changes: 3 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
const MongoDB = require('./mongodb');
const MongoDBError = require('./mongodb-error');
const MongoDBConfigError = require('./mongodb-config-error');
const MongoDBFilterWrapper = require('./mongodb-filter-wrapper');

module.exports = {
MongoDB,
MongoDBError,
MongoDBConfigError
MongoDBConfigError,
MongoDBFilterWrapper
};
155 changes: 155 additions & 0 deletions lib/mongodb-filter-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
'use strict';

class MongoDBFilterWrapper {
static get filtersAllowed() {
return {
equal: '$eq',
not: '$ne',
greater: '$gt',
greaterOrEqual: '$gte',
lesser: '$lt',
lesserOrEqual: '$lte',
in: '$in',
notIn: '$nin',
reg: '$regex',
all: '$all'
};
}

/**
* Enter door to filter process
* @param model {Object} Model that will use the filter
* @param filters {Object} Filters defined as parameters
* @returns {Object} with the filter parsed
*/
static process(model, filters) {

// Validate filters given
if(typeof filters !== 'object' || (Array.isArray(filters) && filters.length === 0) || Object.entries(filters).length === 0)
return {};

// Get model filters
const modelFields = model.constructor.fields || {};

return this.getParsedFilters(filters, modelFields);
}

/**
* Validates filters given and loop calling to parse
* @param filters {Object} Filters defined as parameters
* @param modelFields {Object} Model that will use the filter
* @returns {Array|Object} The filter parsed to use in MongoDB
*/
static getParsedFilters(filters, modelFields) {
const orCondition = Array.isArray(filters);

let parsedFilters = orCondition ? [] : {};

// Only arrays as a filter
if(filters !== undefined && typeof filters === 'object' && Object.keys(filters).length > 0 && !Array.isArray(filters))
filters = [filters];

if(filters && filters.length > 0) {
// First loop over all filters given by parameters
filters.forEach(filterGiven => {
const fullyParsedFilter = this.parseObjectFilter(filterGiven, modelFields);
parsedFilters = (orCondition && Array.isArray(parsedFilters))
? [...parsedFilters, fullyParsedFilter] : Object.assign(parsedFilters, fullyParsedFilter);
});
}

if(orCondition && parsedFilters.length > 0) {
return {
$or: parsedFilters
};
}

return parsedFilters;
}

/**
* Parse the filter comparing given fields name and model fields
* @param filters {Object} One filter given with her type and value(only uses that info)
* @param modelFields {Object} Model that will use the filter if exists
* @returns {Object} The filter parsed with all the values by one
*/
static parseObjectFilter(filters, modelFields) {
const filterToResponse = {};

for(const [field, value] of Object.entries(filters)) {
const fieldName = this.getFieldToCompare(field, modelFields);

const filter = (typeof value !== 'object') ? { value } : value;

filterToResponse[fieldName] = this.createFilter(filter, modelFields[field]);
}
return filterToResponse;
}

/**
* Creates the object or the value(string/array) to use in the filter
* @param filter {Object} Filter given as parameter to parse with type and value
* @param modelFields {Object} Model field that will use the filter, if exists
* @returns {string|Object} Value to use in filter, with type or if is an equal only the value to
* search
*/
static createFilter(filter, modelFields) {
const type = this.getFilterType(filter, modelFields);
const value = this.getValue(filter);

if(type === 'equal')
return value;

return {
[this.filtersAllowed[type]]: value
};
}

/**
* Gets the value to use in the filter, if is a object(suppose ID) transform to string
* else search that exists the key value to return, or only returns an empty string
* @param filter {Object} With all the filter
* @returns {string|number|object|array} With the value to filter
*/
static getValue(filter) {
let value = '';

if(typeof filter === 'object' && filter.value === undefined)
value = filter.str;

if(filter.value !== undefined)
({ value } = filter);

return value;
}

/**
* Transforms the type given in the filter to a filter used in MongoDB
* @param filter {Object} Filter by params to use
* @param modelField {Object} Model fields
* @returns {string} MongoDB type
*/
static getFilterType(filter, modelField) {
let type = 'equal';

if(filter && filter.type !== undefined)
({ type } = filter);
else if(modelField && modelField.type !== undefined)
({ type } = modelField);

return type;
}

/**
* Returns the field to use in filter, if is defined in the Model returns
* the field set, else returns the col given in the filter
* @param fieldName {String} Field defined in params
* @param modelField {Object} Model fields to compare key by fieldName
* @returns {string} Field to use in filter, if exists in Model returns it else will be used as it comes
*/
static getFieldToCompare(fieldName, modelField) {
return (modelField[fieldName] && modelField[fieldName].field) ? modelField[fieldName].field : fieldName;
}
}

module.exports = MongoDBFilterWrapper;
13 changes: 10 additions & 3 deletions lib/mongodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const MongoDBError = require('./mongodb-error');

const ConfigValidator = require('./config-validator');

const MongoDBFilterWrapper = require('./mongodb-filter-wrapper');

const DEFAULT_LIMIT = 500;

const MONGODB_DEFAULT_PROTOCOL = 'mongodb://';
Expand Down Expand Up @@ -200,7 +202,7 @@ class MongoDB {

const page = params.page || 1;

const filters = params.filters || {};
const filters = MongoDBFilterWrapper.process(model, params.filters);

const order = params.order || { $natural: 1 };

Expand All @@ -217,7 +219,12 @@ class MongoDB {
.toArray();

model.lastQueryEmpty = !res.length;
model.totalsParams = { ...params };
model.totalsParams = {
limit,
page,
filters,
order
};

return res.map(this.prepareFieldsForOutput);

Expand Down Expand Up @@ -538,7 +545,7 @@ class MongoDB {
/**
* Multi remove items from the database
* @param {Model} model Model instance
* @param {filter} filter mongodb filter
* @param {{filter: {value: {in: string[]}}}} filter mongodb filter
* @returns {Number} deleted count
*/
async multiRemove(model, filter) {
Expand Down

0 comments on commit 52ecd79

Please sign in to comment.