Source: helpers/database/queries.js

const dbHelpers = require('./dbHelpers');

/**
 * A function to make query results readable by converting MongoDB documents to plain JavaScript objects.
 * @function
 * @async
 * @param {Object} options - Additional options for query results (not used in the function).
 * @returns {Array} - An array of plain JavaScript objects representing the query results.
 * @throws {Error} - Throws an error if there is an issue converting the documents.
 */
async function readable(options) {
    const {
        // Futures options can be placed here
    } = options || {};

    try {
        const result = await this.exec();
        return result.map(doc => doc.toObject());
    } catch(err) {
        return new Error.Log(err).append('')
    }

};

/**
 * Extracts the update properties from the current update operation object.
 * @function
 * @returns {Object} - An object containing the update properties to be applied to the document.
 */
function getUpdateProps() {
    const updateProps = {...this._update.$set};

    delete updateProps.modifiedAt;
    return updateProps;
}

/**
 * Paginates query results based on the provided options.
 * @function
 * @param {Object} options - Pagination options including 'views', 'page', and 'seeMore'.
 * @returns {Object} - The updated query object with pagination applied.
 * @throws {Error} - Throws an error if there is an issue with the pagination query.
 */
function paginate(options) {
    let {
        views,
        page,
        seeMore
    } = options || {};

    try {
        if (views && !seeMore) this.limit(views);
        if (!page) page = 1;

        if (!seeMore) {
            this.options.skip = views * (page - 1);
        } else {
            if (views) this.limit(views * page);
        }

        return this;
    } catch(err) {
        throw new Error.Log(err).append('database.paginate_query');
    }
}

/**
 * Populates all specified relational fields in the query results.
 * @function
 * @param {Object} options - Population options including 'select', 'exclude', and 'levels'.
 * @returns {Object} - The updated query object with relational fields populated.
 * @throws {Error} - Throws an error if there is an issue populating relational fields.
 */
function populateAll(options) {
    try {
        let {
            select,
            exclude,
            levels
        } = options || {};
        const relFields = dbHelpers.findRelFields(this.schema, exclude, levels);
        let populated = this;

        relFields.map(field=>{
            const selection = select && select[field] && select[field].join(' ');
            populated = populated.populate(field);
        });
    
        return populated;
    } catch(err) {
        return new Error.Log(err).append('database.populating_document', this._collection.collectionName);
    }
}

/**
 * Initializes query results by converting MongoDB documents to objects.
 * @function
 * @async
 * @param {boolean} populate - Flag indicating whether to populate related fields in the documents.
 * @returns {Array|Object} - An array of initialized objects or a single initialized object representing the query results.
 * @throws {Error} - Throws an error if there is an issue initializing the documents.
 */
async function initialize(populate){
    let docs;

    if (populate) {
        docs = await this.exec().defaultPopulate();
    } else {
        docs = await this.exec();
    }

    let result = [];

    try {
        if(Array.isArray(docs)) {
            docs.map(doc=>{
                const initialized = doc.initialize();
                result.push(initialized);
            });
        } else if (docs && docs.initialize) {
            result = docs.initialize();
        }
    } catch(err) {
        throw new Error.Log(err);
    }

    return result;
}

module.exports = {
    readable,
    paginate,
    populateAll,
    initialize,
    getUpdateProps
};