@ptc-org/nestjs-query-mongoose
Advanced tools
Comparing version
{ | ||
"name": "@ptc-org/nestjs-query-mongoose", | ||
"version": "6.1.2", | ||
"version": "6.1.3", | ||
"description": "Mongoose adapter for @ptc-org/nestjs-query-core", | ||
@@ -25,3 +25,3 @@ "author": "doug-martin <doug@dougamartin.com>", | ||
"tslib": "^2.6.2", | ||
"@ptc-org/nestjs-query-core": "6.1.2", | ||
"@ptc-org/nestjs-query-core": "6.1.3", | ||
"reflect-metadata": "0.2.2" | ||
@@ -28,0 +28,0 @@ }, |
@@ -34,40 +34,6 @@ /// <reference types="mongoose/types/aggregate" /> | ||
countRelations<Relation extends Document>(RelationClass: Class<Relation>, relationName: string, dto: Entity, filter: Filter<Relation>): Promise<number>; | ||
findRelation<Relation extends Document>(RelationClass: Class<Relation>, relationName: string, dtos: Entity[], opts?: FindRelationOptions<Relation>): Promise<Map<Entity, Relation | undefined>>; | ||
findRelation<Relation extends Document>(RelationClass: Class<Relation>, relationName: string, dto: Entity, opts?: FindRelationOptions<Relation>): Promise<Relation | undefined>; | ||
/** | ||
* Query for an array of relations for multiple dtos. | ||
* @param RelationClass - The class to serialize the relations into. | ||
* @param dtos - The entities to query relations for. | ||
* @param relationName - The name of relation to query for. | ||
* @param opts - A query to filter, page or sort relations. | ||
* @param withDeleted - Also query the soft deleted records | ||
*/ | ||
private batchQueryRelations; | ||
/** | ||
* Query for an array of relations for multiple dtos. | ||
* @param RelationClass - The class to serialize the relations into. | ||
* @param entities - The entities to query relations for. | ||
* @param relationName - The name of relation to query for. | ||
* @param filter - Filter. | ||
* @param query - A query to filter, page or sort relations. | ||
*/ | ||
private batchAggregateRelations; | ||
/** | ||
* Count the number of relations for multiple dtos. | ||
* @param RelationClass - The class to serialize the relations into. | ||
* @param entities - The entities to query relations for. | ||
* @param relationName - The name of relation to query for. | ||
* @param filter - The filter to apply to the relation query. | ||
*/ | ||
private batchCountRelations; | ||
/** | ||
* Query for a relation for multiple dtos. | ||
* @param RelationClass - The class to serialize the relations into. | ||
* @param dtos - The dto to query relations for. | ||
* @param relationName - The name of relation to query for. | ||
* @param opts - A query to filter, page or sort relations. | ||
*/ | ||
private batchFindRelations; | ||
queryRelations<Relation extends Document>(RelationClass: Class<Relation>, relationName: string, entities: Entity[], query: Query<Relation>): Promise<Map<Entity, Relation[]>>; | ||
queryRelations<Relation extends Document>(RelationClass: Class<Relation>, relationName: string, dto: Entity, query: Query<Relation>): Promise<Relation[]>; | ||
findRelation<Relation>(RelationClass: Class<Relation>, relationName: string, dtos: Entity[], opts?: FindRelationOptions<Relation>): Promise<Map<Entity, Relation | undefined>>; | ||
findRelation<Relation>(RelationClass: Class<Relation>, relationName: string, dto: Entity, opts?: FindRelationOptions<Relation>): Promise<Relation | undefined>; | ||
queryRelations<Relation>(RelationClass: Class<Relation>, relationName: string, entities: Entity[], query: Query<Relation>): Promise<Map<Entity, Relation[]>>; | ||
queryRelations<Relation>(RelationClass: Class<Relation>, relationName: string, dto: Entity, query: Query<Relation>): Promise<Relation[]>; | ||
addRelations<Relation extends Document>(relationName: string, id: string, relationIds: (string | number)[], opts?: ModifyRelationOptions<Entity, Relation>): Promise<Entity>; | ||
@@ -84,5 +50,5 @@ setRelations<Relation extends Document>(relationName: string, id: string, relationIds: (string | number)[], opts?: ModifyRelationOptions<Entity, Relation>): Promise<Entity>; | ||
private getReferenceFilter; | ||
private getReferenceIds; | ||
private getReferenceFieldMap; | ||
private getObjectIdReferenceFilter; | ||
private getVirtualReferenceFilter; | ||
private getRefCount; | ||
} |
@@ -12,8 +12,12 @@ "use strict"; | ||
this.checkForReference('AggregateRelations', relationName); | ||
const relationModel = this.getReferenceModel(relationName); | ||
const referenceQueryBuilder = this.getReferenceQueryBuilder(relationName); | ||
if (Array.isArray(dto)) { | ||
return this.batchAggregateRelations(RelationClass, relationName, dto, filter, aggregateQuery); | ||
return dto.reduce(async (mapPromise, entity) => { | ||
const map = await mapPromise; | ||
const refs = await this.aggregateRelations(RelationClass, relationName, entity, filter, aggregateQuery); | ||
return map.set(entity, refs); | ||
}, Promise.resolve(new Map())); | ||
} | ||
const assembler = nestjs_query_core_1.AssemblerFactory.getAssembler(RelationClass, mongoose_1.Document); | ||
const relationModel = this.getReferenceModel(relationName); | ||
const referenceQueryBuilder = this.getReferenceQueryBuilder(relationName); | ||
const refFilter = this.getReferenceFilter(relationName, dto, assembler.convertQuery({ filter }).filter); | ||
@@ -34,3 +38,7 @@ if (!refFilter) { | ||
if (Array.isArray(dto)) { | ||
return this.batchCountRelations(RelationClass, relationName, dto, filter); | ||
return dto.reduce(async (mapPromise, entity) => { | ||
const map = await mapPromise; | ||
const refs = await this.countRelations(RelationClass, relationName, entity, filter); | ||
return map.set(entity, refs); | ||
}, Promise.resolve(new Map())); | ||
} | ||
@@ -48,4 +56,9 @@ const assembler = nestjs_query_core_1.AssemblerFactory.getAssembler(RelationClass, mongoose_1.Document); | ||
this.checkForReference('FindRelation', relationName); | ||
const referenceQueryBuilder = this.getReferenceQueryBuilder(relationName); | ||
if (Array.isArray(dto)) { | ||
return this.batchFindRelations(RelationClass, relationName, dto, opts); | ||
return dto.reduce(async (prev, curr) => { | ||
const map = await prev; | ||
const ref = await this.findRelation(RelationClass, relationName, curr, opts); | ||
return map.set(curr, ref); | ||
}, Promise.resolve(new Map())); | ||
} | ||
@@ -57,3 +70,2 @@ const foundEntity = await this.Model.findById(dto._id ?? dto.id); | ||
const assembler = nestjs_query_core_1.AssemblerFactory.getAssembler(RelationClass, mongoose_1.Document); | ||
const referenceQueryBuilder = this.getReferenceQueryBuilder(relationName); | ||
const filterQuery = referenceQueryBuilder.buildFilterQuery(assembler.convertQuery({ filter: opts?.filter }).filter); | ||
@@ -64,101 +76,11 @@ const populated = await foundEntity.populate({ path: relationName, match: filterQuery }); | ||
} | ||
/** | ||
* Query for an array of relations for multiple dtos. | ||
* @param RelationClass - The class to serialize the relations into. | ||
* @param dtos - The entities to query relations for. | ||
* @param relationName - The name of relation to query for. | ||
* @param opts - A query to filter, page or sort relations. | ||
* @param withDeleted - Also query the soft deleted records | ||
*/ | ||
async batchQueryRelations(RelationClass, relationName, dtos, opts, withDeleted) { | ||
const assembler = nestjs_query_core_1.AssemblerFactory.getAssembler(RelationClass, mongoose_1.Document); | ||
const referenceQueryBuilder = this.getReferenceQueryBuilder(relationName); | ||
const query = assembler.convertQuery(opts); | ||
// If paging is enabled, we need to query each entity individually | ||
if (query.paging) { | ||
const entityRelations = await Promise.all(dtos.map((d) => this.queryRelations(RelationClass, relationName, d, opts))); | ||
return entityRelations.reduce((results, relations, index) => { | ||
const e = dtos[index]; | ||
results.set(e, relations); | ||
return results; | ||
}, new Map()); | ||
} | ||
const refFilter = this.getReferenceFilter(relationName, dtos, query.filter); | ||
const results = new Map(); | ||
if (!refFilter) { | ||
return results; | ||
} | ||
const refFieldMap = this.getReferenceFieldMap(relationName); | ||
if (!refFieldMap) { | ||
return results; | ||
} | ||
const { filterQuery, options } = referenceQueryBuilder.buildQuery({ ...query, filter: refFilter }); | ||
const referenceModel = this.getReferenceModel(relationName); | ||
const entityRelations = await referenceModel.find(filterQuery).sort(options.sort).exec(); | ||
for (const dto of dtos) { | ||
const referenceIds = this.getReferenceIds(refFieldMap.localField, dto); | ||
const refs = entityRelations.filter((er) => { | ||
return referenceIds.some((rid) => { | ||
const oneOrManyIds = er[refFieldMap.foreignField]; | ||
const ids = (Array.isArray(oneOrManyIds) ? oneOrManyIds : [oneOrManyIds]); | ||
return ids.some((id) => id.equals(rid)); | ||
}); | ||
}); | ||
results.set(dto, await assembler.convertToDTOs(refs)); | ||
} | ||
return results; | ||
} | ||
/** | ||
* Query for an array of relations for multiple dtos. | ||
* @param RelationClass - The class to serialize the relations into. | ||
* @param entities - The entities to query relations for. | ||
* @param relationName - The name of relation to query for. | ||
* @param filter - Filter. | ||
* @param query - A query to filter, page or sort relations. | ||
*/ | ||
async batchAggregateRelations(RelationClass, relationName, entities, filter, query) { | ||
const entityRelations = await Promise.all(entities.map((e) => this.aggregateRelations(RelationClass, relationName, e, filter, query))); | ||
return entityRelations.reduce((results, relationAggregate, index) => { | ||
const e = entities[index]; | ||
results.set(e, relationAggregate); | ||
return results; | ||
}, new Map()); | ||
} | ||
/** | ||
* Count the number of relations for multiple dtos. | ||
* @param RelationClass - The class to serialize the relations into. | ||
* @param entities - The entities to query relations for. | ||
* @param relationName - The name of relation to query for. | ||
* @param filter - The filter to apply to the relation query. | ||
*/ | ||
async batchCountRelations(RelationClass, relationName, entities, filter) { | ||
const entityRelations = await Promise.all(entities.map((e) => this.countRelations(RelationClass, relationName, e, filter))); | ||
return entityRelations.reduce((results, relationCount, index) => { | ||
const e = entities[index]; | ||
results.set(e, relationCount); | ||
return results; | ||
}, new Map()); | ||
} | ||
/** | ||
* Query for a relation for multiple dtos. | ||
* @param RelationClass - The class to serialize the relations into. | ||
* @param dtos - The dto to query relations for. | ||
* @param relationName - The name of relation to query for. | ||
* @param opts - A query to filter, page or sort relations. | ||
*/ | ||
async batchFindRelations(RelationClass, relationName, dtos, opts) { | ||
const batchResults = await this.batchQueryRelations(RelationClass, relationName, dtos, { | ||
filter: opts?.filter | ||
}, opts?.withDeleted); | ||
const results = new Map(); | ||
batchResults.forEach((relation, dto) => { | ||
// get just the first one. | ||
results.set(dto, relation[0]); | ||
}); | ||
return results; | ||
} | ||
async queryRelations(RelationClass, relationName, dto, query) { | ||
this.checkForReference('QueryRelations', relationName); | ||
const referenceQueryBuilder = this.getReferenceQueryBuilder(relationName); | ||
if (Array.isArray(dto)) { | ||
return this.batchQueryRelations(RelationClass, relationName, dto, query); | ||
return dto.reduce(async (mapPromise, entity) => { | ||
const map = await mapPromise; | ||
const refs = await this.queryRelations(RelationClass, relationName, entity, query); | ||
return map.set(entity, refs); | ||
}, Promise.resolve(new Map())); | ||
} | ||
@@ -170,3 +92,2 @@ const foundEntity = await this.Model.findById(dto._id ?? dto.id); | ||
const assembler = nestjs_query_core_1.AssemblerFactory.getAssembler(RelationClass, mongoose_1.Document); | ||
const referenceQueryBuilder = this.getReferenceQueryBuilder(relationName); | ||
const { filterQuery, options } = referenceQueryBuilder.buildQuery(assembler.convertQuery(query)); | ||
@@ -283,35 +204,30 @@ const populated = await foundEntity.populate({ path: relationName, match: filterQuery, options }); | ||
getReferenceFilter(refName, entity, filter) { | ||
const refFieldMap = this.getReferenceFieldMap(refName); | ||
if (!refFieldMap) { | ||
return undefined; | ||
} | ||
const referenceIds = this.getReferenceIds(refFieldMap.localField, entity); | ||
const refFilter = { | ||
[refFieldMap.foreignField]: { in: referenceIds } | ||
}; | ||
return (0, nestjs_query_core_1.mergeFilter)(filter ?? {}, refFilter); | ||
} | ||
getReferenceIds(localField, entity) { | ||
const entities = Array.isArray(entity) ? entity : [entity]; | ||
return entities.flatMap((e) => e[localField]).filter((id) => !!id); | ||
} | ||
getReferenceFieldMap(refName) { | ||
if (this.isReferencePath(refName)) { | ||
return { | ||
foreignField: '_id', | ||
localField: refName | ||
}; | ||
return this.getObjectIdReferenceFilter(refName, entity, filter); | ||
} | ||
if (this.isVirtualPath(refName)) { | ||
const virtualType = this.Model.schema.virtualpath(refName); | ||
if (!(0, mongoose_types_helper_1.isVirtualTypeWithReferenceOptions)(virtualType)) { | ||
throw new Error(`Unable to lookup reference type for ${refName}`); | ||
if ((0, mongoose_types_helper_1.isVirtualTypeWithReferenceOptions)(virtualType)) { | ||
return this.getVirtualReferenceFilter(virtualType, entity, filter); | ||
} | ||
return { | ||
foreignField: virtualType.options.foreignField, | ||
localField: virtualType.options.localField | ||
}; | ||
throw new Error(`Unable to lookup reference type for ${refName}`); | ||
} | ||
return undefined; | ||
} | ||
getObjectIdReferenceFilter(refName, entity, filter) { | ||
const referenceIds = entity[refName]; | ||
const refFilter = { | ||
_id: { [Array.isArray(referenceIds) ? 'in' : 'eq']: referenceIds } | ||
}; | ||
return (0, nestjs_query_core_1.mergeFilter)(filter ?? {}, refFilter); | ||
} | ||
getVirtualReferenceFilter(virtualType, entity, filter) { | ||
const { foreignField, localField } = virtualType.options; | ||
const refVal = entity[localField]; | ||
const isArray = Array.isArray(refVal); | ||
const lookupFilter = { | ||
[foreignField]: { [isArray ? 'in' : 'eq']: refVal } | ||
}; | ||
return (0, nestjs_query_core_1.mergeFilter)(filter ?? {}, lookupFilter); | ||
} | ||
getRefCount(relationName, relationIds, filter) { | ||
@@ -318,0 +234,0 @@ const referenceModel = this.getReferenceModel(relationName); |
Sorry, the diff of this file is not supported yet
101070
-7.36%1532
-7.15%+ Added
- Removed