UNPKG

typeorm

Version:

Data-Mapper ORM for TypeScript and ES2023+. Supports MySQL/MariaDB, PostgreSQL, MS SQL Server, Oracle, SAP HANA, SQLite, MongoDB databases.

938 lines • 38.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MongoEntityManager = void 0; const EntityManager_1 = require("./EntityManager"); const DocumentToEntityTransformer_1 = require("../query-builder/transformer/DocumentToEntityTransformer"); const FindOptionsUtils_1 = require("../find-options/FindOptionsUtils"); const PlatformTools_1 = require("../platform/PlatformTools"); const InsertResult_1 = require("../query-builder/result/InsertResult"); const UpdateResult_1 = require("../query-builder/result/UpdateResult"); const DeleteResult_1 = require("../query-builder/result/DeleteResult"); const error_1 = require("../error"); const ObjectUtils_1 = require("../util/ObjectUtils"); /** * Entity manager supposed to work with any entity, automatically find its repository and call its methods, * whatever entity type are you passing. * * This implementation is used for MongoDB driver which has some specifics in its EntityManager. */ class MongoEntityManager extends EntityManager_1.EntityManager { get mongoQueryRunner() { return this.dataSource.driver .queryRunner; } // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- constructor(dataSource) { super(dataSource); this["@instanceof"] = Symbol.for("MongoEntityManager"); } // ------------------------------------------------------------------------- // Overridden Methods // ------------------------------------------------------------------------- /** * Finds entities that match given find options. */ /** * Finds entities that match given find options or conditions. * * @param entityClassOrName * @param optionsOrConditions */ async find(entityClassOrName, optionsOrConditions) { const metadata = this.dataSource.getMetadata(entityClassOrName); const query = this.replaceObjectIdProperty(metadata, this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions)); const cursor = this.createEntityCursor(entityClassOrName, query); const deleteDateColumn = metadata.deleteDateColumn; if (FindOptionsUtils_1.FindOptionsUtils.isFindManyOptions(optionsOrConditions)) { if (optionsOrConditions.select) cursor.project(this.convertFindOptionsSelectToProjectCriteria(optionsOrConditions.select, metadata)); if (optionsOrConditions.skip) cursor.skip(optionsOrConditions.skip); if (optionsOrConditions.take) cursor.limit(optionsOrConditions.take); if (optionsOrConditions.order) cursor.sort(this.convertFindOptionsOrderToOrderCriteria(optionsOrConditions.order)); if (deleteDateColumn && !optionsOrConditions.withDeleted) { this.filterSoftDeleted(cursor, deleteDateColumn, query); } } else if (deleteDateColumn) { this.filterSoftDeleted(cursor, deleteDateColumn, query); } return cursor.toArray(); } /** * Finds entities that match given find options or conditions. * Also counts all entities that match given conditions, * but ignores pagination settings (from and take options). * * @param entityClassOrName * @param options */ async findAndCount(entityClassOrName, options) { return this.executeFindAndCount(entityClassOrName, options); } /** * Finds entities that match given where conditions. * * @param entityClassOrName * @param where */ async findAndCountBy(entityClassOrName, where) { return this.executeFindAndCount(entityClassOrName, where); } /** * Finds entities that match given WHERE conditions. * * @param entityClassOrName * @param where */ async findBy(entityClassOrName, where) { return this.executeFind(entityClassOrName, where); } /** * Finds entities by ids. * Optionally find options can be applied. * * @param entityClassOrName * @param ids * @param optionsOrConditions */ async findByIds(entityClassOrName, ids, optionsOrConditions) { const metadata = this.dataSource.getMetadata(entityClassOrName); const query = this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions) ?? {}; const objectIdClass = PlatformTools_1.PlatformTools.load("mongodb").ObjectId; query["_id"] = { $in: ids.map((id) => { if (typeof id === "string") { return new objectIdClass(id); } if (typeof id === "object") { if (id instanceof objectIdClass) { return id; } const propertyName = metadata.objectIdColumn.propertyName; if (id[propertyName] instanceof objectIdClass) { return id[propertyName]; } } }), }; const cursor = this.createEntityCursor(entityClassOrName, query); if (FindOptionsUtils_1.FindOptionsUtils.isFindManyOptions(optionsOrConditions)) { if (optionsOrConditions.select) cursor.project(this.convertFindOptionsSelectToProjectCriteria(optionsOrConditions.select, metadata)); if (optionsOrConditions.skip) cursor.skip(optionsOrConditions.skip); if (optionsOrConditions.take) cursor.limit(optionsOrConditions.take); if (optionsOrConditions.order) cursor.sort(this.convertFindOptionsOrderToOrderCriteria(optionsOrConditions.order)); } return cursor.toArray(); } /** * Finds first entity that matches given conditions and/or find options. * * @param entityClassOrName * @param options */ async findOne(entityClassOrName, options) { return this.executeFindOne(entityClassOrName, options); } /** * Finds first entity that matches given WHERE conditions. * * @param entityClassOrName * @param where */ async findOneBy(entityClassOrName, where) { return this.executeFindOne(entityClassOrName, where); } /** * Inserts a given entity into the database. * Unlike save method executes a primitive operation without cascades, relations and other operations included. * Executes fast and efficient INSERT query. * Does not check if entity exist in the database, so query will fail if duplicate entity is being inserted. * You can execute bulk inserts using this method. * * @param target * @param entity */ async insert(target, entity) { // todo: convert entity to its database name const result = new InsertResult_1.InsertResult(); if (Array.isArray(entity)) { result.raw = await this.insertMany(target, entity); Object.keys(result.raw.insertedIds).forEach((key) => { const insertedId = result.raw.insertedIds[key]; result.generatedMaps.push(this.dataSource.driver.createGeneratedMap(this.dataSource.getMetadata(target), insertedId)); result.identifiers.push(this.dataSource.driver.createGeneratedMap(this.dataSource.getMetadata(target), insertedId)); }); } else { result.raw = await this.insertOne(target, entity); result.generatedMaps.push(this.dataSource.driver.createGeneratedMap(this.dataSource.getMetadata(target), result.raw.insertedId)); result.identifiers.push(this.dataSource.driver.createGeneratedMap(this.dataSource.getMetadata(target), result.raw.insertedId)); } return result; } /** * Updates entity partially. Entity can be found by a given conditions. * Unlike save method executes a primitive operation without cascades, relations and other operations included. * Executes fast and efficient UPDATE query. * Does not check if entity exist in the database. * * @param target * @param criteria * @param partialEntity */ async update(target, criteria, partialEntity) { const result = new UpdateResult_1.UpdateResult(); if (Array.isArray(criteria)) { const updateResults = await Promise.all(criteria.map((criteriaItem) => { return this.update(target, criteriaItem, partialEntity); })); result.raw = updateResults.map((r) => r.raw); result.affected = updateResults .map((r) => r.affected ?? 0) .reduce((c, r) => c + r, 0); result.generatedMaps = updateResults.reduce((c, r) => c.concat(r.generatedMaps), []); } else { const metadata = this.dataSource.getMetadata(target); const mongoResult = await this.updateMany(target, this.convertMixedCriteria(metadata, criteria), { $set: partialEntity }); result.raw = mongoResult; result.affected = mongoResult.modifiedCount; } return result; } /** * Deletes entities by a given conditions. * Unlike save method executes a primitive operation without cascades, relations and other operations included. * Executes fast and efficient DELETE query. * Does not check if entity exist in the database. * * @param target * @param criteria */ async delete(target, criteria) { const result = new DeleteResult_1.DeleteResult(); if (Array.isArray(criteria)) { const deleteResults = await Promise.all(criteria.map((criteriaItem) => { return this.delete(target, criteriaItem); })); result.raw = deleteResults.map((r) => r.raw); result.affected = deleteResults .map((r) => r.affected ?? 0) .reduce((c, r) => c + r, 0); } else { const mongoResult = await this.deleteMany(target, this.convertMixedCriteria(this.dataSource.getMetadata(target), criteria)); result.raw = mongoResult; result.affected = mongoResult.deletedCount; } return result; } // ------------------------------------------------------------------------- // Public Methods // ------------------------------------------------------------------------- /** * Creates a cursor for a query that can be used to iterate over results from MongoDB. * * @param entityClassOrName * @param query */ createCursor(entityClassOrName, query = {}) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.cursor(metadata.tableName, query); } /** * Creates a cursor for a query that can be used to iterate over results from MongoDB. * This returns modified version of cursor that transforms each result into Entity model. * * @param entityClassOrName * @param query */ createEntityCursor(entityClassOrName, query = {}) { const metadata = this.dataSource.getMetadata(entityClassOrName); const cursor = this.createCursor(entityClassOrName, query); this.applyEntityTransformationToCursor(metadata, cursor); return cursor; } /** * Execute an aggregation framework pipeline against the collection. * * @param entityClassOrName * @param pipeline * @param options */ aggregate(entityClassOrName, pipeline, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.aggregate(metadata.tableName, pipeline, options); } /** * Execute an aggregation framework pipeline against the collection. * This returns modified version of cursor that transforms each result into Entity model. * * @param entityClassOrName * @param pipeline * @param options */ aggregateEntity(entityClassOrName, pipeline, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); const cursor = this.mongoQueryRunner.aggregate(metadata.tableName, pipeline, options); this.applyEntityTransformationToCursor(metadata, cursor); return cursor; } /** * Perform a bulkWrite operation without a fluent API. * * @param entityClassOrName * @param operations * @param options */ bulkWrite(entityClassOrName, operations, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.bulkWrite(metadata.tableName, operations, options); } /** * Count number of matching documents in the db to a query. * * @param entityClassOrName * @param query * @param options */ count(entityClassOrName, query = {}, options = {}) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.count(metadata.tableName, query, options); } /** * Count number of matching documents in the db to a query. * * @param entityClassOrName * @param query * @param options */ countDocuments(entityClassOrName, query = {}, options = {}) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.countDocuments(metadata.tableName, query, options); } /** * Count number of matching documents in the db to a query. * * @param entityClassOrName * @param query * @param options */ countBy(entityClassOrName, query, options) { return this.count(entityClassOrName, query, options); } /** * Creates an index on the db and collection. * * @param entityClassOrName * @param fieldOrSpec * @param options */ createCollectionIndex(entityClassOrName, fieldOrSpec, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.createCollectionIndex(metadata.tableName, fieldOrSpec, options); } /** * Creates multiple indexes in the collection, this method is only supported for MongoDB 2.6 or higher. * Earlier version of MongoDB will throw a command not supported error. * Index specifications are defined at http://docs.mongodb.org/manual/reference/command/createIndexes/. * * @param entityClassOrName * @param indexSpecs */ createCollectionIndexes(entityClassOrName, indexSpecs) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.createCollectionIndexes(metadata.tableName, indexSpecs); } /** * Delete multiple documents on MongoDB. * * @param entityClassOrName * @param query * @param options */ deleteMany(entityClassOrName, query, options = {}) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.deleteMany(metadata.tableName, query, options); } /** * Delete a document on MongoDB. * * @param entityClassOrName * @param query * @param options */ deleteOne(entityClassOrName, query, options = {}) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.deleteOne(metadata.tableName, query, options); } /** * The distinct command returns returns a list of distinct values for the given key across a collection. * * @param entityClassOrName * @param key * @param query * @param options */ distinct(entityClassOrName, key, query, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.distinct(metadata.tableName, key, query, options); } /** * Drops an index from this collection. * * @param entityClassOrName * @param indexName * @param options */ dropCollectionIndex(entityClassOrName, indexName, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.dropCollectionIndex(metadata.tableName, indexName, options); } /** * Drops all indexes from the collection. * * @param entityClassOrName */ dropCollectionIndexes(entityClassOrName) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.dropCollectionIndexes(metadata.tableName); } /** * Find a document and delete it in one atomic operation, requires a write lock for the duration of the operation. * * @param entityClassOrName * @param query * @param options */ findOneAndDelete(entityClassOrName, query, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.findOneAndDelete(metadata.tableName, query, options); } /** * Find a document and replace it in one atomic operation, requires a write lock for the duration of the operation. * * @param entityClassOrName * @param query * @param replacement * @param options */ findOneAndReplace(entityClassOrName, query, replacement, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.findOneAndReplace(metadata.tableName, query, replacement, options); } /** * Find a document and update it in one atomic operation, requires a write lock for the duration of the operation. * * @param entityClassOrName * @param query * @param update * @param options */ findOneAndUpdate(entityClassOrName, query, update, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.findOneAndUpdate(metadata.tableName, query, update, options); } /** * Retrieve all the indexes on the collection. * * @param entityClassOrName */ collectionIndexes(entityClassOrName) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.collectionIndexes(metadata.tableName); } /** * Retrieve all the indexes on the collection. * * @param entityClassOrName * @param indexes */ collectionIndexExists(entityClassOrName, indexes) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.collectionIndexExists(metadata.tableName, indexes); } /** * Retrieves this collections index info. * * @param entityClassOrName * @param options */ collectionIndexInformation(entityClassOrName, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.collectionIndexInformation(metadata.tableName, options); } /** * Initiate an In order bulk write operation, operations will be serially executed in the order they are added, creating a new operation for each switch in types. * * @param entityClassOrName * @param options */ initializeOrderedBulkOp(entityClassOrName, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.initializeOrderedBulkOp(metadata.tableName, options); } /** * Initiate a Out of order batch write operation. All operations will be buffered into insert/update/remove commands executed out of order. * * @param entityClassOrName * @param options */ initializeUnorderedBulkOp(entityClassOrName, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.initializeUnorderedBulkOp(metadata.tableName, options); } /** * Inserts an array of documents into MongoDB. * * @param entityClassOrName * @param docs * @param options */ insertMany(entityClassOrName, docs, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.insertMany(metadata.tableName, docs, options); } /** * Inserts a single document into MongoDB. * * @param entityClassOrName * @param doc * @param options */ insertOne(entityClassOrName, doc, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.insertOne(metadata.tableName, doc, options); } /** * Returns if the collection is a capped collection. * * @param entityClassOrName */ isCapped(entityClassOrName) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.isCapped(metadata.tableName); } /** * Get the list of all indexes information for the collection. * * @param entityClassOrName * @param options */ listCollectionIndexes(entityClassOrName, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.listCollectionIndexes(metadata.tableName, options); } /** * Reindex all indexes on the collection Warning: reIndex is a blocking operation (indexes are rebuilt in the foreground) and will be slow for large collections. * * @param entityClassOrName * @param newName * @param options */ rename(entityClassOrName, newName, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.rename(metadata.tableName, newName, options); } /** * Replace a document on MongoDB. * * @param entityClassOrName * @param query * @param doc * @param options */ replaceOne(entityClassOrName, query, doc, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.replaceOne(metadata.tableName, query, doc, options); } watch(entityClassOrName, pipeline, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.watch(metadata.tableName, pipeline, options); } /** * Update multiple documents on MongoDB. * * @param entityClassOrName * @param query * @param update * @param options */ updateMany(entityClassOrName, query, update, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.updateMany(metadata.tableName, query, update, options); } /** * Update a single document on MongoDB. * * @param entityClassOrName * @param query * @param update * @param options */ updateOne(entityClassOrName, query, update, options) { const metadata = this.dataSource.getMetadata(entityClassOrName); return this.mongoQueryRunner.updateOne(metadata.tableName, query, update, options); } // ------------------------------------------------------------------------- // Protected Methods // ------------------------------------------------------------------------- /** * Replaces the entity's ObjectId property name (e.g. "id") with "_id" in a * query object so that `findOneBy({ id: value })` works as expected. * * @param metadata * @param query */ replaceObjectIdProperty(metadata, query) { if (!query) return query; const objectIdColumn = metadata.objectIdColumn; if (!objectIdColumn) return query; const propertyName = objectIdColumn.propertyName; if (propertyName === "_id") return query; if (!(propertyName in query)) { const hasNested = ("$or" in query && Array.isArray(query.$or)) || ("$and" in query && Array.isArray(query.$and)); if (!hasNested) return query; } const objectIdClass = PlatformTools_1.PlatformTools.load("mongodb").ObjectId; return this.rewriteObjectIdQuery(query, propertyName, objectIdClass); } /** * Recursively rewrites a query object, renaming the given property to * "_id" and converting values to ObjectId instances. Walks into $or/$and. * * @param obj * @param propertyName * @param objectIdClass */ rewriteObjectIdQuery(obj, propertyName, objectIdClass) { const result = {}; for (const [key, value] of Object.entries(obj)) { if (key === propertyName) { result["_id"] = this.convertToObjectId(value, objectIdClass); } else if ((key === "$or" || key === "$and") && Array.isArray(value)) { result[key] = value.map((item) => typeof item === "object" && item !== null ? this.rewriteObjectIdQuery(item, propertyName, objectIdClass) : item); } else { result[key] = value; } } return result; } /** * Converts a query value to ObjectId, handling scalars, arrays, and * MongoDB operator objects (e.g. { $in: [...] }, { $ne: ... }). * * @param value * @param objectIdClass */ convertToObjectId(value, objectIdClass) { if (value instanceof objectIdClass) return value; if (typeof value === "string" || typeof value === "number") return new objectIdClass(value); if (Array.isArray(value)) return value.map((v) => this.convertToObjectId(v, objectIdClass)); if (value !== null && typeof value === "object" && Object.keys(value).some((k) => k.startsWith("$"))) { const result = {}; for (const [k, v] of Object.entries(value)) { result[k] = this.convertToObjectId(v, objectIdClass); } return result; } return value; } /** * Converts FindManyOptions to mongodb query. * * @param optionsOrConditions */ convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions) { if (!optionsOrConditions) return undefined; FindOptionsUtils_1.FindOptionsUtils.rejectJoinOption(optionsOrConditions); FindOptionsUtils_1.FindOptionsUtils.rejectStringArraySelect(optionsOrConditions); FindOptionsUtils_1.FindOptionsUtils.rejectStringArrayRelations(optionsOrConditions); if (FindOptionsUtils_1.FindOptionsUtils.isFindManyOptions(optionsOrConditions)) // If where condition is passed as a string which contains sql we have to ignore // as mongo is not a sql database return typeof optionsOrConditions.where === "string" ? {} : optionsOrConditions.where; return optionsOrConditions; } /** * Converts FindOneOptions to mongodb query. * * @param optionsOrConditions */ convertFindOneOptionsOrConditionsToMongodbQuery(optionsOrConditions) { if (!optionsOrConditions) return undefined; FindOptionsUtils_1.FindOptionsUtils.rejectJoinOption(optionsOrConditions); FindOptionsUtils_1.FindOptionsUtils.rejectStringArraySelect(optionsOrConditions); FindOptionsUtils_1.FindOptionsUtils.rejectStringArrayRelations(optionsOrConditions); if (FindOptionsUtils_1.FindOptionsUtils.isFindOneOptions(optionsOrConditions)) // If where condition is passed as a string which contains sql we have to ignore // as mongo is not a sql database return typeof optionsOrConditions.where === "string" ? {} : optionsOrConditions.where; return optionsOrConditions; } /** * Converts FindOptions into mongodb order by criteria. * * @param order */ convertFindOptionsOrderToOrderCriteria(order) { return Object.keys(order).reduce((orderCriteria, key) => { switch (order[key]) { case "DESC": orderCriteria[key] = -1; break; case "ASC": orderCriteria[key] = 1; break; default: orderCriteria[key] = order[key]; } return orderCriteria; }, {}); } /** * Converts FindOptions into mongodb select by criteria. * * @param selects * @param metadata */ convertFindOptionsSelectToProjectCriteria(selects, metadata) { const projection = {}; const build = (obj, embedPrefix) => { for (const key of Object.keys(obj)) { const value = obj[key]; if (value === undefined || value === false) continue; const propertyPath = embedPrefix ? `${embedPrefix}.${key}` : key; if (metadata.findColumnWithPropertyPathStrict(propertyPath)) { projection[propertyPath] = 1; continue; } const embed = metadata.findEmbeddedWithPropertyPath(propertyPath); if (embed) { if (value === true) { for (const subColumn of embed.columnsFromTree) { projection[subColumn.propertyPath] = 1; } } else if (typeof value === "object") { build(value, propertyPath); } continue; } if (metadata.findRelationWithPropertyPath(propertyPath)) continue; throw new error_1.EntityPropertyNotFoundError(propertyPath, metadata); } }; build(selects, ""); // Translate ObjectIdColumn property name (e.g. "id") to "_id" for MongoDB if (metadata.objectIdColumn) { const propertyName = metadata.objectIdColumn.propertyName; if (propertyName !== "_id" && projection[propertyName] !== undefined) { projection["_id"] = projection[propertyName]; delete projection[propertyName]; } } return projection; } /** * Ensures given id is an id for query. * * @param metadata * @param idMap */ convertMixedCriteria(metadata, idMap) { const objectIdInstance = PlatformTools_1.PlatformTools.load("mongodb").ObjectId; // check first if it's ObjectId compatible: // string, number, Buffer, ObjectId or ObjectId-like if (objectIdInstance.isValid(idMap)) { return { _id: new objectIdInstance(idMap), }; } // if it's some other type of object build a query from the columns // this check needs to be after the ObjectId check, because a valid ObjectId is also an Object instance if (ObjectUtils_1.ObjectUtils.isObject(idMap)) { return metadata.columns.reduce((query, column) => { const columnValue = column.getEntityValue(idMap); if (columnValue !== undefined) query[column.databasePath] = columnValue; return query; }, {}); } // last resort: try to convert it to an ObjectId anyway // most likely it will fail, but we want to be backwards compatible and keep the same thrown Errors. // it can still pass with null/undefined return { _id: new objectIdInstance(idMap), }; } /** * Overrides cursor's toArray and next methods to convert results to entity automatically. * * @param metadata * @param cursor */ applyEntityTransformationToCursor(metadata, cursor) { const queryRunner = this.mongoQueryRunner; cursor["__to_array_func"] = cursor.toArray; cursor.toArray = () => cursor["__to_array_func"]().then(async (results) => { const transformer = new DocumentToEntityTransformer_1.DocumentToEntityTransformer(); const entities = transformer.transformAll(results, metadata); // broadcast "load" events await queryRunner.broadcaster.broadcast("Load", metadata, entities); return entities; }); cursor["__next_func"] = cursor.next; cursor.next = () => cursor["__next_func"]().then(async (result) => { if (!result) { return result; } const transformer = new DocumentToEntityTransformer_1.DocumentToEntityTransformer(); const entity = transformer.transform(result, metadata); // broadcast "load" events await queryRunner.broadcaster.broadcast("Load", metadata, [ entity, ]); return entity; }); } filterSoftDeleted(cursor, deleteDateColumn, query) { const { $or, ...restQuery } = query ?? {}; cursor.filter({ $or: [ { [deleteDateColumn.propertyName]: { $eq: null } }, ...(Array.isArray($or) ? $or : []), ], ...restQuery, }); } /** * Finds first entity that matches given conditions and/or find options. * * @param entityClassOrName * @param optionsOrConditions * @param maybeOptions */ async executeFindOne(entityClassOrName, optionsOrConditions, maybeOptions) { const objectIdClass = PlatformTools_1.PlatformTools.load("mongodb").ObjectId; const id = optionsOrConditions instanceof objectIdClass || typeof optionsOrConditions === "string" ? optionsOrConditions : undefined; const findOneOptionsOrConditions = (id ? maybeOptions : optionsOrConditions); const metadata = this.dataSource.getMetadata(entityClassOrName); const query = this.replaceObjectIdProperty(metadata, this.convertFindOneOptionsOrConditionsToMongodbQuery(findOneOptionsOrConditions)) ?? {}; if (id) { query["_id"] = id instanceof objectIdClass ? id : new objectIdClass(id); } const cursor = this.createEntityCursor(entityClassOrName, query); const deleteDateColumn = this.dataSource.getMetadata(entityClassOrName).deleteDateColumn; if (FindOptionsUtils_1.FindOptionsUtils.isFindOneOptions(findOneOptionsOrConditions)) { if (findOneOptionsOrConditions.select) cursor.project(this.convertFindOptionsSelectToProjectCriteria(findOneOptionsOrConditions.select, metadata)); if (findOneOptionsOrConditions.order) cursor.sort(this.convertFindOptionsOrderToOrderCriteria(findOneOptionsOrConditions.order)); if (deleteDateColumn && !findOneOptionsOrConditions.withDeleted) { this.filterSoftDeleted(cursor, deleteDateColumn, query); } } else if (deleteDateColumn) { this.filterSoftDeleted(cursor, deleteDateColumn, query); } // const result = await cursor.limit(1).next(); const result = await cursor.limit(1).toArray(); return result.length > 0 ? result[0] : null; } async executeFind(entityClassOrName, optionsOrConditions) { const metadata = this.dataSource.getMetadata(entityClassOrName); const query = this.replaceObjectIdProperty(metadata, this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions)); const cursor = this.createEntityCursor(entityClassOrName, query); const deleteDateColumn = metadata.deleteDateColumn; if (FindOptionsUtils_1.FindOptionsUtils.isFindManyOptions(optionsOrConditions)) { if (optionsOrConditions.select) cursor.project(this.convertFindOptionsSelectToProjectCriteria(optionsOrConditions.select, metadata)); if (optionsOrConditions.skip) cursor.skip(optionsOrConditions.skip); if (optionsOrConditions.take) cursor.limit(optionsOrConditions.take); if (optionsOrConditions.order) cursor.sort(this.convertFindOptionsOrderToOrderCriteria(optionsOrConditions.order)); if (deleteDateColumn && !optionsOrConditions.withDeleted) { this.filterSoftDeleted(cursor, deleteDateColumn, query); } } else if (deleteDateColumn) { this.filterSoftDeleted(cursor, deleteDateColumn, query); } return cursor.toArray(); } /** * Finds entities that match given find options or conditions. * * @param entityClassOrName * @param optionsOrConditions */ async executeFindAndCount(entityClassOrName, optionsOrConditions) { const metadata = this.dataSource.getMetadata(entityClassOrName); const query = this.replaceObjectIdProperty(metadata, this.convertFindManyOptionsOrConditionsToMongodbQuery(optionsOrConditions)); const cursor = this.createEntityCursor(entityClassOrName, query); const deleteDateColumn = metadata.deleteDateColumn; if (FindOptionsUtils_1.FindOptionsUtils.isFindManyOptions(optionsOrConditions)) { if (optionsOrConditions.select) cursor.project(this.convertFindOptionsSelectToProjectCriteria(optionsOrConditions.select, metadata)); if (optionsOrConditions.skip) cursor.skip(optionsOrConditions.skip); if (optionsOrConditions.take) cursor.limit(optionsOrConditions.take); if (optionsOrConditions.order) cursor.sort(this.convertFindOptionsOrderToOrderCriteria(optionsOrConditions.order)); if (deleteDateColumn && !optionsOrConditions.withDeleted) { this.filterSoftDeleted(cursor, deleteDateColumn, query); } } else if (deleteDateColumn) { this.filterSoftDeleted(cursor, deleteDateColumn, query); } const [results, count] = await Promise.all([ cursor.toArray(), this.count(entityClassOrName, query), ]); return [results, parseInt(count)]; } } exports.MongoEntityManager = MongoEntityManager; //# sourceMappingURL=MongoEntityManager.js.map