@danceroutine/tango-orm 1.11.11 → 1.11.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{QuerySetState-C8NgnYTc.d.ts → QuerySetState-D9xr3SCH.d.ts} +3 -16
- package/dist/{index-d9NRHwNe.d.ts → index-DIL4gXO6.d.ts} +3 -3
- package/dist/{index-O_dY4tRf.d.ts → index-Db_GZk8M.d.ts} +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/manager/index.d.ts +3 -3
- package/dist/manager/index.js +2 -2
- package/dist/{manager-VoPL1sc3.js → manager-BwiDQLup.js} +2 -2
- package/dist/{manager-VoPL1sc3.js.map → manager-BwiDQLup.js.map} +1 -1
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/{query-D_93uktq.js → query-hvcqTb9O.js} +266 -210
- package/dist/query-hvcqTb9O.js.map +1 -0
- package/dist/{registerModelObjects-UMxYImMK.d.ts → registerModelObjects-DpiMWh5n.d.ts} +2 -2
- package/dist/{registerModelObjects-41ocGQmv.js → registerModelObjects-Dr1YYKt3.js} +2 -2
- package/dist/{registerModelObjects-41ocGQmv.js.map → registerModelObjects-Dr1YYKt3.js.map} +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.js +1 -1
- package/package.json +6 -6
- package/dist/query-D_93uktq.js.map +0 -1
|
@@ -1104,6 +1104,263 @@ var QBuilder = class QBuilder {
|
|
|
1104
1104
|
}
|
|
1105
1105
|
};
|
|
1106
1106
|
//#endregion
|
|
1107
|
+
//#region src/query/hydration/HydrationEntityRegistry.ts
|
|
1108
|
+
/**
|
|
1109
|
+
* Tracks hydrated related records so repeated rows point at one canonical
|
|
1110
|
+
* object per model primary key.
|
|
1111
|
+
*/
|
|
1112
|
+
var HydrationEntityRegistry = class HydrationEntityRegistry {
|
|
1113
|
+
attachPersistedRecordAccessors;
|
|
1114
|
+
static BRAND = "tango.orm.hydration_entity_registry";
|
|
1115
|
+
__tangoBrand = HydrationEntityRegistry.BRAND;
|
|
1116
|
+
recordsByModel = /* @__PURE__ */ new Map();
|
|
1117
|
+
constructor(attachPersistedRecordAccessors) {
|
|
1118
|
+
this.attachPersistedRecordAccessors = attachPersistedRecordAccessors;
|
|
1119
|
+
}
|
|
1120
|
+
static isHydrationEntityRegistry(value) {
|
|
1121
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === HydrationEntityRegistry.BRAND;
|
|
1122
|
+
}
|
|
1123
|
+
canonicalize(node, row) {
|
|
1124
|
+
const primaryKeyValue = row[node.targetPrimaryKey];
|
|
1125
|
+
if (typeof primaryKeyValue !== "string" && typeof primaryKeyValue !== "number") return row;
|
|
1126
|
+
const byModel = this.recordsByModel.get(node.targetModelKey) ?? /* @__PURE__ */ new Map();
|
|
1127
|
+
const existing = byModel.get(primaryKeyValue);
|
|
1128
|
+
if (existing) {
|
|
1129
|
+
Object.assign(existing, row);
|
|
1130
|
+
return existing;
|
|
1131
|
+
}
|
|
1132
|
+
byModel.set(primaryKeyValue, row);
|
|
1133
|
+
this.recordsByModel.set(node.targetModelKey, byModel);
|
|
1134
|
+
this.attachPersistedRecordAccessors?.(row, node.targetModelKey);
|
|
1135
|
+
return row;
|
|
1136
|
+
}
|
|
1137
|
+
};
|
|
1138
|
+
//#endregion
|
|
1139
|
+
//#region src/query/hydration/QueryRowNormalizerStrategy.ts
|
|
1140
|
+
var PassthroughQueryRowNormalizerStrategy = class {
|
|
1141
|
+
normalizeRootRows(rows, _columns) {
|
|
1142
|
+
return [...rows];
|
|
1143
|
+
}
|
|
1144
|
+
normalizeHydratedRowsForParserShape(rows, _columns) {
|
|
1145
|
+
return [...rows];
|
|
1146
|
+
}
|
|
1147
|
+
normalizeTargetRow(row, _targetColumns) {
|
|
1148
|
+
return row;
|
|
1149
|
+
}
|
|
1150
|
+
normalizeColumnValue(_columnType, value) {
|
|
1151
|
+
return value;
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
function createQueryRowNormalizerStrategy(dialect) {
|
|
1155
|
+
return dialect === InternalDialect.SQLITE ? new SqliteRowNormalizerStrategy() : new PassthroughQueryRowNormalizerStrategy();
|
|
1156
|
+
}
|
|
1157
|
+
var SqliteRowNormalizerStrategy = class {
|
|
1158
|
+
normalizeRootRows(rows, columns) {
|
|
1159
|
+
return this.normalizeRows(rows, columns);
|
|
1160
|
+
}
|
|
1161
|
+
normalizeHydratedRowsForParserShape(rows, columns) {
|
|
1162
|
+
return this.normalizeRows(rows, columns);
|
|
1163
|
+
}
|
|
1164
|
+
normalizeTargetRow(row, targetColumns) {
|
|
1165
|
+
return this.normalizeRow(row, this.booleanColumns(targetColumns));
|
|
1166
|
+
}
|
|
1167
|
+
normalizeColumnValue(columnType, value) {
|
|
1168
|
+
return this.isBooleanColumnType(columnType) ? this.normalizeSqliteBoolean(value) : value;
|
|
1169
|
+
}
|
|
1170
|
+
normalizeRows(rows, columns) {
|
|
1171
|
+
const booleanColumns = this.booleanColumns(columns);
|
|
1172
|
+
if (booleanColumns.length === 0) return [...rows];
|
|
1173
|
+
return rows.map((row) => this.normalizeRow(row, booleanColumns));
|
|
1174
|
+
}
|
|
1175
|
+
normalizeRow(row, columns) {
|
|
1176
|
+
let normalized = null;
|
|
1177
|
+
for (const column of columns) {
|
|
1178
|
+
const current = row[column];
|
|
1179
|
+
const next = this.normalizeSqliteBoolean(current);
|
|
1180
|
+
if (next === current) continue;
|
|
1181
|
+
normalized ??= { ...row };
|
|
1182
|
+
normalized[column] = next;
|
|
1183
|
+
}
|
|
1184
|
+
return normalized ?? row;
|
|
1185
|
+
}
|
|
1186
|
+
booleanColumns(columns) {
|
|
1187
|
+
return Object.entries(columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
|
|
1188
|
+
}
|
|
1189
|
+
isBooleanColumnType(value) {
|
|
1190
|
+
return typeof value === "string" && ["bool", "boolean"].includes(value.trim().toLowerCase());
|
|
1191
|
+
}
|
|
1192
|
+
normalizeSqliteBoolean(value) {
|
|
1193
|
+
if (value === 0 || value === "0") return false;
|
|
1194
|
+
if (value === 1 || value === "1") return true;
|
|
1195
|
+
return value;
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
//#endregion
|
|
1199
|
+
//#region src/query/hydration/QueryHydrator.ts
|
|
1200
|
+
/**
|
|
1201
|
+
* Coordinates compiled relation hydration for `QuerySet` evaluation.
|
|
1202
|
+
*/
|
|
1203
|
+
var QueryHydrator = class QueryHydrator {
|
|
1204
|
+
executor;
|
|
1205
|
+
static BRAND = "tango.orm.query_hydrator";
|
|
1206
|
+
__tangoBrand = QueryHydrator.BRAND;
|
|
1207
|
+
normalizer;
|
|
1208
|
+
constructor(executor, normalizer) {
|
|
1209
|
+
this.executor = executor;
|
|
1210
|
+
this.normalizer = normalizer ?? createQueryRowNormalizerStrategy(executor.adapter.dialect);
|
|
1211
|
+
}
|
|
1212
|
+
static isQueryHydrator(value) {
|
|
1213
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === QueryHydrator.BRAND;
|
|
1214
|
+
}
|
|
1215
|
+
async materializeRows(rows, compiled) {
|
|
1216
|
+
const hydratedRows = await this.hydrateRows(rows, compiled);
|
|
1217
|
+
this.attachRootRecordAccessors(hydratedRows);
|
|
1218
|
+
return hydratedRows;
|
|
1219
|
+
}
|
|
1220
|
+
async hydrateRows(rows, compiled) {
|
|
1221
|
+
if (!compiled.hydrationPlan) return rows;
|
|
1222
|
+
const hydratedRows = rows.map((row) => ({ ...row }));
|
|
1223
|
+
this.attachRootRecordAccessors(hydratedRows);
|
|
1224
|
+
const registry = new HydrationEntityRegistry(this.executor.attachPersistedRecordAccessors);
|
|
1225
|
+
const queuedJoinPrefetchOwners = /* @__PURE__ */ new Map();
|
|
1226
|
+
const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
|
|
1227
|
+
for (const row of hydratedRows) this.hydrateJoinNodesForOwner(row, row, compiled.hydrationPlan.joinNodes, registry, queuedJoinPrefetchOwners);
|
|
1228
|
+
for (const node of compiled.hydrationPlan.prefetchNodes) await this.hydratePrefetchNode(node, hydratedRows, registry, compiler);
|
|
1229
|
+
for (const [node, owners] of queuedJoinPrefetchOwners.entries()) await this.hydratePrefetchNode(node, [...owners], registry, compiler);
|
|
1230
|
+
for (const row of hydratedRows) for (const alias of compiled.hydrationPlan.hiddenRootAliases) delete row[alias];
|
|
1231
|
+
return hydratedRows;
|
|
1232
|
+
}
|
|
1233
|
+
attachRootRecordAccessors(rows) {
|
|
1234
|
+
if (!this.executor.attachPersistedRecordAccessors) return;
|
|
1235
|
+
const sourceModelKey = this.executor.meta.modelKey;
|
|
1236
|
+
for (const row of rows) this.executor.attachPersistedRecordAccessors(row, sourceModelKey);
|
|
1237
|
+
}
|
|
1238
|
+
hydrateJoinNodesForOwner(owner, rawRow, nodes, registry, queuedJoinPrefetchOwners) {
|
|
1239
|
+
for (const node of nodes) {
|
|
1240
|
+
if (!node.join) continue;
|
|
1241
|
+
const target = {};
|
|
1242
|
+
let hasTargetValue = false;
|
|
1243
|
+
for (const [column, alias] of Object.entries(node.join.columns)) {
|
|
1244
|
+
const value = rawRow[alias];
|
|
1245
|
+
delete rawRow[alias];
|
|
1246
|
+
target[column] = this.normalizer.normalizeColumnValue(node.targetColumns[column], value);
|
|
1247
|
+
if (value !== null && value !== void 0) hasTargetValue = true;
|
|
1248
|
+
}
|
|
1249
|
+
if (!hasTargetValue) {
|
|
1250
|
+
owner[node.relationName] = null;
|
|
1251
|
+
continue;
|
|
1252
|
+
}
|
|
1253
|
+
const canonical = registry.canonicalize(node, target);
|
|
1254
|
+
owner[node.relationName] = canonical;
|
|
1255
|
+
for (const childNode of node.prefetchChildren) {
|
|
1256
|
+
const queuedOwners = queuedJoinPrefetchOwners?.get(childNode);
|
|
1257
|
+
if (queuedOwners) {
|
|
1258
|
+
queuedOwners.add(canonical);
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
queuedJoinPrefetchOwners?.set(childNode, new Set([canonical]));
|
|
1262
|
+
}
|
|
1263
|
+
this.hydrateJoinNodesForOwner(canonical, rawRow, node.joinChildren, registry, queuedJoinPrefetchOwners);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
async hydratePrefetchNode(node, owners, registry, compiler) {
|
|
1267
|
+
if (owners.length === 0) return;
|
|
1268
|
+
const groupedOwners = this.groupOwnersByAccessor(owners, node.ownerSourceAccessor);
|
|
1269
|
+
const sourceValues = [...groupedOwners.keys()];
|
|
1270
|
+
if (!!!node.throughTable) for (const owner of owners) owner[node.relationName] = node.cardinality === InternalRelationHydrationCardinality.MANY ? [] : null;
|
|
1271
|
+
if (sourceValues.length === 0) return;
|
|
1272
|
+
const sourceChunks = this.chunkValues(sourceValues, 500);
|
|
1273
|
+
const compiledPrefetch = compiler.compilePrefetch(node, sourceChunks[0]);
|
|
1274
|
+
if (compiledPrefetch.kind === InternalPrefetchQueryKind.MANY_TO_MANY) {
|
|
1275
|
+
const idsByOwner = /* @__PURE__ */ new Map();
|
|
1276
|
+
const uniqueTargetIds = /* @__PURE__ */ new Set();
|
|
1277
|
+
for (const chunk of sourceChunks) {
|
|
1278
|
+
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1279
|
+
const throughResult = await this.executor.client.query(chunkCompiled.throughSql, chunkCompiled.throughParams);
|
|
1280
|
+
for (const row of throughResult.rows) {
|
|
1281
|
+
const ownerId = row[chunkCompiled.ownerAlias];
|
|
1282
|
+
const targetId = row[chunkCompiled.targetAlias];
|
|
1283
|
+
if (typeof ownerId !== "string" && typeof ownerId !== "number" || typeof targetId !== "string" && typeof targetId !== "number") continue;
|
|
1284
|
+
const bucket = idsByOwner.get(ownerId) ?? [];
|
|
1285
|
+
bucket.push(targetId);
|
|
1286
|
+
idsByOwner.set(ownerId, bucket);
|
|
1287
|
+
uniqueTargetIds.add(targetId);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
const targets = {};
|
|
1291
|
+
const targetIds = [...uniqueTargetIds.values()];
|
|
1292
|
+
if (targetIds.length > 0) for (const targetChunk of this.chunkValues(targetIds, 500)) {
|
|
1293
|
+
const targetQuery = compiler.compileManyToManyTargets(node, targetChunk);
|
|
1294
|
+
const targetResult = await this.executor.client.query(targetQuery.sql, targetQuery.params);
|
|
1295
|
+
for (const rawTargetRow of targetResult.rows) {
|
|
1296
|
+
const normalized = this.normalizer.normalizeTargetRow(rawTargetRow, compiledPrefetch.targetColumns);
|
|
1297
|
+
const canonical = registry.canonicalize(node, normalized);
|
|
1298
|
+
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, registry);
|
|
1299
|
+
const primaryKey = canonical[node.targetPrimaryKey];
|
|
1300
|
+
if (typeof primaryKey === "string" || typeof primaryKey === "number") targets[primaryKey] = canonical;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
const canonicalChildren = /* @__PURE__ */ new Map();
|
|
1304
|
+
const handledOwners = /* @__PURE__ */ new Set();
|
|
1305
|
+
for (const [ownerId, ids] of idsByOwner.entries()) {
|
|
1306
|
+
const bucket = ids.map((id) => targets[id]).filter((value) => !!value);
|
|
1307
|
+
for (const owner of groupedOwners.get(ownerId) ?? []) {
|
|
1308
|
+
this.primeManyToManyOwnerCache(owner, node.relationName, bucket);
|
|
1309
|
+
handledOwners.add(owner);
|
|
1310
|
+
}
|
|
1311
|
+
for (const child of bucket) canonicalChildren.set(child[node.targetPrimaryKey], child);
|
|
1312
|
+
}
|
|
1313
|
+
for (const owner of owners) if (!handledOwners.has(owner)) this.primeManyToManyOwnerCache(owner, node.relationName, []);
|
|
1314
|
+
const childOwners = [...canonicalChildren.values()];
|
|
1315
|
+
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, registry, compiler);
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
const canonicalChildren = /* @__PURE__ */ new Map();
|
|
1319
|
+
for (const chunk of sourceChunks) {
|
|
1320
|
+
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1321
|
+
const result = await this.executor.client.query(chunkCompiled.sql, chunkCompiled.params);
|
|
1322
|
+
for (const rawResultRow of result.rows) {
|
|
1323
|
+
const normalized = this.normalizer.normalizeTargetRow(rawResultRow, chunkCompiled.targetColumns);
|
|
1324
|
+
const canonical = registry.canonicalize(node, normalized);
|
|
1325
|
+
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, registry);
|
|
1326
|
+
const key = normalized[chunkCompiled.targetKey];
|
|
1327
|
+
if (typeof key !== "string" && typeof key !== "number") continue;
|
|
1328
|
+
for (const owner of groupedOwners.get(key) ?? []) if (node.cardinality === InternalRelationHydrationCardinality.MANY) owner[node.relationName].push(canonical);
|
|
1329
|
+
else if (owner[node.relationName] === null) owner[node.relationName] = canonical;
|
|
1330
|
+
const childPrimaryKey = canonical[node.targetPrimaryKey];
|
|
1331
|
+
if (typeof childPrimaryKey === "string" || typeof childPrimaryKey === "number") canonicalChildren.set(childPrimaryKey, canonical);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
const childOwners = [...canonicalChildren.values()];
|
|
1335
|
+
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, registry, compiler);
|
|
1336
|
+
}
|
|
1337
|
+
primeManyToManyOwnerCache(owner, relationName, bucket) {
|
|
1338
|
+
const existing = owner[relationName];
|
|
1339
|
+
if (existing && typeof existing.primePrefetchCache === "function") {
|
|
1340
|
+
existing.primePrefetchCache(bucket);
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
owner[relationName] = bucket.slice();
|
|
1344
|
+
}
|
|
1345
|
+
chunkValues(values, size) {
|
|
1346
|
+
if (values.length <= size) return [Array.from(values)];
|
|
1347
|
+
const chunks = [];
|
|
1348
|
+
for (let i = 0; i < values.length; i += size) chunks.push(values.slice(i, i + size));
|
|
1349
|
+
return chunks;
|
|
1350
|
+
}
|
|
1351
|
+
groupOwnersByAccessor(owners, accessor) {
|
|
1352
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1353
|
+
for (const owner of owners) {
|
|
1354
|
+
const key = owner[accessor];
|
|
1355
|
+
if (typeof key !== "string" && typeof key !== "number") continue;
|
|
1356
|
+
const bucket = grouped.get(key) ?? [];
|
|
1357
|
+
bucket.push(owner);
|
|
1358
|
+
grouped.set(key, bucket);
|
|
1359
|
+
}
|
|
1360
|
+
return grouped;
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
//#endregion
|
|
1107
1364
|
//#region src/query/QuerySet.ts
|
|
1108
1365
|
/**
|
|
1109
1366
|
* Django-inspired query builder for constructing and executing database queries.
|
|
@@ -1132,9 +1389,13 @@ var QuerySet = class QuerySet {
|
|
|
1132
1389
|
static BRAND = "tango.orm.query_set";
|
|
1133
1390
|
__tangoBrand = QuerySet.BRAND;
|
|
1134
1391
|
evaluationCache;
|
|
1392
|
+
hydrator;
|
|
1393
|
+
rowNormalizer;
|
|
1135
1394
|
constructor(executor, state = {}) {
|
|
1136
1395
|
this.executor = executor;
|
|
1137
1396
|
this.state = state;
|
|
1397
|
+
this.rowNormalizer = createQueryRowNormalizerStrategy(executor.adapter.dialect);
|
|
1398
|
+
this.hydrator = new QueryHydrator(executor, this.rowNormalizer);
|
|
1138
1399
|
}
|
|
1139
1400
|
/**
|
|
1140
1401
|
* Narrow an unknown value to `QuerySet`.
|
|
@@ -1275,7 +1536,7 @@ var QuerySet = class QuerySet {
|
|
|
1275
1536
|
async fetch(shape) {
|
|
1276
1537
|
const baseResult = await this.getOrCreateEvaluationCache();
|
|
1277
1538
|
if (!shape) return baseResult;
|
|
1278
|
-
return new QueryResult(typeof shape === "function" ? baseResult.items.map(shape) : this.normalizeHydratedRowsForParserShape(baseResult.items).map((row) => shape.parse(row)));
|
|
1539
|
+
return new QueryResult(typeof shape === "function" ? baseResult.items.map(shape) : this.rowNormalizer.normalizeHydratedRowsForParserShape(baseResult.items, this.executor.meta.columns).map((row) => shape.parse(row)));
|
|
1279
1540
|
}
|
|
1280
1541
|
/**
|
|
1281
1542
|
* Async iterable surface for `for await (... of queryset)`.
|
|
@@ -1339,7 +1600,7 @@ var QuerySet = class QuerySet {
|
|
|
1339
1600
|
shapeFetchedRow(row, shape) {
|
|
1340
1601
|
if (!shape) return row;
|
|
1341
1602
|
if (typeof shape === "function") return shape(row);
|
|
1342
|
-
const normalizedRow = this.normalizeHydratedRowsForParserShape([row])[0]
|
|
1603
|
+
const normalizedRow = this.rowNormalizer.normalizeHydratedRowsForParserShape([row], this.executor.meta.columns)[0];
|
|
1343
1604
|
return shape.parse(normalizedRow);
|
|
1344
1605
|
}
|
|
1345
1606
|
getOrCreateEvaluationCache() {
|
|
@@ -1352,213 +1613,8 @@ var QuerySet = class QuerySet {
|
|
|
1352
1613
|
async evaluateRows() {
|
|
1353
1614
|
const compiled = new QueryCompiler(this.executor.meta, this.executor.adapter).compile(this.state);
|
|
1354
1615
|
const rows = await this.executor.run(compiled);
|
|
1355
|
-
const normalizedRows = this.
|
|
1356
|
-
|
|
1357
|
-
this.attachRootRecordAccessors(hydratedRows);
|
|
1358
|
-
return new QueryResult(hydratedRows);
|
|
1359
|
-
}
|
|
1360
|
-
normalizeRowsForSchemaParsing(rows) {
|
|
1361
|
-
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return [...rows];
|
|
1362
|
-
const booleanColumns = Object.entries(this.executor.meta.columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
|
|
1363
|
-
if (booleanColumns.length === 0) return [...rows];
|
|
1364
|
-
return rows.map((row) => this.normalizeBooleanColumns(row, booleanColumns));
|
|
1365
|
-
}
|
|
1366
|
-
normalizeHydratedRowsForParserShape(rows) {
|
|
1367
|
-
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return [...rows];
|
|
1368
|
-
const booleanColumns = Object.entries(this.executor.meta.columns).filter(([, value]) => this.isBooleanColumnType(value)).map(([column]) => column);
|
|
1369
|
-
if (booleanColumns.length === 0) return [...rows];
|
|
1370
|
-
return rows.map((row) => this.normalizeBooleanColumns(row, booleanColumns));
|
|
1371
|
-
}
|
|
1372
|
-
async hydrateRows(rows, compiled) {
|
|
1373
|
-
if (!compiled.hydrationPlan) return rows;
|
|
1374
|
-
const hydratedRows = rows.map((row) => ({ ...row }));
|
|
1375
|
-
this.attachRootRecordAccessors(hydratedRows);
|
|
1376
|
-
const canonicalEntities = /* @__PURE__ */ new Map();
|
|
1377
|
-
const queuedJoinPrefetchOwners = /* @__PURE__ */ new Map();
|
|
1378
|
-
const compiler = new QueryCompiler(this.executor.meta, this.executor.adapter);
|
|
1379
|
-
for (const row of hydratedRows) this.hydrateJoinNodesForOwner(row, row, compiled.hydrationPlan.joinNodes, canonicalEntities, queuedJoinPrefetchOwners);
|
|
1380
|
-
for (const node of compiled.hydrationPlan.prefetchNodes) await this.hydratePrefetchNode(node, hydratedRows, canonicalEntities, compiler);
|
|
1381
|
-
for (const [node, owners] of queuedJoinPrefetchOwners.entries()) await this.hydratePrefetchNode(node, [...owners], canonicalEntities, compiler);
|
|
1382
|
-
for (const row of hydratedRows) for (const alias of compiled.hydrationPlan.hiddenRootAliases) delete row[alias];
|
|
1383
|
-
return hydratedRows;
|
|
1384
|
-
}
|
|
1385
|
-
primeManyToManyOwnerCache(owner, relationName, bucket) {
|
|
1386
|
-
const existing = owner[relationName];
|
|
1387
|
-
if (existing && typeof existing.primePrefetchCache === "function") {
|
|
1388
|
-
existing.primePrefetchCache(bucket);
|
|
1389
|
-
return;
|
|
1390
|
-
}
|
|
1391
|
-
owner[relationName] = bucket.slice();
|
|
1392
|
-
}
|
|
1393
|
-
attachRootRecordAccessors(rows) {
|
|
1394
|
-
if (!this.executor.attachPersistedRecordAccessors) return;
|
|
1395
|
-
const sourceModelKey = this.executor.meta.modelKey;
|
|
1396
|
-
for (const row of rows) this.executor.attachPersistedRecordAccessors(row, sourceModelKey);
|
|
1397
|
-
}
|
|
1398
|
-
hydrateJoinNodesForOwner(owner, rawRow, nodes, canonicalEntities, queuedJoinPrefetchOwners) {
|
|
1399
|
-
for (const node of nodes) {
|
|
1400
|
-
if (!node.join) continue;
|
|
1401
|
-
const target = {};
|
|
1402
|
-
let hasTargetValue = false;
|
|
1403
|
-
for (const [column, alias] of Object.entries(node.join.columns)) {
|
|
1404
|
-
const value = rawRow[alias];
|
|
1405
|
-
delete rawRow[alias];
|
|
1406
|
-
target[column] = this.normalizeColumnValue(node.targetColumns[column], value);
|
|
1407
|
-
if (value !== null && value !== void 0) hasTargetValue = true;
|
|
1408
|
-
}
|
|
1409
|
-
if (!hasTargetValue) {
|
|
1410
|
-
owner[node.relationName] = null;
|
|
1411
|
-
continue;
|
|
1412
|
-
}
|
|
1413
|
-
const canonical = this.canonicalizeEntity(node, target, canonicalEntities);
|
|
1414
|
-
owner[node.relationName] = canonical;
|
|
1415
|
-
for (const childNode of node.prefetchChildren) {
|
|
1416
|
-
const queuedOwners = queuedJoinPrefetchOwners?.get(childNode);
|
|
1417
|
-
if (queuedOwners) {
|
|
1418
|
-
queuedOwners.add(canonical);
|
|
1419
|
-
continue;
|
|
1420
|
-
}
|
|
1421
|
-
queuedJoinPrefetchOwners?.set(childNode, new Set([canonical]));
|
|
1422
|
-
}
|
|
1423
|
-
this.hydrateJoinNodesForOwner(canonical, rawRow, node.joinChildren, canonicalEntities, queuedJoinPrefetchOwners);
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
async hydratePrefetchNode(node, owners, canonicalEntities, compiler) {
|
|
1427
|
-
if (owners.length === 0) return;
|
|
1428
|
-
const groupedOwners = this.groupOwnersByAccessor(owners, node.ownerSourceAccessor);
|
|
1429
|
-
const sourceValues = [...groupedOwners.keys()];
|
|
1430
|
-
if (!!!node.throughTable) for (const owner of owners) owner[node.relationName] = node.cardinality === InternalRelationHydrationCardinality.MANY ? [] : null;
|
|
1431
|
-
if (sourceValues.length === 0) return;
|
|
1432
|
-
const sourceChunks = this.chunkValues(sourceValues, 500);
|
|
1433
|
-
const compiledPrefetch = compiler.compilePrefetch(node, sourceChunks[0]);
|
|
1434
|
-
if (compiledPrefetch.kind === InternalPrefetchQueryKind.MANY_TO_MANY) {
|
|
1435
|
-
const idsByOwner = /* @__PURE__ */ new Map();
|
|
1436
|
-
const uniqueTargetIds = /* @__PURE__ */ new Set();
|
|
1437
|
-
for (const chunk of sourceChunks) {
|
|
1438
|
-
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1439
|
-
const throughResult = await this.executor.client.query(chunkCompiled.throughSql, chunkCompiled.throughParams);
|
|
1440
|
-
for (const row of throughResult.rows) {
|
|
1441
|
-
const ownerId = row[chunkCompiled.ownerAlias];
|
|
1442
|
-
const targetId = row[chunkCompiled.targetAlias];
|
|
1443
|
-
if (typeof ownerId !== "string" && typeof ownerId !== "number" || typeof targetId !== "string" && typeof targetId !== "number") continue;
|
|
1444
|
-
const bucket = idsByOwner.get(ownerId) ?? [];
|
|
1445
|
-
bucket.push(targetId);
|
|
1446
|
-
idsByOwner.set(ownerId, bucket);
|
|
1447
|
-
uniqueTargetIds.add(targetId);
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
const targets = {};
|
|
1451
|
-
const targetIds = [...uniqueTargetIds.values()];
|
|
1452
|
-
if (targetIds.length > 0) for (const targetChunk of this.chunkValues(targetIds, 500)) {
|
|
1453
|
-
const targetQuery = compiler.compileManyToManyTargets(node, targetChunk);
|
|
1454
|
-
const targetResult = await this.executor.client.query(targetQuery.sql, targetQuery.params);
|
|
1455
|
-
for (const rawTargetRow of targetResult.rows) {
|
|
1456
|
-
const normalized = this.normalizeTargetRow({ targetColumns: compiledPrefetch.targetColumns }, rawTargetRow);
|
|
1457
|
-
const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
|
|
1458
|
-
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
|
|
1459
|
-
const primaryKey = canonical[node.targetPrimaryKey];
|
|
1460
|
-
if (typeof primaryKey === "string" || typeof primaryKey === "number") targets[primaryKey] = canonical;
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
const canonicalChildren = /* @__PURE__ */ new Map();
|
|
1464
|
-
const handledOwners = /* @__PURE__ */ new Set();
|
|
1465
|
-
for (const [ownerId, ids] of idsByOwner.entries()) {
|
|
1466
|
-
const bucket = ids.map((id) => targets[id]).filter((value) => !!value);
|
|
1467
|
-
for (const owner of groupedOwners.get(ownerId) ?? []) {
|
|
1468
|
-
this.primeManyToManyOwnerCache(owner, node.relationName, bucket);
|
|
1469
|
-
handledOwners.add(owner);
|
|
1470
|
-
}
|
|
1471
|
-
for (const child of bucket) canonicalChildren.set(child[node.targetPrimaryKey], child);
|
|
1472
|
-
}
|
|
1473
|
-
for (const owner of owners) if (!handledOwners.has(owner)) this.primeManyToManyOwnerCache(owner, node.relationName, []);
|
|
1474
|
-
const childOwners = [...canonicalChildren.values()];
|
|
1475
|
-
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, canonicalEntities, compiler);
|
|
1476
|
-
return;
|
|
1477
|
-
}
|
|
1478
|
-
const canonicalChildren = /* @__PURE__ */ new Map();
|
|
1479
|
-
for (const chunk of sourceChunks) {
|
|
1480
|
-
const chunkCompiled = compiler.compilePrefetch(node, chunk);
|
|
1481
|
-
const result = await this.executor.client.query(chunkCompiled.sql, chunkCompiled.params);
|
|
1482
|
-
for (const rawResultRow of result.rows) {
|
|
1483
|
-
const normalized = this.normalizeTargetRow(chunkCompiled, rawResultRow);
|
|
1484
|
-
const canonical = this.canonicalizeEntity(node, normalized, canonicalEntities);
|
|
1485
|
-
this.hydrateJoinNodesForOwner(canonical, normalized, node.joinChildren, canonicalEntities);
|
|
1486
|
-
const key = normalized[chunkCompiled.targetKey];
|
|
1487
|
-
if (typeof key !== "string" && typeof key !== "number") continue;
|
|
1488
|
-
for (const owner of groupedOwners.get(key) ?? []) if (node.cardinality === InternalRelationHydrationCardinality.MANY) owner[node.relationName].push(canonical);
|
|
1489
|
-
else if (owner[node.relationName] === null) owner[node.relationName] = canonical;
|
|
1490
|
-
const childPrimaryKey = canonical[node.targetPrimaryKey];
|
|
1491
|
-
if (typeof childPrimaryKey === "string" || typeof childPrimaryKey === "number") canonicalChildren.set(childPrimaryKey, canonical);
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
const childOwners = [...canonicalChildren.values()];
|
|
1495
|
-
for (const childNode of node.prefetchChildren) await this.hydratePrefetchNode(childNode, childOwners, canonicalEntities, compiler);
|
|
1496
|
-
}
|
|
1497
|
-
chunkValues(values, size) {
|
|
1498
|
-
if (values.length === 0) return [];
|
|
1499
|
-
if (values.length <= size) return [Array.from(values)];
|
|
1500
|
-
const chunks = [];
|
|
1501
|
-
for (let i = 0; i < values.length; i += size) chunks.push(values.slice(i, i + size));
|
|
1502
|
-
return chunks;
|
|
1503
|
-
}
|
|
1504
|
-
groupOwnersByAccessor(owners, accessor) {
|
|
1505
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
1506
|
-
for (const owner of owners) {
|
|
1507
|
-
const key = owner[accessor];
|
|
1508
|
-
if (typeof key !== "string" && typeof key !== "number") continue;
|
|
1509
|
-
const bucket = grouped.get(key) ?? [];
|
|
1510
|
-
bucket.push(owner);
|
|
1511
|
-
grouped.set(key, bucket);
|
|
1512
|
-
}
|
|
1513
|
-
return grouped;
|
|
1514
|
-
}
|
|
1515
|
-
canonicalizeEntity(node, row, canonicalEntities) {
|
|
1516
|
-
const primaryKeyValue = row[node.targetPrimaryKey];
|
|
1517
|
-
if (typeof primaryKeyValue !== "string" && typeof primaryKeyValue !== "number") return row;
|
|
1518
|
-
const byModel = canonicalEntities.get(node.targetModelKey) ?? /* @__PURE__ */ new Map();
|
|
1519
|
-
const existing = byModel.get(primaryKeyValue);
|
|
1520
|
-
if (existing) {
|
|
1521
|
-
Object.assign(existing, row);
|
|
1522
|
-
return existing;
|
|
1523
|
-
}
|
|
1524
|
-
byModel.set(primaryKeyValue, row);
|
|
1525
|
-
canonicalEntities.set(node.targetModelKey, byModel);
|
|
1526
|
-
this.executor.attachPersistedRecordAccessors?.(row, node.targetModelKey);
|
|
1527
|
-
return row;
|
|
1528
|
-
}
|
|
1529
|
-
normalizeTargetRow(prefetch, row) {
|
|
1530
|
-
if (this.executor.adapter.dialect !== InternalDialect.SQLITE) return row;
|
|
1531
|
-
let normalized = null;
|
|
1532
|
-
for (const [column, type] of Object.entries(prefetch.targetColumns)) {
|
|
1533
|
-
if (!this.isBooleanColumnType(type)) continue;
|
|
1534
|
-
const next = this.normalizeSqliteBoolean(row[column]);
|
|
1535
|
-
if (next === row[column]) continue;
|
|
1536
|
-
normalized ??= { ...row };
|
|
1537
|
-
normalized[column] = next;
|
|
1538
|
-
}
|
|
1539
|
-
return normalized ?? row;
|
|
1540
|
-
}
|
|
1541
|
-
normalizeColumnValue(columnType, value) {
|
|
1542
|
-
return this.executor.adapter.dialect === InternalDialect.SQLITE && this.isBooleanColumnType(columnType) ? this.normalizeSqliteBoolean(value) : value;
|
|
1543
|
-
}
|
|
1544
|
-
isBooleanColumnType(value) {
|
|
1545
|
-
return typeof value === "string" && ["bool", "boolean"].includes(value.trim().toLowerCase());
|
|
1546
|
-
}
|
|
1547
|
-
normalizeSqliteBoolean(value) {
|
|
1548
|
-
if (value === 0 || value === "0") return false;
|
|
1549
|
-
if (value === 1 || value === "1") return true;
|
|
1550
|
-
return value;
|
|
1551
|
-
}
|
|
1552
|
-
normalizeBooleanColumns(row, columns) {
|
|
1553
|
-
let normalized = null;
|
|
1554
|
-
for (const column of columns) {
|
|
1555
|
-
const current = row[column];
|
|
1556
|
-
const next = this.normalizeSqliteBoolean(current);
|
|
1557
|
-
if (next === current) continue;
|
|
1558
|
-
if (!normalized) normalized = { ...row };
|
|
1559
|
-
normalized[column] = next;
|
|
1560
|
-
}
|
|
1561
|
-
return normalized ?? row;
|
|
1616
|
+
const normalizedRows = this.rowNormalizer.normalizeRootRows(rows, this.executor.meta.columns);
|
|
1617
|
+
return new QueryResult(await this.hydrator.materializeRows(normalizedRows, compiled));
|
|
1562
1618
|
}
|
|
1563
1619
|
withoutHydrationState() {
|
|
1564
1620
|
const { selectRelated: _selectRelated, prefetchRelated: _prefetchRelated, ...rest } = this.state;
|
|
@@ -1596,4 +1652,4 @@ var query_exports = /* @__PURE__ */ __exportAll({
|
|
|
1596
1652
|
//#endregion
|
|
1597
1653
|
export { isQNodeLike as a, InternalRelationKind as c, QueryCompiler as d, OrmSqlSafetyAdapter as f, QBuilder as i, QueryResult as l, InternalQNodeType as m, ModelQuerySet as n, domain_exports as o, InternalSqlValidationPlanKind as p, QuerySet as r, TableMetaFactory as s, query_exports as t, compiler_exports as u };
|
|
1598
1654
|
|
|
1599
|
-
//# sourceMappingURL=query-
|
|
1655
|
+
//# sourceMappingURL=query-hvcqTb9O.js.map
|