parent
55a25bcb32
commit
6302c31079
@ -1,3 +1,4 @@
|
||||
.idea/*
|
||||
.env
|
||||
node_modules*
|
||||
set.js
|
||||
|
@ -1,89 +0,0 @@
|
||||
/**
|
||||
* @module flitter-orm/src/filter/FilterProxy
|
||||
*/
|
||||
|
||||
const Proxy = require('../model/Proxy')
|
||||
|
||||
/**
|
||||
* A proxy that applies the provided filter to the common model query methods.
|
||||
* @extends module:flitter-orm/model/Proxy~Proxy
|
||||
*/
|
||||
class FilterProxy extends Proxy {
|
||||
constructor(model, filter) {
|
||||
super(model)
|
||||
|
||||
/**
|
||||
* The filter to apply.
|
||||
* @type {module:flitter-orm/src/filter/Filter~Filter}
|
||||
*/
|
||||
this.filter = filter
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#cursor}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {mongodb/Cursor}
|
||||
*/
|
||||
cursor(params = {}, opts = {}) {
|
||||
const filter = this.filter.clone()
|
||||
filter.absorb(params)
|
||||
return super.cursor(filter.write(), opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#find}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {Array<module:flitter-orm/src/model/Model~Model>}
|
||||
*/
|
||||
find(params = {}, opts = {}) {
|
||||
const filter = this.filter.clone()
|
||||
filter.absorb(params)
|
||||
return super.find(filter.write(), opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#findOne}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {module:flitter-orm/src/model/Model~Model}
|
||||
*/
|
||||
findOne(params = {}, opts = {}) {
|
||||
const filter = this.filter.clone()
|
||||
filter.absorb(params)
|
||||
return super.findOne(filter.write(), opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#deleteOne}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {*}
|
||||
*/
|
||||
deleteOne(params = {}, opts = {}) {
|
||||
const filter = this.filter.clone()
|
||||
filter.absorb(params)
|
||||
return super.deleteOne(filter.write(), opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#deleteMany}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {*}
|
||||
*/
|
||||
deleteMany(params = {}, opts = {}) {
|
||||
const filter = this.filter.clone()
|
||||
filter.absorb(params)
|
||||
return super.deleteMany(filter.write(), opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#count}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {number}
|
||||
*/
|
||||
count(params = {}, opts = {}) {
|
||||
const filter = this.filter.clone()
|
||||
filter.absorb(params)
|
||||
return super.count(filter.write(), opts)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = FilterProxy
|
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @module flitter-orm/src/model/CursorBuilder
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wrapper for building up layers to apply to a cursor when it is created.
|
||||
*/
|
||||
class CursorBuilder {
|
||||
/**
|
||||
* Functions that modify the cursor, in order.
|
||||
* The functions take one parameter, the cursor, and return nothing.
|
||||
* @type {Array<function>}
|
||||
*/
|
||||
layers = []
|
||||
|
||||
/**
|
||||
* Apply the builder's layers to a cursor.
|
||||
* @param {mongodb/Cursor} cursor
|
||||
* @returns {Promise<mongodb/Cursor>}
|
||||
*/
|
||||
async apply(cursor) {
|
||||
for ( const layer of this.layers ) {
|
||||
await layer(cursor)
|
||||
}
|
||||
|
||||
return cursor
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a layer that sorts the cursor.
|
||||
* @param {Array<string>} sorts - array of sorts - e.g. '+field'/'-field'/'field'
|
||||
*/
|
||||
sort(sorts) {
|
||||
const sort_obj = {}
|
||||
for ( const sort of sorts ) {
|
||||
if ( sort.startsWith('-') ) {
|
||||
sort_obj[sort.substr(1)] = -1
|
||||
} else if ( sort.startsWith('+') ) {
|
||||
sort_obj[sort.substr(1)] = 1
|
||||
} else {
|
||||
sort_obj[sort] = 1
|
||||
}
|
||||
}
|
||||
|
||||
this.layers.push(cursor => {
|
||||
cursor.sort(sort_obj)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a layer that limits the cursor.
|
||||
* @param {number} to - max number of records
|
||||
*/
|
||||
limit(to) {
|
||||
this.layers.push(cursor => {
|
||||
cursor.limit(to)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a layer that applies a filter to the cursor.
|
||||
* @param {module:flitter-orm/src/filter/Filter~Filter} filter
|
||||
*/
|
||||
filter(filter) {
|
||||
this.layers.push(cursor => {
|
||||
cursor.filter(filter.write())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = CursorBuilder
|
@ -1,74 +0,0 @@
|
||||
/**
|
||||
* @module flitter-orm/src/model/Proxy
|
||||
*/
|
||||
|
||||
/**
|
||||
* A class for creating proxies in front of a model with
|
||||
* relevant information.
|
||||
* @class
|
||||
*/
|
||||
class Proxy {
|
||||
constructor(model) {
|
||||
/**
|
||||
* The model to proxy.
|
||||
* @type {module:flitter-orm/src/model/Model~Model}
|
||||
*/
|
||||
this.model = model
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#cursor}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {mongodb/Cursor}
|
||||
*/
|
||||
cursor(opts = {}) {
|
||||
return this.model.cursor({}, opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#find}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {Array<module:flitter-orm/src/model/Model~Model>}
|
||||
*/
|
||||
find(filter = {}, opts = {}) {
|
||||
return this.model.find(filter, opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#findOne}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {module:flitter-orm/src/model/Model~Model}
|
||||
*/
|
||||
findOne(filter = {}, opts = {}) {
|
||||
return this.model.findOne(filter, opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#deleteOne}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {*}
|
||||
*/
|
||||
deleteOne(filter = {}, opts = {}) {
|
||||
return this.model.deleteOne(filter, opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#deleteMany}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {*}
|
||||
*/
|
||||
deleteMany(filter = {}, opts = {}) {
|
||||
return this.model.deleteMany(filter, opts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the filter and call {@link module:flitter-orm/src/model/Model~Model#count}.
|
||||
* @param {object} [opts = {}] - optional arguments
|
||||
* @returns {number}
|
||||
*/
|
||||
count(filter = {}, opts = {}) {
|
||||
return this.model.count(filter, opts)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = Proxy
|
@ -1,100 +0,0 @@
|
||||
/**
|
||||
* @module flitter-orm/src/model/WithProxy
|
||||
*/
|
||||
|
||||
const Proxy = require('./Proxy')
|
||||
|
||||
/**
|
||||
* Proxy that pre-loads model relationships on query.
|
||||
* @extends module:flitter-orm/src/model/Proxy~Proxy
|
||||
*/
|
||||
class WithProxy extends Proxy {
|
||||
/**
|
||||
* List of relationship names to preload.
|
||||
* @private
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
#relationships = []
|
||||
|
||||
/**
|
||||
* Instantiate the proxy.
|
||||
* @param {module:flitter-orm/src/model/Model~Model} model - static CLASS of the model we're proxying
|
||||
* @param {Array<string>} [with_relationships = []] - relationship names to preload
|
||||
*/
|
||||
constructor(model, with_relationships = []) {
|
||||
super(model)
|
||||
this.#relationships = with_relationships
|
||||
}
|
||||
|
||||
/**
|
||||
* Find multiple records matching the provided filter. Relationships will be pre-loaded.
|
||||
* @param {object} [filter = {}] - some filter parameters
|
||||
* @param {object} [opts = {}] - optional MongoDB arguments
|
||||
* @returns {Promise<Array<module:flitter-orm/src/model/Model~Model>>}
|
||||
*/
|
||||
async find(filter = {}, opts = {}) {
|
||||
const results = await super.find(filter, opts)
|
||||
const promises = results.map(result => this._load_relationships_for(result))
|
||||
await Promise.all(promises)
|
||||
return results
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a single record matching the provided filter. Relationships will be pre-loaded.
|
||||
* @param {object} [filter = {}] - some filter parameters
|
||||
* @param {object} [opts = {}] - optional MongoDB arguments
|
||||
* @returns {Promise<module:flitter-orm/src/model/Model~Model>}
|
||||
*/
|
||||
async findOne(filter = {}, opts = {}) {
|
||||
const result = await super.findOne(filter, opts)
|
||||
await this._load_relationships_for(result)
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-load the relationships configured in this proxy for the
|
||||
* provided model instance.
|
||||
* @param {module:flitter-orm/src/model/Model~Model} model
|
||||
* @returns {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _load_relationships_for(model) {
|
||||
for ( const relation of this.#relationships ) {
|
||||
if ( this._is_getter(model, relation) ) {
|
||||
await model[relation]
|
||||
} else if ( typeof model[relation] === 'function' ) {
|
||||
await model[relation]()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified property name on the object is a virtual getter.
|
||||
* @param {object|function} object - the object to check
|
||||
* @param {string} property - the name of the property
|
||||
* @returns {boolean} - true if object[property] is a getter
|
||||
* @private
|
||||
*/
|
||||
_is_getter(object, property) {
|
||||
return !!this._getPropertyDescriptor(object, property).get
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ancestral property descriptor for the specified object.
|
||||
* This is like 'getOwnPropertyDescriptor', but it searches the entire
|
||||
* prototype chain.
|
||||
* @param {object|function} object - the object to search
|
||||
* @param {string} property - the name of the property
|
||||
* @returns {PropertyDescriptor}
|
||||
* @private
|
||||
*/
|
||||
_getPropertyDescriptor(object, property) {
|
||||
let descending
|
||||
do {
|
||||
descending = Object.getOwnPropertyDescriptor(object, property)
|
||||
} while ( !descending && (object = Object.getPrototypeOf(object)) )
|
||||
return descending
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = WithProxy
|
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @module flitter-orm/src/proxy/model/FilterProxy
|
||||
*/
|
||||
|
||||
const ModelProxy = require('./ModelProxy')
|
||||
|
||||
/**
|
||||
* Proxy that applies a filter to the reference's result set.
|
||||
* @extends module:flitter-orm/src/proxy/model/ModelProxy
|
||||
*/
|
||||
class FilterProxy extends ModelProxy {
|
||||
/**
|
||||
* Instantiate the proxy.
|
||||
* @param {module:flitter-orm/src/model/Model~Model|module:flitter-orm/src/proxy/model/ModelProxy~ModelProxy} model - proxy ref
|
||||
* @param {module:flitter-orm/src/filter/Filter~Filter} filter - the filter to apply
|
||||
*/
|
||||
constructor(model, filter) {
|
||||
super(model)
|
||||
this.builder.filter(filter)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = FilterProxy
|
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @module flitter-orm/src/proxy/model/LimitProxy
|
||||
*/
|
||||
|
||||
const ModelProxy = require('./ModelProxy')
|
||||
|
||||
/**
|
||||
* Proxy that limits the number of records in the reference's result set.
|
||||
* @extends module:flitter-orm/src/proxy/model/ModelProxy~ModelProxy
|
||||
*/
|
||||
class LimitProxy extends ModelProxy {
|
||||
/**
|
||||
* Instantiate the proxy
|
||||
* @param {module:flitter-orm/src/model/Model~Model|module:flitter-orm/src/proxy/model/ModelProxy~ModelProxy} model - proxy ref
|
||||
* @param {number} limit - the number to limit to
|
||||
*/
|
||||
constructor(model, limit) {
|
||||
super(model)
|
||||
this.builder.limit(limit)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = LimitProxy
|
@ -0,0 +1,155 @@
|
||||
/**
|
||||
* @module flitter-orm/src/proxy/model/ModelProxy
|
||||
*/
|
||||
|
||||
const CursorBuilder = require('../../model/CursorBuilder')
|
||||
const { ObjectId } = require('mongodb')
|
||||
|
||||
/**
|
||||
* A proxy class for adding restrictive layers to a model.
|
||||
*/
|
||||
class ModelProxy {
|
||||
/**
|
||||
* Instantiates the proxy.
|
||||
* @param {module:flitter-orm/src/model/Model~Model|module:flitter-orm/src/proxy/model/ModelProxy~ModelProxy} reference - the parent reference
|
||||
*/
|
||||
constructor(reference) {
|
||||
/**
|
||||
* The cursor builder applied by this proxy.
|
||||
* @type {module:flitter-orm/src/model/CursorBuilder~CursorBuilder}
|
||||
*/
|
||||
this.builder = new CursorBuilder()
|
||||
|
||||
/**
|
||||
* The parent reference of this proxy.
|
||||
* @type {module:flitter-orm/src/model/Model~Model|module:flitter-orm/src/proxy/model/ModelProxy~ModelProxy}
|
||||
*/
|
||||
this.reference = reference
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection instance for the proxy reference.
|
||||
* @returns {Promise<mongodb/Collection>}
|
||||
* @private
|
||||
*/
|
||||
async __collection() {
|
||||
return this.reference.__collection()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cursor for the reference, and apply the proxy's builder.
|
||||
* @param {object} [filter = {}] - mongodb filter options
|
||||
* @param {object} [opts = {}] - optional mongodb params
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
async cursor(filter = {}, opts = {}) {
|
||||
const cursor = await this.reference.cursor(filter, opts)
|
||||
return await this.builder.apply(cursor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a set of records subject to this proxy's restrictions.
|
||||
* @param {object} [filter = {}] - mongodb filters to apply
|
||||
* @param {object} [opts = {}] - optional mongodb params
|
||||
* @returns {Promise<Array<module:flitter-orm/src/model/Model~Model>>}
|
||||
*/
|
||||
async find(filter = {}, opts = {}) {
|
||||
const cursor = await this.cursor(filter, opts)
|
||||
return this.reference.from_cursor(cursor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a single record subject to this proxy's restrictions.
|
||||
* @param {object} [filter = {}] - mongodb filters to apply
|
||||
* @param {object} [opts = {}] - optional mongodb params
|
||||
* @returns {Promise<module:flitter-orm/src/model/Model~Model>}
|
||||
*/
|
||||
async findOne(filter, opts) {
|
||||
const cursor = await this.cursor(filter, opts)
|
||||
return (await this.reference.from_cursor(cursor.limit(1)))[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the set of records subject to this proxy's restrictions.
|
||||
* @param {object} [filter = {}] - mongodb filters to apply
|
||||
* @param {object} [opts = {}] - optional mongodb params
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async deleteMany(filter, opts) {
|
||||
const cursor = await this.cursor(filter, opts)
|
||||
const ids = (await cursor.toArray()).map(x => x._id)
|
||||
await this._bulk_delete(ids)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single record subject to this proxy's restrictions.
|
||||
* @param {object} [filter = {}] - mongodb filters to apply
|
||||
* @param {object} [opts = {}] - optional mongodb params
|
||||
* @returns {Promise<module:flitter-orm/src/model/Model~Model>} - the deleted model
|
||||
*/
|
||||
async deleteOne(filter, opts) {
|
||||
const cursor = await this.cursor(filter, opts)
|
||||
const rec = (await this.reference.from_cursor(cursor))[0]
|
||||
return rec.delete()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of model instances from the provided cursor.
|
||||
* @param {mongodb/cursor} cursor
|
||||
* @returns {Promise<Array<module:flitter-orm/src/model/Model~Model>>}
|
||||
*/
|
||||
async from_cursor(cursor) {
|
||||
return this.reference.from_cursor(cursor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all model instances matched by the provided IDs
|
||||
* @param {Array<string|mongodb/ObjectId>} ids
|
||||
* @returns {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async _bulk_delete(ids) {
|
||||
ids = ids.map(x => {
|
||||
if ( typeof x === 'string' ) return ObjectId(x)
|
||||
return x
|
||||
})
|
||||
const coll = await this.__collection()
|
||||
await coll.deleteMany({_id: {$in: ids}})
|
||||
}
|
||||
|
||||
// ----------- Helpers for building proxy chains --------------- //
|
||||
|
||||
/**
|
||||
* Limit the results to a specified number of records.
|
||||
* @param {number} to
|
||||
* @returns {module:flitter-orm/src/proxy/model/LimitProxy~LimitProxy}
|
||||
*/
|
||||
limit(to) {
|
||||
const LimitProxy = require('./LimitProxy')
|
||||
return new LimitProxy(this, to)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the result set by the provided field(s).
|
||||
* @param {string} sorts... - variable number of fields to sort
|
||||
* @example
|
||||
* Model.sort('+first_name', '+last_name', '-create_date')
|
||||
* @returns {module:flitter-orm/src/proxy/model/SortProxy~SortProxy}
|
||||
*/
|
||||
sort(...sorts) {
|
||||
const SortProxy = require('./SortProxy')
|
||||
return new SortProxy(this, sorts)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a filter object whose reference is this proxy.
|
||||
* @param {module:flitter-orm/src/model/Model|module:flitter-orm/src/proxy/model/ModelProxy} ref
|
||||
* @returns {Promise<module:flitter-orm/src/filter/Filter~Filter>}
|
||||
*/
|
||||
async filter(ref = false) {
|
||||
if ( !ref ) ref = this
|
||||
return this.reference.filter(ref)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = ModelProxy
|
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @module flitter-orm/src/proxy/model/SortProxy
|
||||
*/
|
||||
|
||||
const ModelProxy = require('./ModelProxy')
|
||||
|
||||
/**
|
||||
* Proxy that applies a number of sorts to the cursor.
|
||||
* @extends module:flitter-orm/src/proxy/model/ModelProxy~ModelProxy
|
||||
*/
|
||||
class SortProxy extends ModelProxy {
|
||||
/**
|
||||
* Instantiate the proxy.
|
||||
* @param {module:flitter-orm/src/model/Model~Model|module:flitter-orm/src/proxy/model/ModelProxy~ModelProxy} model - proxy ref
|
||||
* @param {Array<string>} sorts - array of sorts ('-field', '+field', or 'field')
|
||||
*/
|
||||
constructor(model, sorts) {
|
||||
super(model)
|
||||
this.builder.sort(sorts)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = SortProxy
|
Loading…
Reference in new issue