orm/src/proxy/model/ModelProxy.js

/**
 * @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
     * @param {module:flitter-orm/src/model/CursorBuilder~CursorBuilder} [builder] - optionally, the cursor builder to use
     */
    constructor(reference, builder = new CursorBuilder()) {
        /**
         * The cursor builder applied by this proxy.
         * @type {module:flitter-orm/src/model/CursorBuilder~CursorBuilder}
         */
        this.builder = builder

        /**
         * 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)
        let ids = (await cursor.toArray()).map(x => x._id)
        ids = ids.map(x => {
            if ( typeof x === 'string' ) return ObjectId(x)
            return x
        })
        const coll = await this.__collection()
        await coll.deleteMany({_id: {$in: 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)
    }

    // ----------- 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