joist-core 2.3.0-next.98 → 2.3.0-next.99
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/build/BaseEntity.cjs.map +1 -1
- package/build/BaseEntity.js.map +1 -1
- package/build/Entity.cjs.map +1 -1
- package/build/Entity.js.map +1 -1
- package/build/EntityManager.cjs.map +1 -1
- package/build/EntityManager.d.cts.map +1 -1
- package/build/EntityManager.d.mts.map +1 -1
- package/build/EntityManager.js.map +1 -1
- package/build/EntityMetadata.cjs.map +1 -1
- package/build/EntityMetadata.js.map +1 -1
- package/build/IndexManager.cjs.map +1 -1
- package/build/IndexManager.js.map +1 -1
- package/build/InstanceData.cjs.map +1 -1
- package/build/InstanceData.js.map +1 -1
- package/build/PluginManager.cjs.map +1 -1
- package/build/PluginManager.js.map +1 -1
- package/build/batchloaders/enumCollectionBatchLoader.cjs.map +1 -1
- package/build/batchloaders/enumCollectionBatchLoader.js.map +1 -1
- package/build/batchloaders/loadBatchLoader.cjs.map +1 -1
- package/build/batchloaders/loadBatchLoader.js.map +1 -1
- package/build/batchloaders/manyToManyBatchLoader.cjs.map +1 -1
- package/build/batchloaders/manyToManyBatchLoader.js.map +1 -1
- package/build/batchloaders/oneToManyBatchLoader.cjs.map +1 -1
- package/build/batchloaders/oneToManyBatchLoader.js.map +1 -1
- package/build/batchloaders/oneToOneBatchLoader.cjs.map +1 -1
- package/build/batchloaders/oneToOneBatchLoader.js.map +1 -1
- package/build/batchloaders/populateBatchLoader.cjs.map +1 -1
- package/build/batchloaders/populateBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveChildrenBatchLoader.cjs.map +1 -1
- package/build/batchloaders/recursiveChildrenBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveM2mBatchLoader.cjs.map +1 -1
- package/build/batchloaders/recursiveM2mBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveParentsBatchLoader.cjs.map +1 -1
- package/build/batchloaders/recursiveParentsBatchLoader.js.map +1 -1
- package/build/changes.cjs.map +1 -1
- package/build/changes.js.map +1 -1
- package/build/config.cjs.map +1 -1
- package/build/config.js.map +1 -1
- package/build/configure.cjs.map +1 -1
- package/build/configure.js.map +1 -1
- package/build/dataloaders/fastWhereFilterHash.cjs.map +1 -1
- package/build/dataloaders/fastWhereFilterHash.js.map +1 -1
- package/build/dataloaders/findByUniqueDataLoader.cjs.map +1 -1
- package/build/dataloaders/findByUniqueDataLoader.d.cts.map +1 -1
- package/build/dataloaders/findByUniqueDataLoader.d.mts.map +1 -1
- package/build/dataloaders/findByUniqueDataLoader.js.map +1 -1
- package/build/dataloaders/findCountDataLoader.cjs.map +1 -1
- package/build/dataloaders/findCountDataLoader.js.map +1 -1
- package/build/dataloaders/findDataLoader.cjs +1 -1
- package/build/dataloaders/findDataLoader.cjs.map +1 -1
- package/build/dataloaders/findDataLoader.js +1 -1
- package/build/dataloaders/findDataLoader.js.map +1 -1
- package/build/dataloaders/findIdsDataLoader.cjs.map +1 -1
- package/build/dataloaders/findIdsDataLoader.js.map +1 -1
- package/build/dataloaders/findOrCreateDataLoader.cjs.map +1 -1
- package/build/dataloaders/findOrCreateDataLoader.d.cts.map +1 -1
- package/build/dataloaders/findOrCreateDataLoader.d.mts.map +1 -1
- package/build/dataloaders/findOrCreateDataLoader.js.map +1 -1
- package/build/dataloaders/findPaginatedDataLoader.cjs.map +1 -1
- package/build/dataloaders/findPaginatedDataLoader.js.map +1 -1
- package/build/dataloaders/lensDataLoader.cjs.map +1 -1
- package/build/dataloaders/lensDataLoader.d.cts.map +1 -1
- package/build/dataloaders/lensDataLoader.d.mts.map +1 -1
- package/build/dataloaders/lensDataLoader.js.map +1 -1
- package/build/dataloaders/manyToManyFindDataLoader.cjs.map +1 -1
- package/build/dataloaders/manyToManyFindDataLoader.d.cts.map +1 -1
- package/build/dataloaders/manyToManyFindDataLoader.d.mts.map +1 -1
- package/build/dataloaders/manyToManyFindDataLoader.js.map +1 -1
- package/build/dataloaders/oneToManyFindDataLoader.cjs.map +1 -1
- package/build/dataloaders/oneToManyFindDataLoader.d.cts.map +1 -1
- package/build/dataloaders/oneToManyFindDataLoader.d.mts.map +1 -1
- package/build/dataloaders/oneToManyFindDataLoader.js.map +1 -1
- package/build/defaults.cjs.map +1 -1
- package/build/defaults.d.cts.map +1 -1
- package/build/defaults.d.mts.map +1 -1
- package/build/defaults.js.map +1 -1
- package/build/drivers/EntityWriter.cjs.map +1 -1
- package/build/drivers/EntityWriter.js.map +1 -1
- package/build/drivers/IdAssigner.cjs.map +1 -1
- package/build/drivers/IdAssigner.js.map +1 -1
- package/build/drivers/driverApi.cjs.map +1 -1
- package/build/drivers/driverApi.js.map +1 -1
- package/build/fields.cjs.map +1 -1
- package/build/fields.js.map +1 -1
- package/build/flush/JoinRows.cjs.map +1 -1
- package/build/flush/JoinRows.js.map +1 -1
- package/build/flush/Todo.cjs.map +1 -1
- package/build/flush/Todo.js.map +1 -1
- package/build/getProperties.cjs.map +1 -1
- package/build/getProperties.js.map +1 -1
- package/build/index.cjs +2 -2
- package/build/index.cjs.map +1 -1
- package/build/index.js +2 -2
- package/build/index.js.map +1 -1
- package/build/json.cjs.map +1 -1
- package/build/json.js.map +1 -1
- package/build/keys.cjs.map +1 -1
- package/build/keys.js.map +1 -1
- package/build/loading/HintTree.cjs.map +1 -1
- package/build/loading/HintTree.js.map +1 -1
- package/build/loading/IsLoadedCache.cjs.map +1 -1
- package/build/loading/IsLoadedCache.js.map +1 -1
- package/build/loading/loadHints.cjs.map +1 -1
- package/build/loading/loadHints.js.map +1 -1
- package/build/loading/loadLens.cjs.map +1 -1
- package/build/loading/loadLens.js.map +1 -1
- package/build/loading/withLoaded.cjs.map +1 -1
- package/build/loading/withLoaded.js.map +1 -1
- package/build/logging/FactoryLogger.cjs.map +1 -1
- package/build/logging/FactoryLogger.d.cts.map +1 -1
- package/build/logging/FactoryLogger.d.mts.map +1 -1
- package/build/logging/FactoryLogger.js.map +1 -1
- package/build/logging/FieldLogger.cjs.map +1 -1
- package/build/logging/FieldLogger.d.cts.map +1 -1
- package/build/logging/FieldLogger.d.mts.map +1 -1
- package/build/logging/FieldLogger.js.map +1 -1
- package/build/logging/ReactionLogger.cjs.map +1 -1
- package/build/logging/ReactionLogger.d.cts.map +1 -1
- package/build/logging/ReactionLogger.d.mts.map +1 -1
- package/build/logging/ReactionLogger.js.map +1 -1
- package/build/newEntity.cjs.map +1 -1
- package/build/newEntity.js.map +1 -1
- package/build/newTestInstance.cjs.map +1 -1
- package/build/newTestInstance.d.cts.map +1 -1
- package/build/newTestInstance.d.mts.map +1 -1
- package/build/newTestInstance.js.map +1 -1
- package/build/normalizeHints.cjs.map +1 -1
- package/build/normalizeHints.js.map +1 -1
- package/build/nowUTC.cjs.map +1 -1
- package/build/nowUTC.js.map +1 -1
- package/build/plugins/ImmutableEntitiesPlugin.cjs.map +1 -1
- package/build/plugins/ImmutableEntitiesPlugin.js.map +1 -1
- package/build/preloading/JsonAggregatePreloader.cjs.map +1 -1
- package/build/preloading/JsonAggregatePreloader.d.cts.map +1 -1
- package/build/preloading/JsonAggregatePreloader.d.mts.map +1 -1
- package/build/preloading/JsonAggregatePreloader.js.map +1 -1
- package/build/preloading/canPreload.cjs.map +1 -1
- package/build/preloading/canPreload.js.map +1 -1
- package/build/preloading/partitionHint.cjs.map +1 -1
- package/build/preloading/partitionHint.js.map +1 -1
- package/build/queries/ConditionBuilder.cjs.map +1 -1
- package/build/queries/ConditionBuilder.js.map +1 -1
- package/build/queries/conditions.cjs.map +1 -1
- package/build/queries/conditions.js.map +1 -1
- package/build/queries/entityQueryUtils.cjs.map +1 -1
- package/build/queries/entityQueryUtils.js.map +1 -1
- package/build/queries/find/Aliases.cjs.map +1 -1
- package/build/queries/find/Aliases.d.cts.map +1 -1
- package/build/queries/find/Aliases.d.mts.map +1 -1
- package/build/queries/find/Aliases.js.map +1 -1
- package/build/queries/find/DeferredAlias.cjs.map +1 -1
- package/build/queries/find/DeferredAlias.js.map +1 -1
- package/build/queries/find/EntityFilter.cjs.map +1 -1
- package/build/queries/find/EntityFilter.js.map +1 -1
- package/build/queries/find/EntityGraphQLFilter.cjs.map +1 -1
- package/build/queries/find/EntityGraphQLFilter.js.map +1 -1
- package/build/queries/find/QueryParser.cjs +1 -1
- package/build/queries/find/QueryParser.cjs.map +1 -1
- package/build/queries/find/QueryParser.collectionJoins.cjs.map +1 -1
- package/build/queries/find/QueryParser.collectionJoins.js.map +1 -1
- package/build/queries/find/QueryParser.d.cts.map +1 -1
- package/build/queries/find/QueryParser.d.mts.map +1 -1
- package/build/queries/find/QueryParser.js +1 -1
- package/build/queries/find/QueryParser.js.map +1 -1
- package/build/queries/find/QueryParser.pruning.cjs.map +1 -1
- package/build/queries/find/QueryParser.pruning.js.map +1 -1
- package/build/queries/find/QueryVisitor.cjs.map +1 -1
- package/build/queries/find/QueryVisitor.js.map +1 -1
- package/build/queries/find/buildFindQuery.cjs.map +1 -1
- package/build/queries/find/buildFindQuery.js.map +1 -1
- package/build/queries/find/scopes.cjs.map +1 -1
- package/build/queries/find/scopes.js.map +1 -1
- package/build/queries/parsedConditions.cjs.map +1 -1
- package/build/queries/parsedConditions.js.map +1 -1
- package/build/queries/renderConditions.cjs.map +1 -1
- package/build/queries/renderConditions.js.map +1 -1
- package/build/queries/skipCondition.cjs.map +1 -1
- package/build/queries/skipCondition.js.map +1 -1
- package/build/queries/sql/AliasAssigner.cjs.map +1 -1
- package/build/queries/sql/AliasAssigner.js.map +1 -1
- package/build/queries/sql/Expr.cjs.map +1 -1
- package/build/queries/sql/Expr.js.map +1 -1
- package/build/queries/sql/Tables.cjs.map +1 -1
- package/build/queries/sql/Tables.d.cts.map +1 -1
- package/build/queries/sql/Tables.d.mts.map +1 -1
- package/build/queries/sql/Tables.js.map +1 -1
- package/build/queries/sql/execute.cjs.map +1 -1
- package/build/queries/sql/execute.js.map +1 -1
- package/build/queries/sql/expressions/arrayAgg.cjs.map +1 -1
- package/build/queries/sql/expressions/arrayAgg.js.map +1 -1
- package/build/queries/sql/expressions/case.cjs.map +1 -1
- package/build/queries/sql/expressions/case.js.map +1 -1
- package/build/queries/sql/expressions/coalesce.cjs.map +1 -1
- package/build/queries/sql/expressions/coalesce.js.map +1 -1
- package/build/queries/sql/expressions/codecs.cjs.map +1 -1
- package/build/queries/sql/expressions/codecs.js.map +1 -1
- package/build/queries/sql/expressions/expression.cjs.map +1 -1
- package/build/queries/sql/expressions/expression.d.cts.map +1 -1
- package/build/queries/sql/expressions/expression.d.mts.map +1 -1
- package/build/queries/sql/expressions/expression.js.map +1 -1
- package/build/queries/sql/expressions/greatest.cjs.map +1 -1
- package/build/queries/sql/expressions/greatest.js.map +1 -1
- package/build/queries/sql/expressions/least.cjs.map +1 -1
- package/build/queries/sql/expressions/least.js.map +1 -1
- package/build/queries/sql/expressions/nullIf.cjs.map +1 -1
- package/build/queries/sql/expressions/nullIf.js.map +1 -1
- package/build/queries/sql/expressions/parseExpression.cjs.map +1 -1
- package/build/queries/sql/expressions/parseExpression.js.map +1 -1
- package/build/queries/sql/expressions/types.d.cts.map +1 -1
- package/build/queries/sql/expressions/types.d.mts.map +1 -1
- package/build/queries/sql/query.cjs +1 -1
- package/build/queries/sql/query.cjs.map +1 -1
- package/build/queries/sql/query.js +1 -1
- package/build/queries/sql/query.js.map +1 -1
- package/build/queries/unnest.cjs.map +1 -1
- package/build/queries/unnest.js.map +1 -1
- package/build/queries/valueFilters.cjs.map +1 -1
- package/build/queries/valueFilters.d.cts.map +1 -1
- package/build/queries/valueFilters.d.mts.map +1 -1
- package/build/queries/valueFilters.js.map +1 -1
- package/build/reactivity/ReactionsManager.cjs.map +1 -1
- package/build/reactivity/ReactionsManager.js.map +1 -1
- package/build/reactivity/reactiveHints.cjs.map +1 -1
- package/build/reactivity/reactiveHints.js.map +1 -1
- package/build/relations/AbstractRelationImpl.cjs.map +1 -1
- package/build/relations/AbstractRelationImpl.js.map +1 -1
- package/build/relations/AsyncProperty.cjs.map +1 -1
- package/build/relations/AsyncProperty.js.map +1 -1
- package/build/relations/AsyncReactiveField.cjs.map +1 -1
- package/build/relations/AsyncReactiveField.js.map +1 -1
- package/build/relations/Collection.cjs.map +1 -1
- package/build/relations/Collection.js.map +1 -1
- package/build/relations/CustomCollection.cjs.map +1 -1
- package/build/relations/CustomCollection.js.map +1 -1
- package/build/relations/CustomReference.cjs.map +1 -1
- package/build/relations/CustomReference.js.map +1 -1
- package/build/relations/EnumCollection.cjs.map +1 -1
- package/build/relations/EnumCollection.js.map +1 -1
- package/build/relations/LazyField.cjs.map +1 -1
- package/build/relations/LazyField.js.map +1 -1
- package/build/relations/ManyToManyCollection.cjs.map +1 -1
- package/build/relations/ManyToManyCollection.js.map +1 -1
- package/build/relations/ManyToManyLargeCollection.cjs.map +1 -1
- package/build/relations/ManyToManyLargeCollection.js.map +1 -1
- package/build/relations/ManyToOneReference.cjs.map +1 -1
- package/build/relations/ManyToOneReference.js.map +1 -1
- package/build/relations/OneToManyCollection.cjs.map +1 -1
- package/build/relations/OneToManyCollection.js.map +1 -1
- package/build/relations/OneToManyLargeCollection.cjs.map +1 -1
- package/build/relations/OneToManyLargeCollection.js.map +1 -1
- package/build/relations/OneToOneReference.cjs.map +1 -1
- package/build/relations/OneToOneReference.js.map +1 -1
- package/build/relations/PolymorphicReference.cjs.map +1 -1
- package/build/relations/PolymorphicReference.js.map +1 -1
- package/build/relations/ReactiveField.cjs.map +1 -1
- package/build/relations/ReactiveField.js.map +1 -1
- package/build/relations/ReactiveGetter.cjs.map +1 -1
- package/build/relations/ReactiveGetter.js.map +1 -1
- package/build/relations/ReactiveManyToMany.cjs.map +1 -1
- package/build/relations/ReactiveManyToMany.js.map +1 -1
- package/build/relations/ReactiveManyToManyOtherSide.cjs.map +1 -1
- package/build/relations/ReactiveManyToManyOtherSide.js.map +1 -1
- package/build/relations/ReactiveReference.cjs.map +1 -1
- package/build/relations/ReactiveReference.js.map +1 -1
- package/build/relations/ReadOnlyCollection.cjs.map +1 -1
- package/build/relations/ReadOnlyCollection.js.map +1 -1
- package/build/relations/RecursiveCollection.cjs.map +1 -1
- package/build/relations/RecursiveCollection.js.map +1 -1
- package/build/relations/RecursiveCycleError.cjs.map +1 -1
- package/build/relations/RecursiveCycleError.js.map +1 -1
- package/build/relations/Reference.cjs.map +1 -1
- package/build/relations/Reference.js.map +1 -1
- package/build/relations/Relation.cjs.map +1 -1
- package/build/relations/Relation.js.map +1 -1
- package/build/relations/hasAsyncMethod.cjs.map +1 -1
- package/build/relations/hasAsyncMethod.js.map +1 -1
- package/build/relations/hasManyDerived.cjs.map +1 -1
- package/build/relations/hasManyDerived.js.map +1 -1
- package/build/relations/hasManyThrough.cjs.map +1 -1
- package/build/relations/hasManyThrough.js.map +1 -1
- package/build/relations/hasOneDerived.cjs.map +1 -1
- package/build/relations/hasOneDerived.js.map +1 -1
- package/build/relations/hasOneThrough.cjs.map +1 -1
- package/build/relations/hasOneThrough.js.map +1 -1
- package/build/relations/hasProperty.cjs.map +1 -1
- package/build/relations/hasProperty.js.map +1 -1
- package/build/relations/isCascadeDelete.cjs.map +1 -1
- package/build/relations/isCascadeDelete.js.map +1 -1
- package/build/resurrection.cjs.map +1 -1
- package/build/resurrection.js.map +1 -1
- package/build/rules.cjs.map +1 -1
- package/build/rules.js.map +1 -1
- package/build/runtimeConfig.cjs.map +1 -1
- package/build/runtimeConfig.js.map +1 -1
- package/build/serde/TypeInfo.cjs.map +1 -1
- package/build/serde/TypeInfo.js.map +1 -1
- package/build/serde/columns.cjs.map +1 -1
- package/build/serde/columns.js.map +1 -1
- package/build/serde/fieldSerde.cjs.map +1 -1
- package/build/serde/fieldSerde.js.map +1 -1
- package/build/serde/serde.cjs.map +1 -1
- package/build/serde/serde.d.cts.map +1 -1
- package/build/serde/serde.d.mts.map +1 -1
- package/build/serde/serde.js.map +1 -1
- package/build/serde/temporalMappers.cjs.map +1 -1
- package/build/serde/temporalMappers.js.map +1 -1
- package/build/upsert.cjs.map +1 -1
- package/build/upsert.d.cts.map +1 -1
- package/build/upsert.d.mts.map +1 -1
- package/build/upsert.js.map +1 -1
- package/build/utils.cjs.map +1 -1
- package/build/utils.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enumCollectionBatchLoader.js","names":[],"sources":["../../src/batchloaders/enumCollectionBatchLoader.ts"],"sourcesContent":["import { type
|
|
1
|
+
{"version":3,"file":"enumCollectionBatchLoader.js","names":[],"sources":["../../src/batchloaders/enumCollectionBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport { type ManyToManyLike, type ParsedFindQuery, keyToNumber } from \"src/index.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const enumCollectionLoadOperation = \"enum-collection-load\";\n\n/** Batches `EnumCollection.load` calls via BatchLoader; keys are the owning entities' tagged ids. */\nexport function enumCollectionBatchLoader(em: EntityManager, collection: ManyToManyLike): BatchLoader<string> {\n return em.getBatchLoader(enumCollectionLoadOperation, collection.joinTableName, (keys) =>\n loadBatch(em, collection, keys),\n );\n}\n\nasync function loadBatch(em: EntityManager, collection: ManyToManyLike, keys: string[]): Promise<void> {\n const { meta, columnName, otherColumnName, joinTableName, hasJoinTableId, otherEnum, fieldName } = collection;\n const joinRows = getEmInternalApi(em).joinRows(collection);\n\n const alias = abbreviation(joinTableName);\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [{ alias, join: \"primary\", table: joinTableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n {\n kind: \"column\",\n alias,\n column: columnName,\n dbType: meta.idDbType,\n cond: { kind: \"in\", value: keys.map((id) => keyToNumber(meta, id)!) },\n },\n ],\n },\n // Id-less join tables have no surrogate id to order by, so order by the FK columns instead.\n orderBys: hasJoinTableId\n ? [{ alias, column: \"id\", order: \"ASC\" }]\n : [\n { alias, column: columnName, order: \"ASC\" },\n { alias, column: otherColumnName, order: \"ASC\" },\n ],\n };\n\n const rows = await em[\"executeFind\"](meta, enumCollectionLoadOperation, query, { limit: undefined });\n await joinRows.loadRows(\n keys.map((id) => [columnName, id]),\n rows,\n );\n\n const api = getEmInternalApi(em);\n for (const id of keys) {\n const entity = em.getEntity(id);\n if (!entity) continue;\n const codes = (joinRows.getOthers(columnName, entity) as number[])\n .sort((a, b) => a - b)\n .map((enumId) => otherEnum!.findById(enumId)!.code);\n api.setPreloadedRelation(entity.idTagged!, fieldName, codes);\n }\n}\n"],"mappings":";;;;;;AAKA,MAAa,8BAA8B;;AAG3C,SAAgB,0BAA0B,IAAmB,YAAiD;CAC5G,OAAO,GAAG,eAAe,6BAA6B,WAAW,gBAAgB,SAC/E,UAAU,IAAI,YAAY,IAAI,CAChC;AACF;AAEA,eAAe,UAAU,IAAmB,YAA4B,MAA+B;CACrG,MAAM,EAAE,MAAM,YAAY,iBAAiB,eAAe,gBAAgB,WAAW,cAAc;CACnG,MAAM,WAAW,iBAAiB,EAAE,CAAC,CAAC,SAAS,UAAU;CAEzD,MAAM,QAAQ,aAAa,aAAa;CACxC,MAAM,QAAyB;EAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;EACxB,QAAQ,CAAC;GAAE;GAAO,MAAM;GAAW,OAAO;EAAc,CAAC;EACzD,WAAW;GACT,MAAM;GACN,IAAI;GACJ,YAAY,CACV;IACE,MAAM;IACN;IACA,QAAQ;IACR,QAAQ,KAAK;IACb,MAAM;KAAE,MAAM;KAAM,OAAO,KAAK,KAAK,OAAO,YAAY,MAAM,EAAE,CAAE;IAAE;GACtE,CACF;EACF;EAEA,UAAU,iBACN,CAAC;GAAE;GAAO,QAAQ;GAAM,OAAO;EAAM,CAAC,IACtC,CACE;GAAE;GAAO,QAAQ;GAAY,OAAO;EAAM,GAC1C;GAAE;GAAO,QAAQ;GAAiB,OAAO;EAAM,CACjD;CACN;CAEA,MAAM,OAAO,MAAM,GAAG,cAAc,CAAC,MAAM,6BAA6B,OAAO,EAAE,OAAO,KAAA,EAAU,CAAC;CACnG,MAAM,SAAS,SACb,KAAK,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,GACjC,IACF;CAEA,MAAM,MAAM,iBAAiB,EAAE;CAC/B,KAAK,MAAM,MAAM,MAAM;EACrB,MAAM,SAAS,GAAG,UAAU,EAAE;EAC9B,IAAI,CAAC,QAAQ;EACb,MAAM,QAAS,SAAS,UAAU,YAAY,MAAM,CAAC,CAClD,MAAM,GAAG,MAAM,IAAI,CAAC,CAAC,CACrB,KAAK,WAAW,UAAW,SAAS,MAAM,CAAC,CAAE,IAAI;EACpD,IAAI,qBAAqB,OAAO,UAAW,WAAW,KAAK;CAC7D;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadBatchLoader.cjs","names":["keyToNumber","abbreviation","lazyExcludedSelects","getEmInternalApi","buildHintTree","tagId"],"sources":["../../src/batchloaders/loadBatchLoader.ts"],"sourcesContent":["import { getInstanceData } from \"
|
|
1
|
+
{"version":3,"file":"loadBatchLoader.cjs","names":["keyToNumber","abbreviation","lazyExcludedSelects","getEmInternalApi","buildHintTree","tagId"],"sources":["../../src/batchloaders/loadBatchLoader.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport { type EntityMetadata } from \"src/EntityMetadata.ts\";\nimport { keyToNumber, tagId } from \"src/keys.ts\";\nimport { buildHintTree } from \"src/loading/HintTree.ts\";\nimport type { LoadHint } from \"src/loading/loadHints.ts\";\nimport { lazyExcludedSelects } from \"src/queries/entityQueryUtils.ts\";\nimport { type ParsedFindQuery, addTablePerClassJoinsAndClassTag } from \"src/queries/find/QueryParser.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const loadOperation = \"load\";\n\n/**\n * Batches em.load-style fetches, writing to identity map (via hydrate) instead of returning values.\n * After the batch resolves, callers retrieve their entity from `em.findExistingInstance`.\n *\n * Accepts tuples of `{ taggedId, hint }` so the preloader can inject join-based\n * preloading into the same SELECT that fetches the entities.\n */\nexport function loadBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n overwriteExisting: boolean = false,\n): BatchLoader<{ taggedId: string; hint: LoadHint<any> | undefined }> {\n return em.getBatchLoader(loadOperation, `${meta.type}-${overwriteExisting}`, async (loads) => {\n const keys = loads.map((l) => keyToNumber(meta, l.taggedId));\n const alias = abbreviation(meta.tableName);\n const query = {\n selects: meta.hasLazyColumns ? lazyExcludedSelects(meta, alias) : [`\"${alias}\".*`],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [{ kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: keys } }],\n },\n orderBys: [{ alias, column: \"id\", order: \"ASC\" }],\n } satisfies ParsedFindQuery;\n addTablePerClassJoinsAndClassTag(query, meta, alias, true);\n // Inject preloading joins into the query if enabled\n const { preloader } = getEmInternalApi(em);\n const preloadHydrator =\n preloader &&\n preloader.addPreloading(meta, buildHintTree(loads.map((l) => ({ entity: l.taggedId, hint: l.hint }))), query);\n const rowData = await em[\"executeFindRowData\"](meta, loadOperation, query, {});\n em[\"hydrateAndFinalize\"](meta.cstr, rowData, {\n overwriteExisting,\n sidecars: (entities) => {\n preloadHydrator && preloadHydrator(rowData, entities);\n // If we're missing any requested rows, mark any requested-but-not-found entities as deleted\n if (rowData.rowCount !== loads.length) {\n const foundIds = new Set<string>();\n for (let i = 0; i < rowData.rowCount; i++) foundIds.add(tagId(meta, rowData.get(i, \"id\")));\n for (const load of loads) {\n if (!foundIds.has(load.taggedId)) {\n const existingEntity = em.findExistingInstance(load.taggedId);\n if (existingEntity) {\n getInstanceData(existingEntity).markDeletedBecauseNotFound();\n }\n }\n }\n }\n },\n });\n });\n}\n"],"mappings":";;;;;;;;;;;AAWA,MAAa,gBAAgB;;;;;;;;AAS7B,SAAgB,gBACd,IACA,MACA,oBAA6B,OACuC;CACpE,OAAO,GAAG,eAAe,eAAe,GAAG,KAAK,KAAK,GAAG,qBAAqB,OAAO,UAAU;EAC5F,MAAM,OAAO,MAAM,KAAK,MAAMA,aAAAA,YAAY,MAAM,EAAE,QAAQ,CAAC;EAC3D,MAAM,QAAQC,cAAAA,aAAa,KAAK,SAAS;EACzC,MAAM,QAAQ;GACZ,SAAS,KAAK,iBAAiBC,iCAAAA,oBAAoB,MAAM,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI;GACjF,QAAQ,CAAC;IAAE;IAAO,MAAM;IAAW,OAAO,KAAK;GAAU,CAAC;GAC1D,WAAW;IACT,MAAM;IACN,IAAI;IACJ,YAAY,CAAC;KAAE,MAAM;KAAU;KAAO,QAAQ;KAAM,QAAQ,KAAK;KAAU,MAAM;MAAE,MAAM;MAAM,OAAO;KAAK;IAAE,CAAC;GAChH;GACA,UAAU,CAAC;IAAE;IAAO,QAAQ;IAAM,OAAO;GAAM,CAAC;EAClD;EACA,iCAAA,iCAAiC,OAAO,MAAM,OAAO,IAAI;EAEzD,MAAM,EAAE,cAAcC,sBAAAA,iBAAiB,EAAE;EACzC,MAAM,kBACJ,aACA,UAAU,cAAc,MAAMC,yBAAAA,cAAc,MAAM,KAAK,OAAO;GAAE,QAAQ,EAAE;GAAU,MAAM,EAAE;EAAK,EAAE,CAAC,GAAG,KAAK;EAC9G,MAAM,UAAU,MAAM,GAAG,qBAAqB,CAAC,MAAM,eAAe,OAAO,CAAC,CAAC;EAC7E,GAAG,qBAAqB,CAAC,KAAK,MAAM,SAAS;GAC3C;GACA,WAAW,aAAa;IACtB,mBAAmB,gBAAgB,SAAS,QAAQ;IAEpD,IAAI,QAAQ,aAAa,MAAM,QAAQ;KACrC,MAAM,2BAAW,IAAI,IAAY;KACjC,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,UAAU,KAAK,SAAS,IAAIC,aAAAA,MAAM,MAAM,QAAQ,IAAI,GAAG,IAAI,CAAC,CAAC;KACzF,KAAK,MAAM,QAAQ,OACjB,IAAI,CAAC,SAAS,IAAI,KAAK,QAAQ,GAAG;MAChC,MAAM,iBAAiB,GAAG,qBAAqB,KAAK,QAAQ;MAC5D,IAAI,gBACF,mBAAA,gBAAgB,cAAc,CAAC,CAAC,2BAA2B;KAE/D;IAEJ;GACF;EACF,CAAC;CACH,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadBatchLoader.js","names":[],"sources":["../../src/batchloaders/loadBatchLoader.ts"],"sourcesContent":["import { getInstanceData } from \"
|
|
1
|
+
{"version":3,"file":"loadBatchLoader.js","names":[],"sources":["../../src/batchloaders/loadBatchLoader.ts"],"sourcesContent":["import { getInstanceData } from \"src/BaseEntity.ts\";\nimport { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport { type EntityMetadata } from \"src/EntityMetadata.ts\";\nimport { keyToNumber, tagId } from \"src/keys.ts\";\nimport { buildHintTree } from \"src/loading/HintTree.ts\";\nimport type { LoadHint } from \"src/loading/loadHints.ts\";\nimport { lazyExcludedSelects } from \"src/queries/entityQueryUtils.ts\";\nimport { type ParsedFindQuery, addTablePerClassJoinsAndClassTag } from \"src/queries/find/QueryParser.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const loadOperation = \"load\";\n\n/**\n * Batches em.load-style fetches, writing to identity map (via hydrate) instead of returning values.\n * After the batch resolves, callers retrieve their entity from `em.findExistingInstance`.\n *\n * Accepts tuples of `{ taggedId, hint }` so the preloader can inject join-based\n * preloading into the same SELECT that fetches the entities.\n */\nexport function loadBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n overwriteExisting: boolean = false,\n): BatchLoader<{ taggedId: string; hint: LoadHint<any> | undefined }> {\n return em.getBatchLoader(loadOperation, `${meta.type}-${overwriteExisting}`, async (loads) => {\n const keys = loads.map((l) => keyToNumber(meta, l.taggedId));\n const alias = abbreviation(meta.tableName);\n const query = {\n selects: meta.hasLazyColumns ? lazyExcludedSelects(meta, alias) : [`\"${alias}\".*`],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [{ kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: keys } }],\n },\n orderBys: [{ alias, column: \"id\", order: \"ASC\" }],\n } satisfies ParsedFindQuery;\n addTablePerClassJoinsAndClassTag(query, meta, alias, true);\n // Inject preloading joins into the query if enabled\n const { preloader } = getEmInternalApi(em);\n const preloadHydrator =\n preloader &&\n preloader.addPreloading(meta, buildHintTree(loads.map((l) => ({ entity: l.taggedId, hint: l.hint }))), query);\n const rowData = await em[\"executeFindRowData\"](meta, loadOperation, query, {});\n em[\"hydrateAndFinalize\"](meta.cstr, rowData, {\n overwriteExisting,\n sidecars: (entities) => {\n preloadHydrator && preloadHydrator(rowData, entities);\n // If we're missing any requested rows, mark any requested-but-not-found entities as deleted\n if (rowData.rowCount !== loads.length) {\n const foundIds = new Set<string>();\n for (let i = 0; i < rowData.rowCount; i++) foundIds.add(tagId(meta, rowData.get(i, \"id\")));\n for (const load of loads) {\n if (!foundIds.has(load.taggedId)) {\n const existingEntity = em.findExistingInstance(load.taggedId);\n if (existingEntity) {\n getInstanceData(existingEntity).markDeletedBecauseNotFound();\n }\n }\n }\n }\n },\n });\n });\n}\n"],"mappings":";;;;;;;;;;AAWA,MAAa,gBAAgB;;;;;;;;AAS7B,SAAgB,gBACd,IACA,MACA,oBAA6B,OACuC;CACpE,OAAO,GAAG,eAAe,eAAe,GAAG,KAAK,KAAK,GAAG,qBAAqB,OAAO,UAAU;EAC5F,MAAM,OAAO,MAAM,KAAK,MAAM,YAAY,MAAM,EAAE,QAAQ,CAAC;EAC3D,MAAM,QAAQ,aAAa,KAAK,SAAS;EACzC,MAAM,QAAQ;GACZ,SAAS,KAAK,iBAAiB,oBAAoB,MAAM,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI;GACjF,QAAQ,CAAC;IAAE;IAAO,MAAM;IAAW,OAAO,KAAK;GAAU,CAAC;GAC1D,WAAW;IACT,MAAM;IACN,IAAI;IACJ,YAAY,CAAC;KAAE,MAAM;KAAU;KAAO,QAAQ;KAAM,QAAQ,KAAK;KAAU,MAAM;MAAE,MAAM;MAAM,OAAO;KAAK;IAAE,CAAC;GAChH;GACA,UAAU,CAAC;IAAE;IAAO,QAAQ;IAAM,OAAO;GAAM,CAAC;EAClD;EACA,iCAAiC,OAAO,MAAM,OAAO,IAAI;EAEzD,MAAM,EAAE,cAAc,iBAAiB,EAAE;EACzC,MAAM,kBACJ,aACA,UAAU,cAAc,MAAM,cAAc,MAAM,KAAK,OAAO;GAAE,QAAQ,EAAE;GAAU,MAAM,EAAE;EAAK,EAAE,CAAC,GAAG,KAAK;EAC9G,MAAM,UAAU,MAAM,GAAG,qBAAqB,CAAC,MAAM,eAAe,OAAO,CAAC,CAAC;EAC7E,GAAG,qBAAqB,CAAC,KAAK,MAAM,SAAS;GAC3C;GACA,WAAW,aAAa;IACtB,mBAAmB,gBAAgB,SAAS,QAAQ;IAEpD,IAAI,QAAQ,aAAa,MAAM,QAAQ;KACrC,MAAM,2BAAW,IAAI,IAAY;KACjC,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,UAAU,KAAK,SAAS,IAAI,MAAM,MAAM,QAAQ,IAAI,GAAG,IAAI,CAAC,CAAC;KACzF,KAAK,MAAM,QAAQ,OACjB,IAAI,CAAC,SAAS,IAAI,KAAK,QAAQ,GAAG;MAChC,MAAM,iBAAiB,GAAG,qBAAqB,KAAK,QAAQ;MAC5D,IAAI,gBACF,gBAAgB,cAAc,CAAC,CAAC,2BAA2B;KAE/D;IAEJ;GACF;EACF,CAAC;CACH,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manyToManyBatchLoader.cjs","names":["getEmInternalApi","abbreviation","keyToNumber"],"sources":["../../src/batchloaders/manyToManyBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"
|
|
1
|
+
{"version":3,"file":"manyToManyBatchLoader.cjs","names":["getEmInternalApi","abbreviation","keyToNumber"],"sources":["../../src/batchloaders/manyToManyBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport { type ManyToManyLike, type ParsedFindQuery, keyToNumber } from \"src/index.ts\";\nimport { abbreviation, getOrSet } from \"src/utils.ts\";\n\nexport const manyToManyLoadOperation = \"m2m-load\";\n\n/** Batches m2m.packages/core/src/relations/ReactiveManyToMany.tsload calls via BatchLoader. */\nexport function manyToManyBatchLoader(em: EntityManager, collection: ManyToManyLike): BatchLoader<string> {\n return em.getBatchLoader(manyToManyLoadOperation, collection.joinTableName, (keys) => loadBatch(collection, keys));\n}\n\nasync function loadBatch<U extends Entity>(collection: ManyToManyLike, keys: string[]): Promise<void> {\n const { em } = collection.entity;\n\n const joinRows = getEmInternalApi(em).joinRows(collection);\n\n // Break out `column_id=string` keys\n const columns: Record<string, string[]> = {};\n const tuples: [string, string][] = [];\n keys.forEach((key) => {\n const [columnId, id] = key.split(\"=\");\n getOrSet(columns, columnId, []).push(id);\n tuples.push([columnId, id]);\n });\n\n const alias = abbreviation(collection.joinTableName);\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [{ alias, join: \"primary\", table: collection.joinTableName }],\n condition: {\n kind: \"exp\",\n op: \"or\",\n conditions: Object.entries(columns).map(([columnId, values]) => {\n // `otherMeta` is only optional because of enum m2ms (EnumCollections), which use their own\n // batch loader, so here it's always an entity-to-entity m2m and `otherMeta` is defined.\n const meta = collection.columnName == columnId ? collection.meta : collection.otherMeta!;\n return {\n kind: \"column\",\n alias,\n column: columnId,\n dbType: meta.idDbType,\n cond: { kind: \"in\", value: values.map((id) => keyToNumber(meta, id)!) },\n };\n }),\n },\n // Id-less join tables have no surrogate id to order by, so order by the FK columns instead.\n orderBys: collection.hasJoinTableId\n ? [{ alias, column: \"id\", order: \"ASC\" }]\n : [\n { alias, column: collection.columnName, order: \"ASC\" },\n { alias, column: collection.otherColumnName, order: \"ASC\" },\n ],\n };\n\n const rows = await em[\"executeFind\"](\n collection.otherMeta!,\n manyToManyLoadOperation,\n query,\n // No LIMIT needed for join rows\n { limit: undefined },\n );\n await joinRows.loadRows(tuples, rows);\n\n const api = getEmInternalApi(em);\n for (const [column, id] of tuples) {\n const entity = em.getEntity(id);\n if (!entity) continue;\n const others = joinRows.getOthers(column, entity) as U[];\n // Determine the field name from the column\n const fieldName = column === collection.columnName ? collection.fieldName : collection.otherFieldName!;\n api.setPreloadedRelation(entity.idTagged!, fieldName, others);\n }\n}\n"],"mappings":";;;;;;;;AAMA,MAAa,0BAA0B;;AAGvC,SAAgB,sBAAsB,IAAmB,YAAiD;CACxG,OAAO,GAAG,eAAe,yBAAyB,WAAW,gBAAgB,SAAS,UAAU,YAAY,IAAI,CAAC;AACnH;AAEA,eAAe,UAA4B,YAA4B,MAA+B;CACpG,MAAM,EAAE,OAAO,WAAW;CAE1B,MAAM,WAAWA,sBAAAA,iBAAiB,EAAE,CAAC,CAAC,SAAS,UAAU;CAGzD,MAAM,UAAoC,CAAC;CAC3C,MAAM,SAA6B,CAAC;CACpC,KAAK,SAAS,QAAQ;EACpB,MAAM,CAAC,UAAU,MAAM,IAAI,MAAM,GAAG;EACpC,cAAA,SAAS,SAAS,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;EACvC,OAAO,KAAK,CAAC,UAAU,EAAE,CAAC;CAC5B,CAAC;CAED,MAAM,QAAQC,cAAAA,aAAa,WAAW,aAAa;CACnD,MAAM,QAAyB;EAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;EACxB,QAAQ,CAAC;GAAE;GAAO,MAAM;GAAW,OAAO,WAAW;EAAc,CAAC;EACpE,WAAW;GACT,MAAM;GACN,IAAI;GACJ,YAAY,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,UAAU,YAAY;IAG9D,MAAM,OAAO,WAAW,cAAc,WAAW,WAAW,OAAO,WAAW;IAC9E,OAAO;KACL,MAAM;KACN;KACA,QAAQ;KACR,QAAQ,KAAK;KACb,MAAM;MAAE,MAAM;MAAM,OAAO,OAAO,KAAK,OAAOC,aAAAA,YAAY,MAAM,EAAE,CAAE;KAAE;IACxE;GACF,CAAC;EACH;EAEA,UAAU,WAAW,iBACjB,CAAC;GAAE;GAAO,QAAQ;GAAM,OAAO;EAAM,CAAC,IACtC,CACE;GAAE;GAAO,QAAQ,WAAW;GAAY,OAAO;EAAM,GACrD;GAAE;GAAO,QAAQ,WAAW;GAAiB,OAAO;EAAM,CAC5D;CACN;CAEA,MAAM,OAAO,MAAM,GAAG,cAAc,CAClC,WAAW,WACX,yBACA,OAEA,EAAE,OAAO,KAAA,EAAU,CACrB;CACA,MAAM,SAAS,SAAS,QAAQ,IAAI;CAEpC,MAAM,MAAMF,sBAAAA,iBAAiB,EAAE;CAC/B,KAAK,MAAM,CAAC,QAAQ,OAAO,QAAQ;EACjC,MAAM,SAAS,GAAG,UAAU,EAAE;EAC9B,IAAI,CAAC,QAAQ;EACb,MAAM,SAAS,SAAS,UAAU,QAAQ,MAAM;EAEhD,MAAM,YAAY,WAAW,WAAW,aAAa,WAAW,YAAY,WAAW;EACvF,IAAI,qBAAqB,OAAO,UAAW,WAAW,MAAM;CAC9D;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manyToManyBatchLoader.js","names":[],"sources":["../../src/batchloaders/manyToManyBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"
|
|
1
|
+
{"version":3,"file":"manyToManyBatchLoader.js","names":[],"sources":["../../src/batchloaders/manyToManyBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport { type ManyToManyLike, type ParsedFindQuery, keyToNumber } from \"src/index.ts\";\nimport { abbreviation, getOrSet } from \"src/utils.ts\";\n\nexport const manyToManyLoadOperation = \"m2m-load\";\n\n/** Batches m2m.packages/core/src/relations/ReactiveManyToMany.tsload calls via BatchLoader. */\nexport function manyToManyBatchLoader(em: EntityManager, collection: ManyToManyLike): BatchLoader<string> {\n return em.getBatchLoader(manyToManyLoadOperation, collection.joinTableName, (keys) => loadBatch(collection, keys));\n}\n\nasync function loadBatch<U extends Entity>(collection: ManyToManyLike, keys: string[]): Promise<void> {\n const { em } = collection.entity;\n\n const joinRows = getEmInternalApi(em).joinRows(collection);\n\n // Break out `column_id=string` keys\n const columns: Record<string, string[]> = {};\n const tuples: [string, string][] = [];\n keys.forEach((key) => {\n const [columnId, id] = key.split(\"=\");\n getOrSet(columns, columnId, []).push(id);\n tuples.push([columnId, id]);\n });\n\n const alias = abbreviation(collection.joinTableName);\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [{ alias, join: \"primary\", table: collection.joinTableName }],\n condition: {\n kind: \"exp\",\n op: \"or\",\n conditions: Object.entries(columns).map(([columnId, values]) => {\n // `otherMeta` is only optional because of enum m2ms (EnumCollections), which use their own\n // batch loader, so here it's always an entity-to-entity m2m and `otherMeta` is defined.\n const meta = collection.columnName == columnId ? collection.meta : collection.otherMeta!;\n return {\n kind: \"column\",\n alias,\n column: columnId,\n dbType: meta.idDbType,\n cond: { kind: \"in\", value: values.map((id) => keyToNumber(meta, id)!) },\n };\n }),\n },\n // Id-less join tables have no surrogate id to order by, so order by the FK columns instead.\n orderBys: collection.hasJoinTableId\n ? [{ alias, column: \"id\", order: \"ASC\" }]\n : [\n { alias, column: collection.columnName, order: \"ASC\" },\n { alias, column: collection.otherColumnName, order: \"ASC\" },\n ],\n };\n\n const rows = await em[\"executeFind\"](\n collection.otherMeta!,\n manyToManyLoadOperation,\n query,\n // No LIMIT needed for join rows\n { limit: undefined },\n );\n await joinRows.loadRows(tuples, rows);\n\n const api = getEmInternalApi(em);\n for (const [column, id] of tuples) {\n const entity = em.getEntity(id);\n if (!entity) continue;\n const others = joinRows.getOthers(column, entity) as U[];\n // Determine the field name from the column\n const fieldName = column === collection.columnName ? collection.fieldName : collection.otherFieldName!;\n api.setPreloadedRelation(entity.idTagged!, fieldName, others);\n }\n}\n"],"mappings":";;;;;;;AAMA,MAAa,0BAA0B;;AAGvC,SAAgB,sBAAsB,IAAmB,YAAiD;CACxG,OAAO,GAAG,eAAe,yBAAyB,WAAW,gBAAgB,SAAS,UAAU,YAAY,IAAI,CAAC;AACnH;AAEA,eAAe,UAA4B,YAA4B,MAA+B;CACpG,MAAM,EAAE,OAAO,WAAW;CAE1B,MAAM,WAAW,iBAAiB,EAAE,CAAC,CAAC,SAAS,UAAU;CAGzD,MAAM,UAAoC,CAAC;CAC3C,MAAM,SAA6B,CAAC;CACpC,KAAK,SAAS,QAAQ;EACpB,MAAM,CAAC,UAAU,MAAM,IAAI,MAAM,GAAG;EACpC,SAAS,SAAS,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE;EACvC,OAAO,KAAK,CAAC,UAAU,EAAE,CAAC;CAC5B,CAAC;CAED,MAAM,QAAQ,aAAa,WAAW,aAAa;CACnD,MAAM,QAAyB;EAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;EACxB,QAAQ,CAAC;GAAE;GAAO,MAAM;GAAW,OAAO,WAAW;EAAc,CAAC;EACpE,WAAW;GACT,MAAM;GACN,IAAI;GACJ,YAAY,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,UAAU,YAAY;IAG9D,MAAM,OAAO,WAAW,cAAc,WAAW,WAAW,OAAO,WAAW;IAC9E,OAAO;KACL,MAAM;KACN;KACA,QAAQ;KACR,QAAQ,KAAK;KACb,MAAM;MAAE,MAAM;MAAM,OAAO,OAAO,KAAK,OAAO,YAAY,MAAM,EAAE,CAAE;KAAE;IACxE;GACF,CAAC;EACH;EAEA,UAAU,WAAW,iBACjB,CAAC;GAAE;GAAO,QAAQ;GAAM,OAAO;EAAM,CAAC,IACtC,CACE;GAAE;GAAO,QAAQ,WAAW;GAAY,OAAO;EAAM,GACrD;GAAE;GAAO,QAAQ,WAAW;GAAiB,OAAO;EAAM,CAC5D;CACN;CAEA,MAAM,OAAO,MAAM,GAAG,cAAc,CAClC,WAAW,WACX,yBACA,OAEA,EAAE,OAAO,KAAA,EAAU,CACrB;CACA,MAAM,SAAS,SAAS,QAAQ,IAAI;CAEpC,MAAM,MAAM,iBAAiB,EAAE;CAC/B,KAAK,MAAM,CAAC,QAAQ,OAAO,QAAQ;EACjC,MAAM,SAAS,GAAG,UAAU,EAAE;EAC9B,IAAI,CAAC,QAAQ;EACb,MAAM,SAAS,SAAS,UAAU,QAAQ,MAAM;EAEhD,MAAM,YAAY,WAAW,WAAW,aAAa,WAAW,YAAY,WAAW;EACvF,IAAI,qBAAqB,OAAO,UAAW,WAAW,MAAM;CAC9D;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oneToManyBatchLoader.cjs","names":["deTagIds","abbreviation","groupBy","maybeResolveReferenceToId","getField","getEmInternalApi"],"sources":["../../src/batchloaders/oneToManyBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"
|
|
1
|
+
{"version":3,"file":"oneToManyBatchLoader.cjs","names":["deTagIds","abbreviation","groupBy","maybeResolveReferenceToId","getField","getEmInternalApi"],"sources":["../../src/batchloaders/oneToManyBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport { getField } from \"src/fields.ts\";\nimport {\n type OneToManyCollection,\n type OneToManyField,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n assertIdsAreTagged,\n deTagIds,\n maybeResolveReferenceToId,\n} from \"src/index.ts\";\nimport { abbreviation, groupBy } from \"src/utils.ts\";\n\nexport const oneToManyLoadOperation = \"o2m-load\";\n\nexport function oneToManyBatchLoader<T extends Entity, U extends Entity>(\n em: EntityManager,\n collection: OneToManyCollection<T, U>,\n): BatchLoader<string> {\n const { meta: oneMeta, fieldName } = collection;\n const batchKey = `${oneMeta.tableName}-${fieldName}`;\n return em.getBatchLoader(oneToManyLoadOperation, batchKey, async (_keys) => {\n const { otherMeta: meta } = collection;\n\n assertIdsAreTagged(_keys);\n const keys = deTagIds(oneMeta, _keys);\n\n const alias = abbreviation(meta.tableName);\n const o2m = oneMeta.allFields[collection.fieldName] as OneToManyField;\n const other = meta.allFields[collection.otherFieldName];\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n {\n kind: \"column\",\n alias: `${alias}${other.aliasSuffix}`,\n column: o2m.otherColumnName,\n dbType: meta.idDbType,\n cond: { kind: \"in\", value: keys },\n },\n ],\n },\n orderBys: [{ alias, column: \"id\", order: \"ASC\" }],\n };\n\n addTablePerClassJoinsAndClassTag(query, meta, alias, true);\n\n const rowData = await em[\"executeFindRowData\"](meta, oneToManyLoadOperation, query, {});\n const entities = em[\"hydrateAndFinalize\"](meta.cstr, rowData);\n\n const entitiesById = groupBy(entities, (entity) => {\n const ownerId = maybeResolveReferenceToId(getField(entity, collection.otherFieldName));\n return ownerId ?? \"dummyNoLongerOwned\";\n });\n\n const api = getEmInternalApi(em);\n for (const key of _keys) {\n api.setPreloadedRelation(key, fieldName, entitiesById.get(key) || []);\n }\n });\n}\n"],"mappings":";;;;;;;;;;AAeA,MAAa,yBAAyB;AAEtC,SAAgB,qBACd,IACA,YACqB;CACrB,MAAM,EAAE,MAAM,SAAS,cAAc;CACrC,MAAM,WAAW,GAAG,QAAQ,UAAU,GAAG;CACzC,OAAO,GAAG,eAAe,wBAAwB,UAAU,OAAO,UAAU;EAC1E,MAAM,EAAE,WAAW,SAAS;EAE5B,aAAA,mBAAmB,KAAK;EACxB,MAAM,OAAOA,aAAAA,SAAS,SAAS,KAAK;EAEpC,MAAM,QAAQC,cAAAA,aAAa,KAAK,SAAS;EACzC,MAAM,MAAM,QAAQ,UAAU,WAAW;EACzC,MAAM,QAAQ,KAAK,UAAU,WAAW;EACxC,MAAM,QAAyB;GAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;GACxB,QAAQ,CAAC;IAAE;IAAO,MAAM;IAAW,OAAO,KAAK;GAAU,CAAC;GAC1D,WAAW;IACT,MAAM;IACN,IAAI;IACJ,YAAY,CACV;KACE,MAAM;KACN,OAAO,GAAG,QAAQ,MAAM;KACxB,QAAQ,IAAI;KACZ,QAAQ,KAAK;KACb,MAAM;MAAE,MAAM;MAAM,OAAO;KAAK;IAClC,CACF;GACF;GACA,UAAU,CAAC;IAAE;IAAO,QAAQ;IAAM,OAAO;GAAM,CAAC;EAClD;EAEA,iCAAA,iCAAiC,OAAO,MAAM,OAAO,IAAI;EAEzD,MAAM,UAAU,MAAM,GAAG,qBAAqB,CAAC,MAAM,wBAAwB,OAAO,CAAC,CAAC;EACtF,MAAM,WAAW,GAAG,qBAAqB,CAAC,KAAK,MAAM,OAAO;EAE5D,MAAM,eAAeC,cAAAA,QAAQ,WAAW,WAAW;GAEjD,OADgBC,aAAAA,0BAA0BC,eAAAA,SAAS,QAAQ,WAAW,cAAc,CACvE,KAAK;EACpB,CAAC;EAED,MAAM,MAAMC,sBAAAA,iBAAiB,EAAE;EAC/B,KAAK,MAAM,OAAO,OAChB,IAAI,qBAAqB,KAAK,WAAW,aAAa,IAAI,GAAG,KAAK,CAAC,CAAC;CAExE,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oneToManyBatchLoader.js","names":[],"sources":["../../src/batchloaders/oneToManyBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"
|
|
1
|
+
{"version":3,"file":"oneToManyBatchLoader.js","names":[],"sources":["../../src/batchloaders/oneToManyBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport { getField } from \"src/fields.ts\";\nimport {\n type OneToManyCollection,\n type OneToManyField,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n assertIdsAreTagged,\n deTagIds,\n maybeResolveReferenceToId,\n} from \"src/index.ts\";\nimport { abbreviation, groupBy } from \"src/utils.ts\";\n\nexport const oneToManyLoadOperation = \"o2m-load\";\n\nexport function oneToManyBatchLoader<T extends Entity, U extends Entity>(\n em: EntityManager,\n collection: OneToManyCollection<T, U>,\n): BatchLoader<string> {\n const { meta: oneMeta, fieldName } = collection;\n const batchKey = `${oneMeta.tableName}-${fieldName}`;\n return em.getBatchLoader(oneToManyLoadOperation, batchKey, async (_keys) => {\n const { otherMeta: meta } = collection;\n\n assertIdsAreTagged(_keys);\n const keys = deTagIds(oneMeta, _keys);\n\n const alias = abbreviation(meta.tableName);\n const o2m = oneMeta.allFields[collection.fieldName] as OneToManyField;\n const other = meta.allFields[collection.otherFieldName];\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n {\n kind: \"column\",\n alias: `${alias}${other.aliasSuffix}`,\n column: o2m.otherColumnName,\n dbType: meta.idDbType,\n cond: { kind: \"in\", value: keys },\n },\n ],\n },\n orderBys: [{ alias, column: \"id\", order: \"ASC\" }],\n };\n\n addTablePerClassJoinsAndClassTag(query, meta, alias, true);\n\n const rowData = await em[\"executeFindRowData\"](meta, oneToManyLoadOperation, query, {});\n const entities = em[\"hydrateAndFinalize\"](meta.cstr, rowData);\n\n const entitiesById = groupBy(entities, (entity) => {\n const ownerId = maybeResolveReferenceToId(getField(entity, collection.otherFieldName));\n return ownerId ?? \"dummyNoLongerOwned\";\n });\n\n const api = getEmInternalApi(em);\n for (const key of _keys) {\n api.setPreloadedRelation(key, fieldName, entitiesById.get(key) || []);\n }\n });\n}\n"],"mappings":";;;;;;;;;AAeA,MAAa,yBAAyB;AAEtC,SAAgB,qBACd,IACA,YACqB;CACrB,MAAM,EAAE,MAAM,SAAS,cAAc;CACrC,MAAM,WAAW,GAAG,QAAQ,UAAU,GAAG;CACzC,OAAO,GAAG,eAAe,wBAAwB,UAAU,OAAO,UAAU;EAC1E,MAAM,EAAE,WAAW,SAAS;EAE5B,mBAAmB,KAAK;EACxB,MAAM,OAAO,SAAS,SAAS,KAAK;EAEpC,MAAM,QAAQ,aAAa,KAAK,SAAS;EACzC,MAAM,MAAM,QAAQ,UAAU,WAAW;EACzC,MAAM,QAAQ,KAAK,UAAU,WAAW;EACxC,MAAM,QAAyB;GAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;GACxB,QAAQ,CAAC;IAAE;IAAO,MAAM;IAAW,OAAO,KAAK;GAAU,CAAC;GAC1D,WAAW;IACT,MAAM;IACN,IAAI;IACJ,YAAY,CACV;KACE,MAAM;KACN,OAAO,GAAG,QAAQ,MAAM;KACxB,QAAQ,IAAI;KACZ,QAAQ,KAAK;KACb,MAAM;MAAE,MAAM;MAAM,OAAO;KAAK;IAClC,CACF;GACF;GACA,UAAU,CAAC;IAAE;IAAO,QAAQ;IAAM,OAAO;GAAM,CAAC;EAClD;EAEA,iCAAiC,OAAO,MAAM,OAAO,IAAI;EAEzD,MAAM,UAAU,MAAM,GAAG,qBAAqB,CAAC,MAAM,wBAAwB,OAAO,CAAC,CAAC;EACtF,MAAM,WAAW,GAAG,qBAAqB,CAAC,KAAK,MAAM,OAAO;EAE5D,MAAM,eAAe,QAAQ,WAAW,WAAW;GAEjD,OADgB,0BAA0B,SAAS,QAAQ,WAAW,cAAc,CACvE,KAAK;EACpB,CAAC;EAED,MAAM,MAAM,iBAAiB,EAAE;EAC/B,KAAK,MAAM,OAAO,OAChB,IAAI,qBAAqB,KAAK,WAAW,aAAa,IAAI,GAAG,KAAK,CAAC,CAAC;CAExE,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oneToOneBatchLoader.cjs","names":["getMetadata","deTagIds","abbreviation","groupBy","maybeResolveReferenceToId","getField","getEmInternalApi"],"sources":["../../src/batchloaders/oneToOneBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"
|
|
1
|
+
{"version":3,"file":"oneToOneBatchLoader.cjs","names":["getMetadata","deTagIds","abbreviation","groupBy","maybeResolveReferenceToId","getField","getEmInternalApi"],"sources":["../../src/batchloaders/oneToOneBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport { type OneToOneField, getMetadata } from \"src/EntityMetadata.ts\";\nimport { getField } from \"src/fields.ts\";\nimport {\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n assertIdsAreTagged,\n deTagIds,\n maybeResolveReferenceToId,\n} from \"src/index.ts\";\nimport { type OneToOneReferenceImpl } from \"src/relations/OneToOneReference.ts\";\nimport { abbreviation, groupBy } from \"src/utils.ts\";\n\nexport const oneToOneLoadOperation = \"o2o-load\";\n\nexport function oneToOneBatchLoader<T extends Entity, U extends Entity>(\n em: EntityManager,\n reference: OneToOneReferenceImpl<T, U>,\n): BatchLoader<string> {\n const meta = getMetadata(reference.entity);\n const batchKey = `${meta.tableName}-${reference.fieldName}`;\n return em.getBatchLoader(oneToOneLoadOperation, batchKey, async (_keys) => {\n const { otherMeta, otherFieldName } = reference;\n\n assertIdsAreTagged(_keys);\n const keys = deTagIds(meta, _keys);\n\n const alias = abbreviation(otherMeta.tableName);\n const o2o = meta.allFields[reference.fieldName] as OneToOneField;\n const other = otherMeta.allFields[reference.otherFieldName];\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [{ alias, join: \"primary\", table: otherMeta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n {\n kind: \"column\",\n alias: `${alias}${other.aliasSuffix}`,\n column: o2o.otherColumnName,\n dbType: meta.idDbType,\n cond: { kind: \"in\", value: keys },\n },\n ],\n },\n orderBys: [],\n };\n addTablePerClassJoinsAndClassTag(query, otherMeta, alias, true);\n\n const rowData = await em[\"executeFindRowData\"](otherMeta, oneToOneLoadOperation, query, {});\n const entities = em[\"hydrateAndFinalize\"](otherMeta.cstr, rowData);\n\n const entitiesByOtherId = groupBy(entities, (entity) => {\n const ownerId = maybeResolveReferenceToId(getField(entity, otherFieldName));\n return ownerId ?? \"dummyNoLongerOwned\";\n });\n\n const api = getEmInternalApi(em);\n for (const key of _keys) {\n const found = entitiesByOtherId.get(key);\n api.setPreloadedRelation(key, reference.fieldName as string, found ? [found[0]] : []);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;AAeA,MAAa,wBAAwB;AAErC,SAAgB,oBACd,IACA,WACqB;CACrB,MAAM,OAAOA,uBAAAA,YAAY,UAAU,MAAM;CACzC,MAAM,WAAW,GAAG,KAAK,UAAU,GAAG,UAAU;CAChD,OAAO,GAAG,eAAe,uBAAuB,UAAU,OAAO,UAAU;EACzE,MAAM,EAAE,WAAW,mBAAmB;EAEtC,aAAA,mBAAmB,KAAK;EACxB,MAAM,OAAOC,aAAAA,SAAS,MAAM,KAAK;EAEjC,MAAM,QAAQC,cAAAA,aAAa,UAAU,SAAS;EAC9C,MAAM,MAAM,KAAK,UAAU,UAAU;EACrC,MAAM,QAAQ,UAAU,UAAU,UAAU;EAC5C,MAAM,QAAyB;GAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;GACxB,QAAQ,CAAC;IAAE;IAAO,MAAM;IAAW,OAAO,UAAU;GAAU,CAAC;GAC/D,WAAW;IACT,MAAM;IACN,IAAI;IACJ,YAAY,CACV;KACE,MAAM;KACN,OAAO,GAAG,QAAQ,MAAM;KACxB,QAAQ,IAAI;KACZ,QAAQ,KAAK;KACb,MAAM;MAAE,MAAM;MAAM,OAAO;KAAK;IAClC,CACF;GACF;GACA,UAAU,CAAC;EACb;EACA,iCAAA,iCAAiC,OAAO,WAAW,OAAO,IAAI;EAE9D,MAAM,UAAU,MAAM,GAAG,qBAAqB,CAAC,WAAW,uBAAuB,OAAO,CAAC,CAAC;EAC1F,MAAM,WAAW,GAAG,qBAAqB,CAAC,UAAU,MAAM,OAAO;EAEjE,MAAM,oBAAoBC,cAAAA,QAAQ,WAAW,WAAW;GAEtD,OADgBC,aAAAA,0BAA0BC,eAAAA,SAAS,QAAQ,cAAc,CAC5D,KAAK;EACpB,CAAC;EAED,MAAM,MAAMC,sBAAAA,iBAAiB,EAAE;EAC/B,KAAK,MAAM,OAAO,OAAO;GACvB,MAAM,QAAQ,kBAAkB,IAAI,GAAG;GACvC,IAAI,qBAAqB,KAAK,UAAU,WAAqB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;EACtF;CACF,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oneToOneBatchLoader.js","names":[],"sources":["../../src/batchloaders/oneToOneBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"
|
|
1
|
+
{"version":3,"file":"oneToOneBatchLoader.js","names":[],"sources":["../../src/batchloaders/oneToOneBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport { type OneToOneField, getMetadata } from \"src/EntityMetadata.ts\";\nimport { getField } from \"src/fields.ts\";\nimport {\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n assertIdsAreTagged,\n deTagIds,\n maybeResolveReferenceToId,\n} from \"src/index.ts\";\nimport { type OneToOneReferenceImpl } from \"src/relations/OneToOneReference.ts\";\nimport { abbreviation, groupBy } from \"src/utils.ts\";\n\nexport const oneToOneLoadOperation = \"o2o-load\";\n\nexport function oneToOneBatchLoader<T extends Entity, U extends Entity>(\n em: EntityManager,\n reference: OneToOneReferenceImpl<T, U>,\n): BatchLoader<string> {\n const meta = getMetadata(reference.entity);\n const batchKey = `${meta.tableName}-${reference.fieldName}`;\n return em.getBatchLoader(oneToOneLoadOperation, batchKey, async (_keys) => {\n const { otherMeta, otherFieldName } = reference;\n\n assertIdsAreTagged(_keys);\n const keys = deTagIds(meta, _keys);\n\n const alias = abbreviation(otherMeta.tableName);\n const o2o = meta.allFields[reference.fieldName] as OneToOneField;\n const other = otherMeta.allFields[reference.otherFieldName];\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [{ alias, join: \"primary\", table: otherMeta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n {\n kind: \"column\",\n alias: `${alias}${other.aliasSuffix}`,\n column: o2o.otherColumnName,\n dbType: meta.idDbType,\n cond: { kind: \"in\", value: keys },\n },\n ],\n },\n orderBys: [],\n };\n addTablePerClassJoinsAndClassTag(query, otherMeta, alias, true);\n\n const rowData = await em[\"executeFindRowData\"](otherMeta, oneToOneLoadOperation, query, {});\n const entities = em[\"hydrateAndFinalize\"](otherMeta.cstr, rowData);\n\n const entitiesByOtherId = groupBy(entities, (entity) => {\n const ownerId = maybeResolveReferenceToId(getField(entity, otherFieldName));\n return ownerId ?? \"dummyNoLongerOwned\";\n });\n\n const api = getEmInternalApi(em);\n for (const key of _keys) {\n const found = entitiesByOtherId.get(key);\n api.setPreloadedRelation(key, reference.fieldName as string, found ? [found[0]] : []);\n }\n });\n}\n"],"mappings":";;;;;;;;;;;AAeA,MAAa,wBAAwB;AAErC,SAAgB,oBACd,IACA,WACqB;CACrB,MAAM,OAAO,YAAY,UAAU,MAAM;CACzC,MAAM,WAAW,GAAG,KAAK,UAAU,GAAG,UAAU;CAChD,OAAO,GAAG,eAAe,uBAAuB,UAAU,OAAO,UAAU;EACzE,MAAM,EAAE,WAAW,mBAAmB;EAEtC,mBAAmB,KAAK;EACxB,MAAM,OAAO,SAAS,MAAM,KAAK;EAEjC,MAAM,QAAQ,aAAa,UAAU,SAAS;EAC9C,MAAM,MAAM,KAAK,UAAU,UAAU;EACrC,MAAM,QAAQ,UAAU,UAAU,UAAU;EAC5C,MAAM,QAAyB;GAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;GACxB,QAAQ,CAAC;IAAE;IAAO,MAAM;IAAW,OAAO,UAAU;GAAU,CAAC;GAC/D,WAAW;IACT,MAAM;IACN,IAAI;IACJ,YAAY,CACV;KACE,MAAM;KACN,OAAO,GAAG,QAAQ,MAAM;KACxB,QAAQ,IAAI;KACZ,QAAQ,KAAK;KACb,MAAM;MAAE,MAAM;MAAM,OAAO;KAAK;IAClC,CACF;GACF;GACA,UAAU,CAAC;EACb;EACA,iCAAiC,OAAO,WAAW,OAAO,IAAI;EAE9D,MAAM,UAAU,MAAM,GAAG,qBAAqB,CAAC,WAAW,uBAAuB,OAAO,CAAC,CAAC;EAC1F,MAAM,WAAW,GAAG,qBAAqB,CAAC,UAAU,MAAM,OAAO;EAEjE,MAAM,oBAAoB,QAAQ,WAAW,WAAW;GAEtD,OADgB,0BAA0B,SAAS,QAAQ,cAAc,CAC5D,KAAK;EACpB,CAAC;EAED,MAAM,MAAM,iBAAiB,EAAE;EAC/B,KAAK,MAAM,OAAO,OAAO;GACvB,MAAM,QAAQ,kBAAkB,IAAI,GAAG;GACvC,IAAI,qBAAqB,KAAK,UAAU,WAAqB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;EACtF;CACF,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"populateBatchLoader.cjs","names":["hintKey","getEmInternalApi","AliasAssigner","keyToNumber","kqDot","indexBy","isPolyHint","getRelationFromMaybePolyKey","ReactiveFieldImpl","oneToManyBatchLoader","manyToManyBatchLoader","oneToOneBatchLoader","loadBatchLoader","toArray","buildHintTree"],"sources":["../../src/batchloaders/populateBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport { type EntityMetadata } from \"../EntityMetadata.ts\";\nimport {\n AliasAssigner,\n type EntityManager,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n getEmInternalApi,\n indexBy,\n keyToNumber,\n kqDot,\n} from \"../index.ts\";\nimport { type HintNode, buildHintTree } from \"../loading/HintTree.ts\";\nimport type { LoadHint } from \"../loading/loadHints.ts\";\nimport { hintKey } from \"../normalizeHints.ts\";\nimport { getRelationFromMaybePolyKey, isPolyHint } from \"../reactivity/reactiveHints.ts\";\nimport { ReactiveFieldImpl } from \"../relations/ReactiveField.ts\";\nimport { toArray } from \"../utils.ts\";\nimport { type BatchLoader } from \"./BatchLoader.ts\";\nimport { loadBatchLoader } from \"./loadBatchLoader.ts\";\nimport { manyToManyBatchLoader } from \"./manyToManyBatchLoader.ts\";\nimport { oneToManyBatchLoader } from \"./oneToManyBatchLoader.ts\";\nimport { oneToOneBatchLoader } from \"./oneToOneBatchLoader.ts\";\n\nexport const populateOperation = \"populate\";\n\nexport function populateBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n hint: LoadHint<any>,\n mode: \"preload\" | \"intermixed\",\n opts: { forceReload?: boolean } = {},\n): BatchLoader<{ entity: Entity; hint: LoadHint<any> }> {\n // For batching populates, we want different levels of course-ness:\n // - preloading populates that are only SQL should be batched together as much as possible, but\n // - intermixed populates (some with custom relations) should be batched as separately as possible\n // (while still batching identical populates together) to reduce the chance of promise deadlocks.\n //\n // I.e. if we know this is a sql-only hint, we can batch all loads for a given entity\n // together do leverage join-based preloading.\n //\n // However, intermixed batches can be prone to promise deadlocking (one relation .load getting\n // stuck in a batch that is asking for its own dependencies to load), so if this is an intermixed\n // hint, then use a batch key that includes the hint itself, which will make it unlikely\n // for non-sql relations to deadlock on each other/themselves.\n const batchKey =\n mode === \"preload\" ? `${meta.tagName}:${opts.forceReload}` : `${meta.tagName}:${hintKey(hint)}:${opts.forceReload}`;\n return em.getBatchLoader(populateOperation, batchKey, async (populates) => {\n async function populateLayer(layerMeta: EntityMetadata | undefined, layerNode: HintNode<Entity>): Promise<void> {\n // Skip join-based preloading if nothing in this layer needs loading. If any entity in the list\n // needs loading, just load everything\n const { preloader } = getEmInternalApi(em);\n // We may not have a layerMeta if we're going through non-field properties\n if (preloader && layerMeta) {\n const preloadThisLayer = Object.entries(layerNode.hints).some(([key, hint]) => {\n for (const entity of hint.entities) {\n if ((entity as any)[key]?.isLoaded === false && !(entity as any)[key]?.isPreloaded) return true;\n }\n return false;\n });\n if (preloadThisLayer) {\n // Do an up-front SQL call of `select id, ...preloads... from table`,\n const assigner = new AliasAssigner();\n const meta = layerMeta;\n const alias = assigner.getAlias(meta.tableName);\n const entities = [...layerNode.entities].filter((e) => !e.isNewEntity);\n const ids = entities.map((e) => keyToNumber(meta, e.id));\n // Create a ParsedFindQuery for `addPreloading` to inject joins into\n const query: ParsedFindQuery = {\n // We already have the entities loaded, so can do just `SELECT a.id` + the preload columns\n selects: [kqDot(alias, \"id\")],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n { kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: ids } },\n ],\n },\n orderBys: [],\n };\n // If we're selecting from small_publishers, join in our base class in case\n // the preloader wants to join on a column like `publishers.group_id`\n addTablePerClassJoinsAndClassTag(query, meta, alias, false);\n const hydrator = preloader.addPreloading(meta, layerNode, query);\n if (hydrator) {\n const rows = await em[\"executeFind\"](meta, populateOperation, query, {});\n const entitiesById = indexBy(entities, (e) => keyToNumber(meta, e.id));\n const entitiesInOrder = rows.map((row) => entitiesById.get(row[\"id\"]));\n hydrator(rows, entitiesInOrder);\n }\n }\n }\n\n // First pass: batch SQL relations directly, fall back to relation.load() for non-SQL\n const batchPromises = new Set<Promise<void>>();\n const relationsToPreload: { preload(): void }[] = [];\n const fallbackPromises: (Promise<any> | undefined)[] = [];\n\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n const field = layerMeta?.allFields[key];\n // Hoist the poly-key parsing out of the per-entity loop; most keys are not poly\n const isPoly = isPolyHint(key);\n let loader: BatchLoader<any> | undefined;\n let batchPromise: Promise<void> | undefined;\n for (const entity of tree.entities) {\n const relation = isPoly ? getRelationFromMaybePolyKey(entity, key) : (entity as any)[key];\n // This happens to let through non-relation hints like 'name' on user, which wasn't intentional,\n // but currently doesn't blow up (somehow), and is depended on by internal tests.\n if (!relation || typeof relation.load !== \"function\") {\n // We don't want to throw on poly hints, because they're not actually loaded on the entity\n if (isPoly) continue;\n throw new Error(`Invalid load hint '${key}' on ${entity}`);\n }\n\n // If we're populating a hasReactiveField, don't bother loading it\n // if it's already been calculated (i.e. we have no reason to believe its value\n // is stale, so we should avoid pulling all of its data into memory).\n //\n // (Note that we can't do this same optimization for ReactiveReferenceImpl, because\n // as a FK, it will always need at least some `.load()` to fetch its entity from the database.\n // So we go ahead and call `.load()`, assuming it will just load its cached value, but it\n // will also check internally if it's marked for recalc, and load its load hint if necessary.\n if (relation instanceof ReactiveFieldImpl && relation.isSet) continue;\n if (relation.isLoaded && !opts.forceReload) continue;\n if (relation.isPreloaded) {\n relation.preload();\n continue;\n }\n\n // For non-derived SQL relations on existing entities, use batch loaders directly\n // (1 shared promise per batch instead of per-entity async overhead).\n // Skip new entities (no id) and derived relations (reactive m2m/m2o have extra logic in load).\n if (!entity.isNewEntity && field) {\n if (field.kind === \"o2m\") {\n batchPromise = (loader ??= oneToManyBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2m\" && !field.derived) {\n batchPromise = (loader ??= manyToManyBatchLoader(em, relation)).load(\n `${relation.columnName}=${entity.id}`,\n );\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"o2o\") {\n batchPromise = (loader ??= oneToOneBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2o\" && !field.derived) {\n const taggedId = relation.idTaggedMaybe;\n if (taggedId) {\n batchPromise = (loader ??= loadBatchLoader(em, field.otherMetadata())).load({\n taggedId,\n hint: undefined,\n });\n relationsToPreload.push(relation);\n continue;\n }\n }\n }\n fallbackPromises.push(relation.load(opts) as Promise<any>);\n }\n if (batchPromise) batchPromises.add(batchPromise);\n }\n\n if (batchPromises.size > 0 || fallbackPromises.length > 0) {\n await Promise.all([Promise.all(batchPromises), Promise.all(fallbackPromises)]);\n }\n for (const relation of relationsToPreload) {\n relation.preload();\n }\n\n // 2nd breadth-width pass to do nested load hints, this will fan out at the sibling level.\n // i.e. populateLayer(...reviews...) & populateLayer(...comments...)\n let nestedLoadPromises: (Promise<void> | undefined)[] | undefined;\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n if (Object.keys(tree.hints).length === 0) continue;\n\n // Get the children we found, i.e. [a1, a2, a3] -> all of their books\n const childrenByParent = new Map<Entity, Entity[]>();\n for (const entity of tree.entities) {\n const relation = getRelationFromMaybePolyKey(entity, key);\n childrenByParent.set(entity, relation ? toArray(getEvenDeleted(relation)) : []);\n }\n if (childrenByParent.size === 0) continue;\n\n // Rewrite our node.entities to be the next layer of children, i.e. children will be all books, for all of\n // `[a1, a2, a3]`, but only the books of `a2` need to recurse into `book: reviews` and only the books of\n // `a3` need to recurse into `book: comments`, so swap `node.entities` (which is currently authors)\n // with the books. This is what prevents our dataloader-merged TreeHint from over-fetching and loading\n // the superset load hint for all entities.\n function rewrite(node: HintNode<Entity>) {\n const next = new Set<Entity>();\n for (const entity of node.entities) {\n const children = childrenByParent.get(entity);\n if (children) for (const child of children) next.add(child);\n }\n node.entities = next;\n Object.values(node.hints).forEach((node) => rewrite(node));\n }\n\n rewrite(tree);\n\n const nextMeta = (layerMeta?.allFields[key] as any)?.otherMetadata?.();\n (nestedLoadPromises ??= []).push(populateLayer(nextMeta, tree));\n }\n if (nestedLoadPromises) await Promise.all(nestedLoadPromises);\n }\n\n await populateLayer(meta, buildHintTree(populates));\n });\n}\n\n/** Probes `relation` to see if it's a m2o/o2m/m2m relation that supports `getWithDeleted`, otherwise calls `get`. */\nfunction getEvenDeleted(relation: any): any {\n return \"getWithDeleted\" in relation ? relation.getWithDeleted : relation.get;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"populateBatchLoader.cjs","names":["hintKey","getEmInternalApi","AliasAssigner","keyToNumber","kqDot","indexBy","isPolyHint","getRelationFromMaybePolyKey","ReactiveFieldImpl","oneToManyBatchLoader","manyToManyBatchLoader","oneToOneBatchLoader","loadBatchLoader","toArray","buildHintTree"],"sources":["../../src/batchloaders/populateBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { loadBatchLoader } from \"src/batchloaders/loadBatchLoader.ts\";\nimport { manyToManyBatchLoader } from \"src/batchloaders/manyToManyBatchLoader.ts\";\nimport { oneToManyBatchLoader } from \"src/batchloaders/oneToManyBatchLoader.ts\";\nimport { oneToOneBatchLoader } from \"src/batchloaders/oneToOneBatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityMetadata } from \"src/EntityMetadata.ts\";\nimport {\n AliasAssigner,\n type EntityManager,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n getEmInternalApi,\n indexBy,\n keyToNumber,\n kqDot,\n} from \"src/index.ts\";\nimport { type HintNode, buildHintTree } from \"src/loading/HintTree.ts\";\nimport type { LoadHint } from \"src/loading/loadHints.ts\";\nimport { hintKey } from \"src/normalizeHints.ts\";\nimport { getRelationFromMaybePolyKey, isPolyHint } from \"src/reactivity/reactiveHints.ts\";\nimport { ReactiveFieldImpl } from \"src/relations/ReactiveField.ts\";\nimport { toArray } from \"src/utils.ts\";\n\nexport const populateOperation = \"populate\";\n\nexport function populateBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n hint: LoadHint<any>,\n mode: \"preload\" | \"intermixed\",\n opts: { forceReload?: boolean } = {},\n): BatchLoader<{ entity: Entity; hint: LoadHint<any> }> {\n // For batching populates, we want different levels of course-ness:\n // - preloading populates that are only SQL should be batched together as much as possible, but\n // - intermixed populates (some with custom relations) should be batched as separately as possible\n // (while still batching identical populates together) to reduce the chance of promise deadlocks.\n //\n // I.e. if we know this is a sql-only hint, we can batch all loads for a given entity\n // together do leverage join-based preloading.\n //\n // However, intermixed batches can be prone to promise deadlocking (one relation .load getting\n // stuck in a batch that is asking for its own dependencies to load), so if this is an intermixed\n // hint, then use a batch key that includes the hint itself, which will make it unlikely\n // for non-sql relations to deadlock on each other/themselves.\n const batchKey =\n mode === \"preload\" ? `${meta.tagName}:${opts.forceReload}` : `${meta.tagName}:${hintKey(hint)}:${opts.forceReload}`;\n return em.getBatchLoader(populateOperation, batchKey, async (populates) => {\n async function populateLayer(layerMeta: EntityMetadata | undefined, layerNode: HintNode<Entity>): Promise<void> {\n // Skip join-based preloading if nothing in this layer needs loading. If any entity in the list\n // needs loading, just load everything\n const { preloader } = getEmInternalApi(em);\n // We may not have a layerMeta if we're going through non-field properties\n if (preloader && layerMeta) {\n const preloadThisLayer = Object.entries(layerNode.hints).some(([key, hint]) => {\n for (const entity of hint.entities) {\n if ((entity as any)[key]?.isLoaded === false && !(entity as any)[key]?.isPreloaded) return true;\n }\n return false;\n });\n if (preloadThisLayer) {\n // Do an up-front SQL call of `select id, ...preloads... from table`,\n const assigner = new AliasAssigner();\n const meta = layerMeta;\n const alias = assigner.getAlias(meta.tableName);\n const entities = [...layerNode.entities].filter((e) => !e.isNewEntity);\n const ids = entities.map((e) => keyToNumber(meta, e.id));\n // Create a ParsedFindQuery for `addPreloading` to inject joins into\n const query: ParsedFindQuery = {\n // We already have the entities loaded, so can do just `SELECT a.id` + the preload columns\n selects: [kqDot(alias, \"id\")],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n { kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: ids } },\n ],\n },\n orderBys: [],\n };\n // If we're selecting from small_publishers, join in our base class in case\n // the preloader wants to join on a column like `publishers.group_id`\n addTablePerClassJoinsAndClassTag(query, meta, alias, false);\n const hydrator = preloader.addPreloading(meta, layerNode, query);\n if (hydrator) {\n const rows = await em[\"executeFind\"](meta, populateOperation, query, {});\n const entitiesById = indexBy(entities, (e) => keyToNumber(meta, e.id));\n const entitiesInOrder = rows.map((row) => entitiesById.get(row[\"id\"]));\n hydrator(rows, entitiesInOrder);\n }\n }\n }\n\n // First pass: batch SQL relations directly, fall back to relation.load() for non-SQL\n const batchPromises = new Set<Promise<void>>();\n const relationsToPreload: { preload(): void }[] = [];\n const fallbackPromises: (Promise<any> | undefined)[] = [];\n\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n const field = layerMeta?.allFields[key];\n // Hoist the poly-key parsing out of the per-entity loop; most keys are not poly\n const isPoly = isPolyHint(key);\n let loader: BatchLoader<any> | undefined;\n let batchPromise: Promise<void> | undefined;\n for (const entity of tree.entities) {\n const relation = isPoly ? getRelationFromMaybePolyKey(entity, key) : (entity as any)[key];\n // This happens to let through non-relation hints like 'name' on user, which wasn't intentional,\n // but currently doesn't blow up (somehow), and is depended on by internal tests.\n if (!relation || typeof relation.load !== \"function\") {\n // We don't want to throw on poly hints, because they're not actually loaded on the entity\n if (isPoly) continue;\n throw new Error(`Invalid load hint '${key}' on ${entity}`);\n }\n\n // If we're populating a hasReactiveField, don't bother loading it\n // if it's already been calculated (i.e. we have no reason to believe its value\n // is stale, so we should avoid pulling all of its data into memory).\n //\n // (Note that we can't do this same optimization for ReactiveReferenceImpl, because\n // as a FK, it will always need at least some `.load()` to fetch its entity from the database.\n // So we go ahead and call `.load()`, assuming it will just load its cached value, but it\n // will also check internally if it's marked for recalc, and load its load hint if necessary.\n if (relation instanceof ReactiveFieldImpl && relation.isSet) continue;\n if (relation.isLoaded && !opts.forceReload) continue;\n if (relation.isPreloaded) {\n relation.preload();\n continue;\n }\n\n // For non-derived SQL relations on existing entities, use batch loaders directly\n // (1 shared promise per batch instead of per-entity async overhead).\n // Skip new entities (no id) and derived relations (reactive m2m/m2o have extra logic in load).\n if (!entity.isNewEntity && field) {\n if (field.kind === \"o2m\") {\n batchPromise = (loader ??= oneToManyBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2m\" && !field.derived) {\n batchPromise = (loader ??= manyToManyBatchLoader(em, relation)).load(\n `${relation.columnName}=${entity.id}`,\n );\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"o2o\") {\n batchPromise = (loader ??= oneToOneBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2o\" && !field.derived) {\n const taggedId = relation.idTaggedMaybe;\n if (taggedId) {\n batchPromise = (loader ??= loadBatchLoader(em, field.otherMetadata())).load({\n taggedId,\n hint: undefined,\n });\n relationsToPreload.push(relation);\n continue;\n }\n }\n }\n fallbackPromises.push(relation.load(opts) as Promise<any>);\n }\n if (batchPromise) batchPromises.add(batchPromise);\n }\n\n if (batchPromises.size > 0 || fallbackPromises.length > 0) {\n await Promise.all([Promise.all(batchPromises), Promise.all(fallbackPromises)]);\n }\n for (const relation of relationsToPreload) {\n relation.preload();\n }\n\n // 2nd breadth-width pass to do nested load hints, this will fan out at the sibling level.\n // i.e. populateLayer(...reviews...) & populateLayer(...comments...)\n let nestedLoadPromises: (Promise<void> | undefined)[] | undefined;\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n if (Object.keys(tree.hints).length === 0) continue;\n\n // Get the children we found, i.e. [a1, a2, a3] -> all of their books\n const childrenByParent = new Map<Entity, Entity[]>();\n for (const entity of tree.entities) {\n const relation = getRelationFromMaybePolyKey(entity, key);\n childrenByParent.set(entity, relation ? toArray(getEvenDeleted(relation)) : []);\n }\n if (childrenByParent.size === 0) continue;\n\n // Rewrite our node.entities to be the next layer of children, i.e. children will be all books, for all of\n // `[a1, a2, a3]`, but only the books of `a2` need to recurse into `book: reviews` and only the books of\n // `a3` need to recurse into `book: comments`, so swap `node.entities` (which is currently authors)\n // with the books. This is what prevents our dataloader-merged TreeHint from over-fetching and loading\n // the superset load hint for all entities.\n function rewrite(node: HintNode<Entity>) {\n const next = new Set<Entity>();\n for (const entity of node.entities) {\n const children = childrenByParent.get(entity);\n if (children) for (const child of children) next.add(child);\n }\n node.entities = next;\n Object.values(node.hints).forEach((node) => rewrite(node));\n }\n\n rewrite(tree);\n\n const nextMeta = (layerMeta?.allFields[key] as any)?.otherMetadata?.();\n (nestedLoadPromises ??= []).push(populateLayer(nextMeta, tree));\n }\n if (nestedLoadPromises) await Promise.all(nestedLoadPromises);\n }\n\n await populateLayer(meta, buildHintTree(populates));\n });\n}\n\n/** Probes `relation` to see if it's a m2o/o2m/m2m relation that supports `getWithDeleted`, otherwise calls `get`. */\nfunction getEvenDeleted(relation: any): any {\n return \"getWithDeleted\" in relation ? relation.getWithDeleted : relation.get;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"populateBatchLoader.js","names":[],"sources":["../../src/batchloaders/populateBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"../Entity.ts\";\nimport { type EntityMetadata } from \"../EntityMetadata.ts\";\nimport {\n AliasAssigner,\n type EntityManager,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n getEmInternalApi,\n indexBy,\n keyToNumber,\n kqDot,\n} from \"../index.ts\";\nimport { type HintNode, buildHintTree } from \"../loading/HintTree.ts\";\nimport type { LoadHint } from \"../loading/loadHints.ts\";\nimport { hintKey } from \"../normalizeHints.ts\";\nimport { getRelationFromMaybePolyKey, isPolyHint } from \"../reactivity/reactiveHints.ts\";\nimport { ReactiveFieldImpl } from \"../relations/ReactiveField.ts\";\nimport { toArray } from \"../utils.ts\";\nimport { type BatchLoader } from \"./BatchLoader.ts\";\nimport { loadBatchLoader } from \"./loadBatchLoader.ts\";\nimport { manyToManyBatchLoader } from \"./manyToManyBatchLoader.ts\";\nimport { oneToManyBatchLoader } from \"./oneToManyBatchLoader.ts\";\nimport { oneToOneBatchLoader } from \"./oneToOneBatchLoader.ts\";\n\nexport const populateOperation = \"populate\";\n\nexport function populateBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n hint: LoadHint<any>,\n mode: \"preload\" | \"intermixed\",\n opts: { forceReload?: boolean } = {},\n): BatchLoader<{ entity: Entity; hint: LoadHint<any> }> {\n // For batching populates, we want different levels of course-ness:\n // - preloading populates that are only SQL should be batched together as much as possible, but\n // - intermixed populates (some with custom relations) should be batched as separately as possible\n // (while still batching identical populates together) to reduce the chance of promise deadlocks.\n //\n // I.e. if we know this is a sql-only hint, we can batch all loads for a given entity\n // together do leverage join-based preloading.\n //\n // However, intermixed batches can be prone to promise deadlocking (one relation .load getting\n // stuck in a batch that is asking for its own dependencies to load), so if this is an intermixed\n // hint, then use a batch key that includes the hint itself, which will make it unlikely\n // for non-sql relations to deadlock on each other/themselves.\n const batchKey =\n mode === \"preload\" ? `${meta.tagName}:${opts.forceReload}` : `${meta.tagName}:${hintKey(hint)}:${opts.forceReload}`;\n return em.getBatchLoader(populateOperation, batchKey, async (populates) => {\n async function populateLayer(layerMeta: EntityMetadata | undefined, layerNode: HintNode<Entity>): Promise<void> {\n // Skip join-based preloading if nothing in this layer needs loading. If any entity in the list\n // needs loading, just load everything\n const { preloader } = getEmInternalApi(em);\n // We may not have a layerMeta if we're going through non-field properties\n if (preloader && layerMeta) {\n const preloadThisLayer = Object.entries(layerNode.hints).some(([key, hint]) => {\n for (const entity of hint.entities) {\n if ((entity as any)[key]?.isLoaded === false && !(entity as any)[key]?.isPreloaded) return true;\n }\n return false;\n });\n if (preloadThisLayer) {\n // Do an up-front SQL call of `select id, ...preloads... from table`,\n const assigner = new AliasAssigner();\n const meta = layerMeta;\n const alias = assigner.getAlias(meta.tableName);\n const entities = [...layerNode.entities].filter((e) => !e.isNewEntity);\n const ids = entities.map((e) => keyToNumber(meta, e.id));\n // Create a ParsedFindQuery for `addPreloading` to inject joins into\n const query: ParsedFindQuery = {\n // We already have the entities loaded, so can do just `SELECT a.id` + the preload columns\n selects: [kqDot(alias, \"id\")],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n { kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: ids } },\n ],\n },\n orderBys: [],\n };\n // If we're selecting from small_publishers, join in our base class in case\n // the preloader wants to join on a column like `publishers.group_id`\n addTablePerClassJoinsAndClassTag(query, meta, alias, false);\n const hydrator = preloader.addPreloading(meta, layerNode, query);\n if (hydrator) {\n const rows = await em[\"executeFind\"](meta, populateOperation, query, {});\n const entitiesById = indexBy(entities, (e) => keyToNumber(meta, e.id));\n const entitiesInOrder = rows.map((row) => entitiesById.get(row[\"id\"]));\n hydrator(rows, entitiesInOrder);\n }\n }\n }\n\n // First pass: batch SQL relations directly, fall back to relation.load() for non-SQL\n const batchPromises = new Set<Promise<void>>();\n const relationsToPreload: { preload(): void }[] = [];\n const fallbackPromises: (Promise<any> | undefined)[] = [];\n\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n const field = layerMeta?.allFields[key];\n // Hoist the poly-key parsing out of the per-entity loop; most keys are not poly\n const isPoly = isPolyHint(key);\n let loader: BatchLoader<any> | undefined;\n let batchPromise: Promise<void> | undefined;\n for (const entity of tree.entities) {\n const relation = isPoly ? getRelationFromMaybePolyKey(entity, key) : (entity as any)[key];\n // This happens to let through non-relation hints like 'name' on user, which wasn't intentional,\n // but currently doesn't blow up (somehow), and is depended on by internal tests.\n if (!relation || typeof relation.load !== \"function\") {\n // We don't want to throw on poly hints, because they're not actually loaded on the entity\n if (isPoly) continue;\n throw new Error(`Invalid load hint '${key}' on ${entity}`);\n }\n\n // If we're populating a hasReactiveField, don't bother loading it\n // if it's already been calculated (i.e. we have no reason to believe its value\n // is stale, so we should avoid pulling all of its data into memory).\n //\n // (Note that we can't do this same optimization for ReactiveReferenceImpl, because\n // as a FK, it will always need at least some `.load()` to fetch its entity from the database.\n // So we go ahead and call `.load()`, assuming it will just load its cached value, but it\n // will also check internally if it's marked for recalc, and load its load hint if necessary.\n if (relation instanceof ReactiveFieldImpl && relation.isSet) continue;\n if (relation.isLoaded && !opts.forceReload) continue;\n if (relation.isPreloaded) {\n relation.preload();\n continue;\n }\n\n // For non-derived SQL relations on existing entities, use batch loaders directly\n // (1 shared promise per batch instead of per-entity async overhead).\n // Skip new entities (no id) and derived relations (reactive m2m/m2o have extra logic in load).\n if (!entity.isNewEntity && field) {\n if (field.kind === \"o2m\") {\n batchPromise = (loader ??= oneToManyBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2m\" && !field.derived) {\n batchPromise = (loader ??= manyToManyBatchLoader(em, relation)).load(\n `${relation.columnName}=${entity.id}`,\n );\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"o2o\") {\n batchPromise = (loader ??= oneToOneBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2o\" && !field.derived) {\n const taggedId = relation.idTaggedMaybe;\n if (taggedId) {\n batchPromise = (loader ??= loadBatchLoader(em, field.otherMetadata())).load({\n taggedId,\n hint: undefined,\n });\n relationsToPreload.push(relation);\n continue;\n }\n }\n }\n fallbackPromises.push(relation.load(opts) as Promise<any>);\n }\n if (batchPromise) batchPromises.add(batchPromise);\n }\n\n if (batchPromises.size > 0 || fallbackPromises.length > 0) {\n await Promise.all([Promise.all(batchPromises), Promise.all(fallbackPromises)]);\n }\n for (const relation of relationsToPreload) {\n relation.preload();\n }\n\n // 2nd breadth-width pass to do nested load hints, this will fan out at the sibling level.\n // i.e. populateLayer(...reviews...) & populateLayer(...comments...)\n let nestedLoadPromises: (Promise<void> | undefined)[] | undefined;\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n if (Object.keys(tree.hints).length === 0) continue;\n\n // Get the children we found, i.e. [a1, a2, a3] -> all of their books\n const childrenByParent = new Map<Entity, Entity[]>();\n for (const entity of tree.entities) {\n const relation = getRelationFromMaybePolyKey(entity, key);\n childrenByParent.set(entity, relation ? toArray(getEvenDeleted(relation)) : []);\n }\n if (childrenByParent.size === 0) continue;\n\n // Rewrite our node.entities to be the next layer of children, i.e. children will be all books, for all of\n // `[a1, a2, a3]`, but only the books of `a2` need to recurse into `book: reviews` and only the books of\n // `a3` need to recurse into `book: comments`, so swap `node.entities` (which is currently authors)\n // with the books. This is what prevents our dataloader-merged TreeHint from over-fetching and loading\n // the superset load hint for all entities.\n function rewrite(node: HintNode<Entity>) {\n const next = new Set<Entity>();\n for (const entity of node.entities) {\n const children = childrenByParent.get(entity);\n if (children) for (const child of children) next.add(child);\n }\n node.entities = next;\n Object.values(node.hints).forEach((node) => rewrite(node));\n }\n\n rewrite(tree);\n\n const nextMeta = (layerMeta?.allFields[key] as any)?.otherMetadata?.();\n (nestedLoadPromises ??= []).push(populateLayer(nextMeta, tree));\n }\n if (nestedLoadPromises) await Promise.all(nestedLoadPromises);\n }\n\n await populateLayer(meta, buildHintTree(populates));\n });\n}\n\n/** Probes `relation` to see if it's a m2o/o2m/m2m relation that supports `getWithDeleted`, otherwise calls `get`. */\nfunction getEvenDeleted(relation: any): any {\n return \"getWithDeleted\" in relation ? relation.getWithDeleted : relation.get;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"populateBatchLoader.js","names":[],"sources":["../../src/batchloaders/populateBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { loadBatchLoader } from \"src/batchloaders/loadBatchLoader.ts\";\nimport { manyToManyBatchLoader } from \"src/batchloaders/manyToManyBatchLoader.ts\";\nimport { oneToManyBatchLoader } from \"src/batchloaders/oneToManyBatchLoader.ts\";\nimport { oneToOneBatchLoader } from \"src/batchloaders/oneToOneBatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityMetadata } from \"src/EntityMetadata.ts\";\nimport {\n AliasAssigner,\n type EntityManager,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n getEmInternalApi,\n indexBy,\n keyToNumber,\n kqDot,\n} from \"src/index.ts\";\nimport { type HintNode, buildHintTree } from \"src/loading/HintTree.ts\";\nimport type { LoadHint } from \"src/loading/loadHints.ts\";\nimport { hintKey } from \"src/normalizeHints.ts\";\nimport { getRelationFromMaybePolyKey, isPolyHint } from \"src/reactivity/reactiveHints.ts\";\nimport { ReactiveFieldImpl } from \"src/relations/ReactiveField.ts\";\nimport { toArray } from \"src/utils.ts\";\n\nexport const populateOperation = \"populate\";\n\nexport function populateBatchLoader(\n em: EntityManager,\n meta: EntityMetadata,\n hint: LoadHint<any>,\n mode: \"preload\" | \"intermixed\",\n opts: { forceReload?: boolean } = {},\n): BatchLoader<{ entity: Entity; hint: LoadHint<any> }> {\n // For batching populates, we want different levels of course-ness:\n // - preloading populates that are only SQL should be batched together as much as possible, but\n // - intermixed populates (some with custom relations) should be batched as separately as possible\n // (while still batching identical populates together) to reduce the chance of promise deadlocks.\n //\n // I.e. if we know this is a sql-only hint, we can batch all loads for a given entity\n // together do leverage join-based preloading.\n //\n // However, intermixed batches can be prone to promise deadlocking (one relation .load getting\n // stuck in a batch that is asking for its own dependencies to load), so if this is an intermixed\n // hint, then use a batch key that includes the hint itself, which will make it unlikely\n // for non-sql relations to deadlock on each other/themselves.\n const batchKey =\n mode === \"preload\" ? `${meta.tagName}:${opts.forceReload}` : `${meta.tagName}:${hintKey(hint)}:${opts.forceReload}`;\n return em.getBatchLoader(populateOperation, batchKey, async (populates) => {\n async function populateLayer(layerMeta: EntityMetadata | undefined, layerNode: HintNode<Entity>): Promise<void> {\n // Skip join-based preloading if nothing in this layer needs loading. If any entity in the list\n // needs loading, just load everything\n const { preloader } = getEmInternalApi(em);\n // We may not have a layerMeta if we're going through non-field properties\n if (preloader && layerMeta) {\n const preloadThisLayer = Object.entries(layerNode.hints).some(([key, hint]) => {\n for (const entity of hint.entities) {\n if ((entity as any)[key]?.isLoaded === false && !(entity as any)[key]?.isPreloaded) return true;\n }\n return false;\n });\n if (preloadThisLayer) {\n // Do an up-front SQL call of `select id, ...preloads... from table`,\n const assigner = new AliasAssigner();\n const meta = layerMeta;\n const alias = assigner.getAlias(meta.tableName);\n const entities = [...layerNode.entities].filter((e) => !e.isNewEntity);\n const ids = entities.map((e) => keyToNumber(meta, e.id));\n // Create a ParsedFindQuery for `addPreloading` to inject joins into\n const query: ParsedFindQuery = {\n // We already have the entities loaded, so can do just `SELECT a.id` + the preload columns\n selects: [kqDot(alias, \"id\")],\n tables: [{ alias, join: \"primary\", table: meta.tableName }],\n condition: {\n kind: \"exp\",\n op: \"and\",\n conditions: [\n { kind: \"column\", alias, column: \"id\", dbType: meta.idDbType, cond: { kind: \"in\", value: ids } },\n ],\n },\n orderBys: [],\n };\n // If we're selecting from small_publishers, join in our base class in case\n // the preloader wants to join on a column like `publishers.group_id`\n addTablePerClassJoinsAndClassTag(query, meta, alias, false);\n const hydrator = preloader.addPreloading(meta, layerNode, query);\n if (hydrator) {\n const rows = await em[\"executeFind\"](meta, populateOperation, query, {});\n const entitiesById = indexBy(entities, (e) => keyToNumber(meta, e.id));\n const entitiesInOrder = rows.map((row) => entitiesById.get(row[\"id\"]));\n hydrator(rows, entitiesInOrder);\n }\n }\n }\n\n // First pass: batch SQL relations directly, fall back to relation.load() for non-SQL\n const batchPromises = new Set<Promise<void>>();\n const relationsToPreload: { preload(): void }[] = [];\n const fallbackPromises: (Promise<any> | undefined)[] = [];\n\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n const field = layerMeta?.allFields[key];\n // Hoist the poly-key parsing out of the per-entity loop; most keys are not poly\n const isPoly = isPolyHint(key);\n let loader: BatchLoader<any> | undefined;\n let batchPromise: Promise<void> | undefined;\n for (const entity of tree.entities) {\n const relation = isPoly ? getRelationFromMaybePolyKey(entity, key) : (entity as any)[key];\n // This happens to let through non-relation hints like 'name' on user, which wasn't intentional,\n // but currently doesn't blow up (somehow), and is depended on by internal tests.\n if (!relation || typeof relation.load !== \"function\") {\n // We don't want to throw on poly hints, because they're not actually loaded on the entity\n if (isPoly) continue;\n throw new Error(`Invalid load hint '${key}' on ${entity}`);\n }\n\n // If we're populating a hasReactiveField, don't bother loading it\n // if it's already been calculated (i.e. we have no reason to believe its value\n // is stale, so we should avoid pulling all of its data into memory).\n //\n // (Note that we can't do this same optimization for ReactiveReferenceImpl, because\n // as a FK, it will always need at least some `.load()` to fetch its entity from the database.\n // So we go ahead and call `.load()`, assuming it will just load its cached value, but it\n // will also check internally if it's marked for recalc, and load its load hint if necessary.\n if (relation instanceof ReactiveFieldImpl && relation.isSet) continue;\n if (relation.isLoaded && !opts.forceReload) continue;\n if (relation.isPreloaded) {\n relation.preload();\n continue;\n }\n\n // For non-derived SQL relations on existing entities, use batch loaders directly\n // (1 shared promise per batch instead of per-entity async overhead).\n // Skip new entities (no id) and derived relations (reactive m2m/m2o have extra logic in load).\n if (!entity.isNewEntity && field) {\n if (field.kind === \"o2m\") {\n batchPromise = (loader ??= oneToManyBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2m\" && !field.derived) {\n batchPromise = (loader ??= manyToManyBatchLoader(em, relation)).load(\n `${relation.columnName}=${entity.id}`,\n );\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"o2o\") {\n batchPromise = (loader ??= oneToOneBatchLoader(em, relation)).load(entity.idTagged!);\n relationsToPreload.push(relation);\n continue;\n } else if (field.kind === \"m2o\" && !field.derived) {\n const taggedId = relation.idTaggedMaybe;\n if (taggedId) {\n batchPromise = (loader ??= loadBatchLoader(em, field.otherMetadata())).load({\n taggedId,\n hint: undefined,\n });\n relationsToPreload.push(relation);\n continue;\n }\n }\n }\n fallbackPromises.push(relation.load(opts) as Promise<any>);\n }\n if (batchPromise) batchPromises.add(batchPromise);\n }\n\n if (batchPromises.size > 0 || fallbackPromises.length > 0) {\n await Promise.all([Promise.all(batchPromises), Promise.all(fallbackPromises)]);\n }\n for (const relation of relationsToPreload) {\n relation.preload();\n }\n\n // 2nd breadth-width pass to do nested load hints, this will fan out at the sibling level.\n // i.e. populateLayer(...reviews...) & populateLayer(...comments...)\n let nestedLoadPromises: (Promise<void> | undefined)[] | undefined;\n for (const [key, tree] of Object.entries(layerNode.hints)) {\n if (Object.keys(tree.hints).length === 0) continue;\n\n // Get the children we found, i.e. [a1, a2, a3] -> all of their books\n const childrenByParent = new Map<Entity, Entity[]>();\n for (const entity of tree.entities) {\n const relation = getRelationFromMaybePolyKey(entity, key);\n childrenByParent.set(entity, relation ? toArray(getEvenDeleted(relation)) : []);\n }\n if (childrenByParent.size === 0) continue;\n\n // Rewrite our node.entities to be the next layer of children, i.e. children will be all books, for all of\n // `[a1, a2, a3]`, but only the books of `a2` need to recurse into `book: reviews` and only the books of\n // `a3` need to recurse into `book: comments`, so swap `node.entities` (which is currently authors)\n // with the books. This is what prevents our dataloader-merged TreeHint from over-fetching and loading\n // the superset load hint for all entities.\n function rewrite(node: HintNode<Entity>) {\n const next = new Set<Entity>();\n for (const entity of node.entities) {\n const children = childrenByParent.get(entity);\n if (children) for (const child of children) next.add(child);\n }\n node.entities = next;\n Object.values(node.hints).forEach((node) => rewrite(node));\n }\n\n rewrite(tree);\n\n const nextMeta = (layerMeta?.allFields[key] as any)?.otherMetadata?.();\n (nestedLoadPromises ??= []).push(populateLayer(nextMeta, tree));\n }\n if (nestedLoadPromises) await Promise.all(nestedLoadPromises);\n }\n\n await populateLayer(meta, buildHintTree(populates));\n });\n}\n\n/** Probes `relation` to see if it's a m2o/o2m/m2m relation that supports `getWithDeleted`, otherwise calls `get`. */\nfunction getEvenDeleted(relation: any): any {\n return \"getWithDeleted\" in relation ? relation.getWithDeleted : relation.get;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recursiveChildrenBatchLoader.cjs","names":["getMetadataForType","abbreviation","kq","deTagIds","groupBy","maybeResolveReferenceToId","getField","isLoadedCollection","isLoadedOneToOneReference"],"sources":["../../src/batchloaders/recursiveChildrenBatchLoader.ts"],"sourcesContent":["import { getMetadataForType } from \"
|
|
1
|
+
{"version":3,"file":"recursiveChildrenBatchLoader.cjs","names":["getMetadataForType","abbreviation","kq","deTagIds","groupBy","maybeResolveReferenceToId","getField","isLoadedCollection","isLoadedOneToOneReference"],"sources":["../../src/batchloaders/recursiveChildrenBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { getMetadataForType } from \"src/configure.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport {\n type ManyToOneField,\n type OneToManyField,\n type OneToOneField,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n deTagIds,\n getField,\n isLoadedCollection,\n isLoadedOneToOneReference,\n kq,\n maybeResolveReferenceToId,\n} from \"src/index.ts\";\nimport { type RecursiveChildrenCollectionImpl } from \"src/relations/RecursiveCollection.ts\";\nimport { abbreviation, groupBy } from \"src/utils.ts\";\n\nexport const recursiveChildrenOperation = \"o2m-recursive\";\n\nexport function recursiveChildrenBatchLoader<T extends Entity, U extends Entity>(\n em: EntityManager,\n collection: RecursiveChildrenCollectionImpl<T, U>,\n): BatchLoader<Entity> {\n let { meta, fieldName } = collection;\n // This could be called from subtypes to get relations defined on the parent. So we need to make sure we are using the\n // correct meta by walking the inheritance tree until we find the meta that actually has the root o2m field\n // We intentionally don't use getMetadataForField here because CTI-specialized fields stay on the subtype metadata,\n // while these raw recursive CTEs need the metadata/table that physically owns the FK column.\n while (!(collection.o2mFieldName in meta.fields) && meta.baseType) meta = getMetadataForType(meta.baseType);\n const batchKey = `${meta.tableName}-${fieldName}`;\n return em.getBatchLoader(recursiveChildrenOperation, batchKey, async (parents) => {\n const o2m = meta.allFields[collection.o2mFieldName] as OneToManyField | OneToOneField;\n const m2o = meta.allFields[o2m.otherFieldName] as ManyToOneField;\n const { columnName } = m2o.serde.columns[0];\n\n const alias = abbreviation(meta.tableName);\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [\n // We still select directly from our table, and join into the CTE so that we\n // can use `addTablePerClassJoinsAndClassTag` to get inheritance added for free.\n { alias, join: \"primary\", table: meta.tableName },\n { alias: `${alias}_cte`, join: \"inner\", table: `${alias}_cte`, col1: `${alias}.id`, col2: `${alias}_cte.id` },\n ],\n orderBys: [{ alias, column: \"id\", order: \"ASC\" }],\n ctes: [\n {\n alias: `${alias}_cte`,\n query: {\n kind: \"raw\",\n // b is our base case, which is the immediate children of the parents, and r is the\n // recursive case of finding their children.\n sql: `\n SELECT b.id, b.${columnName} FROM ${kq(meta.tableName)} b WHERE b.${columnName} = ANY(?)\n UNION\n SELECT r.id, r.${columnName} FROM ${kq(meta.tableName)} r JOIN ${alias}_cte ON r.${columnName} = ${alias}_cte.id\n `,\n // RecursiveChildrenCollectionImpl won't call `.load` on new entities, so we can assume entities have an id\n bindings: [\n deTagIds(\n meta,\n parents.map((e) => e.idTagged),\n ),\n ],\n },\n recursive: true,\n },\n ],\n };\n\n addTablePerClassJoinsAndClassTag(query, meta, alias, true);\n\n const rowData = await em[\"executeFindRowData\"](meta, recursiveChildrenOperation, query, {});\n const entities = em[\"hydrateAndFinalize\"](meta.cstr, rowData);\n\n // For all the entities we found, group them by their parent (or the root node, which has no parent)\n const entitiesById = groupBy(entities, (entity) => {\n const parentId = maybeResolveReferenceToId(getField(entity, m2o.fieldName));\n return parentId ?? \"root\";\n });\n\n // For each found parent, use the preloading infra to inject the children into the relation\n for (const [parentId, children] of entitiesById) {\n if (parentId !== \"root\") {\n getEmInternalApi(em).setPreloadedRelation(parentId, o2m.fieldName, children);\n // I thought we could delay calling this, but currently `em.populate` calls `preload()` explicitly\n (em.getEntity(parentId) as any)[o2m.fieldName].preload();\n }\n }\n\n // Any entity[o2m] still not loaded must be a leaf which didn't have any children; go ahead and mark it as loaded\n for (const entity of [...parents, ...entities]) {\n if (!isLoadedCollection(entity[o2m.fieldName]) && !isLoadedOneToOneReference(entity[o2m.fieldName])) {\n getEmInternalApi(em).setPreloadedRelation(entity.id, o2m.fieldName, []);\n entity[o2m.fieldName].preload();\n }\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;AAoBA,MAAa,6BAA6B;AAE1C,SAAgB,6BACd,IACA,YACqB;CACrB,IAAI,EAAE,MAAM,cAAc;CAK1B,OAAO,EAAE,WAAW,gBAAgB,KAAK,WAAW,KAAK,UAAU,OAAOA,kBAAAA,mBAAmB,KAAK,QAAQ;CAC1G,MAAM,WAAW,GAAG,KAAK,UAAU,GAAG;CACtC,OAAO,GAAG,eAAe,4BAA4B,UAAU,OAAO,YAAY;EAChF,MAAM,MAAM,KAAK,UAAU,WAAW;EACtC,MAAM,MAAM,KAAK,UAAU,IAAI;EAC/B,MAAM,EAAE,eAAe,IAAI,MAAM,QAAQ;EAEzC,MAAM,QAAQC,cAAAA,aAAa,KAAK,SAAS;EACzC,MAAM,QAAyB;GAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;GACxB,QAAQ,CAGN;IAAE;IAAO,MAAM;IAAW,OAAO,KAAK;GAAU,GAChD;IAAE,OAAO,GAAG,MAAM;IAAO,MAAM;IAAS,OAAO,GAAG,MAAM;IAAO,MAAM,GAAG,MAAM;IAAM,MAAM,GAAG,MAAM;GAAS,CAC9G;GACA,UAAU,CAAC;IAAE;IAAO,QAAQ;IAAM,OAAO;GAAM,CAAC;GAChD,MAAM,CACJ;IACE,OAAO,GAAG,MAAM;IAChB,OAAO;KACL,MAAM;KAGN,KAAK;8BACa,WAAW,QAAQC,6BAAAA,GAAG,KAAK,SAAS,EAAE,aAAa,WAAW;;8BAE9D,WAAW,QAAQA,6BAAAA,GAAG,KAAK,SAAS,EAAE,UAAU,MAAM,YAAY,WAAW,KAAK,MAAM;;KAG1G,UAAU,CACRC,aAAAA,SACE,MACA,QAAQ,KAAK,MAAM,EAAE,QAAQ,CAC/B,CACF;IACF;IACA,WAAW;GACb,CACF;EACF;EAEA,iCAAA,iCAAiC,OAAO,MAAM,OAAO,IAAI;EAEzD,MAAM,UAAU,MAAM,GAAG,qBAAqB,CAAC,MAAM,4BAA4B,OAAO,CAAC,CAAC;EAC1F,MAAM,WAAW,GAAG,qBAAqB,CAAC,KAAK,MAAM,OAAO;EAG5D,MAAM,eAAeC,cAAAA,QAAQ,WAAW,WAAW;GAEjD,OADiBC,aAAAA,0BAA0BC,eAAAA,SAAS,QAAQ,IAAI,SAAS,CAC3D,KAAK;EACrB,CAAC;EAGD,KAAK,MAAM,CAAC,UAAU,aAAa,cACjC,IAAI,aAAa,QAAQ;GACvB,sBAAA,iBAAiB,EAAE,CAAC,CAAC,qBAAqB,UAAU,IAAI,WAAW,QAAQ;GAE3E,GAAI,UAAU,QAAQ,CAAC,CAAS,IAAI,UAAU,CAAC,QAAQ;EACzD;EAIF,KAAK,MAAM,UAAU,CAAC,GAAG,SAAS,GAAG,QAAQ,GAC3C,IAAI,CAACC,6BAAAA,mBAAmB,OAAO,IAAI,UAAU,KAAK,CAACC,oCAAAA,0BAA0B,OAAO,IAAI,UAAU,GAAG;GACnG,sBAAA,iBAAiB,EAAE,CAAC,CAAC,qBAAqB,OAAO,IAAI,IAAI,WAAW,CAAC,CAAC;GACtE,OAAO,IAAI,UAAU,CAAC,QAAQ;EAChC;CAEJ,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recursiveChildrenBatchLoader.js","names":[],"sources":["../../src/batchloaders/recursiveChildrenBatchLoader.ts"],"sourcesContent":["import { getMetadataForType } from \"
|
|
1
|
+
{"version":3,"file":"recursiveChildrenBatchLoader.js","names":[],"sources":["../../src/batchloaders/recursiveChildrenBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { getMetadataForType } from \"src/configure.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport {\n type ManyToOneField,\n type OneToManyField,\n type OneToOneField,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n deTagIds,\n getField,\n isLoadedCollection,\n isLoadedOneToOneReference,\n kq,\n maybeResolveReferenceToId,\n} from \"src/index.ts\";\nimport { type RecursiveChildrenCollectionImpl } from \"src/relations/RecursiveCollection.ts\";\nimport { abbreviation, groupBy } from \"src/utils.ts\";\n\nexport const recursiveChildrenOperation = \"o2m-recursive\";\n\nexport function recursiveChildrenBatchLoader<T extends Entity, U extends Entity>(\n em: EntityManager,\n collection: RecursiveChildrenCollectionImpl<T, U>,\n): BatchLoader<Entity> {\n let { meta, fieldName } = collection;\n // This could be called from subtypes to get relations defined on the parent. So we need to make sure we are using the\n // correct meta by walking the inheritance tree until we find the meta that actually has the root o2m field\n // We intentionally don't use getMetadataForField here because CTI-specialized fields stay on the subtype metadata,\n // while these raw recursive CTEs need the metadata/table that physically owns the FK column.\n while (!(collection.o2mFieldName in meta.fields) && meta.baseType) meta = getMetadataForType(meta.baseType);\n const batchKey = `${meta.tableName}-${fieldName}`;\n return em.getBatchLoader(recursiveChildrenOperation, batchKey, async (parents) => {\n const o2m = meta.allFields[collection.o2mFieldName] as OneToManyField | OneToOneField;\n const m2o = meta.allFields[o2m.otherFieldName] as ManyToOneField;\n const { columnName } = m2o.serde.columns[0];\n\n const alias = abbreviation(meta.tableName);\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [\n // We still select directly from our table, and join into the CTE so that we\n // can use `addTablePerClassJoinsAndClassTag` to get inheritance added for free.\n { alias, join: \"primary\", table: meta.tableName },\n { alias: `${alias}_cte`, join: \"inner\", table: `${alias}_cte`, col1: `${alias}.id`, col2: `${alias}_cte.id` },\n ],\n orderBys: [{ alias, column: \"id\", order: \"ASC\" }],\n ctes: [\n {\n alias: `${alias}_cte`,\n query: {\n kind: \"raw\",\n // b is our base case, which is the immediate children of the parents, and r is the\n // recursive case of finding their children.\n sql: `\n SELECT b.id, b.${columnName} FROM ${kq(meta.tableName)} b WHERE b.${columnName} = ANY(?)\n UNION\n SELECT r.id, r.${columnName} FROM ${kq(meta.tableName)} r JOIN ${alias}_cte ON r.${columnName} = ${alias}_cte.id\n `,\n // RecursiveChildrenCollectionImpl won't call `.load` on new entities, so we can assume entities have an id\n bindings: [\n deTagIds(\n meta,\n parents.map((e) => e.idTagged),\n ),\n ],\n },\n recursive: true,\n },\n ],\n };\n\n addTablePerClassJoinsAndClassTag(query, meta, alias, true);\n\n const rowData = await em[\"executeFindRowData\"](meta, recursiveChildrenOperation, query, {});\n const entities = em[\"hydrateAndFinalize\"](meta.cstr, rowData);\n\n // For all the entities we found, group them by their parent (or the root node, which has no parent)\n const entitiesById = groupBy(entities, (entity) => {\n const parentId = maybeResolveReferenceToId(getField(entity, m2o.fieldName));\n return parentId ?? \"root\";\n });\n\n // For each found parent, use the preloading infra to inject the children into the relation\n for (const [parentId, children] of entitiesById) {\n if (parentId !== \"root\") {\n getEmInternalApi(em).setPreloadedRelation(parentId, o2m.fieldName, children);\n // I thought we could delay calling this, but currently `em.populate` calls `preload()` explicitly\n (em.getEntity(parentId) as any)[o2m.fieldName].preload();\n }\n }\n\n // Any entity[o2m] still not loaded must be a leaf which didn't have any children; go ahead and mark it as loaded\n for (const entity of [...parents, ...entities]) {\n if (!isLoadedCollection(entity[o2m.fieldName]) && !isLoadedOneToOneReference(entity[o2m.fieldName])) {\n getEmInternalApi(em).setPreloadedRelation(entity.id, o2m.fieldName, []);\n entity[o2m.fieldName].preload();\n }\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAoBA,MAAa,6BAA6B;AAE1C,SAAgB,6BACd,IACA,YACqB;CACrB,IAAI,EAAE,MAAM,cAAc;CAK1B,OAAO,EAAE,WAAW,gBAAgB,KAAK,WAAW,KAAK,UAAU,OAAO,mBAAmB,KAAK,QAAQ;CAC1G,MAAM,WAAW,GAAG,KAAK,UAAU,GAAG;CACtC,OAAO,GAAG,eAAe,4BAA4B,UAAU,OAAO,YAAY;EAChF,MAAM,MAAM,KAAK,UAAU,WAAW;EACtC,MAAM,MAAM,KAAK,UAAU,IAAI;EAC/B,MAAM,EAAE,eAAe,IAAI,MAAM,QAAQ;EAEzC,MAAM,QAAQ,aAAa,KAAK,SAAS;EACzC,MAAM,QAAyB;GAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;GACxB,QAAQ,CAGN;IAAE;IAAO,MAAM;IAAW,OAAO,KAAK;GAAU,GAChD;IAAE,OAAO,GAAG,MAAM;IAAO,MAAM;IAAS,OAAO,GAAG,MAAM;IAAO,MAAM,GAAG,MAAM;IAAM,MAAM,GAAG,MAAM;GAAS,CAC9G;GACA,UAAU,CAAC;IAAE;IAAO,QAAQ;IAAM,OAAO;GAAM,CAAC;GAChD,MAAM,CACJ;IACE,OAAO,GAAG,MAAM;IAChB,OAAO;KACL,MAAM;KAGN,KAAK;8BACa,WAAW,QAAQ,GAAG,KAAK,SAAS,EAAE,aAAa,WAAW;;8BAE9D,WAAW,QAAQ,GAAG,KAAK,SAAS,EAAE,UAAU,MAAM,YAAY,WAAW,KAAK,MAAM;;KAG1G,UAAU,CACR,SACE,MACA,QAAQ,KAAK,MAAM,EAAE,QAAQ,CAC/B,CACF;IACF;IACA,WAAW;GACb,CACF;EACF;EAEA,iCAAiC,OAAO,MAAM,OAAO,IAAI;EAEzD,MAAM,UAAU,MAAM,GAAG,qBAAqB,CAAC,MAAM,4BAA4B,OAAO,CAAC,CAAC;EAC1F,MAAM,WAAW,GAAG,qBAAqB,CAAC,KAAK,MAAM,OAAO;EAG5D,MAAM,eAAe,QAAQ,WAAW,WAAW;GAEjD,OADiB,0BAA0B,SAAS,QAAQ,IAAI,SAAS,CAC3D,KAAK;EACrB,CAAC;EAGD,KAAK,MAAM,CAAC,UAAU,aAAa,cACjC,IAAI,aAAa,QAAQ;GACvB,iBAAiB,EAAE,CAAC,CAAC,qBAAqB,UAAU,IAAI,WAAW,QAAQ;GAE3E,GAAI,UAAU,QAAQ,CAAC,CAAS,IAAI,UAAU,CAAC,QAAQ;EACzD;EAIF,KAAK,MAAM,UAAU,CAAC,GAAG,SAAS,GAAG,QAAQ,GAC3C,IAAI,CAAC,mBAAmB,OAAO,IAAI,UAAU,KAAK,CAAC,0BAA0B,OAAO,IAAI,UAAU,GAAG;GACnG,iBAAiB,EAAE,CAAC,CAAC,qBAAqB,OAAO,IAAI,IAAI,WAAW,CAAC,CAAC;GACtE,OAAO,IAAI,UAAU,CAAC,QAAQ;EAChC;CAEJ,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recursiveM2mBatchLoader.cjs","names":["getMetadataForField","deTagIds","abbreviation","kqStar","kq","kqDot","keyToTaggedId","getEmInternalApi","isLoadedCollection"],"sources":["../../src/batchloaders/recursiveM2mBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"
|
|
1
|
+
{"version":3,"file":"recursiveM2mBatchLoader.cjs","names":["getMetadataForField","deTagIds","abbreviation","kqStar","kq","kqDot","keyToTaggedId","getEmInternalApi","isLoadedCollection"],"sources":["../../src/batchloaders/recursiveM2mBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport {\n type ManyToManyField,\n type ParsedFindQuery,\n deTagIds,\n getMetadataForField,\n isLoadedCollection,\n keyToTaggedId,\n kq,\n kqDot,\n kqStar,\n} from \"src/index.ts\";\nimport { type RecursiveM2mCollectionImpl } from \"src/relations/RecursiveCollection.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const recursiveM2mOperation = \"m2m-recursive\";\n\nexport function recursiveM2mBatchLoader<T extends Entity, U extends Entity>(\n em: EntityManager,\n collection: RecursiveM2mCollectionImpl<T, U>,\n): BatchLoader<Entity> {\n let { meta, fieldName } = collection;\n meta = getMetadataForField(meta, collection.m2mFieldName);\n const batchKey = `${meta.tableName}-${fieldName}`;\n return em.getBatchLoader(recursiveM2mOperation, batchKey, async (entities) => {\n const m2mField = meta.allFields[collection.m2mFieldName] as ManyToManyField;\n const { joinTableName, columnNames } = m2mField;\n const thisColumn = columnNames[0];\n const otherColumn = columnNames[1];\n\n const seedIds = deTagIds(\n meta,\n entities.map((e) => e.idTagged),\n );\n\n // Use a recursive CTE to walk the join table and return all join rows for the seed\n // entities and all transitively-reachable entities. JoinRows.loadRows then calls\n // em.loadAll internally to hydrate the actual entities on both sides.\n const jt = abbreviation(joinTableName);\n const query: ParsedFindQuery = {\n selects: [kqStar(jt)],\n tables: [{ alias: jt, join: \"primary\", table: \"cte\" }],\n orderBys: [{ alias: jt, column: \"id\", order: \"ASC\" }],\n ctes: [\n {\n alias: \"cte\",\n query: {\n kind: \"raw\",\n // Base case returns full join rows for the seed entities; recursive case\n // follows otherColumn → thisColumn to discover transitively-reachable rows.\n sql: `\n SELECT b.* FROM ${kq(joinTableName)} b WHERE ${kqDot(\"b\", thisColumn)} = ANY(?)\n UNION\n SELECT r.* FROM ${kq(joinTableName)} r JOIN cte ON ${kqDot(\"r\", thisColumn)} = cte.${kq(otherColumn)}\n `,\n bindings: [seedIds],\n },\n recursive: true,\n },\n ],\n };\n\n const joinTableRows = await em[\"executeFind\"](\n meta,\n recursiveM2mOperation,\n query,\n // No LIMIT needed for join rows\n { limit: undefined },\n );\n\n // Collect all entity IDs from both sides of the join rows. Entities that only appear\n // on otherColumn (not thisColumn) are leaf nodes with no outgoing connections — since\n // the CTE transitively discovers everything reachable, we know their m2m is empty and\n // can mark it loaded here, avoiding a separate load via findUnloadedCollections.\n const uniqueIds = new Set(entities.map((e) => e.idTagged));\n for (const row of joinTableRows) {\n uniqueIds.add(keyToTaggedId(meta, row[thisColumn])!);\n uniqueIds.add(keyToTaggedId(meta, row[otherColumn])!);\n }\n\n const m2mCollection = (entities[0] as any)[collection.m2mFieldName];\n const joinRows = getEmInternalApi(em).joinRows(m2mCollection);\n\n // loadRows calls em.loadAll internally to hydrate all referenced entities\n const tuples: [string, string][] = [...uniqueIds].map((id) => [thisColumn, id]);\n await joinRows.loadRows(tuples, joinTableRows);\n\n // Set preloaded relations and call preload() for each entity's m2m collection\n const api = getEmInternalApi(em);\n for (const id of uniqueIds) {\n const entity = em.getEntity(id);\n if (!entity) continue;\n const others = joinRows.getOthers(thisColumn, entity);\n api.setPreloadedRelation(entity.idTagged!, m2mField.fieldName, others);\n (entity as any)[m2mField.fieldName].preload();\n }\n\n // Mark any entity whose m2m is still not loaded as having an empty collection (leaf nodes)\n for (const id of uniqueIds) {\n const entity = em.getEntity(id);\n if (!entity) continue;\n if (!isLoadedCollection((entity as any)[m2mField.fieldName])) {\n api.setPreloadedRelation(entity.idTagged!, m2mField.fieldName, []);\n (entity as any)[m2mField.fieldName].preload();\n }\n }\n });\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAa,wBAAwB;AAErC,SAAgB,wBACd,IACA,YACqB;CACrB,IAAI,EAAE,MAAM,cAAc;CAC1B,OAAOA,uBAAAA,oBAAoB,MAAM,WAAW,YAAY;CACxD,MAAM,WAAW,GAAG,KAAK,UAAU,GAAG;CACtC,OAAO,GAAG,eAAe,uBAAuB,UAAU,OAAO,aAAa;EAC5E,MAAM,WAAW,KAAK,UAAU,WAAW;EAC3C,MAAM,EAAE,eAAe,gBAAgB;EACvC,MAAM,aAAa,YAAY;EAC/B,MAAM,cAAc,YAAY;EAEhC,MAAM,UAAUC,aAAAA,SACd,MACA,SAAS,KAAK,MAAM,EAAE,QAAQ,CAChC;EAKA,MAAM,KAAKC,cAAAA,aAAa,aAAa;EACrC,MAAM,QAAyB;GAC7B,SAAS,CAACC,6BAAAA,OAAO,EAAE,CAAC;GACpB,QAAQ,CAAC;IAAE,OAAO;IAAI,MAAM;IAAW,OAAO;GAAM,CAAC;GACrD,UAAU,CAAC;IAAE,OAAO;IAAI,QAAQ;IAAM,OAAO;GAAM,CAAC;GACpD,MAAM,CACJ;IACE,OAAO;IACP,OAAO;KACL,MAAM;KAGN,KAAK;gCACeC,6BAAAA,GAAG,aAAa,EAAE,WAAWC,6BAAAA,MAAM,KAAK,UAAU,EAAE;;gCAEpDD,6BAAAA,GAAG,aAAa,EAAE,iBAAiBC,6BAAAA,MAAM,KAAK,UAAU,EAAE,SAASD,6BAAAA,GAAG,WAAW,EAAE;;KAEvG,UAAU,CAAC,OAAO;IACpB;IACA,WAAW;GACb,CACF;EACF;EAEA,MAAM,gBAAgB,MAAM,GAAG,cAAc,CAC3C,MACA,uBACA,OAEA,EAAE,OAAO,KAAA,EAAU,CACrB;EAMA,MAAM,YAAY,IAAI,IAAI,SAAS,KAAK,MAAM,EAAE,QAAQ,CAAC;EACzD,KAAK,MAAM,OAAO,eAAe;GAC/B,UAAU,IAAIE,aAAAA,cAAc,MAAM,IAAI,WAAW,CAAE;GACnD,UAAU,IAAIA,aAAAA,cAAc,MAAM,IAAI,YAAY,CAAE;EACtD;EAEA,MAAM,gBAAiB,SAAS,EAAE,CAAS,WAAW;EACtD,MAAM,WAAWC,sBAAAA,iBAAiB,EAAE,CAAC,CAAC,SAAS,aAAa;EAG5D,MAAM,SAA6B,CAAC,GAAG,SAAS,CAAC,CAAC,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC;EAC9E,MAAM,SAAS,SAAS,QAAQ,aAAa;EAG7C,MAAM,MAAMA,sBAAAA,iBAAiB,EAAE;EAC/B,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,SAAS,GAAG,UAAU,EAAE;GAC9B,IAAI,CAAC,QAAQ;GACb,MAAM,SAAS,SAAS,UAAU,YAAY,MAAM;GACpD,IAAI,qBAAqB,OAAO,UAAW,SAAS,WAAW,MAAM;GACrE,OAAgB,SAAS,UAAU,CAAC,QAAQ;EAC9C;EAGA,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,SAAS,GAAG,UAAU,EAAE;GAC9B,IAAI,CAAC,QAAQ;GACb,IAAI,CAACC,6BAAAA,mBAAoB,OAAe,SAAS,UAAU,GAAG;IAC5D,IAAI,qBAAqB,OAAO,UAAW,SAAS,WAAW,CAAC,CAAC;IACjE,OAAgB,SAAS,UAAU,CAAC,QAAQ;GAC9C;EACF;CACF,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recursiveM2mBatchLoader.js","names":[],"sources":["../../src/batchloaders/recursiveM2mBatchLoader.ts"],"sourcesContent":["import { type Entity } from \"
|
|
1
|
+
{"version":3,"file":"recursiveM2mBatchLoader.js","names":[],"sources":["../../src/batchloaders/recursiveM2mBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager, getEmInternalApi } from \"src/EntityManager.ts\";\nimport {\n type ManyToManyField,\n type ParsedFindQuery,\n deTagIds,\n getMetadataForField,\n isLoadedCollection,\n keyToTaggedId,\n kq,\n kqDot,\n kqStar,\n} from \"src/index.ts\";\nimport { type RecursiveM2mCollectionImpl } from \"src/relations/RecursiveCollection.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const recursiveM2mOperation = \"m2m-recursive\";\n\nexport function recursiveM2mBatchLoader<T extends Entity, U extends Entity>(\n em: EntityManager,\n collection: RecursiveM2mCollectionImpl<T, U>,\n): BatchLoader<Entity> {\n let { meta, fieldName } = collection;\n meta = getMetadataForField(meta, collection.m2mFieldName);\n const batchKey = `${meta.tableName}-${fieldName}`;\n return em.getBatchLoader(recursiveM2mOperation, batchKey, async (entities) => {\n const m2mField = meta.allFields[collection.m2mFieldName] as ManyToManyField;\n const { joinTableName, columnNames } = m2mField;\n const thisColumn = columnNames[0];\n const otherColumn = columnNames[1];\n\n const seedIds = deTagIds(\n meta,\n entities.map((e) => e.idTagged),\n );\n\n // Use a recursive CTE to walk the join table and return all join rows for the seed\n // entities and all transitively-reachable entities. JoinRows.loadRows then calls\n // em.loadAll internally to hydrate the actual entities on both sides.\n const jt = abbreviation(joinTableName);\n const query: ParsedFindQuery = {\n selects: [kqStar(jt)],\n tables: [{ alias: jt, join: \"primary\", table: \"cte\" }],\n orderBys: [{ alias: jt, column: \"id\", order: \"ASC\" }],\n ctes: [\n {\n alias: \"cte\",\n query: {\n kind: \"raw\",\n // Base case returns full join rows for the seed entities; recursive case\n // follows otherColumn → thisColumn to discover transitively-reachable rows.\n sql: `\n SELECT b.* FROM ${kq(joinTableName)} b WHERE ${kqDot(\"b\", thisColumn)} = ANY(?)\n UNION\n SELECT r.* FROM ${kq(joinTableName)} r JOIN cte ON ${kqDot(\"r\", thisColumn)} = cte.${kq(otherColumn)}\n `,\n bindings: [seedIds],\n },\n recursive: true,\n },\n ],\n };\n\n const joinTableRows = await em[\"executeFind\"](\n meta,\n recursiveM2mOperation,\n query,\n // No LIMIT needed for join rows\n { limit: undefined },\n );\n\n // Collect all entity IDs from both sides of the join rows. Entities that only appear\n // on otherColumn (not thisColumn) are leaf nodes with no outgoing connections — since\n // the CTE transitively discovers everything reachable, we know their m2m is empty and\n // can mark it loaded here, avoiding a separate load via findUnloadedCollections.\n const uniqueIds = new Set(entities.map((e) => e.idTagged));\n for (const row of joinTableRows) {\n uniqueIds.add(keyToTaggedId(meta, row[thisColumn])!);\n uniqueIds.add(keyToTaggedId(meta, row[otherColumn])!);\n }\n\n const m2mCollection = (entities[0] as any)[collection.m2mFieldName];\n const joinRows = getEmInternalApi(em).joinRows(m2mCollection);\n\n // loadRows calls em.loadAll internally to hydrate all referenced entities\n const tuples: [string, string][] = [...uniqueIds].map((id) => [thisColumn, id]);\n await joinRows.loadRows(tuples, joinTableRows);\n\n // Set preloaded relations and call preload() for each entity's m2m collection\n const api = getEmInternalApi(em);\n for (const id of uniqueIds) {\n const entity = em.getEntity(id);\n if (!entity) continue;\n const others = joinRows.getOthers(thisColumn, entity);\n api.setPreloadedRelation(entity.idTagged!, m2mField.fieldName, others);\n (entity as any)[m2mField.fieldName].preload();\n }\n\n // Mark any entity whose m2m is still not loaded as having an empty collection (leaf nodes)\n for (const id of uniqueIds) {\n const entity = em.getEntity(id);\n if (!entity) continue;\n if (!isLoadedCollection((entity as any)[m2mField.fieldName])) {\n api.setPreloadedRelation(entity.idTagged!, m2mField.fieldName, []);\n (entity as any)[m2mField.fieldName].preload();\n }\n }\n });\n}\n"],"mappings":";;;;;;;;;;;AAiBA,MAAa,wBAAwB;AAErC,SAAgB,wBACd,IACA,YACqB;CACrB,IAAI,EAAE,MAAM,cAAc;CAC1B,OAAO,oBAAoB,MAAM,WAAW,YAAY;CACxD,MAAM,WAAW,GAAG,KAAK,UAAU,GAAG;CACtC,OAAO,GAAG,eAAe,uBAAuB,UAAU,OAAO,aAAa;EAC5E,MAAM,WAAW,KAAK,UAAU,WAAW;EAC3C,MAAM,EAAE,eAAe,gBAAgB;EACvC,MAAM,aAAa,YAAY;EAC/B,MAAM,cAAc,YAAY;EAEhC,MAAM,UAAU,SACd,MACA,SAAS,KAAK,MAAM,EAAE,QAAQ,CAChC;EAKA,MAAM,KAAK,aAAa,aAAa;EACrC,MAAM,QAAyB;GAC7B,SAAS,CAAC,OAAO,EAAE,CAAC;GACpB,QAAQ,CAAC;IAAE,OAAO;IAAI,MAAM;IAAW,OAAO;GAAM,CAAC;GACrD,UAAU,CAAC;IAAE,OAAO;IAAI,QAAQ;IAAM,OAAO;GAAM,CAAC;GACpD,MAAM,CACJ;IACE,OAAO;IACP,OAAO;KACL,MAAM;KAGN,KAAK;gCACe,GAAG,aAAa,EAAE,WAAW,MAAM,KAAK,UAAU,EAAE;;gCAEpD,GAAG,aAAa,EAAE,iBAAiB,MAAM,KAAK,UAAU,EAAE,SAAS,GAAG,WAAW,EAAE;;KAEvG,UAAU,CAAC,OAAO;IACpB;IACA,WAAW;GACb,CACF;EACF;EAEA,MAAM,gBAAgB,MAAM,GAAG,cAAc,CAC3C,MACA,uBACA,OAEA,EAAE,OAAO,KAAA,EAAU,CACrB;EAMA,MAAM,YAAY,IAAI,IAAI,SAAS,KAAK,MAAM,EAAE,QAAQ,CAAC;EACzD,KAAK,MAAM,OAAO,eAAe;GAC/B,UAAU,IAAI,cAAc,MAAM,IAAI,WAAW,CAAE;GACnD,UAAU,IAAI,cAAc,MAAM,IAAI,YAAY,CAAE;EACtD;EAEA,MAAM,gBAAiB,SAAS,EAAE,CAAS,WAAW;EACtD,MAAM,WAAW,iBAAiB,EAAE,CAAC,CAAC,SAAS,aAAa;EAG5D,MAAM,SAA6B,CAAC,GAAG,SAAS,CAAC,CAAC,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC;EAC9E,MAAM,SAAS,SAAS,QAAQ,aAAa;EAG7C,MAAM,MAAM,iBAAiB,EAAE;EAC/B,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,SAAS,GAAG,UAAU,EAAE;GAC9B,IAAI,CAAC,QAAQ;GACb,MAAM,SAAS,SAAS,UAAU,YAAY,MAAM;GACpD,IAAI,qBAAqB,OAAO,UAAW,SAAS,WAAW,MAAM;GACrE,OAAgB,SAAS,UAAU,CAAC,QAAQ;EAC9C;EAGA,KAAK,MAAM,MAAM,WAAW;GAC1B,MAAM,SAAS,GAAG,UAAU,EAAE;GAC9B,IAAI,CAAC,QAAQ;GACb,IAAI,CAAC,mBAAoB,OAAe,SAAS,UAAU,GAAG;IAC5D,IAAI,qBAAqB,OAAO,UAAW,SAAS,WAAW,CAAC,CAAC;IACjE,OAAgB,SAAS,UAAU,CAAC,QAAQ;GAC9C;EACF;CACF,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recursiveParentsBatchLoader.cjs","names":["getMetadataForType","getField","abbreviation","kq","deTagIds"],"sources":["../../src/batchloaders/recursiveParentsBatchLoader.ts"],"sourcesContent":["import { getMetadataForType } from \"
|
|
1
|
+
{"version":3,"file":"recursiveParentsBatchLoader.cjs","names":["getMetadataForType","getField","abbreviation","kq","deTagIds"],"sources":["../../src/batchloaders/recursiveParentsBatchLoader.ts"],"sourcesContent":["import { type BatchLoader } from \"src/batchloaders/BatchLoader.ts\";\nimport { getMetadataForType } from \"src/configure.ts\";\nimport { type Entity } from \"src/Entity.ts\";\nimport { type EntityManager } from \"src/EntityManager.ts\";\nimport {\n type ManyToOneField,\n type ParsedFindQuery,\n addTablePerClassJoinsAndClassTag,\n deTagIds,\n getField,\n kq,\n} from \"src/index.ts\";\nimport { type RecursiveParentsCollectionImpl } from \"src/relations/RecursiveCollection.ts\";\nimport { abbreviation } from \"src/utils.ts\";\n\nexport const recursiveParentsOperation = \"m2o-recursive\";\n\nexport function recursiveParentsBatchLoader<T extends Entity, U extends Entity>(\n em: EntityManager,\n collection: RecursiveParentsCollectionImpl<T, U>,\n): BatchLoader<Entity> {\n let { meta, fieldName } = collection;\n // This could be called from subtypes to get relations defined on the parent. So we need to make sure we are using the\n // correct meta by walking the inheritance tree until we find the meta that actually has the root m2o field\n // We intentionally don't use getMetadataForField here because CTI-specialized fields stay on the subtype metadata,\n // while these raw recursive CTEs need the metadata/table that physically owns the FK column.\n while (!(collection.m2oFieldName in meta.fields) && meta.baseType) meta = getMetadataForType(meta.baseType);\n const batchKey = `${meta.tableName}-${fieldName}`;\n return em.getBatchLoader(recursiveParentsOperation, batchKey, async (children) => {\n const m2o = meta.allFields[collection.m2oFieldName] as ManyToOneField;\n // ...can `getField` return a new entity? In theory, we shouldn't see new entities because\n // RecursiveParentsCollectionImpl.load/isLoaded will see the `ManyToOneReferenceImpl` as loaded.\n const immediateParentIds = children.map((c) => getField(c, m2o.fieldName)).filter((id: any) => id !== undefined);\n const { columnName } = m2o.serde.columns[0];\n\n const alias = abbreviation(meta.tableName);\n const query: ParsedFindQuery = {\n selects: [`\"${alias}\".*`],\n tables: [\n // We still select directly from our table, and join into the CTE so that we\n // can use `addTablePerClassJoinsAndClassTag` to get inheritance added for free.\n { alias, join: \"primary\", table: meta.tableName },\n { alias: `${alias}_cte`, join: \"inner\", table: `${alias}_cte`, col1: `${alias}.id`, col2: `${alias}_cte.id` },\n ],\n orderBys: [{ alias, column: \"id\", order: \"ASC\" }],\n ctes: [\n {\n alias: `${alias}_cte`,\n query: {\n kind: \"raw\",\n // b is our base case, which is the immediate parents of the children we're loading,\n // and r is the recursive case of finding their parents.\n sql: `\n SELECT b.id, b.${columnName} FROM ${kq(meta.tableName)} b WHERE b.id = ANY(?)\n UNION\n SELECT r.id, r.${columnName} FROM ${kq(meta.tableName)} r JOIN ${alias}_cte ON r.id = ${alias}_cte.${columnName}\n `,\n bindings: [deTagIds(meta, immediateParentIds)],\n },\n recursive: true,\n },\n ],\n };\n\n addTablePerClassJoinsAndClassTag(query, meta, alias, true);\n\n const rowData = await em[\"executeFindRowData\"](meta, recursiveParentsOperation, query, {});\n\n // Since we're preloading m2os up the tree, merely having the entities in the EM is enough\n // for the ManyToOneReferenceImpl to find them, so we don't need to map them back to the\n // keys, or push them into the preloader cache.\n em[\"hydrateAndFinalize\"](meta.cstr, rowData);\n });\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,MAAa,4BAA4B;AAEzC,SAAgB,4BACd,IACA,YACqB;CACrB,IAAI,EAAE,MAAM,cAAc;CAK1B,OAAO,EAAE,WAAW,gBAAgB,KAAK,WAAW,KAAK,UAAU,OAAOA,kBAAAA,mBAAmB,KAAK,QAAQ;CAC1G,MAAM,WAAW,GAAG,KAAK,UAAU,GAAG;CACtC,OAAO,GAAG,eAAe,2BAA2B,UAAU,OAAO,aAAa;EAChF,MAAM,MAAM,KAAK,UAAU,WAAW;EAGtC,MAAM,qBAAqB,SAAS,KAAK,MAAMC,eAAAA,SAAS,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC,QAAQ,OAAY,OAAO,KAAA,CAAS;EAC/G,MAAM,EAAE,eAAe,IAAI,MAAM,QAAQ;EAEzC,MAAM,QAAQC,cAAAA,aAAa,KAAK,SAAS;EACzC,MAAM,QAAyB;GAC7B,SAAS,CAAC,IAAI,MAAM,IAAI;GACxB,QAAQ,CAGN;IAAE;IAAO,MAAM;IAAW,OAAO,KAAK;GAAU,GAChD;IAAE,OAAO,GAAG,MAAM;IAAO,MAAM;IAAS,OAAO,GAAG,MAAM;IAAO,MAAM,GAAG,MAAM;IAAM,MAAM,GAAG,MAAM;GAAS,CAC9G;GACA,UAAU,CAAC;IAAE;IAAO,QAAQ;IAAM,OAAO;GAAM,CAAC;GAChD,MAAM,CACJ;IACE,OAAO,GAAG,MAAM;IAChB,OAAO;KACL,MAAM;KAGN,KAAK;+BACc,WAAW,QAAQC,6BAAAA,GAAG,KAAK,SAAS,EAAE;;+BAEtC,WAAW,QAAQA,6BAAAA,GAAG,KAAK,SAAS,EAAE,UAAU,MAAM,iBAAiB,MAAM,OAAO,WAAW;;KAElH,UAAU,CAACC,aAAAA,SAAS,MAAM,kBAAkB,CAAC;IAC/C;IACA,WAAW;GACb,CACF;EACF;EAEA,iCAAA,iCAAiC,OAAO,MAAM,OAAO,IAAI;EAEzD,MAAM,UAAU,MAAM,GAAG,qBAAqB,CAAC,MAAM,2BAA2B,OAAO,CAAC,CAAC;EAKzF,GAAG,qBAAqB,CAAC,KAAK,MAAM,OAAO;CAC7C,CAAC;AACH"}
|