nestjs-query-mikro-orm 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -660,53 +660,81 @@ var RelationQueryBuilder = class {
660
660
  this.filterQueryBuilder = new FilterQueryBuilder(relationRepo);
661
661
  }
662
662
  /**
663
- * Executes a relation select using `em.find` so the implementation is database-agnostic.
663
+ * Executes a relation select using MikroORM native relation population first.
664
664
  */
665
- async selectAndExecute(entity, query) {
666
- const relationMeta = this.getRelationMeta();
665
+ async selectAndExecute(entity, query, loadOptions) {
666
+ const nativeRelations = await this.tryLoadRelationsNatively(entity, loadOptions?.useDataloader, loadOptions?.strategy);
667
+ if (!nativeRelations) {
668
+ return this.queryByOwnerCondition(entity, query);
669
+ }
670
+ const resolvedRelations = nativeRelations;
671
+ if (!this.hasFilterSortingOrPaging(query)) {
672
+ return resolvedRelations;
673
+ }
674
+ if (!this.hasFilterOrSorting(query)) {
675
+ return this.applyPaging(resolvedRelations, query);
676
+ }
677
+ return this.queryResolvedRelations(resolvedRelations, query);
678
+ }
679
+ async batchSelectAndExecute(entities, query, loadOptions) {
680
+ if (entities.length === 0) {
681
+ return /* @__PURE__ */ new Map();
682
+ }
683
+ const nativeRelationsMap = await this.tryBatchLoadRelationsNatively(entities, loadOptions?.useDataloader, loadOptions?.strategy);
684
+ if (!nativeRelationsMap) {
685
+ const results2 = /* @__PURE__ */ new Map();
686
+ await Promise.all(entities.map(async (entity) => {
687
+ const relations = await this.queryByOwnerCondition(entity, query);
688
+ if (relations.length > 0) {
689
+ results2.set(entity, relations);
690
+ }
691
+ }));
692
+ return results2;
693
+ }
694
+ if (!this.hasFilterSortingOrPaging(query)) {
695
+ return nativeRelationsMap;
696
+ }
697
+ if (!this.hasFilterOrSorting(query)) {
698
+ return this.applyPagingToMap(nativeRelationsMap, query);
699
+ }
700
+ const results = /* @__PURE__ */ new Map();
701
+ await Promise.all(entities.map(async (entity) => {
702
+ const relations = nativeRelationsMap.get(entity) ?? [];
703
+ const queried = await this.queryResolvedRelations(relations, query);
704
+ if (queried.length > 0) {
705
+ results.set(entity, queried);
706
+ }
707
+ }));
708
+ return results;
709
+ }
710
+ async populateEntityRelation(entity, useDataloader, strategy) {
667
711
  const em = this.repo.getEntityManager();
668
- const RelationEntity = relationMeta.type;
669
- const baseWhere = this.buildWhereCondition(entity, relationMeta);
670
- const { filterQuery, options } = this.filterQueryBuilder.buildFindOptions(query);
671
- const finalWhere = filterQuery ? {
672
- $and: [
673
- baseWhere,
674
- filterQuery
675
- ]
676
- } : baseWhere;
677
- const findOptions = {};
678
- if (options?.orderBy) findOptions.orderBy = options.orderBy;
679
- if (options?.limit !== void 0) findOptions.limit = options.limit;
680
- if (options?.offset !== void 0) findOptions.offset = options.offset;
681
- return await em.find(RelationEntity, finalWhere, findOptions);
712
+ const emPopulate = em;
713
+ try {
714
+ await emPopulate.populate([
715
+ entity
716
+ ], [
717
+ this.relation
718
+ ], this.getNativePopulateOptions(useDataloader, strategy));
719
+ return true;
720
+ } catch {
721
+ return false;
722
+ }
682
723
  }
683
724
  async count(entity, query) {
684
- const relationMeta = this.getRelationMeta();
685
- const em = this.repo.getEntityManager();
686
- const RelationEntity = relationMeta.type;
687
- const baseWhere = this.buildWhereCondition(entity, relationMeta);
688
- const { filterQuery } = this.filterQueryBuilder.buildFindOptions(query);
689
- const finalWhere = filterQuery ? {
690
- $and: [
691
- baseWhere,
692
- filterQuery
693
- ]
694
- } : baseWhere;
695
- return em.count(RelationEntity, finalWhere);
725
+ const relations = await this.selectAndExecute(entity, {
726
+ ...query,
727
+ paging: void 0,
728
+ sorting: void 0
729
+ });
730
+ return relations.length;
696
731
  }
697
732
  async aggregate(entity, query, aggregateQuery) {
698
- const relationMeta = this.getRelationMeta();
699
- const em = this.repo.getEntityManager();
700
- const RelationEntity = relationMeta.type;
701
- const baseWhere = this.buildWhereCondition(entity, relationMeta);
702
- const { filterQuery } = this.filterQueryBuilder.buildFindOptions(query);
703
- const finalWhere = filterQuery ? {
704
- $and: [
705
- baseWhere,
706
- filterQuery
707
- ]
708
- } : baseWhere;
709
- const rows = await em.find(RelationEntity, finalWhere);
733
+ const rows = await this.selectAndExecute(entity, {
734
+ ...query,
735
+ paging: void 0,
736
+ sorting: void 0
737
+ });
710
738
  const aggs = aggregateQuery;
711
739
  const groupBy = aggs.groupBy ?? [];
712
740
  const makeAggKey = /* @__PURE__ */ __name((func, field) => `${func}_${field}`, "makeAggKey");
@@ -817,6 +845,259 @@ var RelationQueryBuilder = class {
817
845
  }
818
846
  return records;
819
847
  }
848
+ getRelationMeta() {
849
+ const em = this.repo.getEntityManager();
850
+ const metadata = em.getMetadata().get(this.repo.getEntityName());
851
+ const relationProp = metadata.properties[this.relation];
852
+ if (!relationProp) {
853
+ throw new Error(`Unable to find relation '${this.relation}' on entity`);
854
+ }
855
+ return relationProp;
856
+ }
857
+ get entityIndexColName() {
858
+ return "__nestjsQuery__entityIndex__";
859
+ }
860
+ getRelationPrimaryKeysPropertyNameAndColumnsName() {
861
+ const em = this.repo.getEntityManager();
862
+ const relationMeta = this.getRelationMeta();
863
+ const relationEntityMeta = em.getMetadata().get(relationMeta.type);
864
+ return relationEntityMeta.primaryKeys.map((pk) => {
865
+ const prop = relationEntityMeta.properties[pk];
866
+ return {
867
+ propertyName: pk,
868
+ columnName: prop.fieldNames?.[0] || pk
869
+ };
870
+ });
871
+ }
872
+ getNativeLoadOptions(useDataloader = false) {
873
+ if (!useDataloader) {
874
+ return void 0;
875
+ }
876
+ return {
877
+ dataloader: true
878
+ };
879
+ }
880
+ getNativePopulateOptions(useDataloader = false, strategy) {
881
+ const nativeLoadOptions = this.getNativeLoadOptions(useDataloader);
882
+ const options = {};
883
+ if (nativeLoadOptions) {
884
+ options.dataloader = true;
885
+ }
886
+ if (strategy) {
887
+ options.strategy = strategy;
888
+ }
889
+ if (!Object.keys(options).length) {
890
+ return void 0;
891
+ }
892
+ return options;
893
+ }
894
+ async tryBatchLoadRelationsNatively(entities, useDataloader = false, strategy) {
895
+ const em = this.repo.getEntityManager();
896
+ const entityName = this.repo.getEntityName();
897
+ const metadata = em.getMetadata().get(entityName);
898
+ const primaryKey = metadata.primaryKeys[0];
899
+ const entityAndIds = entities.map((entity) => {
900
+ const id = entity[primaryKey];
901
+ return [
902
+ entity,
903
+ id
904
+ ];
905
+ });
906
+ const ids = entityAndIds.map(([, id]) => id).filter((id) => id !== void 0 && id !== null);
907
+ if (!ids.length) {
908
+ return /* @__PURE__ */ new Map();
909
+ }
910
+ const where = {
911
+ [primaryKey]: {
912
+ $in: ids
913
+ }
914
+ };
915
+ const findOptions = {
916
+ ...this.getNativePopulateOptions(useDataloader, strategy) ?? {},
917
+ populate: [
918
+ this.relation
919
+ ]
920
+ };
921
+ let populatedEntities;
922
+ try {
923
+ populatedEntities = await em.find(entityName, where, findOptions);
924
+ } catch {
925
+ return void 0;
926
+ }
927
+ const relationMeta = this.getRelationMeta();
928
+ const relationsMap = /* @__PURE__ */ new Map();
929
+ const populatedById = /* @__PURE__ */ new Map();
930
+ populatedEntities.forEach((populatedEntity) => {
931
+ const id = populatedEntity[primaryKey];
932
+ if (id !== void 0 && id !== null) {
933
+ populatedById.set(this.serializePrimaryKeyValue(id), populatedEntity);
934
+ }
935
+ });
936
+ for (const [entity, id] of entityAndIds) {
937
+ if (id === void 0 || id === null) {
938
+ relationsMap.set(entity, []);
939
+ continue;
940
+ }
941
+ const resolvedEntity = populatedById.get(this.serializePrimaryKeyValue(id));
942
+ if (!resolvedEntity) {
943
+ relationsMap.set(entity, []);
944
+ continue;
945
+ }
946
+ const relations = this.readLoadedRelation(resolvedEntity, relationMeta);
947
+ if (relations === void 0) {
948
+ return void 0;
949
+ }
950
+ relationsMap.set(entity, relations);
951
+ }
952
+ return relationsMap;
953
+ }
954
+ async tryLoadRelationsNatively(entity, useDataloader = false, strategy) {
955
+ const relationsMap = await this.tryBatchLoadRelationsNatively([
956
+ entity
957
+ ], useDataloader, strategy);
958
+ if (!relationsMap) {
959
+ return void 0;
960
+ }
961
+ return relationsMap.get(entity) ?? [];
962
+ }
963
+ readLoadedRelation(entity, relationMeta) {
964
+ const entityRecord = entity;
965
+ const propsRecord = entityRecord.props;
966
+ const relationValue = entityRecord[this.relation] !== void 0 ? entityRecord[this.relation] : propsRecord?.[this.relation];
967
+ if (relationMeta.kind === "1:m" || relationMeta.kind === "m:n") {
968
+ if (!relationValue || typeof relationValue !== "object") {
969
+ return void 0;
970
+ }
971
+ const maybeCollection = relationValue;
972
+ if (typeof maybeCollection.getItems === "function") {
973
+ return maybeCollection.getItems(false);
974
+ }
975
+ if (Array.isArray(relationValue)) {
976
+ return relationValue;
977
+ }
978
+ return void 0;
979
+ }
980
+ if (relationValue === null || relationValue === void 0) {
981
+ return [];
982
+ }
983
+ if (typeof relationValue !== "object") {
984
+ return void 0;
985
+ }
986
+ const maybeReference = relationValue;
987
+ if (maybeReference.$ !== void 0) {
988
+ return maybeReference.$ === null ? [] : [
989
+ maybeReference.$
990
+ ];
991
+ }
992
+ if (typeof maybeReference.get === "function") {
993
+ try {
994
+ const loaded = maybeReference.get();
995
+ return loaded === null || loaded === void 0 ? [] : [
996
+ loaded
997
+ ];
998
+ } catch {
999
+ return void 0;
1000
+ }
1001
+ }
1002
+ if (typeof maybeReference.getEntity === "function") {
1003
+ try {
1004
+ const loaded = maybeReference.getEntity();
1005
+ return loaded === null || loaded === void 0 ? [] : [
1006
+ loaded
1007
+ ];
1008
+ } catch {
1009
+ return void 0;
1010
+ }
1011
+ }
1012
+ if (typeof maybeReference.unwrap === "function") {
1013
+ const loaded = maybeReference.unwrap();
1014
+ return loaded === null || loaded === void 0 ? [] : [
1015
+ loaded
1016
+ ];
1017
+ }
1018
+ return [
1019
+ relationValue
1020
+ ];
1021
+ }
1022
+ applyPaging(relations, query) {
1023
+ const limit = query.paging?.limit;
1024
+ const offset = query.paging?.offset ?? 0;
1025
+ if (limit === void 0 && offset === 0) {
1026
+ return relations;
1027
+ }
1028
+ if (limit === void 0) {
1029
+ return relations.slice(offset);
1030
+ }
1031
+ return relations.slice(offset, offset + limit);
1032
+ }
1033
+ applyPagingToMap(relationsMap, query) {
1034
+ const mapped = /* @__PURE__ */ new Map();
1035
+ for (const [entity, relations] of relationsMap.entries()) {
1036
+ const paged = this.applyPaging(relations, query);
1037
+ if (paged.length > 0) {
1038
+ mapped.set(entity, paged);
1039
+ }
1040
+ }
1041
+ return mapped;
1042
+ }
1043
+ serializePrimaryKeyValue(value) {
1044
+ return typeof value === "string" ? value : JSON.stringify(value);
1045
+ }
1046
+ hasFilterSortingOrPaging(query) {
1047
+ return this.hasFilterOrSorting(query) || Boolean(query.paging?.limit || query.paging?.offset);
1048
+ }
1049
+ hasFilterOrSorting(query) {
1050
+ return Boolean(query.sorting?.length) || Boolean(query.filter && Object.keys(query.filter).length);
1051
+ }
1052
+ async queryResolvedRelations(resolvedRelations, query) {
1053
+ if (resolvedRelations.length === 0) {
1054
+ return [];
1055
+ }
1056
+ const em = this.repo.getEntityManager();
1057
+ const relationMeta = this.getRelationMeta();
1058
+ const RelationEntity = relationMeta.type;
1059
+ const metadata = em.getMetadata().get(RelationEntity);
1060
+ const primaryKey = metadata.primaryKeys[0];
1061
+ const ids = resolvedRelations.map((r) => r[primaryKey]).filter((v) => v !== void 0 && v !== null);
1062
+ if (!ids.length) {
1063
+ return [];
1064
+ }
1065
+ const idFilter = {
1066
+ [primaryKey]: {
1067
+ $in: ids
1068
+ }
1069
+ };
1070
+ const { filterQuery, options } = this.filterQueryBuilder.buildFindOptions(query);
1071
+ const finalWhere = filterQuery ? {
1072
+ $and: [
1073
+ idFilter,
1074
+ filterQuery
1075
+ ]
1076
+ } : idFilter;
1077
+ const findOptions = {};
1078
+ if (options?.orderBy) findOptions.orderBy = options.orderBy;
1079
+ if (options?.limit !== void 0) findOptions.limit = options.limit;
1080
+ if (options?.offset !== void 0) findOptions.offset = options.offset;
1081
+ return em.find(RelationEntity, finalWhere, findOptions);
1082
+ }
1083
+ async queryByOwnerCondition(entity, query) {
1084
+ const relationMeta = this.getRelationMeta();
1085
+ const em = this.repo.getEntityManager();
1086
+ const RelationEntity = relationMeta.type;
1087
+ const baseWhere = this.buildWhereCondition(entity, relationMeta);
1088
+ const { filterQuery, options } = this.filterQueryBuilder.buildFindOptions(query);
1089
+ const finalWhere = filterQuery ? {
1090
+ $and: [
1091
+ baseWhere,
1092
+ filterQuery
1093
+ ]
1094
+ } : baseWhere;
1095
+ const findOptions = {};
1096
+ if (options?.orderBy) findOptions.orderBy = options.orderBy;
1097
+ if (options?.limit !== void 0) findOptions.limit = options.limit;
1098
+ if (options?.offset !== void 0) findOptions.offset = options.offset;
1099
+ return em.find(RelationEntity, finalWhere, findOptions);
1100
+ }
820
1101
  buildWhereCondition(entity, relationMeta) {
821
1102
  const em = this.repo.getEntityManager();
822
1103
  const entityMeta = em.getMetadata().get(this.repo.getEntityName());
@@ -888,16 +1169,14 @@ var RelationQueryBuilder = class {
888
1169
  return {
889
1170
  [relationPrimaryKey]: fkValue
890
1171
  };
891
- } else {
892
- return {
893
- [relationMeta.mappedBy]: entityId
894
- };
895
1172
  }
1173
+ return {
1174
+ [relationMeta.mappedBy]: entityId
1175
+ };
896
1176
  }
897
1177
  if (relationMeta.kind === "1:m") {
898
- const mappedBy = relationMeta.mappedBy;
899
1178
  return {
900
- [mappedBy]: entityId
1179
+ [relationMeta.mappedBy]: entityId
901
1180
  };
902
1181
  }
903
1182
  if (relationMeta.kind === "m:n") {
@@ -905,40 +1184,15 @@ var RelationQueryBuilder = class {
905
1184
  return {
906
1185
  [relationMeta.inversedBy]: entityId
907
1186
  };
908
- } else {
909
- return {
910
- [relationMeta.mappedBy]: entityId
911
- };
912
1187
  }
1188
+ return {
1189
+ [relationMeta.mappedBy]: entityId
1190
+ };
913
1191
  }
914
1192
  return {
915
1193
  [entityPrimaryKey]: entityId
916
1194
  };
917
1195
  }
918
- getRelationMeta() {
919
- const em = this.repo.getEntityManager();
920
- const metadata = em.getMetadata().get(this.repo.getEntityName());
921
- const relationProp = metadata.properties[this.relation];
922
- if (!relationProp) {
923
- throw new Error(`Unable to find relation '${this.relation}' on entity`);
924
- }
925
- return relationProp;
926
- }
927
- get entityIndexColName() {
928
- return "__nestjsQuery__entityIndex__";
929
- }
930
- getRelationPrimaryKeysPropertyNameAndColumnsName() {
931
- const em = this.repo.getEntityManager();
932
- const relationMeta = this.getRelationMeta();
933
- const relationEntityMeta = em.getMetadata().get(relationMeta.type);
934
- return relationEntityMeta.primaryKeys.map((pk) => {
935
- const prop = relationEntityMeta.properties[pk];
936
- return {
937
- propertyName: pk,
938
- columnName: prop.fieldNames?.[0] || pk
939
- };
940
- });
941
- }
942
1196
  };
943
1197
  var AGG_REGEXP = /^(AVG|SUM|COUNT|MAX|MIN|GROUP_BY|group_by|groupBy|avg|sum|count|max|min)_(.*)$/i;
944
1198
  var AggregateBuilder = class _AggregateBuilder {
@@ -1177,17 +1431,25 @@ var RelationQueryService = class {
1177
1431
  static {
1178
1432
  __name(this, "RelationQueryService");
1179
1433
  }
1434
+ relationLoadingStrategy;
1435
+ nativeLoadUseDataloader;
1436
+ constructor(opts) {
1437
+ this.relationLoadingStrategy = opts?.relationLoading?.strategy;
1438
+ this.nativeLoadUseDataloader = opts?.relationLoading?.useDataloader ?? false;
1439
+ }
1180
1440
  async queryRelations(RelationClass, relationName, dto, query) {
1181
1441
  if (Array.isArray(dto)) {
1182
1442
  return this.batchQueryRelations(RelationClass, relationName, dto, query);
1183
1443
  }
1184
- if (this.isRelationClassIdentity(RelationClass, relationName)) {
1185
- const relationQueryBuilder2 = this.getRelationQueryBuilder(relationName);
1186
- return await relationQueryBuilder2.selectAndExecute(dto, query);
1187
- }
1188
- const assembler = nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
1444
+ const bypassAssembler = this.isRelationClassIdentity(RelationClass, relationName);
1445
+ const assembler = bypassAssembler ? void 0 : nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
1446
+ const convertedQuery = assembler ? assembler.convertQuery(query) : query;
1189
1447
  const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1190
- return assembler.convertToDTOs(await relationQueryBuilder.selectAndExecute(dto, assembler.convertQuery(query)));
1448
+ const relations = await relationQueryBuilder.selectAndExecute(dto, convertedQuery, this.getRelationLoadOptions());
1449
+ if (bypassAssembler) {
1450
+ return relations;
1451
+ }
1452
+ return assembler.convertToDTOs(relations);
1191
1453
  }
1192
1454
  async aggregateRelations(RelationClass, relationName, dto, filter, aggregate) {
1193
1455
  if (Array.isArray(dto)) {
@@ -1225,7 +1487,7 @@ var RelationQueryService = class {
1225
1487
  paging: {
1226
1488
  limit: 1
1227
1489
  }
1228
- });
1490
+ }, this.getRelationLoadOptions());
1229
1491
  return relations2[0];
1230
1492
  }
1231
1493
  const assembler = nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
@@ -1235,7 +1497,7 @@ var RelationQueryService = class {
1235
1497
  paging: {
1236
1498
  limit: 1
1237
1499
  }
1238
- });
1500
+ }, this.getRelationLoadOptions());
1239
1501
  const relationEntity = relations[0];
1240
1502
  return relationEntity ? assembler.convertToDTO(relationEntity) : void 0;
1241
1503
  }
@@ -1253,19 +1515,24 @@ var RelationQueryService = class {
1253
1515
  if (!this.foundAllRelations(relationIds, relations)) {
1254
1516
  throw new Error(`Unable to find all ${relationName} to add to ${this.EntityClass.name}`);
1255
1517
  }
1256
- if (meta.kind === "1:m" && meta.mappedBy) {
1518
+ const collection = entity[relationName];
1519
+ if (collection && typeof collection.add === "function") {
1520
+ const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1521
+ const populated = await relationQueryBuilder.populateEntityRelation(entity, this.nativeLoadUseDataloader, this.relationLoadingStrategy);
1522
+ if (!populated) {
1523
+ await collection.init();
1524
+ }
1257
1525
  for (const relation of relations) {
1258
- core.wrap(relation).assign({
1259
- [meta.mappedBy]: entity
1260
- });
1526
+ collection.add(relation);
1261
1527
  }
1262
1528
  await this.repo.getEntityManager().flush();
1263
1529
  return entity;
1264
1530
  }
1265
- const collection = entity[relationName];
1266
- if (collection && typeof collection.add === "function") {
1531
+ if (meta.kind === "1:m" && meta.mappedBy) {
1267
1532
  for (const relation of relations) {
1268
- collection.add(relation);
1533
+ core.wrap(relation).assign({
1534
+ [meta.mappedBy]: entity
1535
+ });
1269
1536
  }
1270
1537
  await this.repo.getEntityManager().flush();
1271
1538
  }
@@ -1289,9 +1556,20 @@ var RelationQueryService = class {
1289
1556
  throw new Error(`Unable to find all ${relationName} to set on ${this.EntityClass.name}`);
1290
1557
  }
1291
1558
  }
1559
+ const collection = entity[relationName];
1560
+ if (collection && typeof collection.set === "function") {
1561
+ const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1562
+ const populated = await relationQueryBuilder.populateEntityRelation(entity, this.nativeLoadUseDataloader, this.relationLoadingStrategy);
1563
+ if (!populated) {
1564
+ await collection.init();
1565
+ }
1566
+ collection.set(relations);
1567
+ await this.repo.getEntityManager().flush();
1568
+ return entity;
1569
+ }
1292
1570
  if (meta.kind === "1:m" && meta.mappedBy) {
1293
1571
  const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1294
- const currentRelations = await relationQueryBuilder.selectAndExecute(entity, {});
1572
+ const currentRelations = await relationQueryBuilder.selectAndExecute(entity, {}, this.getRelationLoadOptions());
1295
1573
  const nextSet = new Set(relations.map((r) => core.wrap(r).getPrimaryKey()));
1296
1574
  for (const currentRelation of currentRelations) {
1297
1575
  const currentPk = core.wrap(currentRelation).getPrimaryKey();
@@ -1307,13 +1585,6 @@ var RelationQueryService = class {
1307
1585
  });
1308
1586
  }
1309
1587
  await this.repo.getEntityManager().flush();
1310
- return entity;
1311
- }
1312
- const collection = entity[relationName];
1313
- if (collection && typeof collection.set === "function") {
1314
- await collection.init();
1315
- collection.set(relations);
1316
- await this.repo.getEntityManager().flush();
1317
1588
  }
1318
1589
  return entity;
1319
1590
  }
@@ -1353,20 +1624,24 @@ var RelationQueryService = class {
1353
1624
  if (!this.foundAllRelations(relationIds, relations)) {
1354
1625
  throw new Error(`Unable to find all ${relationName} to remove from ${this.EntityClass.name}`);
1355
1626
  }
1356
- if (meta.kind === "1:m" && meta.mappedBy) {
1627
+ const collection = entity[relationName];
1628
+ if (collection && typeof collection.remove === "function") {
1629
+ const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1630
+ const populated = await relationQueryBuilder.populateEntityRelation(entity, this.nativeLoadUseDataloader, this.relationLoadingStrategy);
1631
+ if (!populated) {
1632
+ await collection.init();
1633
+ }
1357
1634
  for (const relation of relations) {
1358
- core.wrap(relation).assign({
1359
- [meta.mappedBy]: null
1360
- });
1635
+ collection.remove(relation);
1361
1636
  }
1362
1637
  await this.repo.getEntityManager().flush();
1363
1638
  return entity;
1364
1639
  }
1365
- const collection = entity[relationName];
1366
- if (collection && typeof collection.remove === "function") {
1367
- await collection.init();
1640
+ if (meta.kind === "1:m" && meta.mappedBy) {
1368
1641
  for (const relation of relations) {
1369
- collection.remove(relation);
1642
+ core.wrap(relation).assign({
1643
+ [meta.mappedBy]: null
1644
+ });
1370
1645
  }
1371
1646
  await this.repo.getEntityManager().flush();
1372
1647
  }
@@ -1390,29 +1665,30 @@ var RelationQueryService = class {
1390
1665
  const meta = this.getRelationMeta(relationName);
1391
1666
  if (meta.kind === "1:1" || meta.kind === "m:1") {
1392
1667
  const fkFieldName = `${relationName}Id`;
1393
- const assignData = {};
1668
+ const assignData = {
1669
+ [relationName]: null
1670
+ };
1394
1671
  const ownDescriptor = Object.getOwnPropertyDescriptor(entity, fkFieldName);
1395
1672
  const protoDescriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), fkFieldName);
1396
1673
  const descriptor = ownDescriptor ?? protoDescriptor;
1397
1674
  const canAssignFk = fkFieldName in entity && (!descriptor || Boolean(descriptor.set) || descriptor.writable === true);
1398
1675
  if (canAssignFk) {
1399
1676
  assignData[fkFieldName] = null;
1400
- } else {
1401
- assignData[relationName] = null;
1402
1677
  }
1403
1678
  core.wrap(entity).assign(assignData);
1404
1679
  } else {
1405
- if (meta.kind === "1:m" && meta.mappedBy) {
1406
- core.wrap(relation).assign({
1407
- [meta.mappedBy]: null
1408
- });
1409
- await this.repo.getEntityManager().flush();
1410
- return entity;
1411
- }
1412
1680
  const collection = entity[relationName];
1413
1681
  if (collection && typeof collection.remove === "function") {
1414
- await collection.init();
1682
+ const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1683
+ const populated = await relationQueryBuilder.populateEntityRelation(entity, this.nativeLoadUseDataloader, this.relationLoadingStrategy);
1684
+ if (!populated) {
1685
+ await collection.init();
1686
+ }
1415
1687
  collection.remove(relation);
1688
+ } else if (meta.kind === "1:m" && meta.mappedBy) {
1689
+ core.wrap(relation).assign({
1690
+ [meta.mappedBy]: null
1691
+ });
1416
1692
  }
1417
1693
  }
1418
1694
  await this.repo.getEntityManager().flush();
@@ -1433,15 +1709,18 @@ var RelationQueryService = class {
1433
1709
  const assembler = bypassAssembler ? void 0 : nestjsQueryCore.AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
1434
1710
  const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1435
1711
  const convertedQuery = assembler ? assembler.convertQuery(query) : query;
1436
- const results = /* @__PURE__ */ new Map();
1437
- await Promise.all(entities.map(async (entity) => {
1438
- const relations = await relationQueryBuilder.selectAndExecute(entity, convertedQuery);
1439
- const relationDtos = bypassAssembler ? relations : await assembler.convertToDTOs(relations);
1712
+ const relationsMap = await relationQueryBuilder.batchSelectAndExecute(entities, convertedQuery, this.getRelationLoadOptions());
1713
+ if (bypassAssembler) {
1714
+ return relationsMap;
1715
+ }
1716
+ const dtoMap = /* @__PURE__ */ new Map();
1717
+ for (const [entity, relations] of relationsMap.entries()) {
1718
+ const relationDtos = await assembler.convertToDTOs(relations);
1440
1719
  if (relationDtos.length > 0) {
1441
- results.set(entity, relationDtos);
1720
+ dtoMap.set(entity, relationDtos);
1442
1721
  }
1443
- }));
1444
- return results;
1722
+ }
1723
+ return dtoMap;
1445
1724
  }
1446
1725
  /**
1447
1726
  * Query for an array of relations for multiple dtos.
@@ -1510,6 +1789,12 @@ var RelationQueryService = class {
1510
1789
  const relationEntity = this.getRelationEntity(relationName);
1511
1790
  return RelationClass === relationEntity || RelationClass.name === relationEntity.name;
1512
1791
  }
1792
+ getRelationLoadOptions() {
1793
+ return {
1794
+ strategy: this.relationLoadingStrategy,
1795
+ useDataloader: this.nativeLoadUseDataloader
1796
+ };
1797
+ }
1513
1798
  getRelationMeta(relationName) {
1514
1799
  const em = this.repo.getEntityManager();
1515
1800
  const metadata = em.getMetadata().get(this.repo.getEntityName());
@@ -1568,7 +1853,9 @@ var MikroOrmQueryService = class extends RelationQueryService {
1568
1853
  filterQueryBuilder;
1569
1854
  useSoftDelete;
1570
1855
  constructor(repo, opts) {
1571
- super(), this.repo = repo;
1856
+ super({
1857
+ relationLoading: opts?.relationLoading
1858
+ }), this.repo = repo;
1572
1859
  this.filterQueryBuilder = opts?.filterQueryBuilder ?? new FilterQueryBuilder(this.repo);
1573
1860
  this.useSoftDelete = opts?.useSoftDelete ?? false;
1574
1861
  const serializer = assembler_serializer.getAssemblerSerializer(this.EntityClass);
@@ -1589,14 +1876,12 @@ var MikroOrmQueryService = class extends RelationQueryService {
1589
1876
  return data;
1590
1877
  })(this.EntityClass);
1591
1878
  nestjsQueryCore.AssemblerDeserializer((d) => {
1592
- const wrapped = core.wrap(d, true);
1593
- if (wrapped.getPrimaryKey()) {
1594
- const entity = this.repo.getEntityManager().merge(this.EntityClass, d);
1595
- return entity;
1596
- } else {
1597
- const entity = this.repo.getEntityManager().create(this.EntityClass, d);
1598
- return entity;
1599
- }
1879
+ const entity = this.repo.getEntityManager().create(this.EntityClass, d, {
1880
+ managed: true,
1881
+ convertCustomTypes: true,
1882
+ partial: true
1883
+ });
1884
+ return entity;
1600
1885
  })(this.EntityClass);
1601
1886
  }
1602
1887
  }
@@ -2092,12 +2377,12 @@ var MikroOrmQueryService = class extends RelationQueryService {
2092
2377
  };
2093
2378
 
2094
2379
  // src/lib/providers.ts
2095
- function createMikroOrmQueryServiceProvider(EntityClass, contextName) {
2380
+ function createMikroOrmQueryServiceProvider(EntityClass, contextName, opts) {
2096
2381
  return {
2097
2382
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2098
2383
  provide: nestjsQueryCore.getQueryServiceToken(EntityClass),
2099
2384
  useFactory(repo) {
2100
- return new MikroOrmQueryService(repo);
2385
+ return new MikroOrmQueryService(repo, opts);
2101
2386
  },
2102
2387
  inject: [
2103
2388
  nestjs.getRepositoryToken(EntityClass, contextName)
@@ -2105,15 +2390,17 @@ function createMikroOrmQueryServiceProvider(EntityClass, contextName) {
2105
2390
  };
2106
2391
  }
2107
2392
  __name(createMikroOrmQueryServiceProvider, "createMikroOrmQueryServiceProvider");
2108
- var createMikroOrmQueryServiceProviders = /* @__PURE__ */ __name((entities, contextName) => entities.map((entity) => createMikroOrmQueryServiceProvider(entity, contextName)), "createMikroOrmQueryServiceProviders");
2393
+ var createMikroOrmQueryServiceProviders = /* @__PURE__ */ __name((entities, contextName, opts) => entities.map((entity) => createMikroOrmQueryServiceProvider(entity, contextName, opts)), "createMikroOrmQueryServiceProviders");
2109
2394
 
2110
2395
  // src/lib/nest-query-mikro-orm.module.ts
2111
2396
  var NestjsQueryMikroOrmModule = class _NestjsQueryMikroOrmModule {
2112
2397
  static {
2113
2398
  __name(this, "NestjsQueryMikroOrmModule");
2114
2399
  }
2115
- static forFeature(entities, contextName) {
2116
- const queryServiceProviders = createMikroOrmQueryServiceProviders(entities, contextName);
2400
+ static forFeature(entities, contextNameOrOptions) {
2401
+ const contextName = typeof contextNameOrOptions === "string" ? contextNameOrOptions : contextNameOrOptions?.contextName;
2402
+ const queryServiceOpts = typeof contextNameOrOptions === "string" ? void 0 : contextNameOrOptions?.queryServiceOpts;
2403
+ const queryServiceProviders = createMikroOrmQueryServiceProviders(entities, contextName, queryServiceOpts);
2117
2404
  const mikroOrmModule = nestjs.MikroOrmModule.forFeature(entities, contextName);
2118
2405
  return {
2119
2406
  imports: [