@vantreeseba/drizzle-graphql 15.0.0 → 15.1.0
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 +100 -66
- package/dist/index.cjs +112 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -3
- package/dist/index.d.ts +19 -3
- package/dist/index.js +112 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -458,7 +458,9 @@ type DrizzleErrorCode =
|
|
|
458
458
|
/** A stored value could not be represented by the scalar that transports it. */
|
|
459
459
|
'DRIZZLE_UNREPRESENTABLE_VALUE' |
|
|
460
460
|
/** The request's shared mutation transaction was rolled back, so this field never ran. */
|
|
461
|
-
'DRIZZLE_TRANSACTION_ABORTED'
|
|
461
|
+
'DRIZZLE_TRANSACTION_ABORTED' |
|
|
462
|
+
/** A relation was read on a path it cannot be loaded through (see the message). */
|
|
463
|
+
'DRIZZLE_UNSUPPORTED_RELATION';
|
|
462
464
|
/**
|
|
463
465
|
* What an error was about, published as `extensions.drizzle` beside the code — the runtime
|
|
464
466
|
* half of the `extensions.drizzle` markers a generated field carries.
|
|
@@ -607,8 +609,11 @@ type ColumnDescriber = (column: Column, info: ColumnDocInfo) => string | undefin
|
|
|
607
609
|
* `BuildSchemaConfig.deprecateColumn`.
|
|
608
610
|
*/
|
|
609
611
|
type ColumnDeprecator = (column: Column, info: ColumnDocInfo) => string | undefined;
|
|
610
|
-
/**
|
|
611
|
-
|
|
612
|
+
/**
|
|
613
|
+
* Supplies the GraphQL `description` for a table's object type. Receives the Drizzle table
|
|
614
|
+
* itself as well as its schema key. See `BuildSchemaConfig.describeTable`.
|
|
615
|
+
*/
|
|
616
|
+
type TableDescriber = (tableName: string, table: Table) => string | undefined;
|
|
612
617
|
/**
|
|
613
618
|
* Supplies the GraphQL `description` for a relation field. See
|
|
614
619
|
* `BuildSchemaConfig.describeRelation`.
|
|
@@ -2444,6 +2449,17 @@ type BuildSchemaConfig = {
|
|
|
2444
2449
|
* ```ts
|
|
2445
2450
|
* buildSchema(db, { describeTable: (tableName) => tableDocs[tableName] });
|
|
2446
2451
|
* ```
|
|
2452
|
+
*
|
|
2453
|
+
* The second argument is the Drizzle table itself, so documentation attached to the table
|
|
2454
|
+
* where it is declared can be read straight off it:
|
|
2455
|
+
*
|
|
2456
|
+
* ```ts
|
|
2457
|
+
* const tableDocs = new WeakMap<object, string>();
|
|
2458
|
+
* export const Users = pgTable('users', { ... });
|
|
2459
|
+
* tableDocs.set(Users, 'A person who can sign in.');
|
|
2460
|
+
*
|
|
2461
|
+
* buildSchema(db, { describeTable: (_tableName, table) => tableDocs.get(table) });
|
|
2462
|
+
* ```
|
|
2447
2463
|
*/
|
|
2448
2464
|
describeTable?: TableDescriber;
|
|
2449
2465
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -458,7 +458,9 @@ type DrizzleErrorCode =
|
|
|
458
458
|
/** A stored value could not be represented by the scalar that transports it. */
|
|
459
459
|
'DRIZZLE_UNREPRESENTABLE_VALUE' |
|
|
460
460
|
/** The request's shared mutation transaction was rolled back, so this field never ran. */
|
|
461
|
-
'DRIZZLE_TRANSACTION_ABORTED'
|
|
461
|
+
'DRIZZLE_TRANSACTION_ABORTED' |
|
|
462
|
+
/** A relation was read on a path it cannot be loaded through (see the message). */
|
|
463
|
+
'DRIZZLE_UNSUPPORTED_RELATION';
|
|
462
464
|
/**
|
|
463
465
|
* What an error was about, published as `extensions.drizzle` beside the code — the runtime
|
|
464
466
|
* half of the `extensions.drizzle` markers a generated field carries.
|
|
@@ -607,8 +609,11 @@ type ColumnDescriber = (column: Column, info: ColumnDocInfo) => string | undefin
|
|
|
607
609
|
* `BuildSchemaConfig.deprecateColumn`.
|
|
608
610
|
*/
|
|
609
611
|
type ColumnDeprecator = (column: Column, info: ColumnDocInfo) => string | undefined;
|
|
610
|
-
/**
|
|
611
|
-
|
|
612
|
+
/**
|
|
613
|
+
* Supplies the GraphQL `description` for a table's object type. Receives the Drizzle table
|
|
614
|
+
* itself as well as its schema key. See `BuildSchemaConfig.describeTable`.
|
|
615
|
+
*/
|
|
616
|
+
type TableDescriber = (tableName: string, table: Table) => string | undefined;
|
|
612
617
|
/**
|
|
613
618
|
* Supplies the GraphQL `description` for a relation field. See
|
|
614
619
|
* `BuildSchemaConfig.describeRelation`.
|
|
@@ -2444,6 +2449,17 @@ type BuildSchemaConfig = {
|
|
|
2444
2449
|
* ```ts
|
|
2445
2450
|
* buildSchema(db, { describeTable: (tableName) => tableDocs[tableName] });
|
|
2446
2451
|
* ```
|
|
2452
|
+
*
|
|
2453
|
+
* The second argument is the Drizzle table itself, so documentation attached to the table
|
|
2454
|
+
* where it is declared can be read straight off it:
|
|
2455
|
+
*
|
|
2456
|
+
* ```ts
|
|
2457
|
+
* const tableDocs = new WeakMap<object, string>();
|
|
2458
|
+
* export const Users = pgTable('users', { ... });
|
|
2459
|
+
* tableDocs.set(Users, 'A person who can sign in.');
|
|
2460
|
+
*
|
|
2461
|
+
* buildSchema(db, { describeTable: (_tableName, table) => tableDocs.get(table) });
|
|
2462
|
+
* ```
|
|
2447
2463
|
*/
|
|
2448
2464
|
describeTable?: TableDescriber;
|
|
2449
2465
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1789,6 +1789,48 @@ const extractRelationJoinColumns = (relEntry, parentTable, targetTable) => {
|
|
|
1789
1789
|
};
|
|
1790
1790
|
};
|
|
1791
1791
|
/**
|
|
1792
|
+
* The junction table and its two columns for a `.through()` (many-to-many) relation:
|
|
1793
|
+
* `sourceCol` holds the parent's key, `targetCol` the target's. `undefined` for a relation
|
|
1794
|
+
* with no junction; `null` for one whose junction cannot be used here — more than one column
|
|
1795
|
+
* on either side, or columns that are not on the junction table.
|
|
1796
|
+
*/
|
|
1797
|
+
const extractJunctionColumns = (relEntry) => {
|
|
1798
|
+
const rel = relEntry.relation ?? relEntry;
|
|
1799
|
+
const junction = rel?.throughTable;
|
|
1800
|
+
if (!junction) return;
|
|
1801
|
+
const sourceEntries = rel.through?.source;
|
|
1802
|
+
const targetEntries = rel.through?.target;
|
|
1803
|
+
if (sourceEntries?.length !== 1 || targetEntries?.length !== 1) return null;
|
|
1804
|
+
const junctionCols = Object.values(getColumns(junction));
|
|
1805
|
+
const sourceCol = unwrapRelationColumn(sourceEntries[0]);
|
|
1806
|
+
const targetCol = unwrapRelationColumn(targetEntries[0]);
|
|
1807
|
+
if (!sourceCol || !targetCol || !junctionCols.includes(sourceCol) || !junctionCols.includes(targetCol)) return null;
|
|
1808
|
+
return {
|
|
1809
|
+
table: junction,
|
|
1810
|
+
sourceCol,
|
|
1811
|
+
targetCol
|
|
1812
|
+
};
|
|
1813
|
+
};
|
|
1814
|
+
/**
|
|
1815
|
+
* The relation's own declared `where` as a condition on a query that reads the target table,
|
|
1816
|
+
* or `undefined` when it has none. The eager path gets this from drizzle's `with:`; the batched
|
|
1817
|
+
* field resolver and `<relation>Aggregate` read the target table directly, so they have to add
|
|
1818
|
+
* it themselves or they return rows the relation hides.
|
|
1819
|
+
*
|
|
1820
|
+
* A reversed filter (inherited from the relation on the other side) is written against the
|
|
1821
|
+
* parent table, so it is applied as "the row's parent key belongs to a parent that passes it".
|
|
1822
|
+
* `parentKeyCol` is the parent's join column and `groupCol` the column in the query that holds
|
|
1823
|
+
* the parent key for each row — the target's foreign key, or the junction's source column.
|
|
1824
|
+
*/
|
|
1825
|
+
const relationOwnCondition = (relEntry, parentTable, targetTable, parentKeyCol, groupCol) => {
|
|
1826
|
+
const rel = relEntry.relation ?? relEntry;
|
|
1827
|
+
const ownWhere = relationOwnWhere(rel);
|
|
1828
|
+
if (!ownWhere) return;
|
|
1829
|
+
if (!isRelationFilterReversed(rel)) return relationsFilterToSQL(targetTable, ownWhere);
|
|
1830
|
+
const parentFilter = relationsFilterToSQL(parentTable, ownWhere);
|
|
1831
|
+
return sql`${groupCol} in (select ${parentKeyCol} from ${parentTable} where ${parentFilter})`;
|
|
1832
|
+
};
|
|
1833
|
+
/**
|
|
1792
1834
|
* Whether a to-one relation can back a relation orderBy hop. `.through()` (junction)
|
|
1793
1835
|
* relations are excluded — the ordering subquery only joins the direct target, never the
|
|
1794
1836
|
* junction table. (Relation *filters* do support `.through()` — this guard is orderBy-only.)
|
|
@@ -2952,6 +2994,11 @@ const getUniqueColumnSets = (table, getTableConfig) => {
|
|
|
2952
2994
|
//#endregion
|
|
2953
2995
|
//#region src/util/builders/common/distinct.ts
|
|
2954
2996
|
/**
|
|
2997
|
+
* Alias the parent key comes back under when the pass runs through a junction table. See
|
|
2998
|
+
* `selectDistinctKeys`' `junction` parameter.
|
|
2999
|
+
*/
|
|
3000
|
+
const DISTINCT_PARENT = "__dgql_distinct_parent";
|
|
3001
|
+
/**
|
|
2955
3002
|
* Keeps the first row of each distinct combination of the requested columns, following the
|
|
2956
3003
|
* query's own ordering, then applies `limit`/`offset` to what survives — and returns the
|
|
2957
3004
|
* surviving rows' primary key values in that order.
|
|
@@ -2964,9 +3011,14 @@ const getUniqueColumnSets = (table, getTableConfig) => {
|
|
|
2964
3011
|
* `partitionBy` prepends extra columns to the window's partition. A to-many relation field
|
|
2965
3012
|
* uses it to pass the foreign key, which makes the pass distinct *within each parent* rather
|
|
2966
3013
|
* than across the whole batch — the same meaning `distinct` has on a root list.
|
|
3014
|
+
*
|
|
3015
|
+
* `junction` is for a many-to-many relation: the target is joined to the junction `on` the
|
|
3016
|
+
* given condition, and each returned key also carries the parent it was kept for, under
|
|
3017
|
+
* {@link DISTINCT_PARENT}. A target row linked to several parents can be kept for one and
|
|
3018
|
+
* dropped for another, so a key alone cannot narrow such a batch.
|
|
2967
3019
|
*/
|
|
2968
3020
|
const selectDistinctKeys = async (params) => {
|
|
2969
|
-
const { db, table, tableName, distinct, pkNames, where, orderBy, limit, offset, partitionBy } = params;
|
|
3021
|
+
const { db, table, tableName, distinct, pkNames, where, orderBy, limit, offset, partitionBy, junction } = params;
|
|
2970
3022
|
const cols = getColumns(table);
|
|
2971
3023
|
if (!pkNames.length) throw drizzleError(`Table ${tableName} has no primary key, so 'distinct' cannot be applied to it.`, { code: "DRIZZLE_INVALID_DISTINCT" });
|
|
2972
3024
|
const requestedCols = distinct.map((name) => cols[name]).filter(Boolean);
|
|
@@ -2975,12 +3027,17 @@ const selectDistinctKeys = async (params) => {
|
|
|
2975
3027
|
const orderEntries = orderBy ? orderByEntries(orderBy) : [];
|
|
2976
3028
|
const windowOrder = [...orderEntries.flatMap(([column, direction, nulls]) => orderExpressions(cols[column], direction, nulls, cols[column])), ...primaryKeyOrderExprs(table, pkNames)];
|
|
2977
3029
|
const rowNumber = sql`row_number() over (partition by ${sql.join(partitionCols, sql`, `)} order by ${sql.join(windowOrder, sql`, `)})`.as(DISTINCT_RN);
|
|
2978
|
-
const
|
|
3030
|
+
const selected = db.select({
|
|
2979
3031
|
...cols,
|
|
3032
|
+
...junction ? { [DISTINCT_PARENT]: junction.parentKey } : {},
|
|
2980
3033
|
[DISTINCT_RN]: rowNumber
|
|
2981
|
-
}).from(table)
|
|
3034
|
+
}).from(table);
|
|
3035
|
+
const sub = (junction ? selected.innerJoin(junction.table, junction.on) : selected).where(where).as("__dgql_distinct");
|
|
2982
3036
|
const outerOrder = [...orderEntries.flatMap(([column, direction, nulls]) => orderExpressions(sub[column], direction, nulls, cols[column])), ...pkNames.filter((name) => sub[name]).map((name) => asc(sub[name]))];
|
|
2983
|
-
let query = db.select(
|
|
3037
|
+
let query = db.select({
|
|
3038
|
+
...Object.fromEntries(pkNames.map((name) => [name, sub[name]])),
|
|
3039
|
+
...junction ? { [DISTINCT_PARENT]: sub[DISTINCT_PARENT] } : {}
|
|
3040
|
+
}).from(sub).where(eq(sub[DISTINCT_RN], 1)).orderBy(...outerOrder);
|
|
2984
3041
|
if (offset) query = query.offset(offset);
|
|
2985
3042
|
if (limit != null) query = query.limit(limit);
|
|
2986
3043
|
return await query;
|
|
@@ -4601,16 +4658,17 @@ const identifyRows = (field, args) => {
|
|
|
4601
4658
|
* Window functions require PostgreSQL, MySQL >= 8.0, or SQLite >= 3.25.
|
|
4602
4659
|
* Returns raw rows (NOT remapped); the caller groups + remaps them.
|
|
4603
4660
|
*/
|
|
4604
|
-
const batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, columns, orderCtx, whereArgs) => {
|
|
4661
|
+
const batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, columns, orderCtx, whereArgs, junction) => {
|
|
4605
4662
|
const cols = columns ?? getColumns(targetTable);
|
|
4606
4663
|
const orderExprs = [...orderByArg ? extractOrderBy(targetTable, orderByArg, orderCtx, whereArgs) : [], ...primaryKeyOrderExprs(targetTable, pkNames)];
|
|
4607
4664
|
const orderClause = orderExprs.length ? sql` order by ${sql.join(orderExprs, sql`, `)}` : sql``;
|
|
4608
4665
|
const RN = "__drizzle_graphql_rn";
|
|
4609
|
-
const
|
|
4610
|
-
const
|
|
4666
|
+
const partitionCol = junction ? junction.sourceCol : foreignCol;
|
|
4667
|
+
const rowNumber = sql`row_number() over (partition by ${partitionCol}${orderClause})`.as(RN);
|
|
4668
|
+
const sub = withJunction(db.select({
|
|
4611
4669
|
...cols,
|
|
4612
4670
|
[RN]: rowNumber
|
|
4613
|
-
}).from(targetTable).where(whereCondition).as("__paginated");
|
|
4671
|
+
}).from(targetTable), junction, foreignCol).where(whereCondition).as("__paginated");
|
|
4614
4672
|
const lower = offset ?? 0;
|
|
4615
4673
|
const windowConds = [gt(sub[RN], lower)];
|
|
4616
4674
|
if (limit != null) windowConds.push(lte(sub[RN], lower + limit));
|
|
@@ -4619,6 +4677,14 @@ const batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereC
|
|
|
4619
4677
|
return rows;
|
|
4620
4678
|
};
|
|
4621
4679
|
/**
|
|
4680
|
+
* Alias the parent key is selected under when a relation goes through a junction table. The
|
|
4681
|
+
* key lives on the junction, not the target, so it has to be selected explicitly to group the
|
|
4682
|
+
* rows by parent — and a target row linked to several parents comes back once per parent.
|
|
4683
|
+
*/
|
|
4684
|
+
const PARENT_KEY = "__drizzle_graphql_parent";
|
|
4685
|
+
/** `from(target)`, joined to the junction table when the relation has one. */
|
|
4686
|
+
const withJunction = (query, junction, foreignCol) => junction ? query.innerJoin(junction.table, eq(junction.targetCol, foreignCol)) : query;
|
|
4687
|
+
/**
|
|
4622
4688
|
* Creates a RelationResolverFactory that generates field-level resolvers for each relation.
|
|
4623
4689
|
* Each resolver:
|
|
4624
4690
|
* 1. Returns pre-fetched data if the parent resolver already included it (eager path, zero cost).
|
|
@@ -4640,6 +4706,10 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
|
|
|
4640
4706
|
const joinCols = extractRelationJoinColumns(relEntry, parentTable, targetTable);
|
|
4641
4707
|
if (!joinCols) return;
|
|
4642
4708
|
const { localColPropName, foreignCol, foreignColPropName } = joinCols;
|
|
4709
|
+
const junction = extractJunctionColumns(relEntry);
|
|
4710
|
+
const groupCol = junction ? junction.sourceCol : foreignCol;
|
|
4711
|
+
const groupKey = junction ? PARENT_KEY : foreignColPropName;
|
|
4712
|
+
const ownCondition = relationOwnCondition(relEntry, parentTable, targetTable, getColumns(parentTable)[localColPropName], groupCol);
|
|
4643
4713
|
const defaultDeleted = relationDeletedDefault(policies?.softDelete, targetTableName, isOne && relEntry.relation?.optional === false);
|
|
4644
4714
|
const limitPolicy = isOne ? void 0 : limits?.(targetTableName);
|
|
4645
4715
|
const targetPkNames = relEntry.targetPkNames ?? [];
|
|
@@ -4717,11 +4787,12 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
|
|
|
4717
4787
|
loaderKey: `${tableName}::${relationName}::${argsKey}`
|
|
4718
4788
|
};
|
|
4719
4789
|
});
|
|
4790
|
+
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" });
|
|
4720
4791
|
return getOrCreateLoader(context, loaderKey, async (parentIds) => {
|
|
4721
4792
|
const executor = resolveExecutor(db, context);
|
|
4722
4793
|
const uniqueIds = [...new Set(parentIds)];
|
|
4723
4794
|
const scope = resolveScope(policies, context, filterCtx);
|
|
4724
|
-
let whereCondition = withScope(scope, targetTableName, targetTable, and(inArray(
|
|
4795
|
+
let whereCondition = withScope(scope, targetTableName, targetTable, and(inArray(groupCol, uniqueIds), ownCondition, whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName)) : void 0, cursorValues ? buildCursorCondition(targetTable, cursorEntries, cursorValues, nullOrdering) : void 0), deleted ?? defaultDeleted);
|
|
4725
4796
|
if (distinct) {
|
|
4726
4797
|
const keys = await selectDistinctKeys({
|
|
4727
4798
|
db: executor,
|
|
@@ -4731,27 +4802,41 @@ const createRelationResolverFactory = (db, tables, nullOrdering, filterCtx, limi
|
|
|
4731
4802
|
pkNames: targetPkNames,
|
|
4732
4803
|
where: whereCondition,
|
|
4733
4804
|
orderBy: orderByArg,
|
|
4734
|
-
partitionBy: [
|
|
4805
|
+
partitionBy: [groupCol],
|
|
4806
|
+
junction: junction ? {
|
|
4807
|
+
table: junction.table,
|
|
4808
|
+
on: eq(junction.targetCol, foreignCol),
|
|
4809
|
+
parentKey: groupCol
|
|
4810
|
+
} : void 0
|
|
4735
4811
|
});
|
|
4736
4812
|
if (!keys.length) return parentIds.map(() => isOne ? null : []);
|
|
4737
|
-
whereCondition = and(whereCondition, primaryKeyRestriction(targetTable, targetPkNames, keys));
|
|
4813
|
+
whereCondition = and(whereCondition, junction ? or(...keys.map((key) => and(eq(groupCol, key[DISTINCT_PARENT]), primaryKeyRestriction(targetTable, targetPkNames, [key])))) : primaryKeyRestriction(targetTable, targetPkNames, keys));
|
|
4738
4814
|
}
|
|
4815
|
+
const selection = junction ? {
|
|
4816
|
+
...columns ?? getColumns(targetTable),
|
|
4817
|
+
[PARENT_KEY]: groupCol
|
|
4818
|
+
} : columns;
|
|
4739
4819
|
let rows;
|
|
4740
|
-
if (limit != null || offset != null) rows = await batchedPaginatedRelationQuery(executor, targetTable, foreignCol, whereCondition, orderByArg, limit ?? null, offset ?? null, targetPkNames,
|
|
4820
|
+
if (limit != null || offset != null) rows = await batchedPaginatedRelationQuery(executor, targetTable, foreignCol, whereCondition, orderByArg, limit ?? null, offset ?? null, targetPkNames, selection, relationFilterCtx(filterCtx, targetTableName), whereArg, junction);
|
|
4741
4821
|
else {
|
|
4742
|
-
let q = (
|
|
4822
|
+
let q = withJunction((selection ? executor.select(selection) : executor.select()).from(targetTable), junction, foreignCol).where(whereCondition);
|
|
4743
4823
|
const orderExprs = cursorEntries ? cursorOrderExprs(targetTable, cursorEntries) : orderByArg ? extractOrderBy(targetTable, orderByArg, relationFilterCtx(filterCtx, targetTableName), whereArg) : [];
|
|
4744
4824
|
if (orderExprs.length) q = q.orderBy(...orderExprs);
|
|
4745
4825
|
rows = await q;
|
|
4746
4826
|
}
|
|
4747
4827
|
if (cursorEntries) attachRowCursors(rows, cursorEntries);
|
|
4828
|
+
const keyOf = (row) => {
|
|
4829
|
+
const key = String(row[groupKey]);
|
|
4830
|
+
if (junction) delete row[PARENT_KEY];
|
|
4831
|
+
return key;
|
|
4832
|
+
};
|
|
4748
4833
|
if (isOne) {
|
|
4749
|
-
const byKey = new Map(rows.map((row) => [
|
|
4834
|
+
const byKey = new Map(rows.map((row) => [keyOf(row), row]));
|
|
4750
4835
|
remapToGraphQLArrayOutput(rows, targetTableName, targetTable);
|
|
4751
4836
|
return parentIds.map((id) => byKey.get(String(id)) ?? null);
|
|
4752
4837
|
}
|
|
4753
4838
|
const grouped = new Map(uniqueIds.map((id) => [String(id), []]));
|
|
4754
|
-
for (const row of rows) grouped.get(
|
|
4839
|
+
for (const row of rows) grouped.get(keyOf(row))?.push(row);
|
|
4755
4840
|
remapToGraphQLArrayOutput(rows, targetTableName, targetTable);
|
|
4756
4841
|
return parentIds.map((id) => grouped.get(String(id)) ?? []);
|
|
4757
4842
|
}).load(localValue);
|
|
@@ -5086,7 +5171,7 @@ const generateSelectFields = (tables, tableName, relationMap, fromTableName, fro
|
|
|
5086
5171
|
});
|
|
5087
5172
|
const shell = new GraphQLObjectType({
|
|
5088
5173
|
name: typeName,
|
|
5089
|
-
description: cacheCtx.docs.describeTable?.(tableName),
|
|
5174
|
+
description: cacheCtx.docs.describeTable?.(tableName, table),
|
|
5090
5175
|
fields: () => ({
|
|
5091
5176
|
...tableFields,
|
|
5092
5177
|
...container.fields
|
|
@@ -5120,7 +5205,7 @@ const generateSelectFields = (tables, tableName, relationMap, fromTableName, fro
|
|
|
5120
5205
|
defaultName: resolveTypeName(targetTableName, typeNameMapper),
|
|
5121
5206
|
table: targetTableName
|
|
5122
5207
|
}),
|
|
5123
|
-
description: cacheCtx.docs.describeTable?.(targetTableName),
|
|
5208
|
+
description: cacheCtx.docs.describeTable?.(targetTableName, targetTable),
|
|
5124
5209
|
fields: () => ({
|
|
5125
5210
|
...targetTableFields,
|
|
5126
5211
|
...capturedTargetContainer.fields
|
|
@@ -5271,7 +5356,7 @@ const generateTableTypes = (tableName, tables, relationMap, withReturning, relat
|
|
|
5271
5356
|
defaultName: resolveTypeName(tableName, typeNameMapper),
|
|
5272
5357
|
table: tableName
|
|
5273
5358
|
}),
|
|
5274
|
-
description: cacheCtx.docs.describeTable?.(tableName),
|
|
5359
|
+
description: cacheCtx.docs.describeTable?.(tableName, tables[tableName]),
|
|
5275
5360
|
fields: {
|
|
5276
5361
|
...tableFields,
|
|
5277
5362
|
...relationFields
|
|
@@ -5838,6 +5923,10 @@ const createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, fi
|
|
|
5838
5923
|
return;
|
|
5839
5924
|
}
|
|
5840
5925
|
const { localColPropName, foreignCol } = joinCols;
|
|
5926
|
+
const junction = extractJunctionColumns(relEntry);
|
|
5927
|
+
if (junction === null) return;
|
|
5928
|
+
const groupCol = junction ? junction.sourceCol : foreignCol;
|
|
5929
|
+
const ownCondition = relationOwnCondition(relEntry, parentTable, targetTable, getColumns(parentTable)[localColPropName], groupCol);
|
|
5841
5930
|
const defaultDeleted = relationDeletedDefault(policies?.softDelete, targetTableName, false);
|
|
5842
5931
|
const targetTypeName = resolveTypeName(targetTableName, typeNameMapper);
|
|
5843
5932
|
const type = generateAggregateTypes(targetTable, targetTableName, targetTypeName, cacheCtx);
|
|
@@ -5868,11 +5957,12 @@ const createRelationAggregateFactory = (db, tables, cacheCtx, typeNameMapper, fi
|
|
|
5868
5957
|
const loader = getOrCreateLoader(context, loaderKey, async (parentIds) => {
|
|
5869
5958
|
const executor = resolveExecutor(db, context);
|
|
5870
5959
|
const uniqueIds = [...new Set(parentIds)];
|
|
5871
|
-
const whereCondition = withScope(resolveScope(policies, context, filterCtx), targetTableName, targetTable, and(inArray(
|
|
5872
|
-
const
|
|
5873
|
-
[GROUP_KEY]:
|
|
5960
|
+
const whereCondition = withScope(resolveScope(policies, context, filterCtx), targetTableName, targetTable, and(inArray(groupCol, uniqueIds), ownCondition, whereArg ? extractFilters(targetTable, targetTableName, whereArg, relationFilterCtx(filterCtx, targetTableName)) : void 0), deleted ?? defaultDeleted);
|
|
5961
|
+
const selected = executor.select({
|
|
5962
|
+
[GROUP_KEY]: groupCol,
|
|
5874
5963
|
...request.selection
|
|
5875
|
-
}).from(targetTable)
|
|
5964
|
+
}).from(targetTable);
|
|
5965
|
+
const rows = await (junction ? selected.innerJoin(junction.table, eq(junction.targetCol, foreignCol)) : selected).where(whereCondition).groupBy(groupCol);
|
|
5876
5966
|
const byKey = new Map(rows.map((row) => [row[GROUP_KEY], row]));
|
|
5877
5967
|
return parentIds.map((id) => byKey.get(id) ?? {});
|
|
5878
5968
|
});
|