kitcn 0.32.0 → 0.32.2
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/CHANGELOG.md +32 -0
- package/dist/aggregate/index.d.ts +2 -2
- package/dist/auth/generated/index.d.ts +1 -1
- package/dist/auth/index.d.ts +7 -7
- package/dist/{capabilities-CD-Ij91k.d.ts → capabilities-BJm_VSDT.d.ts} +60 -0
- package/dist/{generated-contract-disabled-PdNGvNYP.d.ts → generated-contract-disabled-BRm1dNQE.d.ts} +9 -9
- package/dist/orm/aggregate-index/index.d.ts +1 -1
- package/dist/orm/index.d.ts +2 -2
- package/dist/orm/index.js +185 -25
- package/dist/orm/migrations/index.d.ts +2 -2
- package/dist/{where-clause-compiler-FDJTqLDC.d.ts → where-clause-compiler-Dgf4lrO-.d.ts} +126 -126
- package/package.json +1 -1
package/dist/orm/index.js
CHANGED
|
@@ -1322,6 +1322,20 @@ const DEFAULT_RELATION_FAN_OUT_MAX_KEYS = 1e3;
|
|
|
1322
1322
|
* satisfies the limit.
|
|
1323
1323
|
*/
|
|
1324
1324
|
const RELATION_FILTER_STREAM_CHUNK = 32;
|
|
1325
|
+
/**
|
|
1326
|
+
* Marks a relation config that only exists to answer "does a matching child
|
|
1327
|
+
* exist?" for a `where` clause. The lowered load may stop at the first
|
|
1328
|
+
* surviving child, but it must not widen the window it walks to find one --
|
|
1329
|
+
* that window is what decides the answer, so changing it would change results
|
|
1330
|
+
* rather than just their cost.
|
|
1331
|
+
*
|
|
1332
|
+
* A symbol, not a field: the emitted object is fed to the same relation loader
|
|
1333
|
+
* that serves a caller's `with`, and nothing a caller can write should be able
|
|
1334
|
+
* to claim this.
|
|
1335
|
+
*/
|
|
1336
|
+
const RELATION_EXISTENCE_PROBE = Symbol("kitcn.relationExistenceProbe");
|
|
1337
|
+
/** Lowering a subtree with this set emits no existence probes anywhere in it. */
|
|
1338
|
+
const NO_PROBE_RELATIONS = /* @__PURE__ */ new Set();
|
|
1325
1339
|
const DEFAULT_AGGREGATE_CARTESIAN_MAX_KEYS = 4096;
|
|
1326
1340
|
const DEFAULT_AGGREGATE_WORK_BUDGET = 16384;
|
|
1327
1341
|
const PUBLIC_ID_FIELD = "id";
|
|
@@ -1619,6 +1633,20 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
1619
1633
|
* intervening write would make it stale.
|
|
1620
1634
|
*/
|
|
1621
1635
|
_documentByNormalizedId = /* @__PURE__ */ new Map();
|
|
1636
|
+
/**
|
|
1637
|
+
* Single target documents resolved by an eq-pinned key during one execution,
|
|
1638
|
+
* keyed on the read itself: table, index, join columns and their values.
|
|
1639
|
+
*
|
|
1640
|
+
* `_documentByNormalizedId` only covers a join on the primary id. A relation
|
|
1641
|
+
* joined on any other column resolves its target with `.first()` instead, and
|
|
1642
|
+
* the same one-row-at-a-time membership predicate re-issues that read once per
|
|
1643
|
+
* drain. The index is fixed for the whole relation and the key is pinned by
|
|
1644
|
+
* `eq`, so the first row is a pure function of this key within an execution.
|
|
1645
|
+
*
|
|
1646
|
+
* Same scope and staleness argument as `_documentByNormalizedId`: not handed
|
|
1647
|
+
* to the next run by `_forExecution`, so an intervening write is still seen.
|
|
1648
|
+
*/
|
|
1649
|
+
_firstDocumentByFieldKey = /* @__PURE__ */ new Map();
|
|
1622
1650
|
constructor(schema, tableConfig, edgeMetadata, db, config, mode, _allEdges, rls, relationLoading, vectorSearchProvider, configuredIndex, countIndexReadiness) {
|
|
1623
1651
|
super();
|
|
1624
1652
|
this.schema = schema;
|
|
@@ -2391,7 +2419,47 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
2391
2419
|
}
|
|
2392
2420
|
}
|
|
2393
2421
|
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Relation keys the filter tree mentions exactly once, across every OR/AND/NOT
|
|
2424
|
+
* branch at this table level.
|
|
2425
|
+
*
|
|
2426
|
+
* `_mergeWithConfig` collapses the whole tree into one load per relation key,
|
|
2427
|
+
* so a key two branches disagree about has to be loaded the way both branches
|
|
2428
|
+
* can read. Only a key with a single occurrence has one predicate to satisfy,
|
|
2429
|
+
* and only then can that predicate be pushed into the read plan.
|
|
2430
|
+
*
|
|
2431
|
+
* Relation values are not descended into: they belong to the target table and
|
|
2432
|
+
* get their own count when that level is lowered.
|
|
2433
|
+
*/
|
|
2434
|
+
_collectSingleOccurrenceRelations(filter, tableConfig) {
|
|
2435
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2436
|
+
const walk = (node) => {
|
|
2437
|
+
if (!this._isRecord(node)) return;
|
|
2438
|
+
for (const [key, value] of Object.entries(node)) {
|
|
2439
|
+
if (value === void 0) continue;
|
|
2440
|
+
if (key === "OR" || key === "AND") {
|
|
2441
|
+
if (!Array.isArray(value)) continue;
|
|
2442
|
+
for (const sub of value) walk(sub);
|
|
2443
|
+
continue;
|
|
2444
|
+
}
|
|
2445
|
+
if (key === "NOT") {
|
|
2446
|
+
walk(value);
|
|
2447
|
+
continue;
|
|
2448
|
+
}
|
|
2449
|
+
if (!tableConfig.relations[key]) continue;
|
|
2450
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
2451
|
+
}
|
|
2452
|
+
};
|
|
2453
|
+
walk(filter);
|
|
2454
|
+
const single = /* @__PURE__ */ new Set();
|
|
2455
|
+
for (const [key, count] of counts) if (count === 1) single.add(key);
|
|
2456
|
+
return single;
|
|
2457
|
+
}
|
|
2394
2458
|
_buildFilterWithConfig(filter, tableConfig) {
|
|
2459
|
+
if (!this._isRecord(filter)) return {};
|
|
2460
|
+
return this._buildFilterWithConfigForLevel(filter, tableConfig, this._collectSingleOccurrenceRelations(filter, tableConfig));
|
|
2461
|
+
}
|
|
2462
|
+
_buildFilterWithConfigForLevel(filter, tableConfig, probeEligible) {
|
|
2395
2463
|
if (!this._isRecord(filter)) return {};
|
|
2396
2464
|
const result = {};
|
|
2397
2465
|
const entries = Object.entries(filter);
|
|
@@ -2401,27 +2469,36 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
2401
2469
|
if (key === "OR" || key === "AND") {
|
|
2402
2470
|
if (!Array.isArray(value) || value.length === 0) continue;
|
|
2403
2471
|
for (const sub of value) {
|
|
2404
|
-
const nested = this.
|
|
2472
|
+
const nested = this._buildFilterWithConfigForLevel(sub, tableConfig, probeEligible);
|
|
2405
2473
|
this._mergeWithConfig(result, nested);
|
|
2406
2474
|
}
|
|
2407
2475
|
continue;
|
|
2408
2476
|
}
|
|
2409
2477
|
if (key === "NOT") {
|
|
2410
|
-
const nested = this.
|
|
2478
|
+
const nested = this._buildFilterWithConfigForLevel(value, tableConfig, probeEligible);
|
|
2411
2479
|
this._mergeWithConfig(result, nested);
|
|
2412
2480
|
continue;
|
|
2413
2481
|
}
|
|
2414
2482
|
this._assertNoLegacyPublicFieldName(key);
|
|
2415
2483
|
const relation = tableConfig.relations[key];
|
|
2416
2484
|
if (!relation) continue;
|
|
2485
|
+
const probe = relation.relationType !== "one" && probeEligible.has(key) ? { [RELATION_EXISTENCE_PROBE]: true } : void 0;
|
|
2417
2486
|
if (typeof value === "boolean") {
|
|
2418
|
-
result[key] = true;
|
|
2487
|
+
result[key] = probe ?? true;
|
|
2419
2488
|
continue;
|
|
2420
2489
|
}
|
|
2421
2490
|
const targetTableConfig = this._getTableConfigByDbName(relation.targetTableName);
|
|
2422
2491
|
if (!targetTableConfig) continue;
|
|
2423
|
-
const nested = this.
|
|
2424
|
-
|
|
2492
|
+
const nested = this._buildFilterWithConfigForLevel(value, targetTableConfig, probe ? this._collectSingleOccurrenceRelations(value, targetTableConfig) : NO_PROBE_RELATIONS);
|
|
2493
|
+
const hasNested = Object.keys(nested).length > 0;
|
|
2494
|
+
if (!probe) {
|
|
2495
|
+
result[key] = hasNested ? { with: nested } : true;
|
|
2496
|
+
continue;
|
|
2497
|
+
}
|
|
2498
|
+
const emitted = { ...probe };
|
|
2499
|
+
if (Object.keys(value).length > 0) emitted.where = value;
|
|
2500
|
+
if (hasNested) emitted.with = nested;
|
|
2501
|
+
result[key] = emitted;
|
|
2425
2502
|
}
|
|
2426
2503
|
return result;
|
|
2427
2504
|
}
|
|
@@ -4637,18 +4714,75 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
4637
4714
|
if (!this._allEdges) return [];
|
|
4638
4715
|
return this._allEdges.filter((edge) => edge.sourceTable === tableName);
|
|
4639
4716
|
}
|
|
4717
|
+
/**
|
|
4718
|
+
* A memo entry is a snapshot, so every caller has to get its own document.
|
|
4719
|
+
*
|
|
4720
|
+
* Relation loaders write nested `with` results and `extras` straight onto the
|
|
4721
|
+
* target they were handed, and `hydrateDateFieldsForRead` copies every own key
|
|
4722
|
+
* it finds. Two loads that share one entry would therefore publish each
|
|
4723
|
+
* other's fields — a relation asked for as `true` coming back carrying a
|
|
4724
|
+
* nested relation only the `where` requested. Those writes are all top-level,
|
|
4725
|
+
* so a shallow copy is exactly as much isolation as they need.
|
|
4726
|
+
*/
|
|
4727
|
+
_ownedCopy(doc) {
|
|
4728
|
+
return doc === null || doc === void 0 ? null : { ...doc };
|
|
4729
|
+
}
|
|
4640
4730
|
async _getById(tableName, id) {
|
|
4641
4731
|
if (id === null || id === void 0) return null;
|
|
4642
4732
|
const normalizedId = this.db.normalizeId(tableName, id);
|
|
4643
4733
|
if (normalizedId === null) return null;
|
|
4644
4734
|
const existing = this._documentByNormalizedId.get(normalizedId);
|
|
4645
|
-
if (existing) return await existing;
|
|
4735
|
+
if (existing) return this._ownedCopy(await existing);
|
|
4646
4736
|
const pending = Promise.resolve(this.db.get(normalizedId)).catch((error) => {
|
|
4647
4737
|
this._documentByNormalizedId.delete(normalizedId);
|
|
4648
4738
|
throw error;
|
|
4649
4739
|
});
|
|
4650
4740
|
this._documentByNormalizedId.set(normalizedId, pending);
|
|
4651
|
-
return await pending;
|
|
4741
|
+
return this._ownedCopy(await pending);
|
|
4742
|
+
}
|
|
4743
|
+
/**
|
|
4744
|
+
* Identity of one `_firstByFields` read, or null when the join values cannot
|
|
4745
|
+
* be encoded losslessly.
|
|
4746
|
+
*
|
|
4747
|
+
* `JSON.stringify` alone is not safe here. It renders every `ArrayBuffer` as
|
|
4748
|
+
* `{}` and `NaN`/`Infinity`/`-Infinity` as `null`, so two distinct join values
|
|
4749
|
+
* would share one entry, and it throws outright on `int64`. The relation
|
|
4750
|
+
* loader's own per-batch key map cannot catch that, because the residual
|
|
4751
|
+
* relation `where` hands it one row at a time — a map of one never compares
|
|
4752
|
+
* two values. This key is the only thing that tells them apart.
|
|
4753
|
+
*
|
|
4754
|
+
* `convexToJson` is the same wire encoding the client uses for query keys, so
|
|
4755
|
+
* every Convex value round-trips distinctly. Anything it rejects is not a
|
|
4756
|
+
* Convex value and simply goes unmemoized.
|
|
4757
|
+
*/
|
|
4758
|
+
_firstByFieldsMemoKey(tableName, fields, values, indexName) {
|
|
4759
|
+
try {
|
|
4760
|
+
return JSON.stringify([
|
|
4761
|
+
tableName,
|
|
4762
|
+
indexName,
|
|
4763
|
+
fields,
|
|
4764
|
+
values.map((value) => convexToJson(value))
|
|
4765
|
+
]);
|
|
4766
|
+
} catch {
|
|
4767
|
+
return null;
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
/**
|
|
4771
|
+
* `_getById` for a relation joined on something other than the primary id:
|
|
4772
|
+
* resolve one target document by an eq-pinned key, memoized per execution.
|
|
4773
|
+
*/
|
|
4774
|
+
async _firstByFields(tableName, fields, values, indexName) {
|
|
4775
|
+
const read = () => this._queryByFields(this.db.query(tableName), fields, values, indexName).first();
|
|
4776
|
+
const key = this._firstByFieldsMemoKey(tableName, fields, values, indexName);
|
|
4777
|
+
if (key === null) return await read();
|
|
4778
|
+
const existing = this._firstDocumentByFieldKey.get(key);
|
|
4779
|
+
if (existing) return this._ownedCopy(await existing);
|
|
4780
|
+
const pending = Promise.resolve(read()).catch((error) => {
|
|
4781
|
+
this._firstDocumentByFieldKey.delete(key);
|
|
4782
|
+
throw error;
|
|
4783
|
+
});
|
|
4784
|
+
this._firstDocumentByFieldKey.set(key, pending);
|
|
4785
|
+
return this._ownedCopy(await pending);
|
|
4652
4786
|
}
|
|
4653
4787
|
_getRelationConcurrency() {
|
|
4654
4788
|
const value = this.relationLoading?.concurrency;
|
|
@@ -4800,7 +4934,7 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
4800
4934
|
const resolveTargetMatch = async (values) => {
|
|
4801
4935
|
let target = null;
|
|
4802
4936
|
if (useGetById) target = await this._getById(edge.targetTable, values[0]);
|
|
4803
|
-
else target = await this.
|
|
4937
|
+
else target = await this._firstByFields(edge.targetTable, targetFields, values, targetIndexName);
|
|
4804
4938
|
if (!target) return false;
|
|
4805
4939
|
return this._evaluateTableFilter(target, targetTableConfig, whereRecord);
|
|
4806
4940
|
};
|
|
@@ -4902,7 +5036,7 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
4902
5036
|
const fetched = await this._mapWithConcurrency(entries, async ([key, values]) => {
|
|
4903
5037
|
let target = null;
|
|
4904
5038
|
if (useGetById) target = await this._getById(edge.targetTable, values[0]);
|
|
4905
|
-
else target = await this.
|
|
5039
|
+
else target = await this._firstByFields(edge.targetTable, targetFields, values, indexName);
|
|
4906
5040
|
return {
|
|
4907
5041
|
key,
|
|
4908
5042
|
target
|
|
@@ -4955,9 +5089,10 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
4955
5089
|
* parents in the round.
|
|
4956
5090
|
*/
|
|
4957
5091
|
async _readBoundedThroughLinks(params) {
|
|
4958
|
-
const {
|
|
5092
|
+
const { applyAmbientTargetFilters, applyConfigTargetFilter, edge, enforceTargetKeyCap, entries, fetchLimit, fetchTargets, scanLimit, targetFields, throughIndexName, throughTableConfig } = params;
|
|
4959
5093
|
const throughTargetFields = edge.through.targetFields;
|
|
4960
5094
|
const cursors = entries.map(([key, values]) => ({
|
|
5095
|
+
ambientLinks: 0,
|
|
4961
5096
|
buffered: [],
|
|
4962
5097
|
exhausted: false,
|
|
4963
5098
|
iterator: this._queryByFields(this.db.query(edge.through.table), edge.through.sourceFields, values, throughIndexName)[Symbol.asyncIterator](),
|
|
@@ -4984,10 +5119,12 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
4984
5119
|
};
|
|
4985
5120
|
/** Target key -> the surviving document, absent when it did not survive. */
|
|
4986
5121
|
const survivorByKey = /* @__PURE__ */ new Map();
|
|
5122
|
+
/** Target keys that cleared the ambient filters, survivors or not. */
|
|
5123
|
+
const ambientKeys = /* @__PURE__ */ new Set();
|
|
4987
5124
|
const resolvedKeys = /* @__PURE__ */ new Set();
|
|
4988
5125
|
const survivors = [];
|
|
4989
5126
|
while (true) {
|
|
4990
|
-
const active = cursors.filter((cursor) => cursor.links.length < fetchLimit && !(cursor.exhausted && cursor.buffered.length === 0));
|
|
5127
|
+
const active = cursors.filter((cursor) => cursor.links.length < fetchLimit && (scanLimit === void 0 || cursor.ambientLinks < scanLimit) && !(cursor.exhausted && cursor.buffered.length === 0));
|
|
4991
5128
|
if (active.length === 0) break;
|
|
4992
5129
|
const candidatesPerCursor = await this._mapWithConcurrency(active, async (cursor) => {
|
|
4993
5130
|
const need = fetchLimit - cursor.links.length;
|
|
@@ -5006,7 +5143,12 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
5006
5143
|
enforceTargetKeyCap(resolvedKeys.size + newKeys.size);
|
|
5007
5144
|
const fetched = await fetchTargets(Array.from(newKeys.entries()));
|
|
5008
5145
|
for (const key of newKeys.keys()) resolvedKeys.add(key);
|
|
5009
|
-
const
|
|
5146
|
+
const ambientTargets = await applyAmbientTargetFilters(fetched.map((entry) => entry.target).filter((target) => !!target));
|
|
5147
|
+
for (const target of ambientTargets) {
|
|
5148
|
+
const key = this._buildRelationKey(target, targetFields);
|
|
5149
|
+
if (key) ambientKeys.add(key);
|
|
5150
|
+
}
|
|
5151
|
+
const surviving = await applyConfigTargetFilter(ambientTargets);
|
|
5010
5152
|
for (const target of surviving) {
|
|
5011
5153
|
const key = this._buildRelationKey(target, targetFields);
|
|
5012
5154
|
if (!key || survivorByKey.has(key)) continue;
|
|
@@ -5018,8 +5160,11 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
5018
5160
|
const cursor = active[i];
|
|
5019
5161
|
for (const link of candidatesPerCursor[i]) {
|
|
5020
5162
|
if (cursor.links.length >= fetchLimit) break;
|
|
5163
|
+
if (scanLimit !== void 0 && cursor.ambientLinks >= scanLimit) break;
|
|
5021
5164
|
const key = this._buildRelationKey(link, throughTargetFields);
|
|
5022
|
-
if (!key
|
|
5165
|
+
if (!key) continue;
|
|
5166
|
+
if (ambientKeys.has(key)) cursor.ambientLinks += 1;
|
|
5167
|
+
if (!survivorByKey.has(key)) continue;
|
|
5023
5168
|
cursor.links.push(link);
|
|
5024
5169
|
}
|
|
5025
5170
|
}
|
|
@@ -5072,6 +5217,7 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
5072
5217
|
const relationDefinition = tableConfig.relations[relationName];
|
|
5073
5218
|
const strict = tableConfig.strict !== false;
|
|
5074
5219
|
const hasPostFetchTargetFilter = this.rls?.mode !== "skip" && isRlsEnabled(targetTableConfig.table) || Boolean(relationDefinition?.where) || Boolean(relationConfig && typeof relationConfig === "object" && "where" in relationConfig && relationConfig.where);
|
|
5220
|
+
const isExistenceProbe = Boolean(relationConfig) && typeof relationConfig === "object" && relationConfig[RELATION_EXISTENCE_PROBE] === true;
|
|
5075
5221
|
let orderSpecs = [];
|
|
5076
5222
|
if (relationConfig && typeof relationConfig === "object" && "orderBy" in relationConfig) {
|
|
5077
5223
|
let orderByValue = relationConfig.orderBy;
|
|
@@ -5086,15 +5232,22 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
5086
5232
|
if (effectivePerParentLimit === void 0 && !this.allowFullScan) throw new Error(`Relation "${tableConfig.name}.${relationName}" requires limit, allowFullScan: true, or defineSchema(..., { defaults: { defaultLimit } }).`);
|
|
5087
5233
|
const perParentOffset = relationConfig && typeof relationConfig === "object" && "offset" in relationConfig ? relationConfig.offset : void 0;
|
|
5088
5234
|
if (perParentOffset !== void 0 && typeof perParentOffset !== "number") throw new Error("Only numeric offset is supported in kitcn ORM.");
|
|
5235
|
+
const probeFetchLimit = isExistenceProbe ? 1 : void 0;
|
|
5236
|
+
const probeScanLimit = isExistenceProbe ? effectivePerParentLimit : void 0;
|
|
5237
|
+
const canBoundPerParentRead = effectivePerParentLimit !== void 0 || isExistenceProbe;
|
|
5089
5238
|
const applyOffsetAndLimit = (items) => {
|
|
5090
5239
|
let result = items;
|
|
5091
5240
|
if (perParentOffset !== void 0 && perParentOffset > 0) result = result.slice(perParentOffset);
|
|
5092
5241
|
if (effectivePerParentLimit !== void 0) result = result.slice(0, effectivePerParentLimit);
|
|
5093
5242
|
return result;
|
|
5094
5243
|
};
|
|
5095
|
-
const
|
|
5096
|
-
|
|
5097
|
-
if (relationDefinition?.where)
|
|
5244
|
+
const applyAmbientTargetFilters = async (candidateTargets) => {
|
|
5245
|
+
const visibleTargets = await this._applyRlsSelectFilter(candidateTargets, targetTableConfig);
|
|
5246
|
+
if (!relationDefinition?.where) return visibleTargets;
|
|
5247
|
+
return visibleTargets.filter((target) => this._evaluateTableFilter(target, targetTableConfig, relationDefinition.where));
|
|
5248
|
+
};
|
|
5249
|
+
const applyConfigTargetFilter = async (ambientTargets) => {
|
|
5250
|
+
let filteredTargets = ambientTargets;
|
|
5098
5251
|
if (relationConfig && typeof relationConfig === "object" && "where" in relationConfig) {
|
|
5099
5252
|
const whereFilter = relationConfig.where;
|
|
5100
5253
|
if (typeof whereFilter === "function") {
|
|
@@ -5107,6 +5260,7 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
5107
5260
|
}
|
|
5108
5261
|
return filteredTargets;
|
|
5109
5262
|
};
|
|
5263
|
+
const applyPostFetchTargetFilters = async (candidateTargets) => applyConfigTargetFilter(await applyAmbientTargetFilters(candidateTargets));
|
|
5110
5264
|
let targets = [];
|
|
5111
5265
|
let throughBySourceKey;
|
|
5112
5266
|
let targetFiltersApplied = false;
|
|
@@ -5134,21 +5288,23 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
5134
5288
|
return await this._mapWithConcurrency(keyEntries, async ([key, values]) => {
|
|
5135
5289
|
let target = null;
|
|
5136
5290
|
if (useGetById) target = await this._getById(edge.targetTable, values[0]);
|
|
5137
|
-
else target = await this.
|
|
5291
|
+
else target = await this._firstByFields(edge.targetTable, targetFields, values, indexName);
|
|
5138
5292
|
return {
|
|
5139
5293
|
key,
|
|
5140
5294
|
target
|
|
5141
5295
|
};
|
|
5142
5296
|
});
|
|
5143
5297
|
};
|
|
5144
|
-
if (orderSpecs.length === 0 &&
|
|
5298
|
+
if (orderSpecs.length === 0 && canBoundPerParentRead) {
|
|
5145
5299
|
const bounded = await this._readBoundedThroughLinks({
|
|
5146
|
-
|
|
5300
|
+
applyAmbientTargetFilters,
|
|
5301
|
+
applyConfigTargetFilter,
|
|
5147
5302
|
edge,
|
|
5148
5303
|
enforceTargetKeyCap,
|
|
5149
5304
|
entries,
|
|
5150
|
-
fetchLimit: Math.max(perParentOffset ?? 0, 0) + effectivePerParentLimit,
|
|
5305
|
+
fetchLimit: probeFetchLimit ?? Math.max(perParentOffset ?? 0, 0) + effectivePerParentLimit,
|
|
5151
5306
|
fetchTargets: fetchThroughTargets,
|
|
5307
|
+
scanLimit: probeScanLimit,
|
|
5152
5308
|
targetFields,
|
|
5153
5309
|
throughIndexName,
|
|
5154
5310
|
throughTableConfig
|
|
@@ -5188,22 +5344,25 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
5188
5344
|
});
|
|
5189
5345
|
const orderServedByIndex = orderSpecs.length === 0 || orderPushdownDirection !== null;
|
|
5190
5346
|
const applyPushdownOrder = (query) => orderPushdownDirection ? query.order(orderPushdownDirection) : query;
|
|
5191
|
-
const streamPostFetchTargetFilters = orderServedByIndex && hasPostFetchTargetFilter &&
|
|
5347
|
+
const streamPostFetchTargetFilters = orderServedByIndex && hasPostFetchTargetFilter && canBoundPerParentRead;
|
|
5192
5348
|
targetFiltersApplied = streamPostFetchTargetFilters;
|
|
5193
5349
|
targets = (await this._mapWithConcurrency(entries, async ([, values]) => {
|
|
5194
5350
|
const query = this._queryByFields(this.db.query(edge.targetTable), targetFields, values, indexName);
|
|
5195
|
-
if (orderServedByIndex && !hasPostFetchTargetFilter &&
|
|
5196
|
-
const fetchLimit = (perParentOffset ?? 0) + (effectivePerParentLimit ?? 0);
|
|
5351
|
+
if (orderServedByIndex && !hasPostFetchTargetFilter && canBoundPerParentRead) {
|
|
5352
|
+
const fetchLimit = probeFetchLimit ?? (perParentOffset ?? 0) + (effectivePerParentLimit ?? 0);
|
|
5197
5353
|
return await applyPushdownOrder(query).take(fetchLimit);
|
|
5198
5354
|
}
|
|
5199
5355
|
if (streamPostFetchTargetFilters) {
|
|
5200
5356
|
const visibleTargets = [];
|
|
5201
|
-
const fetchLimit = Math.max(perParentOffset ?? 0, 0) + (effectivePerParentLimit ?? 0);
|
|
5357
|
+
const fetchLimit = probeFetchLimit ?? Math.max(perParentOffset ?? 0, 0) + (effectivePerParentLimit ?? 0);
|
|
5202
5358
|
let batch = [];
|
|
5359
|
+
let ambientSeen = 0;
|
|
5203
5360
|
const drain = async () => {
|
|
5204
5361
|
if (batch.length === 0) return;
|
|
5205
|
-
const
|
|
5362
|
+
const ambientTargets = await applyAmbientTargetFilters(batch);
|
|
5206
5363
|
batch = [];
|
|
5364
|
+
ambientSeen += ambientTargets.length;
|
|
5365
|
+
const filtered = await applyConfigTargetFilter(ambientTargets);
|
|
5207
5366
|
for (const row of filtered) {
|
|
5208
5367
|
if (visibleTargets.length >= fetchLimit) return;
|
|
5209
5368
|
visibleTargets.push(row);
|
|
@@ -5215,6 +5374,7 @@ var GelRelationalQuery = class GelRelationalQuery extends QueryPromise {
|
|
|
5215
5374
|
if (batch.length < chunk) continue;
|
|
5216
5375
|
await drain();
|
|
5217
5376
|
if (visibleTargets.length >= fetchLimit) break;
|
|
5377
|
+
if (probeScanLimit !== void 0 && ambientSeen >= probeScanLimit) break;
|
|
5218
5378
|
}
|
|
5219
5379
|
if (visibleTargets.length < fetchLimit) await drain();
|
|
5220
5380
|
return visibleTargets;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as MigrationStep, D as defineMigration, E as buildMigrationPlan, O as defineMigrationSet, S as MigrationStateMap, T as MigrationWriteMode, _ as MigrationManifestEntry, a as MAX_STATUS_RUN_LIMIT, b as MigrationRunStatus, c as MigrationRunChunkArgs, d as MigrationAppliedState, f as MigrationDefinition, g as MigrationDriftIssue, h as MigrationDocContext, k as detectMigrationDrift, l as MigrationStatusArgs, m as MigrationDoc, o as MigrationCancelArgs, p as MigrationDirection, s as MigrationRunArgs, u as createMigrationHandlers, v as MigrationMigrateOne, w as MigrationTableName, x as MigrationSet, y as MigrationPlan } from "../../capabilities-
|
|
2
|
-
import { Ct as migrationCapability, dt as MIGRATION_STORAGE_TABLE_NAMES, ft as injectMigrationStorageTables, lt as MIGRATION_RUN_TABLE, mt as migrationStorageTables, pt as migrationExtension, ut as MIGRATION_STATE_TABLE } from "../../where-clause-compiler-
|
|
1
|
+
import { C as MigrationStep, D as defineMigration, E as buildMigrationPlan, O as defineMigrationSet, S as MigrationStateMap, T as MigrationWriteMode, _ as MigrationManifestEntry, a as MAX_STATUS_RUN_LIMIT, b as MigrationRunStatus, c as MigrationRunChunkArgs, d as MigrationAppliedState, f as MigrationDefinition, g as MigrationDriftIssue, h as MigrationDocContext, k as detectMigrationDrift, l as MigrationStatusArgs, m as MigrationDoc, o as MigrationCancelArgs, p as MigrationDirection, s as MigrationRunArgs, u as createMigrationHandlers, v as MigrationMigrateOne, w as MigrationTableName, x as MigrationSet, y as MigrationPlan } from "../../capabilities-BJm_VSDT.js";
|
|
2
|
+
import { Ct as migrationCapability, dt as MIGRATION_STORAGE_TABLE_NAMES, ft as injectMigrationStorageTables, lt as MIGRATION_RUN_TABLE, mt as migrationStorageTables, pt as migrationExtension, ut as MIGRATION_STATE_TABLE } from "../../where-clause-compiler-Dgf4lrO-.js";
|
|
3
3
|
export { MAX_STATUS_RUN_LIMIT, MIGRATION_RUN_TABLE, MIGRATION_STATE_TABLE, MIGRATION_STORAGE_TABLE_NAMES, type MigrationAppliedState, type MigrationCancelArgs, type MigrationDefinition, type MigrationDirection, type MigrationDoc, type MigrationDocContext, type MigrationDriftIssue, type MigrationManifestEntry, type MigrationMigrateOne, type MigrationPlan, type MigrationRunArgs, type MigrationRunChunkArgs, type MigrationRunStatus, type MigrationSet, type MigrationStateMap, type MigrationStatusArgs, type MigrationStep, type MigrationTableName, type MigrationWriteMode, buildMigrationPlan, createMigrationHandlers, defineMigration, defineMigrationSet, detectMigrationDrift, injectMigrationStorageTables, migrationCapability, migrationExtension, migrationStorageTables };
|