@@ -15,7 +15,9 @@ import {
1515import graphqlFields from 'graphql-fields';
1616import { Inject } from 'typedi';
1717import { Min } from 'class-validator'
18- import { Fields, StandardDeleteResponse, UserId, PageInfo, RawFields } from 'warthog';
18+ import { Fields, StandardDeleteResponse, UserId, PageInfo, RawFields, NestedFields } from 'warthog';
19+
20+ import { mergeParameterKeys } from '../../utils';
1921
2022import {
2123 {{className}}CreateInput,
@@ -96,118 +98,159 @@ export class {{className}}Resolver {
9698
9799 @Query(() => [{{className}}])
98100 async {{camelNamePlural}}(
99- @Args() { where, orderBy, limit, offset }: {{className}}WhereArgs,
101+ @Args() { where: _where , orderBy, limit, offset }: {{className}}WhereArgs,
100102 @Fields() fields: string[]
101103 ): Promise<{{className}}[]> {
102- {{#crossFilters}}
103- if (where) {
104- const id_in = where.id_in;
104+ const where = <{{className}}WhereInput>(_where || {})
105105
106- {{#fieldResolvers}}
107- {{#relationType.isOTO}}
106+ {{#fieldResolvers}}
107+ // remove relation filters to enable warthog query builders
108+ {{#relationType.isTO}}
108109 const { {{fieldName}} } = where
110+ delete where.{{fieldName}};
111+ {{/relationType.isTO}}
112+
113+ {{#relationType.isTM}}
114+ const { {{fieldName}}_some, {{fieldName}}_none, {{fieldName}}_every } = where
115+
116+ if ((+!!{{fieldName}}_some) + (+!!{{fieldName}}_none) + (+!!{{fieldName}}_every) > 1) {
117+ throw new Error(`A query can have at most one of none, some, every clauses on a relation field`)
118+ }
119+
120+ delete where.{{fieldName}}_some;
121+ delete where.{{fieldName}}_none;
122+ delete where.{{fieldName}}_every;
123+ {{/relationType.isTM}}
124+ {{/fieldResolvers}}
125+
126+ let mainQuery = this.service.buildFindQueryWithParams(
127+ <any>where,
128+ orderBy,
129+ undefined,
130+ fields,
131+ 'main'
132+ ).take(undefined); // remove LIMIT
133+
134+ let parameters = mergeParameterKeys({}, 'main', mainQuery.getQueryAndParameters()[1]);
135+
136+ {{#crossFilters}}
137+
138+ {{#fieldResolvers}}
139+ {{#relationType.isTO}}
140+
109141 if ({{fieldName}}) {
110- where.id_in = (
111- await this.{{fieldName}}Service
112- .getQueryBuilder({{fieldName}})
113- .leftJoinAndSelect('{{tableName}}.{{relatedTsProp}}', '{{relatedTsProp}}')
114- .getMany()
115- )
116- .filter(f => f.{{relatedTsProp}})
117- .map(f => f.{{relatedTsProp}}!.id);
118- delete where.{{fieldName}};
142+ // OTO or MTO
143+ const {{fieldName}}Query = this.{{fieldName}}Service.buildFindQueryWithParams(
144+ <any>{{fieldName}},
145+ undefined,
146+ undefined,
147+ ['id'],
148+ '{{fieldName}}'
149+ ).take(undefined); // remove the default LIMIT
150+
151+
152+ mainQuery = mainQuery
153+ .leftJoin('{{typeormAliasName}}.{{fieldName}}', '{{fieldName}}')
154+ .andWhere(`{{fieldName}}.id IN (${ {{fieldName}}Query.getQuery() })`);
155+
156+ parameters = parameters = mergeParameterKeys(
157+ parameters,
158+ '{{fieldName}}',
159+ {{fieldName}}Query.getQueryAndParameters()[1]);;
160+
119161 }
120- {{/relationType.isOTO}}
121-
122- {{#relationType.isModifier}}
123- const { {{fieldName}}_some, {{fieldName}}_none, {{fieldName}}_every } = where
124- if ({{fieldName}}_some || {{fieldName}}_none || {{fieldName}}_every) {
125- const result = await this.{{fieldName}}Service
126- .getQueryBuilder({{fieldName}}_some || {{fieldName}}_none || {{fieldName}}_every)
127- .leftJoinAndSelect('{{tableName}}.{{relatedTsProp}}', '{{relatedTsProp}}')
128- .getMany();
129- let {{relatedTsProp}}Ids: string[] = []
130- {{#relationType.isOTM}}
131- {{relatedTsProp}}Ids = Array.from(new Set(result.map(v => v!.{{relatedTsProp}}!.id)));
132- {{/relationType.isOTM}}
133- {{#relationType.isMTM}}
134- Array.from(new Set(result.map(v => v!.{{relatedTsProp}}!.map(c => {{relatedTsProp}}Ids.push(c.id)))));
135- {{/relationType.isMTM}}
136-
137- delete where.{{fieldName}}_some
138- delete where.{{fieldName}}_none
139- delete where.{{fieldName}}_every
140-
141- {{#relationType.isOTM}}
142- if ({{fieldName}}_some) {
143- where.id_in = {{relatedTsProp}}Ids;
144- }
145- {{/relationType.isOTM}}
162+ {{/relationType.isTO}}
163+
164+ {{#relationType.isTM}}
165+
166+ const {{fieldName}}Filter = {{fieldName}}_some || {{fieldName}}_none || {{fieldName}}_every
167+
168+ if ({{fieldName}}Filter) {
169+
170+ const {{fieldName}}Query = this.{{fieldName}}Service.buildFindQueryWithParams(<any>{{fieldName}}Filter,
171+ undefined,
172+ undefined,
173+ ['id'],
174+ '{{fieldName}}'
175+ ).take(undefined); //remove the default LIMIT
176+
177+ parameters = mergeParameterKeys(
178+ parameters,
179+ '{{fieldName}}',
180+ {{fieldName}}Query.getQueryAndParameters()[1]);
181+
182+ const subQueryFiltered = this.service
183+ .getQueryBuilder()
184+ .select([])
185+ .leftJoin(
186+ '{{typeormAliasName}}.{{fieldName}}',
187+ '{{fieldName}}_filtered',
188+ `{{fieldName}}_filtered.id IN (${ {{fieldName}}Query.getQuery() })`
189+ )
190+ .groupBy('{{typeormAliasName}}_id')
191+ .addSelect('count({{fieldName}}_filtered.id)', 'cnt_filtered')
192+ .addSelect('{{typeormAliasName}}.id', '{{typeormAliasName}}_id');
193+
194+ const subQueryTotal = this.service
195+ .getQueryBuilder()
196+ .select([])
197+ .leftJoin('{{typeormAliasName}}.{{fieldName}}', '{{fieldName}}_total')
198+ .groupBy('{{typeormAliasName}}_id')
199+ .addSelect('count({{fieldName}}_total.id)', 'cnt_total')
200+ .addSelect('{{typeormAliasName}}.id', '{{typeormAliasName}}_id');
201+
202+ const subQuery = `
203+ SELECT
204+ f.{{typeormAliasName}}_id {{typeormAliasName}}_id, f.cnt_filtered cnt_filtered, t.cnt_total cnt_total
205+ FROM
206+ (${subQueryTotal.getQuery()}) t, (${subQueryFiltered.getQuery()}) f
207+ WHERE
208+ t.{{typeormAliasName}}_id = f.{{typeormAliasName}}_id`;
146209
147- {{#relationType.isMTM}}
148- if ({{fieldName}}_some || {{fieldName}}_every) {
149- where.id_in = {{relatedTsProp}}Ids;
150- }
151- {{/relationType.isMTM}}
152-
153210
154211 if ({{fieldName}}_none) {
155- where.id_in = (
156- await getRepository({{returnTypeFunc}}).find({
157- select: ['id'],
158- where: { id: Not(In({{relatedTsProp}}Ids)) }
159- })
160- ).map(c => c.id);
212+ mainQuery = mainQuery.andWhere(`{{typeormAliasName}}.id IN
213+ (SELECT
214+ {{fieldName}}_subq.{{typeormAliasName}}_id
215+ FROM
216+ (${subQuery}) {{fieldName}}_subq
217+ WHERE
218+ {{fieldName}}_subq.cnt_filtered = 0
219+ )`)
220+ }
221+
222+ if ({{fieldName}}_some) {
223+ mainQuery = mainQuery.andWhere(`{{typeormAliasName}}.id IN
224+ (SELECT
225+ {{fieldName}}_subq.{{typeormAliasName}}_id
226+ FROM
227+ (${subQuery}) {{fieldName}}_subq
228+ WHERE
229+ {{fieldName}}_subq.cnt_filtered > 0
230+ )`)
161231 }
162232
163- {{#relationType.isOTM}}
164233 if ({{fieldName}}_every) {
165- getConnection().transaction(async em => {
166- const finalIds = [];
167-
168- // Group entity
169- const g = _.chain(result)
170- .groupBy(v => v!.{{relatedTsProp}}!.id)
171- .value();
172-
173- // Get all entities with their relations without filtering them
174- for (const { id, {{fieldName}} } of await em.getRepository({{rootArgType}}).find({
175- where: { id: In({{relatedTsProp}}Ids) },
176- relations: ['{{fieldName}}']
177- })) {
178- if ({{fieldName}} && {{fieldName}}.length === g[id].length) {
179- finalIds.push(id);
180- }
181- }
182- where.id_in = finalIds;
183- });
234+ mainQuery = mainQuery.andWhere(`{{typeormAliasName}}.id IN
235+ (SELECT
236+ {{fieldName}}_subq.{{typeormAliasName}}_id
237+ FROM
238+ (${subQuery}) {{fieldName}}_subq
239+ WHERE
240+ {{fieldName}}_subq.cnt_filtered > 0
241+ AND {{fieldName}}_subq.cnt_filtered = {{fieldName}}_subq.cnt_total
242+ )`)
184243 }
185- {{/relationType.isOTM}}
186244 }
187- {{/relationType.isModifier }}
245+ {{/relationType.isTM }}
188246
189- {{#relationType.isMTO}}
190- const { {{fieldName}} } = where
191- if ({{fieldName}}) {
192- const entityIds: string[] = [];
193- (
194- await this.{{fieldName}}Service
195- .getQueryBuilder({{fieldName}})
196- .leftJoinAndSelect('{{tableName}}.{{relatedTsProp}}', '{{relatedTsProp}}')
197- .getMany()
198- ).map((c: any) => entityIds.push(...c.{{relatedTsProp}}!.map((v: any) => v.id)));
199- where.id_in = entityIds;
200- delete where.{{fieldName}};
201- }
202- {{/relationType.isMTO}}
203- {{/fieldResolvers}}
204-
205- if (id_in) {
206- where.id_in = where.id_in ? [...id_in, ...where.id_in] : id_in;
207- }
208- }
247+ {{/fieldResolvers}}
248+
209249 {{/crossFilters}}
210- return this.service.find<{{className}}WhereInput>(where, orderBy, limit, offset, fields);
250+
251+ mainQuery = mainQuery.setParameters(parameters);
252+
253+ return mainQuery.take(limit || 50).skip(offset || 0).getMany();
211254 }
212255
213256 @Query(() => {{className}}, { nullable: true })
0 commit comments