@vantreeseba/drizzle-graphql 15.1.5 → 15.1.6
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/README.md +16 -4
- package/dist/index.cjs +211 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +211 -71
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1375,7 +1375,10 @@ can count or summarise related rows without fetching them:
|
|
|
1375
1375
|
All parents in a selection are aggregated with a single
|
|
1376
1376
|
`SELECT <fk>, … WHERE <fk> IN (…) GROUP BY <fk>` per request, so `postsAggregate` on a list of
|
|
1377
1377
|
users is one extra query, not one per user. Differently-aliased selections with different
|
|
1378
|
-
`where` arguments are batched separately.
|
|
1378
|
+
`where` arguments are batched separately. A batch whose parent keys would bind more than
|
|
1379
|
+
10,000 parameters (10,000 parents on a single-column key, 5,000 on a two-column one) is split
|
|
1380
|
+
into several such queries, which keeps it within SQLite's 32,766- and Postgres/MySQL's
|
|
1381
|
+
65,535-parameter limits; the batched relation fields split the same way.
|
|
1379
1382
|
|
|
1380
1383
|
## Distinct
|
|
1381
1384
|
|
|
@@ -1604,8 +1607,9 @@ mutation {
|
|
|
1604
1607
|
out the rest of the row. Listing an unsupplied column in `update` is an error rather than
|
|
1605
1608
|
a silent no-op
|
|
1606
1609
|
- That holds per row: a batch whose rows supply different columns is split into one
|
|
1607
|
-
statement per run of consecutive rows that write the same columns, run in one transaction
|
|
1608
|
-
|
|
1610
|
+
statement per run of consecutive rows that write the same columns, run in one transaction
|
|
1611
|
+
(on a synchronous SQLite driver such as better-sqlite3 too), so a row that left a column out
|
|
1612
|
+
keeps it. The rows come back in the order they were sent
|
|
1609
1613
|
- `target` must match one of the table's unique constraints exactly; anything else is
|
|
1610
1614
|
rejected with the list of valid targets, instead of the database's opaque "no unique or
|
|
1611
1615
|
exclusion constraint matching" error
|
|
@@ -2486,7 +2490,15 @@ Generated schemas resolve nested relations without N+1 query explosions:
|
|
|
2486
2490
|
`ROW_NUMBER() OVER (PARTITION BY …)` window function — still one query, not one per
|
|
2487
2491
|
parent. A relation joined on several columns (`from: [a, b], to: [x, y]`) is batched,
|
|
2488
2492
|
grouped and partitioned by all of them, matching the parents with
|
|
2489
|
-
`(a = … AND b = …) OR …` instead of `IN (…)
|
|
2493
|
+
`(a = … AND b = …) OR …` instead of `IN (…)`, nested as a balanced tree so a batch of
|
|
2494
|
+
thousands of parents stays within SQLite's expression-depth limit. A batch over 10,000 key
|
|
2495
|
+
parameters runs as several queries, each parent read whole by one of them. The parent's join values are bound as the
|
|
2496
|
+
column holds them, not in their GraphQL output form, so a relation joined on a timestamp,
|
|
2497
|
+
`date`, `bigint` or decimal column works on this path and in `<relation>Aggregate` too. A
|
|
2498
|
+
parent row this library remapped keeps its original values for that; a parent your own
|
|
2499
|
+
resolver built in GraphQL form (an ISO string for a timestamp, say) has them mapped back the
|
|
2500
|
+
way an input value would be, which cuts a `'string'`-mode microsecond timestamp to
|
|
2501
|
+
milliseconds — return the row as drizzle read it to keep the full precision.
|
|
2490
2502
|
|
|
2491
2503
|
```Typescript
|
|
2492
2504
|
// entities.fieldResolvers is keyed by table name, then relation name
|
package/dist/index.cjs
CHANGED
|
@@ -1259,6 +1259,39 @@ const remapToGraphQLCore = (key, value, tableName, column, relationMap) => {
|
|
|
1259
1259
|
}
|
|
1260
1260
|
return value;
|
|
1261
1261
|
};
|
|
1262
|
+
/**
|
|
1263
|
+
* Where a remapped row keeps the values its columns held before remapping, by property name —
|
|
1264
|
+
* only for the columns remapping changed. The output form is not always reversible: a
|
|
1265
|
+
* microsecond timestamp read in `'string'` mode leaves as an ISO string the `DateTime` input
|
|
1266
|
+
* side would cut to milliseconds. A relation field correlating on the row's join columns reads
|
|
1267
|
+
* them from here (see `parentJoinValues`). Non-enumerable, so the row's own keys, JSON form and
|
|
1268
|
+
* equality are unchanged.
|
|
1269
|
+
*/
|
|
1270
|
+
const RAW_COLUMN_VALUES = Symbol("drizzle-graphql.rawColumnValues");
|
|
1271
|
+
/**
|
|
1272
|
+
* The value column `key` held in `row` before remapping: `{ found: true, value }` when remapping
|
|
1273
|
+
* changed it, `{ found: false }` when it did not (or the row was never remapped), in which case
|
|
1274
|
+
* `row[key]` is still the column's own value.
|
|
1275
|
+
*/
|
|
1276
|
+
const rawColumnValue = (row, key) => {
|
|
1277
|
+
const raw = row[RAW_COLUMN_VALUES];
|
|
1278
|
+
return raw && key in raw ? {
|
|
1279
|
+
found: true,
|
|
1280
|
+
value: raw[key]
|
|
1281
|
+
} : { found: false };
|
|
1282
|
+
};
|
|
1283
|
+
const keepRawColumnValue = (row, key, value) => {
|
|
1284
|
+
let raw = row[RAW_COLUMN_VALUES];
|
|
1285
|
+
if (!raw) {
|
|
1286
|
+
raw = Object.create(null);
|
|
1287
|
+
Object.defineProperty(row, RAW_COLUMN_VALUES, {
|
|
1288
|
+
value: raw,
|
|
1289
|
+
enumerable: false,
|
|
1290
|
+
configurable: true
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
if (!(key in raw)) raw[key] = value;
|
|
1294
|
+
};
|
|
1262
1295
|
const remapToGraphQLSingleOutput = (queryOutput, tableName, table, relationMap) => {
|
|
1263
1296
|
const columns = (0, drizzle_orm.getColumns)(table);
|
|
1264
1297
|
for (const [key, value] of Object.entries(queryOutput)) {
|
|
@@ -1276,7 +1309,9 @@ const remapToGraphQLSingleOutput = (queryOutput, tableName, table, relationMap)
|
|
|
1276
1309
|
delete queryOutput[key];
|
|
1277
1310
|
continue;
|
|
1278
1311
|
}
|
|
1279
|
-
|
|
1312
|
+
const remapped = remapToGraphQLCore(key, value, tableName, column, relationMap);
|
|
1313
|
+
if (column && remapped !== value) keepRawColumnValue(queryOutput, key, value);
|
|
1314
|
+
queryOutput[key] = remapped;
|
|
1280
1315
|
}
|
|
1281
1316
|
return queryOutput;
|
|
1282
1317
|
};
|
|
@@ -1855,17 +1890,104 @@ const extractRelationJoinColumnPairs = (relEntry, parentTable, targetTable) => {
|
|
|
1855
1890
|
}
|
|
1856
1891
|
return pairs;
|
|
1857
1892
|
};
|
|
1893
|
+
/** A decimal written as text, in a form `canonicalDecimal` can normalise. */
|
|
1894
|
+
const DECIMAL_TEXT = /^([+-]?)(\d*)(?:\.(\d*))?$/;
|
|
1895
|
+
/**
|
|
1896
|
+
* Decimal text without the zeros that do not change its value: `1.500` and `01.5` are both
|
|
1897
|
+
* `1.5`, as SQL compares them. Anything else (an exponent, `NaN`) is returned unchanged.
|
|
1898
|
+
*/
|
|
1899
|
+
const canonicalDecimal = (value) => {
|
|
1900
|
+
const match = DECIMAL_TEXT.exec(value);
|
|
1901
|
+
if (!match || !match[2] && !match[3]) return value;
|
|
1902
|
+
const whole = match[2].replace(/^0+(?=\d)/, "") || "0";
|
|
1903
|
+
const fraction = (match[3] ?? "").replace(/0+$/, "");
|
|
1904
|
+
const digits = fraction ? `${whole}.${fraction}` : whole;
|
|
1905
|
+
return match[1] === "-" && digits !== "0" ? `-${digits}` : digits;
|
|
1906
|
+
};
|
|
1907
|
+
/** An ISO instant without trailing zeros in its fraction: `…:00.100Z` and `…:00.1Z` agree. */
|
|
1908
|
+
const trimIsoFraction = (iso) => iso.replace(/(\.\d*?)0+Z$/, "$1Z").replace(/\.Z$/, "Z");
|
|
1909
|
+
/**
|
|
1910
|
+
* One join value in a form both sides of a relation produce for the same SQL value. The parent's
|
|
1911
|
+
* key and a related row's can arrive in different JS forms — a `Date` and the text of the same
|
|
1912
|
+
* instant, `1.50` and `1.500` from decimals of different scale — and `String()` would tell them
|
|
1913
|
+
* apart (it even drops a `Date`'s milliseconds).
|
|
1914
|
+
*/
|
|
1915
|
+
const joinValueKey = (value, column) => {
|
|
1916
|
+
if (value instanceof Date) {
|
|
1917
|
+
if (Number.isNaN(value.getTime())) return String(value);
|
|
1918
|
+
if (column && isDateOnlyColumn(column)) return dateToCalendarDate(value) ?? value.toISOString();
|
|
1919
|
+
return trimIsoFraction(value.toISOString());
|
|
1920
|
+
}
|
|
1921
|
+
if (typeof value === "bigint") return value.toString();
|
|
1922
|
+
if (value instanceof Uint8Array) return Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString("hex");
|
|
1923
|
+
if (typeof value === "string" && column) {
|
|
1924
|
+
if (isMySqlDateTimeStringColumn(column)) return trimIsoFraction(mySqlDateTimeTextToIso(value));
|
|
1925
|
+
if (isPgTimestampStringColumn(column)) return trimIsoFraction(pgTimestampTextToIso(value));
|
|
1926
|
+
if ((column.dataType ?? "") === "string numeric") return canonicalDecimal(value);
|
|
1927
|
+
}
|
|
1928
|
+
return String(value);
|
|
1929
|
+
};
|
|
1858
1930
|
/**
|
|
1859
1931
|
* A string identifying one combination of join-column values, for grouping a batch's rows by
|
|
1860
|
-
* parent.
|
|
1932
|
+
* parent. `columns` are the columns the values were read from, in the same order: the parent's
|
|
1933
|
+
* join columns for a parent key, the target's (or the junction's) for a related row. Values the
|
|
1934
|
+
* database compares equal give the same key — see {@link joinValueKey}.
|
|
1935
|
+
*/
|
|
1936
|
+
const joinKeyOf = (values, columns) => values.length === 1 ? joinValueKey(values[0], columns?.[0]) : JSON.stringify(values.map((value, i) => joinValueKey(value, columns?.[i])));
|
|
1937
|
+
/**
|
|
1938
|
+
* The parent row's values for the relation's join columns, as the database holds them. The row
|
|
1939
|
+
* a relation field resolves under has been remapped to its GraphQL form — a `Date` is an ISO
|
|
1940
|
+
* string by then, a `bigint` a decimal string — which can neither be bound back through every
|
|
1941
|
+
* column's encoder (SQLite's integer timestamp calls `getTime()` on it) nor be compared with the
|
|
1942
|
+
* related rows' decoded values. The values from before remapping are used when the row kept
|
|
1943
|
+
* them; otherwise the output form is mapped back the way an input value would be.
|
|
1944
|
+
*/
|
|
1945
|
+
const parentJoinValues = (parent, pairs) => pairs.map(({ localColPropName, localCol }) => {
|
|
1946
|
+
const raw = rawColumnValue(parent, localColPropName);
|
|
1947
|
+
if (raw.found) return raw.value;
|
|
1948
|
+
const value = parent[localColPropName];
|
|
1949
|
+
if (value == null) return value;
|
|
1950
|
+
try {
|
|
1951
|
+
return remapFromGraphQLCore(value, localCol, localColPropName);
|
|
1952
|
+
} catch {
|
|
1953
|
+
return value;
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
/**
|
|
1957
|
+
* At most this many bound parameters for the parent keys of one batched relation query. SQLite
|
|
1958
|
+
* accepts 32,766 per statement, Postgres and MySQL 65,535; the rest is left to the request's own
|
|
1959
|
+
* filters. A batch with more parents runs as several queries.
|
|
1960
|
+
*/
|
|
1961
|
+
const MAX_PARENT_KEY_PARAMS = 1e4;
|
|
1962
|
+
/**
|
|
1963
|
+
* The parent keys of one batch split into the groups each query reads, so that no query binds
|
|
1964
|
+
* more than {@link MAX_PARENT_KEY_PARAMS} key values. Every parent lands in exactly one group, so
|
|
1965
|
+
* a per-parent result — its rows, its window, its count — is the same whichever query made it.
|
|
1861
1966
|
*/
|
|
1862
|
-
const
|
|
1967
|
+
const parentKeyChunks = (keys, width) => {
|
|
1968
|
+
const size = Math.max(1, Math.floor(MAX_PARENT_KEY_PARAMS / Math.max(1, width)));
|
|
1969
|
+
const chunks = [];
|
|
1970
|
+
for (let i = 0; i < keys.length; i += size) chunks.push(keys.slice(i, i + size));
|
|
1971
|
+
return chunks;
|
|
1972
|
+
};
|
|
1973
|
+
/**
|
|
1974
|
+
* `or` over `conditions` as a balanced tree. A flat `a OR b OR c …` is parsed left-deep, and
|
|
1975
|
+
* SQLite refuses an expression more than 1,000 levels deep; balanced, a batch of thousands of
|
|
1976
|
+
* parents stays a few levels deep.
|
|
1977
|
+
*/
|
|
1978
|
+
const balancedOr = (conditions) => {
|
|
1979
|
+
if (conditions.length <= 2) return (0, drizzle_orm.or)(...conditions);
|
|
1980
|
+
const middle = Math.ceil(conditions.length / 2);
|
|
1981
|
+
return (0, drizzle_orm.or)(balancedOr(conditions.slice(0, middle)), balancedOr(conditions.slice(middle)));
|
|
1982
|
+
};
|
|
1863
1983
|
/**
|
|
1864
1984
|
* The rows whose `cols` hold one of `keys`: `col IN (…)` for a single join column, and an `OR`
|
|
1865
1985
|
* of per-parent equalities for several — `(a = 1 AND b = 1) OR (a = 1 AND b = 2)` — which
|
|
1866
|
-
* every dialect accepts, unlike a row-value `IN` list.
|
|
1986
|
+
* every dialect accepts, unlike a row-value `IN` list. The `OR` is nested as a balanced tree.
|
|
1987
|
+
* Callers split a large batch with {@link parentKeyChunks} first, which keeps either form within
|
|
1988
|
+
* the dialects' parameter limits.
|
|
1867
1989
|
*/
|
|
1868
|
-
const parentKeyCondition = (cols, keys) => cols.length === 1 ? (0, drizzle_orm.inArray)(cols[0], keys.map((key) => key[0])) : (
|
|
1990
|
+
const parentKeyCondition = (cols, keys) => cols.length === 1 ? (0, drizzle_orm.inArray)(cols[0], keys.map((key) => key[0])) : balancedOr(keys.map((key) => (0, drizzle_orm.and)(...cols.map((col, i) => (0, drizzle_orm.eq)(col, key[i])))));
|
|
1869
1991
|
/**
|
|
1870
1992
|
* The junction table and its two columns for a `.through()` (many-to-many) relation:
|
|
1871
1993
|
* `sourceCol` holds the parent's key, `targetCol` the target's. `undefined` for a relation
|
|
@@ -4961,7 +5083,7 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
|
|
|
4961
5083
|
if (!parentTable || !targetTable) return;
|
|
4962
5084
|
const joinPairs = extractRelationJoinColumnPairs(relEntry, parentTable, targetTable);
|
|
4963
5085
|
if (!joinPairs) return;
|
|
4964
|
-
const
|
|
5086
|
+
const localCols = joinPairs.map((pair) => pair.localCol);
|
|
4965
5087
|
const foreignCol = joinPairs[0].foreignCol;
|
|
4966
5088
|
const junction = extractJunctionColumns(relEntry);
|
|
4967
5089
|
const groupCols = junction ? [junction.sourceCol] : joinPairs.map((pair) => pair.foreignCol);
|
|
@@ -5007,7 +5129,7 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
|
|
|
5007
5129
|
};
|
|
5008
5130
|
const resolve = async (parent, args, context, info) => {
|
|
5009
5131
|
if (parent[relationName] !== void 0) return parent[relationName];
|
|
5010
|
-
const localValues =
|
|
5132
|
+
const localValues = parentJoinValues(parent, joinPairs);
|
|
5011
5133
|
if (localValues.some((value) => value == null)) return isOne ? null : [];
|
|
5012
5134
|
const { where: whereArg, limit: requestedLimit, offset, after, deleted } = args ?? {};
|
|
5013
5135
|
const { orderByArg, limit, distinct, cursorEntries, cursorValues, columns, loaderKey } = getOrCreateRequestValue(context, info?.fieldNodes?.[0], `relation:${tableName}::${relationName}`, () => {
|
|
@@ -5051,55 +5173,60 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
|
|
|
5051
5173
|
if (junction === null) throw drizzleError(`Relation '${tableName}.${relationName}' goes through a junction with a composite key, which can only be loaded together with its parent. Leave it to eager loading (eagerLoadRelations).`, { code: "DRIZZLE_UNSUPPORTED_RELATION" });
|
|
5052
5174
|
return getOrCreateLoader(context, loaderKey, async (parentKeys) => {
|
|
5053
5175
|
const executor = resolveExecutor(db, context);
|
|
5054
|
-
const uniqueKeys = [...new Map(parentKeys.map((key) => [joinKeyOf(key), key])).values()];
|
|
5176
|
+
const uniqueKeys = [...new Map(parentKeys.map((key) => [joinKeyOf(key, localCols), key])).values()];
|
|
5055
5177
|
const scope = resolveScope(policies, context, filterCtx);
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
const
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5178
|
+
const loadChunk = async (chunkKeys) => {
|
|
5179
|
+
let whereCondition = withScope(scope, targetTableName, targetTable, (0, drizzle_orm.and)(parentKeyCondition(groupCols, chunkKeys), ownCondition, whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName, scope)) : void 0, cursorValues ? buildCursorCondition(targetTable, cursorEntries, cursorValues, nullOrdering) : void 0), deleted ?? defaultDeleted);
|
|
5180
|
+
if (distinct) {
|
|
5181
|
+
const keys = await selectDistinctKeys({
|
|
5182
|
+
db: executor,
|
|
5183
|
+
table: targetTable,
|
|
5184
|
+
tableName: targetTableName,
|
|
5185
|
+
distinct,
|
|
5186
|
+
pkNames: targetPkNames,
|
|
5187
|
+
where: whereCondition,
|
|
5188
|
+
orderBy: orderByArg,
|
|
5189
|
+
partitionBy: groupCols,
|
|
5190
|
+
junction: junction ? {
|
|
5191
|
+
table: junction.table,
|
|
5192
|
+
on: (0, drizzle_orm.eq)(junction.targetCol, foreignCol),
|
|
5193
|
+
parentKey: junction.sourceCol
|
|
5194
|
+
} : void 0
|
|
5195
|
+
});
|
|
5196
|
+
if (!keys.length) return /* @__PURE__ */ new Map();
|
|
5197
|
+
whereCondition = (0, drizzle_orm.and)(whereCondition, junction ? (0, drizzle_orm.or)(...keys.map((key) => (0, drizzle_orm.and)((0, drizzle_orm.eq)(junction.sourceCol, key[DISTINCT_PARENT]), primaryKeyRestriction(targetTable, targetPkNames, [key])))) : primaryKeyRestriction(targetTable, targetPkNames, keys));
|
|
5198
|
+
}
|
|
5199
|
+
const selection = junction ? {
|
|
5200
|
+
...columns ?? (0, drizzle_orm.getColumns)(targetTable),
|
|
5201
|
+
[PARENT_KEY]: junction.sourceCol
|
|
5202
|
+
} : columns;
|
|
5203
|
+
let rows;
|
|
5204
|
+
if (limit != null || offset != null) rows = await batchedPaginatedRelationQuery(executor, targetTable, foreignCol, groupCols, whereCondition, orderByArg, limit ?? null, offset ?? null, targetPkNames, selection, relationFilterCtx(filterCtx, targetTableName, scope), whereArg, junction);
|
|
5205
|
+
else {
|
|
5206
|
+
let q = withJunction((selection ? executor.select(selection) : executor.select()).from(targetTable), junction, foreignCol).where(whereCondition);
|
|
5207
|
+
const orderExprs = cursorEntries ? cursorOrderExprs(targetTable, cursorEntries) : orderByArg ? extractOrderBy(targetTable, orderByArg, relationFilterCtx(filterCtx, targetTableName, scope), whereArg) : [];
|
|
5208
|
+
if (orderExprs.length) q = q.orderBy(...orderExprs);
|
|
5209
|
+
rows = await q;
|
|
5210
|
+
}
|
|
5211
|
+
if (cursorEntries) attachRowCursors(rows, cursorEntries);
|
|
5212
|
+
const keyOf = (row) => {
|
|
5213
|
+
const key = joinKeyOf(groupKeys.map((name) => row[name]), groupCols);
|
|
5214
|
+
if (junction) delete row[PARENT_KEY];
|
|
5215
|
+
return key;
|
|
5216
|
+
};
|
|
5217
|
+
if (isOne) {
|
|
5218
|
+
const byKey = new Map(rows.map((row) => [keyOf(row), row]));
|
|
5219
|
+
remapToGraphQLArrayOutput(rows, targetTableName, targetTable);
|
|
5220
|
+
return byKey;
|
|
5221
|
+
}
|
|
5222
|
+
const grouped = new Map(chunkKeys.map((key) => [joinKeyOf(key, localCols), []]));
|
|
5223
|
+
for (const row of rows) grouped.get(keyOf(row))?.push(row);
|
|
5096
5224
|
remapToGraphQLArrayOutput(rows, targetTableName, targetTable);
|
|
5097
|
-
return
|
|
5098
|
-
}
|
|
5099
|
-
const
|
|
5100
|
-
for (const
|
|
5101
|
-
|
|
5102
|
-
return parentKeys.map((key) => grouped.get(joinKeyOf(key)) ?? []);
|
|
5225
|
+
return grouped;
|
|
5226
|
+
};
|
|
5227
|
+
const results = /* @__PURE__ */ new Map();
|
|
5228
|
+
for (const chunk of parentKeyChunks(uniqueKeys, groupCols.length)) for (const [key, value] of await loadChunk(chunk)) results.set(key, value);
|
|
5229
|
+
return parentKeys.map((key) => results.get(joinKeyOf(key, localCols)) ?? (isOne ? null : []));
|
|
5103
5230
|
}).load(localValues);
|
|
5104
5231
|
};
|
|
5105
5232
|
return async (parent, args, context, info) => {
|
|
@@ -6185,7 +6312,7 @@ const createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, fi
|
|
|
6185
6312
|
}
|
|
6186
6313
|
return;
|
|
6187
6314
|
}
|
|
6188
|
-
const
|
|
6315
|
+
const localCols = joinPairs.map((pair) => pair.localCol);
|
|
6189
6316
|
const foreignCol = joinPairs[0].foreignCol;
|
|
6190
6317
|
const junction = extractJunctionColumns(relEntry);
|
|
6191
6318
|
if (junction === null) return;
|
|
@@ -6215,23 +6342,26 @@ const createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, fi
|
|
|
6215
6342
|
};
|
|
6216
6343
|
});
|
|
6217
6344
|
if (!Object.keys(request.selection).length) return {};
|
|
6218
|
-
const localValues =
|
|
6345
|
+
const localValues = parentJoinValues(parent, joinPairs);
|
|
6219
6346
|
if (localValues.some((value) => value == null)) return assembleAggregateRow({}, request, target);
|
|
6220
6347
|
const whereArg = args?.where;
|
|
6221
6348
|
const deleted = args?.deleted;
|
|
6222
6349
|
const loader = getOrCreateLoader(context, loaderKey, async (parentKeys) => {
|
|
6223
6350
|
const executor = resolveExecutor(db, context);
|
|
6224
|
-
const uniqueKeys = [...new Map(parentKeys.map((key) => [joinKeyOf(key), key])).values()];
|
|
6351
|
+
const uniqueKeys = [...new Map(parentKeys.map((key) => [joinKeyOf(key, localCols), key])).values()];
|
|
6225
6352
|
const scope = resolveScope(policies, context, filterCtx);
|
|
6226
|
-
const whereCondition = withScope(scope, targetTableName, targetTable, (0, drizzle_orm.and)(parentKeyCondition(groupCols, uniqueKeys), ownCondition, whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName, scope)) : void 0), deleted ?? defaultDeleted);
|
|
6227
6353
|
const groupSelection = Object.fromEntries(groupAliases.map((alias, i) => [alias, groupCols[i]]));
|
|
6228
|
-
const
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6354
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
6355
|
+
for (const chunk of parentKeyChunks(uniqueKeys, groupCols.length)) {
|
|
6356
|
+
const whereCondition = withScope(scope, targetTableName, targetTable, (0, drizzle_orm.and)(parentKeyCondition(groupCols, chunk), ownCondition, whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName, scope)) : void 0), deleted ?? defaultDeleted);
|
|
6357
|
+
const selected = executor.select({
|
|
6358
|
+
...groupSelection,
|
|
6359
|
+
...request.selection
|
|
6360
|
+
}).from(targetTable);
|
|
6361
|
+
const rows = await (junction ? selected.innerJoin(junction.table, (0, drizzle_orm.eq)(junction.targetCol, foreignCol)) : selected).where(whereCondition).groupBy(...groupCols);
|
|
6362
|
+
for (const row of rows) byKey.set(joinKeyOf(groupAliases.map((alias) => row[alias]), groupCols), row);
|
|
6363
|
+
}
|
|
6364
|
+
return parentKeys.map((key) => byKey.get(joinKeyOf(key, localCols)) ?? {});
|
|
6235
6365
|
});
|
|
6236
6366
|
return assembleAggregateRow(await loader.load(localValues), request, target);
|
|
6237
6367
|
} catch (e) {
|
|
@@ -8318,7 +8448,7 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
8318
8448
|
serverOwned: Object.keys(contextColumns ?? {}),
|
|
8319
8449
|
buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName, scope))
|
|
8320
8450
|
});
|
|
8321
|
-
const
|
|
8451
|
+
const batchQuery = (executor, { values: rows, plan }) => {
|
|
8322
8452
|
let query = executor.insert(table).values(rows).returning(returning);
|
|
8323
8453
|
const setWhere = withScope(scope, tableName, table, plan.setWhere);
|
|
8324
8454
|
query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? {
|
|
@@ -8330,12 +8460,22 @@ const buildWriteResolvers = (primaryKeyPropNames) => {
|
|
|
8330
8460
|
set: plan.set,
|
|
8331
8461
|
setWhere
|
|
8332
8462
|
});
|
|
8333
|
-
return
|
|
8463
|
+
return query;
|
|
8334
8464
|
};
|
|
8335
|
-
if (batches.length === 1) return
|
|
8336
|
-
return target.transaction(
|
|
8337
|
-
const
|
|
8338
|
-
|
|
8465
|
+
if (batches.length === 1) return await batchQuery(target, batches[0]);
|
|
8466
|
+
return target.transaction((tx) => {
|
|
8467
|
+
const execute = (batch) => {
|
|
8468
|
+
const query = batchQuery(tx, batch);
|
|
8469
|
+
return typeof query.all === "function" ? query.all() : query;
|
|
8470
|
+
};
|
|
8471
|
+
const first = execute(batches[0]);
|
|
8472
|
+
if (typeof first?.then === "function") return (async () => {
|
|
8473
|
+
const rows = [...await first];
|
|
8474
|
+
for (const batch of batches.slice(1)) rows.push(...await execute(batch));
|
|
8475
|
+
return rows;
|
|
8476
|
+
})();
|
|
8477
|
+
const rows = [...first];
|
|
8478
|
+
for (const batch of batches.slice(1)) rows.push(...execute(batch));
|
|
8339
8479
|
return rows;
|
|
8340
8480
|
});
|
|
8341
8481
|
};
|