UNPKG

mongoose-query-toolkit

Version:

A toolkit for handling Mongoose queries with support for search, filtering, pagination, sorting, field selection, and population

112 lines (111 loc) 4.02 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.QueryToolkit = void 0; class QueryToolkit { constructor(model, options = {}) { this.model = model; this.populatableFields = []; this.searchFields = options.searchFields || []; this.filterableFields = options.filterableFields || []; this.selectableFields = options.selectableFields || []; this.populatableFields = options.populatableFields || []; } buildSearchQuery(q) { if (!q || !this.searchFields.length) return {}; return { $or: this.searchFields.map((field) => ({ [field]: { $regex: q, $options: 'i' }, })), }; } buildFilterQuery(options) { const filterQuery = {}; for (const key of this.filterableFields) { if (options[key] !== undefined) { filterQuery[key] = options[key]; } } return filterQuery; } parseSortString(sort) { const sortQuery = {}; if (!sort) return sortQuery; sort.split(',').forEach((field) => { const order = field.startsWith('-') ? -1 : 1; const fieldName = field.startsWith('-') ? field.substring(1) : field; sortQuery[fieldName] = order; }); return sortQuery; } buildSelectQuery(select) { if (!select) return null; // If selectableFields is empty, allow all fields if (this.selectableFields.length === 0) { return select.replace(/,/g, ' '); } // Filter fields based on selectableFields const fields = select.split(','); const validFields = fields.filter(field => { // Handle exclusion fields (fields with minus prefix) const fieldName = field.startsWith('-') ? field.substring(1) : field; return this.selectableFields.includes(fieldName); }); return validFields.join(' '); } buildPopulateFields(populate) { if (!populate) return []; // Convert comma-separated fields to array const fields = populate.split(',').map(field => field.trim()); // If populatableFields is empty, allow all fields if (this.populatableFields.length === 0) { return fields; } // Filter fields based on populatableFields return fields.filter(field => this.populatableFields.includes(field)); } async findWithOptions(options = {}) { const { q, page = 1, limit = 10, sort, select, populate, ...filterOptions } = options; const skip = (page - 1) * limit; const query = { ...this.buildSearchQuery(q || ''), ...this.buildFilterQuery(filterOptions), }; const sortQuery = this.parseSortString(sort); const selectQuery = this.buildSelectQuery(select); const populateFields = this.buildPopulateFields(populate); let findQuery = this.model.find(query); if (sortQuery && Object.keys(sortQuery).length > 0) { findQuery = findQuery.sort(sortQuery); } if (selectQuery) { findQuery = findQuery.select(selectQuery); } // Apply populate fields populateFields.forEach(field => { // Using type assertion to handle the TypeScript error findQuery = findQuery.populate(field); }); const [docs, totalDocs] = await Promise.all([ findQuery .skip(skip) .limit(limit) .exec(), this.model.countDocuments(query), ]); const totalPages = Math.ceil(totalDocs / limit); return { docs, totalDocs, limit, page, totalPages, hasNextPage: page < totalPages, hasPrevPage: page > 1, }; } } exports.QueryToolkit = QueryToolkit;