/**
* @module flitter-orm/src/filter/Filter
*/
const Focus = require('./Focus')
const FilterProxy = require('../proxy/model/FilterProxy')
const { ObjectId } = require('mongodb')
/**
* An accumulating MongoDB filter object.
* @class
*/
class Filter {
/**
* The root query object.
* @type {object}
* @private
*/
_root = {$and: []}
constructor(model) {
/**
* The model in question.
* @type: {module:flitter-orm/src/model/Model}
*/
this._model = model
}
/**
* Clone this filter into a new filter instance.
* Ensures that modifications to the cloned filter do
* no affect this existing filter.
* @returns {module:flitter-orm/src/filter/Filter~Filter}
*/
clone() {
const filter = new this.constructor(this._model)
filter.absorb(this._root)
return filter
}
/**
* Focus on a particular field.
* @param {string} field
* @returns {module:flitter-orm/src/filter/Focus~Focus}
*/
field(field) {
return new Focus(this, field)
}
/**
* End the filter and apply it to the model's proxy.
* @returns {module:flitter-orm/src/proxy/model/FilterProxy~FilterProxy}
*/
end() {
return new FilterProxy(this._model, this)
}
/**
* Asserts that a field must equal the value.
* @param {string} field
* @param {*} value
* @returns {module:flitter-orm/src/filter/Filter~Filter}
*/
equal(field, value) {
return this.field(field).equal(value).end()
}
/**
* Asserts that a field must be less than the value.
* @param {string} field
* @param {*} value
* @returns {module:flitter-orm/src/filter/Filter~Filter}
*/
less_than(field, value) {
return this.field(field).less_than(value).end()
}
/**
* Asserts that a field must be greater than the value.
* @param {string} field
* @param {*} value
* @returns {module:flitter-orm/src/filter/Filter~Filter}
*/
greater_than(field, value) {
return this.field(field).greater_than(value).end()
}
/**
* Asserts that a field must be less than or equal to the value.
* @param {string} field
* @param {*} value
* @returns {module:flitter-orm/src/filter/Filter~Filter}
*/
less_than_equal(field, value) {
return this.field(field).less_than_equal(value).end()
}
/**
* Asserts that a field must be greater than or equal to the value.
* @param {string} field
* @param {*} value
* @returns {module:flitter-orm/src/filter/Filter~Filter}
*/
greater_than_equal(field, value) {
return this.field(field).greater_than_equal(value).end()
}
/**
* Asserts that a field must be in the array of options for the value.
* @param {string} field
* @param {*} value
* @returns {module:flitter-orm/src/filter/Filter~Filter}
*/
in(field, value) {
return this.field(field).in(value).end()
}
/**
* Write the accumulated filter to a MongoDB compatible filter object.
* @returns {object}
*/
write() {
if ( this._root.$and.length < 1 ) return {}
return this.__exec_resolve(this._root)
}
/**
* Resolve the filter object from structures to primitives.
* @param {object} object
* @returns {object}
* @private
*/
__exec_resolve(object) {
if ( Array.isArray(object) ) {
const return_arr = []
for ( const elem of object ) {
const val = typeof elem === 'function' ? elem() : elem
return_arr.push(this.__exec_resolve(val))
}
return return_arr
} else if ( typeof object === 'object' && !(object instanceof Date) && !(object instanceof ObjectId) ) {
const return_obj = {}
for ( const prop in object ) {
const elem = object[prop]
const val = typeof elem === 'function' ? elem() : elem
return_obj[prop] = this.__exec_resolve(val)
}
return return_obj
} else if ( typeof object === 'function' ) {
return this.__exec_resolve(object())
} else {
return object
}
}
/**
* Merge this filter with another.
* @param {module:flitter-orm/src/filter/Filter~Filter} other
* @returns {module:flitter-orm/src/filter/Filter~Filter}
*/
absorb(other) {
if ( other._root ) {
this._root.$and = this._root.$and.concat(other._root.$and)
} else {
for ( const key in other ) {
if ( key === '$and' ) {
this._root.$and = this._root.$and.concat(other.$and)
} else {
this._root.$and.push({ [key]: other[key] })
}
}
}
return this
}
/**
* Callback to break a field's focus and merge the filters for that field into this filter.
* @param {module:flitter-orm/src/filter/Focus~Focus} focus
* @returns {module:flitter-orm/src/filter/Filter~Filter}
* @private
*/
_unfocus(focus) {
this._root.$and.push({ [focus._field]: focus._root })
return this
}
}
module.exports = exports = Filter