Skip to content

Commit 3f533f6

Browse files
committed
🚧 wip: make columns work in single queries
1 parent 8edc1c4 commit 3f533f6

5 files changed

Lines changed: 103 additions & 160 deletions

File tree

‎example/src/main.ts‎

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,6 @@ export const db = drizzle(
2929
},
3030
);
3131

32-
// console.log(db._.schema);
33-
// console.log(db._.relations);
34-
3532
/*
3633
3734
Next, we can create a rumble instance. The creator returns a set of functions which you
@@ -372,14 +369,11 @@ schemaBuilder.mutationFields((t) => {
372369
// inject all graphql selections which ought to be queried from the db according to the gql request
373370
query(
374371
// merge multiple filter objects which should be applied to the query
375-
mergeFilters(
376-
{
377-
// only retrieve the newly created user
378-
where: { id: newUser.id },
379-
},
380-
// only retrieve the user if the caller is allowed to read it
381-
ctx.abilities.users.filter("read").query.single,
382-
),
372+
// only retrieve the user if the caller is allowed to read it
373+
ctx.abilities.users.filter("read").merge({
374+
// only retrieve the newly created user
375+
where: { id: newUser.id },
376+
}).query.single,
383377
),
384378
)
385379
.then(assertFindFirstExists)

‎lib/abilityBuilder.ts‎

Lines changed: 25 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -332,10 +332,10 @@ export const createAbilityBuilder = <
332332
queryFilters?: DrizzleQueryFunctionInput<DB, TableName>,
333333
) {
334334
const internalTransformer = (
335-
queryFilters?: DrizzleQueryFunctionInput<DB, TableName>,
335+
filters?: DrizzleQueryFunctionInput<DB, TableName>,
336336
) => {
337337
const limit = lazy(() => {
338-
let limit = queryFilters?.limit as number | undefined;
338+
let limit = filters?.limit as number | undefined;
339339

340340
if (
341341
defaultLimit &&
@@ -351,7 +351,7 @@ export const createAbilityBuilder = <
351351
// we acutally need to define multiple return objects since we do not want to use delete for
352352
// performance reasons and an undefined columns field on a drizzle filter will prevent any
353353
// column from being selected at all
354-
if (queryFilters?.columns) {
354+
if (filters?.columns) {
355355
return {
356356
/**
357357
* Query filters for the drizzle query API.
@@ -367,8 +367,8 @@ export const createAbilityBuilder = <
367367
* For find first calls
368368
*/
369369
single: {
370-
where: queryFilters?.where,
371-
columns: queryFilters?.columns,
370+
where: filters?.where,
371+
columns: filters?.columns,
372372
} as Pick<
373373
NonNullable<
374374
NonNullable<
@@ -381,8 +381,8 @@ export const createAbilityBuilder = <
381381
* For find many calls
382382
*/
383383
many: {
384-
where: queryFilters?.where,
385-
columns: queryFilters?.columns,
384+
where: filters?.where,
385+
columns: filters?.columns,
386386
get limit() {
387387
return limit();
388388
},
@@ -416,10 +416,10 @@ export const createAbilityBuilder = <
416416
*/
417417
sql: {
418418
get where() {
419-
return queryFilters?.where
419+
return filters?.where
420420
? relationsFilterToSQL(
421421
tableSchema as any,
422-
queryFilters.where,
422+
filters.where,
423423
)
424424
: undefined;
425425
},
@@ -441,7 +441,7 @@ export const createAbilityBuilder = <
441441
* For find first calls
442442
*/
443443
single: {
444-
where: queryFilters?.where,
444+
where: filters?.where,
445445
} as Pick<
446446
NonNullable<
447447
NonNullable<
@@ -454,7 +454,7 @@ export const createAbilityBuilder = <
454454
* For find many calls
455455
*/
456456
many: {
457-
where: queryFilters?.where,
457+
where: filters?.where,
458458
get limit() {
459459
return limit();
460460
},
@@ -488,10 +488,10 @@ export const createAbilityBuilder = <
488488
*/
489489
sql: {
490490
get where() {
491-
return queryFilters?.where
491+
return filters?.where
492492
? relationsFilterToSQL(
493493
tableSchema as any,
494-
queryFilters.where,
494+
filters.where,
495495
)
496496
: undefined;
497497
},
@@ -505,19 +505,17 @@ export const createAbilityBuilder = <
505505
/**
506506
* Merges the current query filters with the provided filters for this call only
507507
*/
508-
function merge<
509-
P extends NonNullable<DrizzleQueryFunctionInput<DB, TableName>>[],
510-
>(...p: P) {
511-
const merged = mergeFilters(ret.query.many, ...p);
508+
function merge(
509+
p: NonNullable<DrizzleQueryFunctionInput<DB, TableName>>,
510+
) {
511+
const merged = mergeFilters(ret.query.many, p);
512512
return internalTransformer(merged);
513513
}
514514

515515
(ret as any).merge = merge;
516516

517517
return ret as typeof ret & {
518-
merge: typeof merge<
519-
NonNullable<DrizzleQueryFunctionInput<DB, TableName>>[]
520-
>;
518+
merge: typeof merge;
521519
};
522520
}
523521

@@ -571,53 +569,14 @@ export const createAbilityBuilder = <
571569
return transformToResponse(blockEverythingFilter);
572570
}
573571

574-
let highestLimit: number | undefined;
575-
for (let i = 0; i < allQueryFilters.length; i++) {
576-
const conditionObject = allQueryFilters[i];
577-
if (conditionObject?.limit) {
578-
if (
579-
highestLimit === undefined ||
580-
(conditionObject.limit as number) > highestLimit
581-
) {
582-
highestLimit = conditionObject.limit as number;
583-
}
584-
}
585-
}
586-
587-
let allowedColumns: Set<string> | undefined;
588-
for (let i = 0; i < allQueryFilters.length; i++) {
589-
const conditionObject = allQueryFilters[i];
590-
if (conditionObject?.columns) {
591-
if (allowedColumns === undefined) {
592-
allowedColumns = new Set(
593-
Object.keys(conditionObject.columns),
594-
);
595-
} else {
596-
const fields = Object.keys(conditionObject.columns);
597-
for (let i = 0; i < fields.length; i++) {
598-
allowedColumns.add(fields[i]);
599-
}
600-
}
601-
}
602-
}
572+
const mergedFilters =
573+
allQueryFilters.length === 1
574+
? allQueryFilters[0]
575+
: allQueryFilters.reduce((a, b) => {
576+
return mergeFilters(a, b);
577+
}, {});
603578

604-
const accumulatedWhereConditions = allQueryFilters
605-
.filter((o) => o?.where)
606-
.map((o) => o!.where);
607-
608-
return transformToResponse({
609-
where:
610-
accumulatedWhereConditions.length > 0
611-
? { OR: accumulatedWhereConditions }
612-
: undefined,
613-
columns: allowedColumns
614-
? allowedColumns.values().reduce((prev, curr) => {
615-
prev[curr] = true;
616-
return prev;
617-
}, {} as any)
618-
: undefined,
619-
limit: highestLimit,
620-
} as any);
579+
return transformToResponse(mergedFilters as any);
621580
},
622581
};
623582
},
Lines changed: 57 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,67 @@
1-
import { merge } from "es-toolkit";
1+
import { toMerged } from "es-toolkit";
22

3-
export function mergeFilters<Filter>(...filters: Partial<Filter>[]) {
4-
const allWhereClauses = filters
5-
.map((f) => (f as any).where)
6-
.filter((w) => w !== undefined && w !== null);
3+
export function mergeFilters<
4+
FilterA extends Record<string, any>,
5+
FilterB extends Record<string, any>,
6+
>(filterA?: Partial<FilterA>, filterB?: Partial<FilterB>) {
7+
const where =
8+
filterA?.where && filterB?.where
9+
? { AND: [filterA?.where, filterB?.where] }
10+
: (filterA?.where ?? filterB?.where);
711

8-
const uniqueColumns = new Set(
9-
filters.flatMap((f) =>
10-
(f as any).columns ? Object.keys((f as any).columns) : [],
11-
),
12-
);
13-
const mappedUniqueColumns =
14-
uniqueColumns.size > 0
15-
? uniqueColumns.values().reduce((prev, curr) => {
16-
prev[curr] = true;
17-
return prev;
18-
}, {} as any)
12+
const columns =
13+
filterA?.columns || filterB?.columns
14+
? new Set(
15+
[
16+
Object.entries(filterA?.columns ?? {}),
17+
Object.entries(filterB?.columns ?? {}),
18+
]
19+
.flat()
20+
.filter(([, v]) => v === true)
21+
.map(([k]) => k),
22+
)
23+
.entries()
24+
.reduce(
25+
(acc, [key]) => {
26+
acc[key] = true;
27+
return acc;
28+
},
29+
{} as Record<string, true>,
30+
)
1931
: undefined;
2032

21-
const mergedExtras = {};
22-
let touchedExtras = false;
23-
for (let i = 0; i < filters.length; i++) {
24-
const filter = filters[i];
25-
if ((filter as any).extras) {
26-
merge(mergedExtras, (filter as any).extras);
27-
touchedExtras = true;
28-
}
29-
}
33+
const extras =
34+
filterA?.extras || filterB?.extras
35+
? toMerged(filterA?.extras ?? {}, filterB?.extras ?? {})
36+
: undefined;
37+
38+
const orderBy =
39+
filterA?.orderBy || filterB?.orderBy
40+
? toMerged(filterA?.orderBy ?? {}, filterB?.orderBy ?? {})
41+
: undefined;
3042

31-
const mergedOrderBy = {};
32-
let touchedOrderBy = false;
33-
for (let i = 0; i < filters.length; i++) {
34-
const filter = filters[i];
35-
if ((filter as any).orderBy) {
36-
merge(mergedOrderBy, (filter as any).orderBy);
37-
touchedOrderBy = true;
38-
}
39-
}
43+
const limit =
44+
filterA?.limit || filterB?.limit
45+
? Math.min(filterA?.limit ?? Infinity, filterB?.limit ?? Infinity)
46+
: undefined;
4047

41-
const lowestLimit: number = Math.min(
42-
...filters.map((f) => (f as any).limit ?? Infinity),
43-
);
44-
const lowestOffset: number = Math.min(
45-
...filters.map((f) => (f as any).offset ?? Infinity),
46-
);
48+
const offset =
49+
filterA?.offset || filterB?.offset
50+
? Math.min(filterA?.offset ?? Infinity, filterB?.offset ?? Infinity)
51+
: undefined;
4752

48-
const mergedWith = {};
49-
let touchedWith = false;
50-
for (let i = 0; i < filters.length; i++) {
51-
const filter = filters[i];
52-
if ((filter as any).with) {
53-
merge(mergedWith, (filter as any).with);
54-
touchedWith = true;
55-
}
56-
}
53+
const with_ =
54+
filterA?.with || filterB?.with
55+
? toMerged(filterA?.with ?? {}, filterB?.with ?? {})
56+
: undefined;
5757

5858
return {
59-
where:
60-
allWhereClauses.length > 0
61-
? ({ AND: allWhereClauses } as any)
62-
: undefined,
63-
columns: mappedUniqueColumns,
64-
extras: touchedExtras ? mergedExtras : undefined,
65-
orderBy: touchedOrderBy ? mergedOrderBy : undefined,
66-
limit: lowestLimit < Infinity ? lowestLimit : undefined,
67-
offset: lowestOffset < Infinity ? lowestOffset : undefined,
68-
with: touchedWith ? mergedWith : undefined,
69-
} as Filter;
59+
where,
60+
columns,
61+
extras,
62+
orderBy,
63+
limit,
64+
offset,
65+
with: with_,
66+
} as unknown as FilterA & FilterB;
7067
}

‎lib/query.ts‎

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import pluralize from "pluralize";
22
import { assertFindFirstExists } from "./helpers/asserts";
33
import { mapNullFieldsToUndefined } from "./helpers/mapNullFieldsToUndefined";
4-
import { mergeFilters } from "./helpers/mergeFilters";
54
import { tableHelper } from "./helpers/tableHelpers";
65
import type { OrderArgImplementerType } from "./orderArg";
76
import type { MakePubSubInstanceType } from "./pubsub";
@@ -144,15 +143,12 @@ export const createQueryImplementer = <
144143

145144
return (db.query as any)[table].findMany(
146145
query(
147-
mergeFilters(
148-
ctx.abilities[table].filter(listAction).query.many,
149-
{
150-
offset: mappedArgs.offset,
151-
limit: mappedArgs.limit,
152-
where: mappedArgs.where,
153-
orderBy: mappedArgs.orderBy,
154-
} as any,
155-
) as any,
146+
ctx.abilities[table].filter(listAction).merge({
147+
offset: mappedArgs.offset,
148+
limit: mappedArgs.limit,
149+
where: mappedArgs.where,
150+
orderBy: mappedArgs.orderBy,
151+
} as any).query.many as any,
156152
),
157153
);
158154
},
@@ -168,18 +164,18 @@ export const createQueryImplementer = <
168164
resolve: (query, _root, args, ctx, _info) => {
169165
Object.setPrototypeOf(args, Object.prototype);
170166

171-
const merged = mergeFilters(
172-
ctx.abilities[table].filter(readAction).query.single,
173-
{ where: { [primaryKeyField.name]: args.id } },
174-
) as any;
167+
const filter = (ctx.abilities as any)[table]
168+
.filter(readAction)
169+
.merge({ where: { [primaryKeyField.name]: args.id } })
170+
.query.single;
171+
const q = query(filter);
172+
173+
if (filter.columns) {
174+
q.columns = filter.columns;
175+
}
175176

176177
return (db.query as any)[table]
177-
.findFirst(
178-
query({
179-
where: merged.where,
180-
columns: merged.columns,
181-
} as any),
182-
)
178+
.findFirst(q)
183179
.then(assertFindFirstExists);
184180
},
185181
}),

0 commit comments

Comments
 (0)