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.mjs CHANGED
@@ -654,53 +654,81 @@ var RelationQueryBuilder = class {
654
654
  this.filterQueryBuilder = new FilterQueryBuilder(relationRepo);
655
655
  }
656
656
  /**
657
- * Executes a relation select using `em.find` so the implementation is database-agnostic.
657
+ * Executes a relation select using MikroORM native relation population first.
658
658
  */
659
- async selectAndExecute(entity, query) {
660
- const relationMeta = this.getRelationMeta();
659
+ async selectAndExecute(entity, query, loadOptions) {
660
+ const nativeRelations = await this.tryLoadRelationsNatively(entity, loadOptions?.useDataloader, loadOptions?.strategy);
661
+ if (!nativeRelations) {
662
+ return this.queryByOwnerCondition(entity, query);
663
+ }
664
+ const resolvedRelations = nativeRelations;
665
+ if (!this.hasFilterSortingOrPaging(query)) {
666
+ return resolvedRelations;
667
+ }
668
+ if (!this.hasFilterOrSorting(query)) {
669
+ return this.applyPaging(resolvedRelations, query);
670
+ }
671
+ return this.queryResolvedRelations(resolvedRelations, query);
672
+ }
673
+ async batchSelectAndExecute(entities, query, loadOptions) {
674
+ if (entities.length === 0) {
675
+ return /* @__PURE__ */ new Map();
676
+ }
677
+ const nativeRelationsMap = await this.tryBatchLoadRelationsNatively(entities, loadOptions?.useDataloader, loadOptions?.strategy);
678
+ if (!nativeRelationsMap) {
679
+ const results2 = /* @__PURE__ */ new Map();
680
+ await Promise.all(entities.map(async (entity) => {
681
+ const relations = await this.queryByOwnerCondition(entity, query);
682
+ if (relations.length > 0) {
683
+ results2.set(entity, relations);
684
+ }
685
+ }));
686
+ return results2;
687
+ }
688
+ if (!this.hasFilterSortingOrPaging(query)) {
689
+ return nativeRelationsMap;
690
+ }
691
+ if (!this.hasFilterOrSorting(query)) {
692
+ return this.applyPagingToMap(nativeRelationsMap, query);
693
+ }
694
+ const results = /* @__PURE__ */ new Map();
695
+ await Promise.all(entities.map(async (entity) => {
696
+ const relations = nativeRelationsMap.get(entity) ?? [];
697
+ const queried = await this.queryResolvedRelations(relations, query);
698
+ if (queried.length > 0) {
699
+ results.set(entity, queried);
700
+ }
701
+ }));
702
+ return results;
703
+ }
704
+ async populateEntityRelation(entity, useDataloader, strategy) {
661
705
  const em = this.repo.getEntityManager();
662
- const RelationEntity = relationMeta.type;
663
- const baseWhere = this.buildWhereCondition(entity, relationMeta);
664
- const { filterQuery, options } = this.filterQueryBuilder.buildFindOptions(query);
665
- const finalWhere = filterQuery ? {
666
- $and: [
667
- baseWhere,
668
- filterQuery
669
- ]
670
- } : baseWhere;
671
- const findOptions = {};
672
- if (options?.orderBy) findOptions.orderBy = options.orderBy;
673
- if (options?.limit !== void 0) findOptions.limit = options.limit;
674
- if (options?.offset !== void 0) findOptions.offset = options.offset;
675
- return await em.find(RelationEntity, finalWhere, findOptions);
706
+ const emPopulate = em;
707
+ try {
708
+ await emPopulate.populate([
709
+ entity
710
+ ], [
711
+ this.relation
712
+ ], this.getNativePopulateOptions(useDataloader, strategy));
713
+ return true;
714
+ } catch {
715
+ return false;
716
+ }
676
717
  }
677
718
  async count(entity, query) {
678
- const relationMeta = this.getRelationMeta();
679
- const em = this.repo.getEntityManager();
680
- const RelationEntity = relationMeta.type;
681
- const baseWhere = this.buildWhereCondition(entity, relationMeta);
682
- const { filterQuery } = this.filterQueryBuilder.buildFindOptions(query);
683
- const finalWhere = filterQuery ? {
684
- $and: [
685
- baseWhere,
686
- filterQuery
687
- ]
688
- } : baseWhere;
689
- return em.count(RelationEntity, finalWhere);
719
+ const relations = await this.selectAndExecute(entity, {
720
+ ...query,
721
+ paging: void 0,
722
+ sorting: void 0
723
+ });
724
+ return relations.length;
690
725
  }
691
726
  async aggregate(entity, query, aggregateQuery) {
692
- const relationMeta = this.getRelationMeta();
693
- const em = this.repo.getEntityManager();
694
- const RelationEntity = relationMeta.type;
695
- const baseWhere = this.buildWhereCondition(entity, relationMeta);
696
- const { filterQuery } = this.filterQueryBuilder.buildFindOptions(query);
697
- const finalWhere = filterQuery ? {
698
- $and: [
699
- baseWhere,
700
- filterQuery
701
- ]
702
- } : baseWhere;
703
- const rows = await em.find(RelationEntity, finalWhere);
727
+ const rows = await this.selectAndExecute(entity, {
728
+ ...query,
729
+ paging: void 0,
730
+ sorting: void 0
731
+ });
704
732
  const aggs = aggregateQuery;
705
733
  const groupBy = aggs.groupBy ?? [];
706
734
  const makeAggKey = /* @__PURE__ */ __name((func, field) => `${func}_${field}`, "makeAggKey");
@@ -811,6 +839,259 @@ var RelationQueryBuilder = class {
811
839
  }
812
840
  return records;
813
841
  }
842
+ getRelationMeta() {
843
+ const em = this.repo.getEntityManager();
844
+ const metadata = em.getMetadata().get(this.repo.getEntityName());
845
+ const relationProp = metadata.properties[this.relation];
846
+ if (!relationProp) {
847
+ throw new Error(`Unable to find relation '${this.relation}' on entity`);
848
+ }
849
+ return relationProp;
850
+ }
851
+ get entityIndexColName() {
852
+ return "__nestjsQuery__entityIndex__";
853
+ }
854
+ getRelationPrimaryKeysPropertyNameAndColumnsName() {
855
+ const em = this.repo.getEntityManager();
856
+ const relationMeta = this.getRelationMeta();
857
+ const relationEntityMeta = em.getMetadata().get(relationMeta.type);
858
+ return relationEntityMeta.primaryKeys.map((pk) => {
859
+ const prop = relationEntityMeta.properties[pk];
860
+ return {
861
+ propertyName: pk,
862
+ columnName: prop.fieldNames?.[0] || pk
863
+ };
864
+ });
865
+ }
866
+ getNativeLoadOptions(useDataloader = false) {
867
+ if (!useDataloader) {
868
+ return void 0;
869
+ }
870
+ return {
871
+ dataloader: true
872
+ };
873
+ }
874
+ getNativePopulateOptions(useDataloader = false, strategy) {
875
+ const nativeLoadOptions = this.getNativeLoadOptions(useDataloader);
876
+ const options = {};
877
+ if (nativeLoadOptions) {
878
+ options.dataloader = true;
879
+ }
880
+ if (strategy) {
881
+ options.strategy = strategy;
882
+ }
883
+ if (!Object.keys(options).length) {
884
+ return void 0;
885
+ }
886
+ return options;
887
+ }
888
+ async tryBatchLoadRelationsNatively(entities, useDataloader = false, strategy) {
889
+ const em = this.repo.getEntityManager();
890
+ const entityName = this.repo.getEntityName();
891
+ const metadata = em.getMetadata().get(entityName);
892
+ const primaryKey = metadata.primaryKeys[0];
893
+ const entityAndIds = entities.map((entity) => {
894
+ const id = entity[primaryKey];
895
+ return [
896
+ entity,
897
+ id
898
+ ];
899
+ });
900
+ const ids = entityAndIds.map(([, id]) => id).filter((id) => id !== void 0 && id !== null);
901
+ if (!ids.length) {
902
+ return /* @__PURE__ */ new Map();
903
+ }
904
+ const where = {
905
+ [primaryKey]: {
906
+ $in: ids
907
+ }
908
+ };
909
+ const findOptions = {
910
+ ...this.getNativePopulateOptions(useDataloader, strategy) ?? {},
911
+ populate: [
912
+ this.relation
913
+ ]
914
+ };
915
+ let populatedEntities;
916
+ try {
917
+ populatedEntities = await em.find(entityName, where, findOptions);
918
+ } catch {
919
+ return void 0;
920
+ }
921
+ const relationMeta = this.getRelationMeta();
922
+ const relationsMap = /* @__PURE__ */ new Map();
923
+ const populatedById = /* @__PURE__ */ new Map();
924
+ populatedEntities.forEach((populatedEntity) => {
925
+ const id = populatedEntity[primaryKey];
926
+ if (id !== void 0 && id !== null) {
927
+ populatedById.set(this.serializePrimaryKeyValue(id), populatedEntity);
928
+ }
929
+ });
930
+ for (const [entity, id] of entityAndIds) {
931
+ if (id === void 0 || id === null) {
932
+ relationsMap.set(entity, []);
933
+ continue;
934
+ }
935
+ const resolvedEntity = populatedById.get(this.serializePrimaryKeyValue(id));
936
+ if (!resolvedEntity) {
937
+ relationsMap.set(entity, []);
938
+ continue;
939
+ }
940
+ const relations = this.readLoadedRelation(resolvedEntity, relationMeta);
941
+ if (relations === void 0) {
942
+ return void 0;
943
+ }
944
+ relationsMap.set(entity, relations);
945
+ }
946
+ return relationsMap;
947
+ }
948
+ async tryLoadRelationsNatively(entity, useDataloader = false, strategy) {
949
+ const relationsMap = await this.tryBatchLoadRelationsNatively([
950
+ entity
951
+ ], useDataloader, strategy);
952
+ if (!relationsMap) {
953
+ return void 0;
954
+ }
955
+ return relationsMap.get(entity) ?? [];
956
+ }
957
+ readLoadedRelation(entity, relationMeta) {
958
+ const entityRecord = entity;
959
+ const propsRecord = entityRecord.props;
960
+ const relationValue = entityRecord[this.relation] !== void 0 ? entityRecord[this.relation] : propsRecord?.[this.relation];
961
+ if (relationMeta.kind === "1:m" || relationMeta.kind === "m:n") {
962
+ if (!relationValue || typeof relationValue !== "object") {
963
+ return void 0;
964
+ }
965
+ const maybeCollection = relationValue;
966
+ if (typeof maybeCollection.getItems === "function") {
967
+ return maybeCollection.getItems(false);
968
+ }
969
+ if (Array.isArray(relationValue)) {
970
+ return relationValue;
971
+ }
972
+ return void 0;
973
+ }
974
+ if (relationValue === null || relationValue === void 0) {
975
+ return [];
976
+ }
977
+ if (typeof relationValue !== "object") {
978
+ return void 0;
979
+ }
980
+ const maybeReference = relationValue;
981
+ if (maybeReference.$ !== void 0) {
982
+ return maybeReference.$ === null ? [] : [
983
+ maybeReference.$
984
+ ];
985
+ }
986
+ if (typeof maybeReference.get === "function") {
987
+ try {
988
+ const loaded = maybeReference.get();
989
+ return loaded === null || loaded === void 0 ? [] : [
990
+ loaded
991
+ ];
992
+ } catch {
993
+ return void 0;
994
+ }
995
+ }
996
+ if (typeof maybeReference.getEntity === "function") {
997
+ try {
998
+ const loaded = maybeReference.getEntity();
999
+ return loaded === null || loaded === void 0 ? [] : [
1000
+ loaded
1001
+ ];
1002
+ } catch {
1003
+ return void 0;
1004
+ }
1005
+ }
1006
+ if (typeof maybeReference.unwrap === "function") {
1007
+ const loaded = maybeReference.unwrap();
1008
+ return loaded === null || loaded === void 0 ? [] : [
1009
+ loaded
1010
+ ];
1011
+ }
1012
+ return [
1013
+ relationValue
1014
+ ];
1015
+ }
1016
+ applyPaging(relations, query) {
1017
+ const limit = query.paging?.limit;
1018
+ const offset = query.paging?.offset ?? 0;
1019
+ if (limit === void 0 && offset === 0) {
1020
+ return relations;
1021
+ }
1022
+ if (limit === void 0) {
1023
+ return relations.slice(offset);
1024
+ }
1025
+ return relations.slice(offset, offset + limit);
1026
+ }
1027
+ applyPagingToMap(relationsMap, query) {
1028
+ const mapped = /* @__PURE__ */ new Map();
1029
+ for (const [entity, relations] of relationsMap.entries()) {
1030
+ const paged = this.applyPaging(relations, query);
1031
+ if (paged.length > 0) {
1032
+ mapped.set(entity, paged);
1033
+ }
1034
+ }
1035
+ return mapped;
1036
+ }
1037
+ serializePrimaryKeyValue(value) {
1038
+ return typeof value === "string" ? value : JSON.stringify(value);
1039
+ }
1040
+ hasFilterSortingOrPaging(query) {
1041
+ return this.hasFilterOrSorting(query) || Boolean(query.paging?.limit || query.paging?.offset);
1042
+ }
1043
+ hasFilterOrSorting(query) {
1044
+ return Boolean(query.sorting?.length) || Boolean(query.filter && Object.keys(query.filter).length);
1045
+ }
1046
+ async queryResolvedRelations(resolvedRelations, query) {
1047
+ if (resolvedRelations.length === 0) {
1048
+ return [];
1049
+ }
1050
+ const em = this.repo.getEntityManager();
1051
+ const relationMeta = this.getRelationMeta();
1052
+ const RelationEntity = relationMeta.type;
1053
+ const metadata = em.getMetadata().get(RelationEntity);
1054
+ const primaryKey = metadata.primaryKeys[0];
1055
+ const ids = resolvedRelations.map((r) => r[primaryKey]).filter((v) => v !== void 0 && v !== null);
1056
+ if (!ids.length) {
1057
+ return [];
1058
+ }
1059
+ const idFilter = {
1060
+ [primaryKey]: {
1061
+ $in: ids
1062
+ }
1063
+ };
1064
+ const { filterQuery, options } = this.filterQueryBuilder.buildFindOptions(query);
1065
+ const finalWhere = filterQuery ? {
1066
+ $and: [
1067
+ idFilter,
1068
+ filterQuery
1069
+ ]
1070
+ } : idFilter;
1071
+ const findOptions = {};
1072
+ if (options?.orderBy) findOptions.orderBy = options.orderBy;
1073
+ if (options?.limit !== void 0) findOptions.limit = options.limit;
1074
+ if (options?.offset !== void 0) findOptions.offset = options.offset;
1075
+ return em.find(RelationEntity, finalWhere, findOptions);
1076
+ }
1077
+ async queryByOwnerCondition(entity, query) {
1078
+ const relationMeta = this.getRelationMeta();
1079
+ const em = this.repo.getEntityManager();
1080
+ const RelationEntity = relationMeta.type;
1081
+ const baseWhere = this.buildWhereCondition(entity, relationMeta);
1082
+ const { filterQuery, options } = this.filterQueryBuilder.buildFindOptions(query);
1083
+ const finalWhere = filterQuery ? {
1084
+ $and: [
1085
+ baseWhere,
1086
+ filterQuery
1087
+ ]
1088
+ } : baseWhere;
1089
+ const findOptions = {};
1090
+ if (options?.orderBy) findOptions.orderBy = options.orderBy;
1091
+ if (options?.limit !== void 0) findOptions.limit = options.limit;
1092
+ if (options?.offset !== void 0) findOptions.offset = options.offset;
1093
+ return em.find(RelationEntity, finalWhere, findOptions);
1094
+ }
814
1095
  buildWhereCondition(entity, relationMeta) {
815
1096
  const em = this.repo.getEntityManager();
816
1097
  const entityMeta = em.getMetadata().get(this.repo.getEntityName());
@@ -882,16 +1163,14 @@ var RelationQueryBuilder = class {
882
1163
  return {
883
1164
  [relationPrimaryKey]: fkValue
884
1165
  };
885
- } else {
886
- return {
887
- [relationMeta.mappedBy]: entityId
888
- };
889
1166
  }
1167
+ return {
1168
+ [relationMeta.mappedBy]: entityId
1169
+ };
890
1170
  }
891
1171
  if (relationMeta.kind === "1:m") {
892
- const mappedBy = relationMeta.mappedBy;
893
1172
  return {
894
- [mappedBy]: entityId
1173
+ [relationMeta.mappedBy]: entityId
895
1174
  };
896
1175
  }
897
1176
  if (relationMeta.kind === "m:n") {
@@ -899,40 +1178,15 @@ var RelationQueryBuilder = class {
899
1178
  return {
900
1179
  [relationMeta.inversedBy]: entityId
901
1180
  };
902
- } else {
903
- return {
904
- [relationMeta.mappedBy]: entityId
905
- };
906
1181
  }
1182
+ return {
1183
+ [relationMeta.mappedBy]: entityId
1184
+ };
907
1185
  }
908
1186
  return {
909
1187
  [entityPrimaryKey]: entityId
910
1188
  };
911
1189
  }
912
- getRelationMeta() {
913
- const em = this.repo.getEntityManager();
914
- const metadata = em.getMetadata().get(this.repo.getEntityName());
915
- const relationProp = metadata.properties[this.relation];
916
- if (!relationProp) {
917
- throw new Error(`Unable to find relation '${this.relation}' on entity`);
918
- }
919
- return relationProp;
920
- }
921
- get entityIndexColName() {
922
- return "__nestjsQuery__entityIndex__";
923
- }
924
- getRelationPrimaryKeysPropertyNameAndColumnsName() {
925
- const em = this.repo.getEntityManager();
926
- const relationMeta = this.getRelationMeta();
927
- const relationEntityMeta = em.getMetadata().get(relationMeta.type);
928
- return relationEntityMeta.primaryKeys.map((pk) => {
929
- const prop = relationEntityMeta.properties[pk];
930
- return {
931
- propertyName: pk,
932
- columnName: prop.fieldNames?.[0] || pk
933
- };
934
- });
935
- }
936
1190
  };
937
1191
  var AGG_REGEXP = /^(AVG|SUM|COUNT|MAX|MIN|GROUP_BY|group_by|groupBy|avg|sum|count|max|min)_(.*)$/i;
938
1192
  var AggregateBuilder = class _AggregateBuilder {
@@ -1171,17 +1425,25 @@ var RelationQueryService = class {
1171
1425
  static {
1172
1426
  __name(this, "RelationQueryService");
1173
1427
  }
1428
+ relationLoadingStrategy;
1429
+ nativeLoadUseDataloader;
1430
+ constructor(opts) {
1431
+ this.relationLoadingStrategy = opts?.relationLoading?.strategy;
1432
+ this.nativeLoadUseDataloader = opts?.relationLoading?.useDataloader ?? false;
1433
+ }
1174
1434
  async queryRelations(RelationClass, relationName, dto, query) {
1175
1435
  if (Array.isArray(dto)) {
1176
1436
  return this.batchQueryRelations(RelationClass, relationName, dto, query);
1177
1437
  }
1178
- if (this.isRelationClassIdentity(RelationClass, relationName)) {
1179
- const relationQueryBuilder2 = this.getRelationQueryBuilder(relationName);
1180
- return await relationQueryBuilder2.selectAndExecute(dto, query);
1181
- }
1182
- const assembler = AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
1438
+ const bypassAssembler = this.isRelationClassIdentity(RelationClass, relationName);
1439
+ const assembler = bypassAssembler ? void 0 : AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
1440
+ const convertedQuery = assembler ? assembler.convertQuery(query) : query;
1183
1441
  const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1184
- return assembler.convertToDTOs(await relationQueryBuilder.selectAndExecute(dto, assembler.convertQuery(query)));
1442
+ const relations = await relationQueryBuilder.selectAndExecute(dto, convertedQuery, this.getRelationLoadOptions());
1443
+ if (bypassAssembler) {
1444
+ return relations;
1445
+ }
1446
+ return assembler.convertToDTOs(relations);
1185
1447
  }
1186
1448
  async aggregateRelations(RelationClass, relationName, dto, filter, aggregate) {
1187
1449
  if (Array.isArray(dto)) {
@@ -1219,7 +1481,7 @@ var RelationQueryService = class {
1219
1481
  paging: {
1220
1482
  limit: 1
1221
1483
  }
1222
- });
1484
+ }, this.getRelationLoadOptions());
1223
1485
  return relations2[0];
1224
1486
  }
1225
1487
  const assembler = AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
@@ -1229,7 +1491,7 @@ var RelationQueryService = class {
1229
1491
  paging: {
1230
1492
  limit: 1
1231
1493
  }
1232
- });
1494
+ }, this.getRelationLoadOptions());
1233
1495
  const relationEntity = relations[0];
1234
1496
  return relationEntity ? assembler.convertToDTO(relationEntity) : void 0;
1235
1497
  }
@@ -1247,19 +1509,24 @@ var RelationQueryService = class {
1247
1509
  if (!this.foundAllRelations(relationIds, relations)) {
1248
1510
  throw new Error(`Unable to find all ${relationName} to add to ${this.EntityClass.name}`);
1249
1511
  }
1250
- if (meta.kind === "1:m" && meta.mappedBy) {
1512
+ const collection = entity[relationName];
1513
+ if (collection && typeof collection.add === "function") {
1514
+ const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1515
+ const populated = await relationQueryBuilder.populateEntityRelation(entity, this.nativeLoadUseDataloader, this.relationLoadingStrategy);
1516
+ if (!populated) {
1517
+ await collection.init();
1518
+ }
1251
1519
  for (const relation of relations) {
1252
- wrap(relation).assign({
1253
- [meta.mappedBy]: entity
1254
- });
1520
+ collection.add(relation);
1255
1521
  }
1256
1522
  await this.repo.getEntityManager().flush();
1257
1523
  return entity;
1258
1524
  }
1259
- const collection = entity[relationName];
1260
- if (collection && typeof collection.add === "function") {
1525
+ if (meta.kind === "1:m" && meta.mappedBy) {
1261
1526
  for (const relation of relations) {
1262
- collection.add(relation);
1527
+ wrap(relation).assign({
1528
+ [meta.mappedBy]: entity
1529
+ });
1263
1530
  }
1264
1531
  await this.repo.getEntityManager().flush();
1265
1532
  }
@@ -1283,9 +1550,20 @@ var RelationQueryService = class {
1283
1550
  throw new Error(`Unable to find all ${relationName} to set on ${this.EntityClass.name}`);
1284
1551
  }
1285
1552
  }
1553
+ const collection = entity[relationName];
1554
+ if (collection && typeof collection.set === "function") {
1555
+ const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1556
+ const populated = await relationQueryBuilder.populateEntityRelation(entity, this.nativeLoadUseDataloader, this.relationLoadingStrategy);
1557
+ if (!populated) {
1558
+ await collection.init();
1559
+ }
1560
+ collection.set(relations);
1561
+ await this.repo.getEntityManager().flush();
1562
+ return entity;
1563
+ }
1286
1564
  if (meta.kind === "1:m" && meta.mappedBy) {
1287
1565
  const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1288
- const currentRelations = await relationQueryBuilder.selectAndExecute(entity, {});
1566
+ const currentRelations = await relationQueryBuilder.selectAndExecute(entity, {}, this.getRelationLoadOptions());
1289
1567
  const nextSet = new Set(relations.map((r) => wrap(r).getPrimaryKey()));
1290
1568
  for (const currentRelation of currentRelations) {
1291
1569
  const currentPk = wrap(currentRelation).getPrimaryKey();
@@ -1301,13 +1579,6 @@ var RelationQueryService = class {
1301
1579
  });
1302
1580
  }
1303
1581
  await this.repo.getEntityManager().flush();
1304
- return entity;
1305
- }
1306
- const collection = entity[relationName];
1307
- if (collection && typeof collection.set === "function") {
1308
- await collection.init();
1309
- collection.set(relations);
1310
- await this.repo.getEntityManager().flush();
1311
1582
  }
1312
1583
  return entity;
1313
1584
  }
@@ -1347,20 +1618,24 @@ var RelationQueryService = class {
1347
1618
  if (!this.foundAllRelations(relationIds, relations)) {
1348
1619
  throw new Error(`Unable to find all ${relationName} to remove from ${this.EntityClass.name}`);
1349
1620
  }
1350
- if (meta.kind === "1:m" && meta.mappedBy) {
1621
+ const collection = entity[relationName];
1622
+ if (collection && typeof collection.remove === "function") {
1623
+ const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1624
+ const populated = await relationQueryBuilder.populateEntityRelation(entity, this.nativeLoadUseDataloader, this.relationLoadingStrategy);
1625
+ if (!populated) {
1626
+ await collection.init();
1627
+ }
1351
1628
  for (const relation of relations) {
1352
- wrap(relation).assign({
1353
- [meta.mappedBy]: null
1354
- });
1629
+ collection.remove(relation);
1355
1630
  }
1356
1631
  await this.repo.getEntityManager().flush();
1357
1632
  return entity;
1358
1633
  }
1359
- const collection = entity[relationName];
1360
- if (collection && typeof collection.remove === "function") {
1361
- await collection.init();
1634
+ if (meta.kind === "1:m" && meta.mappedBy) {
1362
1635
  for (const relation of relations) {
1363
- collection.remove(relation);
1636
+ wrap(relation).assign({
1637
+ [meta.mappedBy]: null
1638
+ });
1364
1639
  }
1365
1640
  await this.repo.getEntityManager().flush();
1366
1641
  }
@@ -1384,29 +1659,30 @@ var RelationQueryService = class {
1384
1659
  const meta = this.getRelationMeta(relationName);
1385
1660
  if (meta.kind === "1:1" || meta.kind === "m:1") {
1386
1661
  const fkFieldName = `${relationName}Id`;
1387
- const assignData = {};
1662
+ const assignData = {
1663
+ [relationName]: null
1664
+ };
1388
1665
  const ownDescriptor = Object.getOwnPropertyDescriptor(entity, fkFieldName);
1389
1666
  const protoDescriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(entity), fkFieldName);
1390
1667
  const descriptor = ownDescriptor ?? protoDescriptor;
1391
1668
  const canAssignFk = fkFieldName in entity && (!descriptor || Boolean(descriptor.set) || descriptor.writable === true);
1392
1669
  if (canAssignFk) {
1393
1670
  assignData[fkFieldName] = null;
1394
- } else {
1395
- assignData[relationName] = null;
1396
1671
  }
1397
1672
  wrap(entity).assign(assignData);
1398
1673
  } else {
1399
- if (meta.kind === "1:m" && meta.mappedBy) {
1400
- wrap(relation).assign({
1401
- [meta.mappedBy]: null
1402
- });
1403
- await this.repo.getEntityManager().flush();
1404
- return entity;
1405
- }
1406
1674
  const collection = entity[relationName];
1407
1675
  if (collection && typeof collection.remove === "function") {
1408
- await collection.init();
1676
+ const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1677
+ const populated = await relationQueryBuilder.populateEntityRelation(entity, this.nativeLoadUseDataloader, this.relationLoadingStrategy);
1678
+ if (!populated) {
1679
+ await collection.init();
1680
+ }
1409
1681
  collection.remove(relation);
1682
+ } else if (meta.kind === "1:m" && meta.mappedBy) {
1683
+ wrap(relation).assign({
1684
+ [meta.mappedBy]: null
1685
+ });
1410
1686
  }
1411
1687
  }
1412
1688
  await this.repo.getEntityManager().flush();
@@ -1427,15 +1703,18 @@ var RelationQueryService = class {
1427
1703
  const assembler = bypassAssembler ? void 0 : AssemblerFactory.getAssembler(RelationClass, this.getRelationEntity(relationName));
1428
1704
  const relationQueryBuilder = this.getRelationQueryBuilder(relationName);
1429
1705
  const convertedQuery = assembler ? assembler.convertQuery(query) : query;
1430
- const results = /* @__PURE__ */ new Map();
1431
- await Promise.all(entities.map(async (entity) => {
1432
- const relations = await relationQueryBuilder.selectAndExecute(entity, convertedQuery);
1433
- const relationDtos = bypassAssembler ? relations : await assembler.convertToDTOs(relations);
1706
+ const relationsMap = await relationQueryBuilder.batchSelectAndExecute(entities, convertedQuery, this.getRelationLoadOptions());
1707
+ if (bypassAssembler) {
1708
+ return relationsMap;
1709
+ }
1710
+ const dtoMap = /* @__PURE__ */ new Map();
1711
+ for (const [entity, relations] of relationsMap.entries()) {
1712
+ const relationDtos = await assembler.convertToDTOs(relations);
1434
1713
  if (relationDtos.length > 0) {
1435
- results.set(entity, relationDtos);
1714
+ dtoMap.set(entity, relationDtos);
1436
1715
  }
1437
- }));
1438
- return results;
1716
+ }
1717
+ return dtoMap;
1439
1718
  }
1440
1719
  /**
1441
1720
  * Query for an array of relations for multiple dtos.
@@ -1504,6 +1783,12 @@ var RelationQueryService = class {
1504
1783
  const relationEntity = this.getRelationEntity(relationName);
1505
1784
  return RelationClass === relationEntity || RelationClass.name === relationEntity.name;
1506
1785
  }
1786
+ getRelationLoadOptions() {
1787
+ return {
1788
+ strategy: this.relationLoadingStrategy,
1789
+ useDataloader: this.nativeLoadUseDataloader
1790
+ };
1791
+ }
1507
1792
  getRelationMeta(relationName) {
1508
1793
  const em = this.repo.getEntityManager();
1509
1794
  const metadata = em.getMetadata().get(this.repo.getEntityName());
@@ -1562,7 +1847,9 @@ var MikroOrmQueryService = class extends RelationQueryService {
1562
1847
  filterQueryBuilder;
1563
1848
  useSoftDelete;
1564
1849
  constructor(repo, opts) {
1565
- super(), this.repo = repo;
1850
+ super({
1851
+ relationLoading: opts?.relationLoading
1852
+ }), this.repo = repo;
1566
1853
  this.filterQueryBuilder = opts?.filterQueryBuilder ?? new FilterQueryBuilder(this.repo);
1567
1854
  this.useSoftDelete = opts?.useSoftDelete ?? false;
1568
1855
  const serializer = getAssemblerSerializer(this.EntityClass);
@@ -1583,14 +1870,12 @@ var MikroOrmQueryService = class extends RelationQueryService {
1583
1870
  return data;
1584
1871
  })(this.EntityClass);
1585
1872
  AssemblerDeserializer((d) => {
1586
- const wrapped = wrap(d, true);
1587
- if (wrapped.getPrimaryKey()) {
1588
- const entity = this.repo.getEntityManager().merge(this.EntityClass, d);
1589
- return entity;
1590
- } else {
1591
- const entity = this.repo.getEntityManager().create(this.EntityClass, d);
1592
- return entity;
1593
- }
1873
+ const entity = this.repo.getEntityManager().create(this.EntityClass, d, {
1874
+ managed: true,
1875
+ convertCustomTypes: true,
1876
+ partial: true
1877
+ });
1878
+ return entity;
1594
1879
  })(this.EntityClass);
1595
1880
  }
1596
1881
  }
@@ -2086,12 +2371,12 @@ var MikroOrmQueryService = class extends RelationQueryService {
2086
2371
  };
2087
2372
 
2088
2373
  // src/lib/providers.ts
2089
- function createMikroOrmQueryServiceProvider(EntityClass, contextName) {
2374
+ function createMikroOrmQueryServiceProvider(EntityClass, contextName, opts) {
2090
2375
  return {
2091
2376
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2092
2377
  provide: getQueryServiceToken(EntityClass),
2093
2378
  useFactory(repo) {
2094
- return new MikroOrmQueryService(repo);
2379
+ return new MikroOrmQueryService(repo, opts);
2095
2380
  },
2096
2381
  inject: [
2097
2382
  getRepositoryToken(EntityClass, contextName)
@@ -2099,15 +2384,17 @@ function createMikroOrmQueryServiceProvider(EntityClass, contextName) {
2099
2384
  };
2100
2385
  }
2101
2386
  __name(createMikroOrmQueryServiceProvider, "createMikroOrmQueryServiceProvider");
2102
- var createMikroOrmQueryServiceProviders = /* @__PURE__ */ __name((entities, contextName) => entities.map((entity) => createMikroOrmQueryServiceProvider(entity, contextName)), "createMikroOrmQueryServiceProviders");
2387
+ var createMikroOrmQueryServiceProviders = /* @__PURE__ */ __name((entities, contextName, opts) => entities.map((entity) => createMikroOrmQueryServiceProvider(entity, contextName, opts)), "createMikroOrmQueryServiceProviders");
2103
2388
 
2104
2389
  // src/lib/nest-query-mikro-orm.module.ts
2105
2390
  var NestjsQueryMikroOrmModule = class _NestjsQueryMikroOrmModule {
2106
2391
  static {
2107
2392
  __name(this, "NestjsQueryMikroOrmModule");
2108
2393
  }
2109
- static forFeature(entities, contextName) {
2110
- const queryServiceProviders = createMikroOrmQueryServiceProviders(entities, contextName);
2394
+ static forFeature(entities, contextNameOrOptions) {
2395
+ const contextName = typeof contextNameOrOptions === "string" ? contextNameOrOptions : contextNameOrOptions?.contextName;
2396
+ const queryServiceOpts = typeof contextNameOrOptions === "string" ? void 0 : contextNameOrOptions?.queryServiceOpts;
2397
+ const queryServiceProviders = createMikroOrmQueryServiceProviders(entities, contextName, queryServiceOpts);
2111
2398
  const mikroOrmModule = MikroOrmModule.forFeature(entities, contextName);
2112
2399
  return {
2113
2400
  imports: [