Skip to content

Commit ed0e36c

Browse files
Merge pull request #85 from teamstarter/develop
feat(dataloader): Optimize all list calls by removing useless where and include properties to allow batching. Small clean on internal types.
2 parents 129dc1b + 60fdde3 commit ed0e36c

File tree

3 files changed

+87
-43
lines changed

3 files changed

+87
-43
lines changed

lib/createListResolver.js

+36-14
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ function allowOrderOnAssociations(findOptions, model) {
7575
// By default we take the direction detected by GraphQL-sequelize
7676
// It will be 'ASC' if 'reverse:' was not specified.
7777
// But this will only work for the first field.
78-
var direction = index === 0 ? findOptions.order[0][1] : 'ASC';
78+
var direction = index === 0 && findOptions.order ? findOptions.order[0][1] : 'ASC';
7979
// When reverse is not already removed by graphql-sequelize
8080
// we try to detect it ourselves. Happens for multiple fields sort.
8181
if (singleOrder.search('reverse:') === 0) {
@@ -92,7 +92,8 @@ function allowOrderOnAssociations(findOptions, model) {
9292
if (typeof model.associations[associationName] === 'undefined') {
9393
throw new Error("Association ".concat(associationName, " unknown on model ").concat(model.name, " order"));
9494
}
95-
if (typeof findOptions.include === 'undefined') {
95+
if (typeof findOptions.include === 'undefined' ||
96+
typeof findOptions.include === 'string') {
9697
findOptions.include = [];
9798
}
9899
var modelInclude = {
@@ -101,7 +102,10 @@ function allowOrderOnAssociations(findOptions, model) {
101102
if (model.associations[associationName].as) {
102103
modelInclude.as = model.associations[associationName].as;
103104
}
104-
findOptions.include.push(modelInclude);
105+
// Type assertion to specify the type of findOptions.include as an array
106+
if ('push' in findOptions.include) {
107+
findOptions.include.push(modelInclude);
108+
}
105109
var modelSort = {
106110
model: model.associations[associationName].target
107111
};
@@ -139,17 +143,19 @@ function allowOrderOnAssociations(findOptions, model) {
139143
* to
140144
* order = [['id', 'ASC'], ['fullname', 'DESC']
141145
*/
142-
findOptions.order.map(function (order) {
143-
// Handle multiple sort fields.
144-
if (order[0].search(',') === -1) {
145-
checkForAssociationSort(order[0], 0);
146-
return;
147-
}
148-
var multipleOrder = order[0].split(',');
149-
for (var index in multipleOrder) {
150-
checkForAssociationSort(multipleOrder[index], parseInt(index));
151-
}
152-
});
146+
if ('map' in findOptions.order) {
147+
findOptions.order.map(function (order) {
148+
// Handle multiple sort fields.
149+
if (order[0].search(',') === -1) {
150+
checkForAssociationSort(order[0], 0);
151+
return;
152+
}
153+
var multipleOrder = order[0].split(',');
154+
for (var index in multipleOrder) {
155+
checkForAssociationSort(multipleOrder[index], parseInt(index));
156+
}
157+
});
158+
}
153159
findOptions.order = processedOrder;
154160
return findOptions;
155161
}
@@ -175,6 +181,22 @@ function trimAndOptimizeFindOptions(_a) {
175181
graphqlTypeDeclaration.list.removeUnusedAttributes === false
176182
? findOptions
177183
: (0, removeUnusedAttributes_1["default"])(findOptions, info, graphqlTypeDeclaration.model, models);
184+
// As sequelize-dataloader does not support the include option, we have to remove it.
185+
// It does not differenciate between an empty include and an include with models so we have to remove it.
186+
if (trimedFindOptions.include &&
187+
typeof trimedFindOptions.include === 'object' &&
188+
'length' in trimedFindOptions.include &&
189+
trimedFindOptions.include.length === 0) {
190+
delete trimedFindOptions.include;
191+
}
192+
// As sequelize-dataloader does not support the where option, we have to remove it.
193+
// It does not differenciate between an empty where and a where with properties so we have to remove it.
194+
if (trimedFindOptions.where &&
195+
// Symbols like [Op.and] are not returned by Object.keys and must be handled separately.
196+
Object.getOwnPropertySymbols(trimedFindOptions.where).length === 0 &&
197+
Object.keys(trimedFindOptions.where).length === 0) {
198+
delete trimedFindOptions.where;
199+
}
178200
if (!
179201
// If we have a list with a limit and an offset
180202
(trimedFindOptions.limit &&

src/createListResolver.ts

+50-19
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { resolver } from 'graphql-sequelize'
2-
import { Model } from 'sequelize'
2+
import { FindOptions, Model } from 'sequelize'
33
import removeUnusedAttributes from './removeUnusedAttributes'
44
import { GlobalBeforeHook, ModelDeclarationType } from './types/types'
55

6-
function allowOrderOnAssociations(findOptions: any, model: any) {
6+
function allowOrderOnAssociations(findOptions: FindOptions<any>, model: any) {
77
if (typeof findOptions.order === 'undefined') {
88
return findOptions
99
}
@@ -16,7 +16,8 @@ function allowOrderOnAssociations(findOptions: any, model: any) {
1616
// By default we take the direction detected by GraphQL-sequelize
1717
// It will be 'ASC' if 'reverse:' was not specified.
1818
// But this will only work for the first field.
19-
let direction = index === 0 ? findOptions.order[0][1] : 'ASC'
19+
let direction =
20+
index === 0 && findOptions.order ? findOptions.order[0][1] : 'ASC'
2021
// When reverse is not already removed by graphql-sequelize
2122
// we try to detect it ourselves. Happens for multiple fields sort.
2223
if (singleOrder.search('reverse:') === 0) {
@@ -35,7 +36,10 @@ function allowOrderOnAssociations(findOptions: any, model: any) {
3536
`Association ${associationName} unknown on model ${model.name} order`
3637
)
3738
}
38-
if (typeof findOptions.include === 'undefined') {
39+
if (
40+
typeof findOptions.include === 'undefined' ||
41+
typeof findOptions.include === 'string'
42+
) {
3943
findOptions.include = []
4044
}
4145

@@ -47,7 +51,10 @@ function allowOrderOnAssociations(findOptions: any, model: any) {
4751
modelInclude.as = model.associations[associationName].as
4852
}
4953

50-
findOptions.include.push(modelInclude)
54+
// Type assertion to specify the type of findOptions.include as an array
55+
if ('push' in findOptions.include) {
56+
findOptions.include.push(modelInclude)
57+
}
5158

5259
const modelSort: any = {
5360
model: model.associations[associationName].target,
@@ -89,23 +96,25 @@ function allowOrderOnAssociations(findOptions: any, model: any) {
8996
* to
9097
* order = [['id', 'ASC'], ['fullname', 'DESC']
9198
*/
92-
findOptions.order.map((order: any) => {
93-
// Handle multiple sort fields.
94-
if (order[0].search(',') === -1) {
95-
checkForAssociationSort(order[0], 0)
96-
return
97-
}
98-
const multipleOrder = order[0].split(',')
99-
for (const index in multipleOrder) {
100-
checkForAssociationSort(multipleOrder[index], parseInt(index))
101-
}
102-
})
99+
if ('map' in findOptions.order) {
100+
findOptions.order.map((order: any) => {
101+
// Handle multiple sort fields.
102+
if (order[0].search(',') === -1) {
103+
checkForAssociationSort(order[0], 0)
104+
return
105+
}
106+
const multipleOrder = order[0].split(',')
107+
for (const index in multipleOrder) {
108+
checkForAssociationSort(multipleOrder[index], parseInt(index))
109+
}
110+
})
111+
}
103112
findOptions.order = processedOrder
104113
return findOptions
105114
}
106115

107116
const argsAdvancedProcessing = (
108-
findOptions: any,
117+
findOptions: FindOptions<any>,
109118
args: any,
110119
context: any,
111120
info: any,
@@ -135,7 +144,7 @@ async function trimAndOptimizeFindOptions({
135144
info,
136145
models,
137146
}: {
138-
findOptions: any
147+
findOptions: FindOptions<any>
139148
graphqlTypeDeclaration: any
140149
info: any
141150
models: any
@@ -151,6 +160,28 @@ async function trimAndOptimizeFindOptions({
151160
models
152161
)
153162

163+
// As sequelize-dataloader does not support the include option, we have to remove it.
164+
// It does not differenciate between an empty include and an include with models so we have to remove it.
165+
if (
166+
trimedFindOptions.include &&
167+
typeof trimedFindOptions.include === 'object' &&
168+
'length' in trimedFindOptions.include &&
169+
trimedFindOptions.include.length === 0
170+
) {
171+
delete trimedFindOptions.include
172+
}
173+
174+
// As sequelize-dataloader does not support the where option, we have to remove it.
175+
// It does not differenciate between an empty where and a where with properties so we have to remove it.
176+
if (
177+
trimedFindOptions.where &&
178+
// Symbols like [Op.and] are not returned by Object.keys and must be handled separately.
179+
Object.getOwnPropertySymbols(trimedFindOptions.where).length === 0 &&
180+
Object.keys(trimedFindOptions.where).length === 0
181+
) {
182+
delete trimedFindOptions.where
183+
}
184+
154185
if (
155186
// If we have a list with a limit and an offset
156187
trimedFindOptions.limit &&
@@ -230,7 +261,7 @@ export default function createListResolver(
230261
? graphqlTypeDeclaration.list.contextToOptions
231262
: undefined,
232263
before: async (findOptions: any, args: any, context: any, info: any) => {
233-
let processedFindOptions = argsAdvancedProcessing(
264+
let processedFindOptions: FindOptions<any> = argsAdvancedProcessing(
234265
findOptions,
235266
args,
236267
context,

src/types/types.d.ts

+1-10
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,12 @@ import {
1010
} from 'graphql'
1111
import {
1212
Association,
13-
Attributes,
14-
FindOptions as FO,
15-
Includeable,
13+
FindOptions,
1614
Model,
1715
ModelStatic,
1816
Sequelize,
1917
} from 'sequelize/types'
2018

21-
// graphql-sequelize does not have typescript support. So we have to reproduce what it is based on
22-
// the sequelize implementation.
23-
export type FindOptions<M extends Model> = Omit<
24-
FO<Attributes<M>>,
25-
'include'
26-
> & { include: Includeable[] }
27-
2819
export type Action = 'list' | 'create' | 'delete' | 'update' | 'count'
2920
export type ActionList = Array<Action>
3021

0 commit comments

Comments
 (0)