@@ -17,17 +17,17 @@ module.exports = {queryCalculator, produceResult};
17
17
* @private
18
18
*/
19
19
20
- var _ = require ( 'lodash' ) ;
21
- var deleteKey = require ( 'key-del' ) ;
22
- var {
20
+ const _ = require ( 'lodash' ) ;
21
+ const deleteKey = require ( 'key-del' ) ;
22
+ const {
23
23
GraphQLError
24
24
} = require ( 'graphql' ) ;
25
- var {
25
+ const {
26
26
createNode,
27
27
getRootNode,
28
28
nodeType
29
29
} = require ( './functions' ) ;
30
- var _execution = require ( 'graphql/execution' ) ;
30
+ const _execution = require ( 'graphql/execution' ) ;
31
31
32
32
/**
33
33
* queryCalculator - calling function for the recursive calculation algorithm.
@@ -42,7 +42,7 @@ var _execution = require('graphql/execution');
42
42
* @param {object } options
43
43
* @return {object } returns the query result in JSON format
44
44
*/
45
- function queryCalculator ( db , threshold , validationContext , options , format ) {
45
+ function queryCalculator ( db , threshold , validationContext , options ) {
46
46
try {
47
47
/* Only run for single queries */
48
48
if ( _ . size ( validationContext . getDocument ( ) . definitions ) > 1 ) {
@@ -53,44 +53,44 @@ function queryCalculator(db, threshold, validationContext, options, format) {
53
53
return data ;
54
54
}
55
55
56
- /* Create the three data structures */
57
- var labels = new Map ( ) ;
58
- var sizeMap = new Map ( ) ;
59
- var results = new Map ( ) ;
56
+ const documentAST = validationContext . getDocument ( ) ;
60
57
61
- var documentAST = validationContext . getDocument ( ) ;
62
- /* Parse query to remove location properties */
63
- var query = deleteKey ( documentAST . definitions [ 0 ] . selectionSet . selections , 'loc' ) ;
64
- var queryType = validationContext . getSchema ( ) . getQueryType ( ) ;
65
- var rootNode = getRootNode ( db , queryType ) ;
66
58
/* Set the execution context used for the resolver functions */
67
- var exeContext = _execution . buildExecutionContext ( options . schema , documentAST , options . rootValue , db , options . variables , options . operationName , options . fieldResolver ) ;
68
- var fieldNodes = documentAST . definitions [ 0 ] . selectionSet . selections ;
69
- /* Contains the path from the root field to the current field during calculation */
70
- var path ;
59
+ const exeContext = _execution . buildExecutionContext ( options . schema , documentAST , options . rootValue , db , options . variables , options . operationName , options . fieldResolver ) ;
60
+ const fieldNodes = documentAST . definitions [ 0 ] . selectionSet . selections ;
61
+
62
+ /* Additional parameters needed for the calculation */
63
+ let calculationContext = {
64
+ exeContext : exeContext ,
65
+ fieldNodes : fieldNodes ,
66
+ queryType : validationContext . getSchema ( ) . getQueryType ( ) ,
67
+ source : options . rootValue ,
68
+ path : null
69
+ } ;
71
70
72
- var fieldInfo = {
73
- exeContext : exeContext ,
74
- fieldNodes : fieldNodes ,
75
- queryType : queryType
71
+ let structures = {
72
+ labels : new Map ( ) ,
73
+ sizeMap : new Map ( ) ,
74
+ results : new Map ( )
76
75
} ;
77
76
78
- return calculate ( labels , sizeMap , results , rootNode , query , queryType , options . rootValue , path , fieldInfo , validationContext )
77
+ /* Parse query to remove location properties */
78
+ const query = deleteKey ( documentAST . definitions [ 0 ] . selectionSet . selections , 'loc' ) ;
79
+ const rootNode = getRootNode ( db , calculationContext . queryType ) ;
80
+
81
+ return calculate ( structures , rootNode , query , calculationContext )
79
82
. then ( ( ) => {
80
83
let stringNodeQuery = JSON . stringify ( [ rootNode , query ] ) ;
81
- const querySize = arrSum ( sizeMap . get ( stringNodeQuery ) ) ;
84
+ const querySize = arrSum ( structures . sizeMap . get ( stringNodeQuery ) ) ;
82
85
console . log ( 'Size of result: ' + querySize ) ;
83
86
if ( querySize > threshold ) {
84
87
validationContext . reportError (
85
88
new GraphQLError (
86
89
`Calculation: Size of query result is ${ querySize } , which exceeds maximum size of ${ threshold } ` )
87
90
) ;
88
91
}
89
- if ( validationContext . getErrors ( ) . length ) {
90
- return Promise . resolve ( { errors : format ( validationContext . getErrors ( ) ) } ) ;
91
- }
92
92
let data = {
93
- results : results ,
93
+ results : structures . results ,
94
94
validationContext : validationContext ,
95
95
index : stringNodeQuery
96
96
} ;
@@ -114,57 +114,54 @@ function queryCalculator(db, threshold, validationContext, options, format) {
114
114
* underlying data source again. A detailed explanation of this algorithm can be found in the Master's thesis
115
115
* "Combining Result Size Estimation and Query Execution for the GraphQL Query Language" by Andreas Lundquist.
116
116
*
117
- * @param {object } labels the labels Map
118
- * @param {object } sizeMap the sizeMap Map
119
- * @param {object } results the results Map
120
- * @param {object } u node
121
- * @param {object } query (sub)query to be calculated
122
- * @param {object } parentType type of the parent node
123
- * @param {object } source
124
- * @param {object } current_path
117
+ * @param {object } structures contains three map structures: labels, sizeMap and results
118
+ * @param {object } u node
119
+ * @param {object } query (sub)query to be calculated
120
+ * @param {object } calculationContext contains additional information needed for the calculation
121
+ * @param {object } path contains the path from the root node to the current node
125
122
* @return {promise }
126
123
* @private
127
124
*/
128
- function calculate ( labels , sizeMap , results , u , query , parentType , source , current_path , fieldInfo , validationContext ) {
125
+ function calculate ( structures , u , query , calculationContext , path ) {
129
126
/* These three strings are used the data structures labels, sizeMap and results */
130
127
let stringNodeQuery = JSON . stringify ( [ u , query ] ) ;
131
128
let stringQuery = JSON . stringify ( query ) ;
132
129
let stringNode = JSON . stringify ( u ) ;
133
130
/* Check if query is already in labels for this node [1] */
134
- if ( ! doQueryExistOnNode ( labels , stringNode , stringQuery ) ) {
131
+ if ( ! doQueryExistOnNode ( structures . labels , stringNode , stringQuery ) ) {
135
132
/* Add query to labels [2] and initialize data structures if needed */
136
- addQueryToLabels ( labels , stringNode , stringQuery ) ;
137
- initializeDataStructures ( sizeMap , results , stringNodeQuery ) ;
133
+ addQueryToLabels ( structures . labels , stringNode , stringQuery ) ;
134
+ initializeDataStructures ( structures . sizeMap , structures . results , stringNodeQuery ) ;
138
135
if ( query . length > 1 ) {
139
136
/* The query consists of multiple subqueries [27] */
140
- return calculateAllSubqueries ( labels , sizeMap , results , query , stringNodeQuery , u , parentType , source , current_path , fieldInfo , validationContext ) ;
137
+ return calculateAllSubqueries ( structures , query , stringNodeQuery , u , calculationContext , path ) ;
141
138
} else if ( ! ( query [ 0 ] . selectionSet ) ) {
142
139
/* The query consists of a single field [3] */
143
- sizeMap . get ( stringNodeQuery ) . push ( 3 ) ;
144
- return getScalarField ( results , stringNodeQuery , query , source , parentType , current_path , fieldInfo ) ;
140
+ structures . sizeMap . get ( stringNodeQuery ) . push ( 3 ) ;
141
+ return getScalarField ( structures . results , stringNodeQuery , query , calculationContext , path ) ;
145
142
//return Promise.resolve();
146
143
} else if ( query [ 0 ] . kind === 'Field' ) {
147
144
/* The query consists of a field with a subselection [9] */
148
145
let fieldName = query [ 0 ] . name . value ;
149
- let fieldDef = parentType . getFields ( ) [ fieldName ] ;
150
- current_path = addPath ( current_path , fieldName ) ;
151
- return getField ( query , fieldDef , source , current_path , fieldInfo )
146
+ let fieldDef = calculationContext . queryType . getFields ( ) [ fieldName ] ;
147
+ path = addPath ( path , fieldName ) ;
148
+ return getField ( query , fieldDef , calculationContext , path )
152
149
. then ( src => {
153
- results . get ( stringNodeQuery ) . push ( "\"" + fieldName + "\"" + ":" ) ;
150
+ structures . results . get ( stringNodeQuery ) . push ( "\"" + fieldName + "\"" + ":" ) ;
154
151
/* Add to sizeMap depending on the type of the field [15-21] */
155
152
if ( fieldDef . astNode . type . kind === 'ListType' ) {
156
- sizeMap . get ( stringNodeQuery ) . push ( 4 ) ;
153
+ structures . sizeMap . get ( stringNodeQuery ) . push ( 4 ) ;
157
154
} else if ( src != null ) {
158
- sizeMap . get ( stringNodeQuery ) . push ( 2 ) ;
155
+ structures . sizeMap . get ( stringNodeQuery ) . push ( 2 ) ;
159
156
} else {
160
- sizeMap . get ( stringNodeQuery ) . push ( 3 ) ;
157
+ structures . sizeMap . get ( stringNodeQuery ) . push ( 3 ) ;
161
158
}
162
159
/* Recursively run the calculate function for every resulting edge [11-14] */
163
- return calculateRelatedNodes ( labels , sizeMap , results , src , stringNodeQuery , query , fieldDef , current_path , fieldInfo , validationContext ) ;
160
+ return calculateRelatedNodes ( structures , src , stringNodeQuery , query , fieldDef , calculationContext , path ) ;
164
161
} ) ;
165
162
} else if ( query [ 0 ] . kind === 'InlineFragment' ) {
166
163
/* The query consists of an inline fragment [22] */
167
- return calculateInlineFragment ( labels , sizeMap , results , stringNodeQuery , u , query , source , current_path , fieldInfo , validationContext ) ;
164
+ return calculateInlineFragment ( structures , stringNodeQuery , u , query , calculationContext , path ) ;
168
165
}
169
166
} else {
170
167
/* The query already exists in labels for this node */
@@ -197,27 +194,27 @@ function initializeDataStructures(sizeMap, results, stringNodeQuery){
197
194
}
198
195
}
199
196
200
- function calculateAllSubqueries ( labels , sizeMap , results , query , stringNodeQuery , u , parentType , source , current_path , fieldInfo , validationContext ) {
197
+ function calculateAllSubqueries ( structures , query , stringNodeQuery , u , calculationContext , path ) {
201
198
return Promise . all ( query . map ( function ( subquery , index ) {
202
199
if ( index !== 0 ) {
203
- results . get ( stringNodeQuery ) . push ( "," ) ;
200
+ structures . results . get ( stringNodeQuery ) . push ( "," ) ;
204
201
}
205
202
let stringNodeSubquery = JSON . stringify ( [ u , [ subquery ] ] ) ;
206
- results . get ( stringNodeQuery ) . push ( [ stringNodeSubquery ] ) ;
207
- return calculate ( labels , sizeMap , results , u , [ subquery ] , parentType , source , current_path , fieldInfo , validationContext )
203
+ structures . results . get ( stringNodeQuery ) . push ( [ stringNodeSubquery ] ) ;
204
+ return calculate ( structures , u , [ subquery ] , calculationContext , path )
208
205
. then ( x => {
209
- sizeMap . get ( stringNodeQuery ) . push ( sizeMap . get ( stringNodeSubquery ) ) ;
206
+ structures . sizeMap . get ( stringNodeQuery ) . push ( structures . sizeMap . get ( stringNodeSubquery ) ) ;
210
207
return x ;
211
208
} ) ;
212
209
} ) ) ;
213
210
}
214
211
215
212
/* Adds a field with a scalar value (leaf node) to the results structure */
216
- function getScalarField ( results , stringNodeQuery , query , source , parentType , current_path , fieldInfo ) {
213
+ function getScalarField ( results , stringNodeQuery , query , calculationContext , path ) {
217
214
let fieldName = query [ 0 ] . name . value ;
218
- let fieldDef = parentType . getFields ( ) [ fieldName ] ;
219
- current_path = addPath ( current_path , fieldName ) ;
220
- return getField ( query , fieldDef , source , current_path , fieldInfo )
215
+ let fieldDef = calculationContext . queryType . getFields ( ) [ fieldName ] ;
216
+ path = addPath ( path , fieldName ) ;
217
+ return getField ( query , fieldDef , calculationContext , path )
221
218
. then ( result => {
222
219
let value = formatScalarResult ( result , fieldName ) ;
223
220
results . get ( stringNodeQuery ) . push ( "\"" + fieldName + "\"" + ":" ) ;
@@ -229,11 +226,11 @@ function getScalarField(results, stringNodeQuery, query, source, parentType, cur
229
226
/**
230
227
* Builds the resolver info and args, then executes the corresponding resolver function.
231
228
*/
232
- function getField ( query , fieldDef , source , current_path , fieldInfo ) {
233
- let resolveFn = fieldDef . resolve || fieldInfo . exeContext . fieldResolver ;
234
- let info = _execution . buildResolveInfo ( fieldInfo . exeContext , fieldDef , fieldInfo . fieldNodes , fieldInfo . queryType , current_path ) ;
235
- let args = ( 0 , _execution . getArgumentValues ( fieldDef , query [ 0 ] , fieldInfo . exeContext . variableValues ) ) ;
236
- return Promise . resolve ( resolveFn ( source , args , fieldInfo . exeContext . contextValue , info ) ) ;
229
+ function getField ( query , fieldDef , calculationContext , path ) {
230
+ let resolveFn = fieldDef . resolve || calculationContext . exeContext . fieldResolver ;
231
+ let info = _execution . buildResolveInfo ( calculationContext . exeContext , fieldDef , calculationContext . fieldNodes , calculationContext . queryType , path ) ;
232
+ let args = ( 0 , _execution . getArgumentValues ( fieldDef , query [ 0 ] , calculationContext . exeContext . variableValues ) ) ;
233
+ return Promise . resolve ( resolveFn ( calculationContext . source , args , calculationContext . exeContext . contextValue , info ) ) ;
237
234
}
238
235
239
236
function formatScalarResult ( result , fieldName ) {
@@ -270,54 +267,57 @@ function formatScalarResult(result, fieldName){
270
267
return value ;
271
268
}
272
269
273
- function calculateRelatedNodes ( labels , sizeMap , results , src , stringNodeQuery , query , fieldDef , current_path , fieldInfo , validationContext ) {
274
- let currentType = fieldDef . astNode . type . kind === 'ListType' ? fieldDef . type . ofType : fieldDef . type ;
270
+ function calculateRelatedNodes ( structures , src , stringNodeQuery , query , fieldDef , calculationContext , path ) {
271
+ calculationContext . queryType = fieldDef . astNode . type . kind === 'ListType' ? fieldDef . type . ofType : fieldDef . type ; ;
275
272
/* If multiple related nodes exist */
276
273
if ( Array . isArray ( src ) ) {
277
- results . get ( stringNodeQuery ) . push ( "[" ) ;
274
+ structures . results . get ( stringNodeQuery ) . push ( "[" ) ;
278
275
return Promise . all ( src . map ( function ( srcItem , index ) {
279
276
if ( index !== 0 ) {
280
- results . get ( stringNodeQuery ) . push ( "," ) ;
277
+ structures . results . get ( stringNodeQuery ) . push ( "," ) ;
281
278
}
282
- return calculateSingleNode ( labels , sizeMap , results , query , srcItem , fieldDef , currentType , stringNodeQuery , current_path , fieldInfo , validationContext ) ;
279
+ calculationContext . source = srcItem ;
280
+ return calculateSingleNode ( structures , query , fieldDef , stringNodeQuery , calculationContext , path ) ;
283
281
} ) )
284
282
. then ( x => {
285
- results . get ( stringNodeQuery ) . push ( "]" ) ;
283
+ structures . results . get ( stringNodeQuery ) . push ( "]" ) ;
286
284
return x ;
287
285
} ) ;
288
286
/* If no related nodes exist */
289
287
} else if ( src == null ) {
290
- sizeMap . get ( stringNodeQuery ) . push ( 2 ) ;
291
- results . get ( stringNodeQuery ) . push ( "null" ) ;
288
+ structures . sizeMap . get ( stringNodeQuery ) . push ( 2 ) ;
289
+ structures . results . get ( stringNodeQuery ) . push ( "null" ) ;
292
290
return Promise . resolve ( ) ;
293
291
/* If only a single related node exists */
294
292
} else {
295
- return calculateSingleNode ( labels , sizeMap , results , query , src , fieldDef , currentType , stringNodeQuery , current_path , fieldInfo , validationContext ) ;
293
+ calculationContext . source = src ;
294
+ return calculateSingleNode ( structures , query , fieldDef , stringNodeQuery , calculationContext , path ) ;
296
295
}
297
296
}
298
297
299
- function calculateSingleNode ( labels , sizeMap , results , query , source , fieldDef , currentType , stringNodeQuery , current_path , fieldInfo , validationContext ) {
300
- sizeMap . get ( stringNodeQuery ) . push ( 2 ) ;
301
- let relatedNode = createNode ( source , fieldDef ) ;
298
+ function calculateSingleNode ( structures , query , fieldDef , stringNodeQuery , calculationContext , path ) {
299
+ structures . sizeMap . get ( stringNodeQuery ) . push ( 2 ) ;
300
+ let relatedNode = createNode ( calculationContext . source , fieldDef ) ;
302
301
let stringRelatedNodeSubquery = JSON . stringify ( [ relatedNode , query [ 0 ] . selectionSet . selections ] ) ;
303
- results . get ( stringNodeQuery ) . push ( "{" ) ;
304
- results . get ( stringNodeQuery ) . push ( [ stringRelatedNodeSubquery ] ) ;
305
- results . get ( stringNodeQuery ) . push ( "}" ) ;
306
- return calculate ( labels , sizeMap , results , relatedNode , query [ 0 ] . selectionSet . selections , currentType , source , current_path , fieldInfo , validationContext )
302
+ structures . results . get ( stringNodeQuery ) . push ( "{" ) ;
303
+ structures . results . get ( stringNodeQuery ) . push ( [ stringRelatedNodeSubquery ] ) ;
304
+ structures . results . get ( stringNodeQuery ) . push ( "}" ) ;
305
+ return calculate ( structures , relatedNode , query [ 0 ] . selectionSet . selections , calculationContext , path )
307
306
. then ( x => {
308
- sizeMap . get ( stringNodeQuery ) . push ( sizeMap . get ( stringRelatedNodeSubquery ) ) ;
307
+ structures . sizeMap . get ( stringNodeQuery ) . push ( structures . sizeMap . get ( stringRelatedNodeSubquery ) ) ;
309
308
return x ;
310
309
} ) ;
311
310
}
312
311
313
- function calculateInlineFragment ( labels , sizeMap , results , stringNodeQuery , u , query , source , current_path , fieldInfo , validationContext ) {
312
+ function calculateInlineFragment ( structures , stringNodeQuery , u , query , calculationContext , path ) {
314
313
let onType = query [ 0 ] . typeCondition . name . value ;
315
314
if ( nodeType ( u ) === onType ) {
316
315
let stringNodeSubquery = JSON . stringify ( [ u , query [ 0 ] . selectionSet . selections ] ) ;
317
- results . get ( stringNodeQuery ) . push ( [ stringNodeSubquery ] ) ;
318
- return calculate ( labels , sizeMap , results , u , query [ 0 ] . selectionSet . selections , validationContext . getSchema ( ) . getType ( onType ) , source , current_path , fieldInfo , validationContext )
316
+ structures . results . get ( stringNodeQuery ) . push ( [ stringNodeSubquery ] ) ;
317
+ calculationContext . queryType = fieldInfo . exeContext . schema . getType ( onType ) ;
318
+ return calculate ( structures , u , query [ 0 ] . selectionSet . selections , calculationContext , path )
319
319
. then ( x => {
320
- sizeMap . get ( stringNodeQuery ) . push ( sizeMap . get ( stringNodeSubquery ) ) ;
320
+ structures . sizeMap . get ( stringNodeQuery ) . push ( structures . sizeMap . get ( stringNodeSubquery ) ) ;
321
321
return x ;
322
322
} ) ;
323
323
} else {
0 commit comments