UNPKG

@warlock.js/cascade

Version:

ORM for managing databases

670 lines (669 loc) 18.5 kB
import events from'@mongez/events';import {Random}from'@mongez/reinforcements';import {database}from'../database.js';class Query { /** * Connection instance */ database = database; /** * class event name */ eventName = "mongodb.query." + Random.id(); /** * Set the database instance */ setDatabase(database) { this.database = database; return this; } /** * Get collection query for the given collection name */ query(collection) { return this.database.collection(collection); } /** * Get current active session from database object */ getCurrentSession() { return this.database.getActiveSession()?.session; } /** * Create a new document in the given collection */ async create(collection, data, { session = this.getCurrentSession() } = {}) { const query = this.query(collection); await this.trigger("creating saving", { collection, data, query, isMany: false, }); const result = await query.insertOne(data, { session, }); const document = { ...data, _id: result.insertedId, }; await this.trigger("created saved", { collection, document, isMany: false, }); return document; } /** * Create many documents in the given collection */ async createMany(collection, data, { session = this.getCurrentSession() } = {}) { const query = this.query(collection); await this.trigger("creating saving", { collection, data, query, isMany: true, }); const result = await query.insertMany(data, { session, }); const documents = data.map((data, index) => ({ ...data, _id: result.insertedIds[index], })); await this.trigger("created saved", { collection, documents, isMany: true, }); return documents; } /** * Update model by the given id */ async update(collection, filter, data, options) { // get the query of the current collection const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("updating saving", { collection, filter, data, query, options, isMany: false, }); const result = await query.findOneAndUpdate(filter, { $set: data, }, { returnDocument: "after", ...options, }); const output = result?.ok ? result.value : null; await this.trigger("updated saved", { collection, filter, data, document: output, isMany: false, }); return output; } /** * Update a single document in the given collection */ async updateOne(collection, filter, update, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("updating saving", { collection, filter, update, options, query, isMany: false, }); const result = await query.updateOne(filter, update, options); await this.trigger("updated saved", { collection, filter, update, options, result, isMany: false, }); return result; } /** * Update many documents */ async updateMany(collection, filter, updateOptions, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("updating saving", { collection, filter, updateOptions, options, query, isMany: true, }); const result = await query.updateMany(filter, updateOptions, options); await this.trigger("updated saved", { collection, filter, updateOptions, options, result: result, isMany: true, }); return result; } /** * Increment the value of the given field by the given amount */ async increment(collection, filter, field, amount = 1) { const query = this.query(collection); const result = await query.findOneAndUpdate(filter, { $inc: { [field]: amount }, }); return result; } /** * Decrement the value of the given field by the given amount */ async decrement(collection, filter, field, amount = 1) { const query = this.query(collection); const result = await query.updateOne(filter, { $inc: { [field]: -amount }, }); return result; } /** * Find and increment the value of the given field by the given amount */ async findAndIncrement(collection, filter, field, amount = 1) { const query = this.query(collection); const result = await query.findOneAndUpdate(filter, { $inc: { [field]: amount }, }); return result; } /** * Find and decrement the value of the given field by the given amount */ async findAndDecrement(collection, filter, field, amount = 1) { const query = this.query(collection); const result = await query.findOneAndUpdate(filter, { $inc: { [field]: -amount }, }); return result; } /** * Replace the entire document for the given document id with the given new data */ async replace(collection, filter, data, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("replacing saving", { collection, filter, data, query, }); const result = await query.findOneAndReplace(filter, data, { returnDocument: "after", ...options, }); const output = result?.ok ? result.value : null; await this.trigger("replaced saved", { collection, filter, data, output, }); return output; } /** * Find and update the document for the given filter with the given data or create a new document/record * if filter has no matching */ async upsert(collection, filter, data, options) { // get the query of the current collection const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("upserting saving", { collection, filter, data, query, options, }); // execute the update operation const result = await query.findOneAndUpdate(filter, { $set: data, }, { returnDocument: "after", upsert: true, ...options, }); const output = result?.ok ? result.value : null; await this.trigger("upserted saved", { collection, filter, data, output, }); return output; } /** * Perform a single delete operation for the given collection */ async deleteOne(collection, filter, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("deleting", { collection, filter, query, options, isMany: false, }); const result = await query.deleteOne(filter, options); const isDeleted = result.deletedCount > 0; await this.trigger("deleted", { collection, filter, isDeleted, count: result.deletedCount, result, isMany: false, }); return isDeleted; } /** * Delete multiple documents from the given collection */ async delete(collection, filter = {}, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("deleting", { collection, filter, query, isMany: true, }); const result = await query.deleteMany(filter, options); const output = result.deletedCount; await this.trigger("deleted", { collection, filter, output, result, count: result.deletedCount, isMany: true, }); return output; } /** * Alias to delete * * @alias delete */ async deleteMany(collection, filter = {}, options) { return this.delete(collection, filter, options); } /** * Check if document exists for the given collection with the given filter */ async exists(collection, filter = {}, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("fetching", { collection, filter, query, options, isMany: false, }); // Use findOne with projection to minimize data transfer const document = await query.findOne(filter, { ...options, projection: { _id: 1 }, // Only return the _id field }); await this.trigger("fetched", { collection, filter, document, exists: !!document, }); return !!document; } /** * Find a single document for the given collection with the given filter */ async first(collection, filter = {}, findOptions) { const documents = await this.list(collection, filter, { limit: 1, ...findOptions, }); return documents[0]; } /** * Find last document for the given collection with the given filter */ async last(collection, filter = {}, findOptions) { const documents = await this.latest(collection, filter, { limit: 1, ...findOptions, }); return documents[0]; } /** * Find multiple document for the given collection with the given filter */ async list(collection, filter = {}, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); if (options?.deselect || options?.select) { (options?.deselect || []).forEach(field => { }); (options?.select || []).forEach(field => { }); delete options.deselect; delete options.select; } await this.trigger("fetching", { collection, filter, query, options, isMany: true, }); const findOperation = query.find(filter, options); const documents = await findOperation.toArray(); await this.trigger(this.eventName + ".fetched", { collection, filter, documents, options, isMany: true, count: documents.length, }); return documents; } /** * Find latest documents for the given collection with the given filter */ async latest(collection, filter = {}, findOptions) { return this.list(collection, filter, { sort: { id: "desc", }, ...findOptions, }); } /** * Find oldest documents for the given collection with the given filter */ async oldest(collection, filter = {}, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("fetching", { collection, filter, query, options, isMany: true, }); const documents = await query .find(filter, options) .sort({ id: "asc", }) .toArray(); await this.trigger("fetched", { collection, filter, documents, options, count: documents.length, isMany: true, }); return documents; } /** * Get distinct values for the given collection with the given filter */ async distinct(collection, field, filter = {}, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("fetching", { collection, filter, query, isMany: true, }); const data = await query.distinct(field, filter, options); await this.trigger("fetched", { collection, filter, data, count: data.length, isMany: true, }); return data; } /** * Count documents for the given collection with the given filter */ async count(collection, filter = {}, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("counting", { collection, filter, query, }); const output = await query.countDocuments(filter, options); await this.trigger("counted", { collection, filter, output, }); return output; } /** * Create an explain fetch query */ async explain(collection, filter = {}, options, verbosity) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("explaining", { collection, filter, query, }); const result = await query .find(filter, { explain: true, ...options, }) .explain(verbosity); await this.trigger("explained", { collection, filter, result, }); return result; } /** * Create aggregate query */ async aggregate(collection, pipeline, options) { const query = this.query(collection); options = this.prepareQueryOptions(options); await this.trigger("aggregating", { collection, pipeline, options, query, }); const aggregate = await query.aggregate(pipeline, options); await this.trigger("aggregated", { collection, pipeline, options, aggregate, }); return aggregate; } /** * Trigger event */ async trigger(eventName, payload = {}) { return Promise.all(eventName .split(" ") .map(async (eventName) => events.triggerAllAsync(this.eventName + "." + eventName, payload))); } /** * Listen on creating event */ onCreating(callback) { this.on("creating", callback); return this; } /** * Listen on created event */ onCreated(callback) { this.on("created", callback); return this; } /** * Listen on updating event */ onUpdating(callback) { this.on("updating", callback); return this; } /** * Listen on updated event */ onUpdated(callback) { this.on("updated", callback); return this; } /** * Listen on upserting event */ onUpserting(callback) { this.on("upserting", callback); return this; } /** * Listen on upserted event */ onUpserted(callback) { this.on("upserted", callback); return this; } /** * Listen on replacing event */ onReplacing(callback) { this.on("replacing", callback); return this; } /** * Listen on replaced event */ onReplaced(callback) { this.on("replaced", callback); return this; } /** * Listen on saving event */ onSaving(callback) { this.on("saving", callback); return this; } /** * Listen on saved event */ onSaved(callback) { this.on("saved", callback); return this; } /** * Listen on fetching event */ onFetching(callback) { this.on("fetching", callback); return this; } /** * Listen on fetched event */ onFetched(callback) { this.on("fetched", callback); return this; } /** * Listen on counting event */ onCounting(callback) { this.on("counting", callback); return this; } /** * Listen on counted event */ onCounted(callback) { this.on("counted", callback); return this; } /** * Listen on explaining event */ onExplaining(callback) { this.on("explaining", callback); return this; } /** * Listen on explained event */ onExplained(callback) { this.on("explained", callback); return this; } /** * Listen on deleting event */ onDeleting(callback) { this.on("deleting", callback); return this; } /** * Listen on deleted event */ onDeleted(callback) { this.on("deleted", callback); return this; } /** * Listen to the given event */ on(event, callback) { events.on(this.eventName + "." + event, callback); } /** * Prepare query options and add session if not exists */ prepareQueryOptions(options = {}) { if (!options?.session) { options.session = this.getCurrentSession(); } return options; } } const query = new Query();export{Query,query};//# sourceMappingURL=query.js.map