UNPKG

@onesy/mongo

Version:

Utils for easier using of mongodb library.

848 lines (823 loc) 36.2 kB
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; const _excluded = ["total", "sort", "limit", "skip"], _excluded2 = ["total", "sort", "limit", "skip", "projection"], _excluded3 = ["projection"], _excluded4 = ["add_date"], _excluded5 = ["update_date"], _excluded6 = ["add_date", "update_date"], _excluded7 = ["add_date"], _excluded8 = ["update_date"]; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } import * as mongodb from 'mongodb'; import is from '@onesy/utils/is'; import copy from '@onesy/utils/copy'; import wait from '@onesy/utils/wait'; import getObjectValue from '@onesy/utils/getObjectValue'; import setObjectValue from '@onesy/utils/setObjectValue'; import { Query, getMongoMatch, MongoResponse } from '@onesy/models'; import { OnesyMongoError, DeveloperError } from '@onesy/errors'; import OnesyDate from '@onesy/date/OnesyDate'; import duration from '@onesy/date/duration'; import Mongo from './Mongo'; import OnesyMongo from './OnesyMongo'; export class BaseCollection { constructor(collectionName, mongo, Model, defaults) { this.collectionName = collectionName; this.mongo = mongo; this.Model = Model; this.defaults = defaults; _defineProperty(this, "collections", {}); _defineProperty(this, "onesyLog", void 0); if (!(mongo && mongo instanceof Mongo)) throw new OnesyMongoError(`Mongo instance is required`); if (!collectionName) throw new OnesyMongoError(`Collection name is required`); // log inherit from Mongo // so it can be configured on per use basis this.onesyLog = mongo.onesyLog; } get sort() { return { [this.sortProperty]: this.sortAscending }; } get sortProperty() { return 'added_at'; } get sortAscending() { return -1; } get addedProperty() { return 'added_at'; } get updatedProperty() { return 'updated_at'; } get projection() { return; } get db() { return new Promise(async resolve => { const db = await this.mongo.connection; return resolve(db); }); } async collection() { let name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.collectionName; let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const db = await this.db; if (!this.collections[name]) this.collections[name] = db.collection(name); const collections = await this.mongo.getCollections(); // Good to create a collection in advance if it doesn't exist atm // as it might fail if you wanna add a document within a transaction // on a non existing mongo collection atm if (!collections?.find(item => item.name === this.collectionName)) { const collection = await db.createCollection(this.collectionName, options); this.mongo.collections.push({ name: collection.collectionName }); // Add collection to Query model collections Query.collections.push(collection.collectionName); Query.keys.allowed.push(collection.collectionName); this.onesyLog.info(`${this.collectionName} collection created`); } return this.collections[name]; } async transaction(method) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { retries: 5, retriesWait: 140 }; if (!is('function', method)) throw new DeveloperError('First argument has to be a function'); const transactionOptions = { readPreference: mongodb.ReadPreference.primary, readConcern: { level: 'local' }, writeConcern: { w: 'majority' } }; let response; const retriesTotal = is('number', options.retries) ? options.retries : 5; const retriesWait = is('number', options.retriesWait) ? options.retriesWait : 140; let retries = retriesTotal; let error; let codeName = 'WriteConflict'; while (codeName === 'WriteConflict' && retries > 0) { error = undefined; codeName = undefined; if (retries < retriesTotal) await wait(retriesWait); const session = this.mongo.client.startSession(); try { await session.withTransaction(async () => { response = await method(session); return response; }, transactionOptions); } catch (error_) { error = error_; codeName = error_.codeName || error_.message?.codeName; } finally { await session.endSession(); } retries--; } if (error) throw new DeveloperError(error); return response; } async count() { let query = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Query(); let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const defaults = this.getDefaults('count'); const response = await collection.countDocuments(_objectSpread(_objectSpread({}, defaults), this.query(query)), options); return this.response(start, collection, 'count', response); } catch (error) { this.response(start, collection, 'count'); throw new OnesyMongoError(error); } } async exists(query) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const defaults = this.getDefaults('exists'); const response = await collection.findOne(_objectSpread(_objectSpread({}, defaults), this.query(query)), _objectSpread({ projection: { _id: 1 } }, options)); return this.response(start, collection, 'exists', !!response); } catch (error) { this.response(start, collection, 'exists'); throw new OnesyMongoError(error); } } async find(query) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const { total, sort, limit, skip } = options, optionsOther = _objectWithoutProperties(options, _excluded); const defaults = this.getDefaults('find'); const optionsMongo = _objectSpread({}, optionsOther); if (!optionsMongo.projection) { optionsMongo.projection = BaseCollection.isOnesyQuery(query) && query.projection || this.projection; if (!optionsMongo.projection) delete optionsMongo.projection; } optionsMongo.sort = (BaseCollection.isOnesyQuery(query) ? query.sort : sort) || this.sort; optionsMongo.skip = (BaseCollection.isOnesyQuery(query) ? query.skip : skip) || 0; optionsMongo.limit = (BaseCollection.isOnesyQuery(query) ? query.limit : limit) || 15; const queryMongo = _objectSpread(_objectSpread({}, defaults), this.query(query)); const response_ = await collection.find(queryMongo, optionsMongo).toArray(); const response = new MongoResponse(response_); response.sort = optionsMongo.sort; response.size = response_.length; response.skip = optionsMongo.skip; response.limit = optionsMongo.limit; if (BaseCollection.isOnesyQuery(query) ? query.total : total) { response['total'] = await collection.find(queryMongo, { projection: { _id: 1 } }).count(); } return this.response(start, collection, 'find', response); } catch (error) { this.response(start, collection, 'find'); throw new OnesyMongoError(error); } } async findOne(query) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const defaults = this.getDefaults('findOne'); if (!options.projection) { options.projection = BaseCollection.isOnesyQuery(query) && query.projection || this.projection; if (!options.projection) delete options.projection; } const response = await collection.findOne(_objectSpread(_objectSpread({}, defaults), this.query(query)), options); return this.response(start, collection, 'findOne', response); } catch (error) { this.response(start, collection, 'findOne'); throw new OnesyMongoError(error); } } async aggregate() { let query = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Query(); let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const defaults = this.getDefaults('aggregate'); const response = await collection.aggregate([ // defaults ...(defaults || []), ...this.query(query)], _objectSpread(_objectSpread({}, Mongo.defaults.aggregateOptions), options)).toArray(); return this.response(start, collection, 'aggregate', response); } catch (error) { this.response(start, collection, 'aggregate'); throw new OnesyMongoError(error); } } async searchMany(query) { let additional = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { pre: [], prePagination: [], post: [], options: [], lookups: [] }; let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const { total: optionsTotal, sort: optionsSort, limit: optionsLimit = 15, skip: optionsSkip, projection: optionsProjection } = options, optionsOther = _objectWithoutProperties(options, _excluded2); const defaults = this.getDefaults('searchMany'); const optionsMongo = _objectSpread({}, optionsOther); const projection = (BaseCollection.isOnesyQuery(query) ? query.projection : optionsProjection) || this.projection; const sort = (BaseCollection.isOnesyQuery(query) ? query.sort : optionsSort) || this.sort; const { limit = optionsLimit, skip = optionsSkip, next, previous } = BaseCollection.isOnesyQuery(query) ? query : options; const hasPaginator = next || previous; const paginatorProperty = Object.keys(next || previous || {})[0]; const pre = additional.pre || []; const pre_pagination = additional.prePagination || []; const post = additional.post || []; const queries = { search: (BaseCollection.isOnesyQuery(query) ? query.queries.search[this.collectionName] : []) || [], api: (BaseCollection.isOnesyQuery(query) ? query.queries.api[this.collectionName] : []) || [], permissions: (BaseCollection.isOnesyQuery(query) ? query.queries.permissions[this.collectionName] : []) || [], aggregate: (BaseCollection.isOnesyQuery(query) ? query.queries.aggregate[this.collectionName] : []) || [] }; const queryMongo = [ // defaults ...(defaults || []), ...((BaseCollection.isOnesyQuery(query) ? query.query : query) || []), ...pre, ...queries.aggregate, // Search ...(queries.search.length ? getMongoMatch(queries.search, query.settings.type) : []), // API ...(queries.api.length ? getMongoMatch(queries.api) : []), // Permissions ...(queries.permissions.length ? getMongoMatch(queries.permissions, '$or') : []), ...pre_pagination]; const pipeline = [...queryMongo, // Next paginator ...(next ? getMongoMatch([next]) : []), // Previous paginator ...(previous ? getMongoMatch([previous]) : []), ...(hasPaginator ? [{ $sort: { [paginatorProperty]: next ? -1 : 1 } }] : []), ...(sort && !hasPaginator ? [{ $sort: sort }] : []), // Either skip or a paginator ...(query?.skip !== undefined && !hasPaginator ? [{ $skip: skip }] : []), // +1 so we know if there's a next page { $limit: limit + 1 }, ...(sort && hasPaginator ? [{ $sort: sort }] : []), ...(projection ? [{ $project: projection }] : []), ...post]; const response_ = await collection.aggregate(pipeline, _objectSpread(_objectSpread({}, Mongo.defaults.aggregateOptions), optionsMongo)).toArray(); // Add results and limit const objects = !hasPaginator || next || response_.length <= limit ? response_.slice(0, limit) : response_.slice(1, limit + 1); const first = objects[0]; const last = objects[objects.length - 1]; const response = new MongoResponse(objects); response.sort = sort; response.skip = skip; response.limit = limit; // Add hasNext, next, previous response['hasNext'] = previous || response_.length > objects.length; response['hasPrevious'] = !hasPaginator ? query.skip > 0 : next || response_.length > objects.length; if (last) response['next'] = OnesyMongo.createPaginator(last, [this.sortProperty], sort); if (first) response['previous'] = OnesyMongo.createPaginator(first, [this.sortProperty], sort, 'previous'); // lookups await this.lookups(response.response, additional.lookups, options.request); // options if (!!additional.options?.length) { const optionsResponse = await collection.aggregate([...queryMongo, ...additional.options.map(item => ['array', undefined].includes(item.version) ? { $unwind: `$${item.property}` } : undefined).filter(Boolean), ...additional.options.map(item => ({ $group: { _id: item.name, value: { $addToSet: item.property.startsWith('$') ? item.property : `$${item.property}` } } }))], _objectSpread(_objectSpread({}, Mongo.defaults.aggregateOptions), optionsMongo)).toArray(); const optionsMongoResponse = {}; optionsResponse.forEach(item => optionsMongoResponse[item._id] = item.value?.flatMap(item => item) || []); for (const optionName of Object.keys(optionsMongoResponse)) { const optionRequest = additional.options.find(item => item.name === optionName); if (optionRequest.lookup) await this.lookups(optionsMongoResponse[optionName], [optionRequest.lookup], options.request); } response.options = optionsMongoResponse; } // Count total only if it's requested by the query let total; if (BaseCollection.isOnesyQuery(query) ? query.total : optionsTotal) { const total_ = await collection.aggregate([...queryMongo, // Limit count for performance reasons { $limit: Mongo.defaults.limitCount }, { $group: { _id: null, count: { $sum: 1 } } }], _objectSpread(_objectSpread({}, Mongo.defaults.aggregateOptions), optionsMongo)).toArray(); total = total_[0] && total_[0].count; response['total'] = total || 0; } return this.response(start, collection, 'searchMany', response); } catch (error) { this.response(start, collection, 'searchMany'); throw new OnesyMongoError(error); } } async searchOne(query) { let additional = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { pre: [], post: [], lookups: [] }; let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const { projection: optionsProjection } = options, optionsOther = _objectWithoutProperties(options, _excluded3); const defaults = this.getDefaults('searchOne'); const optionsMongo = _objectSpread({}, optionsOther); const limit = 1; const projection = (BaseCollection.isOnesyQuery(query) ? query.projection : optionsProjection) || this.projection; const pre = additional.pre || []; const post = additional.post || []; const queries = { search: (BaseCollection.isOnesyQuery(query) ? query.queries.search[this.collectionName] : []) || [], api: (BaseCollection.isOnesyQuery(query) ? query.queries.api[this.collectionName] : []) || [], permissions: (BaseCollection.isOnesyQuery(query) ? query.queries.permissions[this.collectionName] : []) || [], aggregate: (BaseCollection.isOnesyQuery(query) ? query.queries.aggregate[this.collectionName] : []) || [] }; const queryMongo = [ // defaults ...(defaults || []), ...((BaseCollection.isOnesyQuery(query) ? query.query : query) || []), ...pre, ...queries.aggregate, // Search ...(queries.search.length ? getMongoMatch(queries.search, query.settings.type) : []), // API ...(queries.api.length ? getMongoMatch(queries.api) : []), // Permissions ...(queries.permissions.length ? getMongoMatch(queries.permissions, '$or') : [])]; const pipeline = [...queryMongo, { $limit: limit }, ...(projection ? [{ $project: projection }] : []), ...post]; const response = await collection.aggregate(pipeline, _objectSpread(_objectSpread({}, Mongo.defaults.aggregateOptions), optionsMongo)).toArray(); // lookups await this.lookups(response, additional.lookups, options.request); return this.response(start, collection, 'searchOne', response[0]); } catch (error) { this.response(start, collection, 'searchOne'); throw new OnesyMongoError(error); } } async addOne(value_) { let options_ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const options = _objectSpread({ add_date: true }, options_); const { add_date } = options, optionsMongo = _objectWithoutProperties(options, _excluded4); const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const value = BaseCollection.value(value_); if (!value) throw new OnesyMongoError(`No value provided`); if (add_date) setObjectValue(value, this.addedProperty || 'added_at', OnesyDate.utc.milliseconds); const response = await collection.insertOne(value, optionsMongo); return this.response(start, collection, 'addOne', _objectSpread({ _id: response.insertedId }, value)); } catch (error) { this.response(start, collection, 'addOne'); throw new OnesyMongoError(error); } } async updateOne(query, value) { let options_ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : { lookups: [] }; const options = _objectSpread({ update_date: true }, options_); const { update_date } = options, optionsMongo = _objectWithoutProperties(options, _excluded5); const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const defaults = this.getDefaults('updateOne'); if (value !== undefined && !is('object', value)) throw new OnesyMongoError(`Value has to be an object with update values`); if (is('object', value) && update_date) value[this.updatedProperty || 'updated_at'] = OnesyDate.utc.milliseconds; const update = {}; const operators = {}; // use both update values // and operator values in the same object Object.keys(value).forEach(item => { if (item.startsWith('$')) operators[item] = value[item];else update[item] = value[item]; }); const response = await collection.findOneAndUpdate(_objectSpread(_objectSpread({}, defaults), this.query(query)), _objectSpread(_objectSpread({}, operators), (!!Object.keys(update).length || operators['$set']) && { $set: _objectSpread(_objectSpread({}, operators['$set']), update) }), _objectSpread({ returnDocument: 'after' }, optionsMongo)); // lookups await this.lookups(response.value, options.lookups, options.request); return this.response(start, collection, 'updateOne', response.value); } catch (error) { this.response(start, collection, 'updateOne'); throw new OnesyMongoError(error); } } async removeOne(query) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const defaults = this.getDefaults('removeOne'); const response = await collection.findOneAndDelete(_objectSpread(_objectSpread({}, defaults), this.query(query)), options); return this.response(start, collection, 'removeOne', response.value); } catch (error) { this.response(start, collection, 'removeOne'); throw new OnesyMongoError(error); } } async updateOneOrAdd(query, value) { let options_ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; const options = _objectSpread({ add_date: true, update_date: true }, options_); const { add_date, update_date } = options, optionsMongo = _objectWithoutProperties(options, _excluded6); const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const defaults = this.getDefaults('updateOneOrAdd'); if (!is('object', value)) throw new OnesyMongoError(`Value has to be an object with properties and values`); if (update_date) value[this.updatedProperty || 'updated_at'] = OnesyDate.utc.milliseconds; let setOnInsert; if (add_date) setOnInsert = { [this.addedProperty || 'added_at']: OnesyDate.utc.milliseconds }; const response = await collection.findOneAndUpdate(_objectSpread(_objectSpread({}, defaults), this.query(query)), _objectSpread({ $set: value }, setOnInsert && { $setOnInsert: setOnInsert }), _objectSpread({ upsert: true, returnDocument: 'after' }, optionsMongo)); return this.response(start, collection, 'updateOneOrAdd', response.value); } catch (error) { this.response(start, collection, 'updateOneOrAdd'); throw new OnesyMongoError(error); } } async addMany(values_) { let options_ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const options = _objectSpread({ add_date: true }, options_); const { add_date } = options, optionsMongo = _objectWithoutProperties(options, _excluded7); const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { let values = values_.map(item => BaseCollection.value(item)); if (!values?.length) throw new OnesyMongoError(`Values have to be a non empty array`); if (add_date) values = values.map(item => { setObjectValue(item, this.addedProperty || 'added_at', OnesyDate.utc.milliseconds); return item; }); let response = await collection.insertMany(values, _objectSpread({ ordered: false }, optionsMongo)); if (!options.original) { const ids = Object.keys(response.insertedIds || {}).map(item => response.insertedIds?.[item]); response = values.filter(item => !!ids.find(id => item._id.toString() === id.toString())); } return this.response(start, collection, 'addMany', response); } catch (error) { this.response(start, collection, 'addMany'); throw new OnesyMongoError(error); } } async updateMany(query, value) { let options_ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; const options = _objectSpread({ update_date: true }, options_); const { update_date } = options, optionsMongo = _objectWithoutProperties(options, _excluded8); const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const defaults = this.getDefaults('updateMany'); if (value !== undefined && !is('object', value)) throw new OnesyMongoError(`Value has to be an object with properties and values`); if (is('object', value) && update_date) value[this.updatedProperty || 'updated_at'] = OnesyDate.utc.milliseconds; const update = {}; const operators = {}; // use both update values // and operator values in the same object Object.keys(value).forEach(item => { if (item.startsWith('$')) operators[item] = value[item];else update[item] = value[item]; }); const response = await collection.updateMany(_objectSpread(_objectSpread({}, defaults), this.query(query)), _objectSpread(_objectSpread({}, operators), (!!Object.keys(update).length || operators['$set']) && { $set: _objectSpread(_objectSpread({}, operators['$set']), update) }), _objectSpread({}, optionsMongo)); return this.response(start, collection, 'updateMany', response); } catch (error) { this.response(start, collection, 'updateMany'); throw new OnesyMongoError(error); } } async removeMany(query) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { const defaults = this.getDefaults('removeMany'); const response = await collection.deleteMany(_objectSpread(_objectSpread({}, defaults), this.query(query)), _objectSpread({ ordered: false }, options)); return this.response(start, collection, 'removeMany', response); } catch (error) { this.response(start, collection, 'removeMany'); throw new OnesyMongoError(error); } } async bulkWrite() { let values = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; let options_ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const options = _objectSpread({}, options_); const collection = await this.collection(); const start = OnesyDate.utc.milliseconds; try { if (!values?.length) throw new OnesyMongoError(`Values have to be a non empty array`); const response = await collection.bulkWrite(values, _objectSpread({ ordered: false }, options)); return this.response(start, collection, 'bulkWrite', response); } catch (error) { this.response(start, collection, 'bulkWrite'); throw new OnesyMongoError(error); } } async lookups(value_, lookups, request) { const value = is('array', value_) ? value_ : [value_]; if (!!value.length && !!lookups?.length) { for (const lookup of lookups) { try { if (lookup.objects) { const ids = []; // Get the ids to lookup value.forEach(item => { const valueProperty = !lookup.property ? item : getObjectValue(item, lookup.property); ids.push(...this.getLookupIDs(valueProperty)); }); if (!!ids.length) { // Search objects const query = lookup.query || []; if (BaseCollection.isOnesyQuery(query)) { if (is('array', query.query)) { query.query.unshift({ $match: { _id: { $in: ids } } }); if (lookup.projection) { query.query.push({ $project: _objectSpread({}, lookup.projection) }); } } } else { query.unshift({ $match: { _id: { $in: ids } } }); if (lookup.projection) { query.push({ $project: _objectSpread({}, lookup.projection) }); } } const method = lookup.objects.aggregate.bind(lookup.objects); let response = await method(query, lookup.options); if ([true, undefined].includes(lookup.toObjectResponse)) { response = response.map(item => { if (item.toObjectResponse) return item.toObjectResponse(request); return item; }); } const responseMap = {}; response.forEach(item => { responseMap[(item._id || item.id).toString()] = item; }); // Update all the id objects value.forEach((item, index) => { const valueItem = this.updateLookupProperty(item, item, responseMap, lookup); if (!lookup.property && valueItem) value[index] = valueItem; }); } } } catch (error) { console.error(`Lookups error`, error); } } } } updateLookupProperty(mongoObject, object, responseMap, lookup) { let array = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; const valueProperty = array ? object : !lookup.property ? object : getObjectValue(object, lookup.property); // string if (is('string', valueProperty)) { const valueResponse = responseMap[valueProperty]; if (valueResponse !== undefined) { if (lookup.property) setObjectValue(mongoObject, lookup.property, valueResponse);else return valueResponse; } } // mongoDB ObjectId else if (mongodb.ObjectId.isValid(valueProperty)) { const valueResponse = responseMap[valueProperty?.toString()]; if (valueResponse !== undefined) { if (lookup.property) setObjectValue(mongoObject, lookup.property, valueResponse);else return valueResponse; } } // object else if (is('object', valueProperty)) { const id = valueProperty?.id || valueProperty?._id; const valueResponse = responseMap[id?.toString()]; const previous = copy(getObjectValue(mongoObject, lookup.property)); if (lookup.override) { lookup.override.forEach(item => { setObjectValue(valueResponse, item, getObjectValue(previous, item)); }); } if (valueResponse !== undefined) { if (lookup.property) setObjectValue(mongoObject, lookup.property, valueResponse);else return valueResponse; } } // array else if (is('array', valueProperty)) { valueProperty.forEach((valuePropertyItem, index) => { const lookupItem = _objectSpread({}, lookup); lookupItem.property = `${lookupItem.property || ''}${lookupItem.property ? '.' : ''}${index}`; this.updateLookupProperty(mongoObject, valuePropertyItem, responseMap, lookupItem, true); }); } } getLookupIDs(value) { const ids = []; if (is('string', value) || mongodb.ObjectId.isValid(value) || is('array', value) || is('object', value)) { if (is('string', value)) ids.push(new mongodb.ObjectId(value));else if (mongodb.ObjectId.isValid(value)) ids.push(value);else if (is('object', value)) ids.push(new mongodb.ObjectId(value?.id || value?._id));else if (is('array', value)) ids.push(...value.flatMap(item => this.getLookupIDs(item))); } return ids; } toModel(value) { if (!this.Model || [null, undefined].includes(value)) return value; return is('array', value) ? value.map(item => new this.Model(item, false)) : new this.Model(value, false); } response(start, collection, method, value, req) { if (is('number', start)) { const arguments_ = []; if (collection) arguments_.push(`Collection: ${collection.collectionName}`); if (method) arguments_.push(`Method: ${method}`); if (req?.id) arguments_.push(`Request ID: ${req.id}`); arguments_.push(`Duration: ${duration(OnesyDate.utc.milliseconds - start, true)}`); this.onesyLog.debug(...arguments_); } if (value && this.Model !== undefined) { switch (method) { case 'find': case 'searchMany': value.response = this.toModel(value.response); break; case 'findOne': case 'aggregate': case 'searchOne': case 'addOne': case 'updateOne': case 'removeOne': case 'updateOneOrAdd': return this.toModel(value); default: break; } } return value; } query(query) { let aggregate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (BaseCollection.isOnesyQuery(query)) { if (aggregate) { return [...(query?.query || []), ...(query?.queries?.aggregate?.[this.collectionName] || [])]; } else { return _objectSpread(_objectSpread({}, query?.query), query?.queries?.find?.[this.collectionName]); } } return aggregate ? query || [] : query; } getDefaults(method) { let value = ['aggregate', 'searchMany', 'searchOne'].includes(method) ? [] : {}; // static if (['aggregate', 'searchMany', 'searchOne'].includes(method)) { // query if (is('array', BaseCollection.defaults?.query)) value.push(...BaseCollection.defaults?.query); // queryArray if (is('array', BaseCollection.defaults?.queryArray)) value.push(...BaseCollection.defaults?.queryArray); // method if (is('array', BaseCollection.defaults?.[method])) value.push(...BaseCollection.defaults?.[method]); } else { // query if (is('object', BaseCollection.defaults?.query)) value = _objectSpread(_objectSpread({}, value), BaseCollection.defaults?.query); // queryObject if (is('object', BaseCollection.defaults?.queryObject)) value = _objectSpread(_objectSpread({}, value), BaseCollection.defaults?.queryObject); // method if (is('object', BaseCollection.defaults?.[method])) value = _objectSpread(_objectSpread({}, value), BaseCollection.defaults?.[method]); } // instance if (['aggregate', 'searchMany', 'searchOne'].includes(method)) { // query if (is('array', this.defaults?.query)) value.push(...this.defaults?.query); // queryArray if (is('array', this.defaults?.queryArray)) value.push(...this.defaults?.queryArray); // method if (is('array', this.defaults?.[method])) value.push(...this.defaults?.[method]); } else { // query if (is('object', this.defaults?.query)) value = _objectSpread(_objectSpread({}, value), this.defaults?.query); // queryObject if (is('object', this.defaults?.queryObject)) value = _objectSpread(_objectSpread({}, value), this.defaults?.queryObject); // method if (is('object', this.defaults?.[method])) value = _objectSpread(_objectSpread({}, value), this.defaults?.[method]); } } static value(value) { // Getter object method if (is('function', value?.toObject)) return value.toObject(); return _objectSpread({}, value); } static isOnesyQuery(value) { return value instanceof Query || value?.hasOwnProperty('query') && value?.hasOwnProperty('queries'); } } _defineProperty(BaseCollection, "defaults", void 0); export default BaseCollection;