@abedmurrar/objection 0.0.0-stage → 4.0.0-alpha.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.
Files changed (190) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +69 -2
  3. package/lib/.eslintrc.json +8 -0
  4. package/lib/initialize.js +14 -0
  5. package/lib/model/AjvValidator.js +325 -0
  6. package/lib/model/Model.js +942 -0
  7. package/lib/model/ModifierNotFoundError.js +12 -0
  8. package/lib/model/NotFoundError.js +23 -0
  9. package/lib/model/RelationDoesNotExistError.js +14 -0
  10. package/lib/model/ValidationError.js +54 -0
  11. package/lib/model/Validator.js +26 -0
  12. package/lib/model/getModel.js +9 -0
  13. package/lib/model/graph/ModelGraph.js +80 -0
  14. package/lib/model/graph/ModelGraphBuilder.js +241 -0
  15. package/lib/model/graph/ModelGraphEdge.js +52 -0
  16. package/lib/model/graph/ModelGraphNode.js +240 -0
  17. package/lib/model/inheritModel.js +28 -0
  18. package/lib/model/modelBindKnex.js +83 -0
  19. package/lib/model/modelClone.js +121 -0
  20. package/lib/model/modelColPropMap.js +34 -0
  21. package/lib/model/modelId.js +62 -0
  22. package/lib/model/modelJsonAttributes.js +92 -0
  23. package/lib/model/modelParseRelations.js +82 -0
  24. package/lib/model/modelQueryProps.js +147 -0
  25. package/lib/model/modelSet.js +136 -0
  26. package/lib/model/modelTableMetadata.js +83 -0
  27. package/lib/model/modelToJson.js +187 -0
  28. package/lib/model/modelUtils.js +47 -0
  29. package/lib/model/modelValidate.js +48 -0
  30. package/lib/model/modelValues.js +126 -0
  31. package/lib/model/modelVisitor.js +41 -0
  32. package/lib/objection.js +114 -0
  33. package/lib/queryBuilder/FunctionBuilder.js +339 -0
  34. package/lib/queryBuilder/InternalOptions.js +29 -0
  35. package/lib/queryBuilder/JoinBuilder.js +157 -0
  36. package/lib/queryBuilder/QueryBuilder.js +1398 -0
  37. package/lib/queryBuilder/QueryBuilderBase.js +884 -0
  38. package/lib/queryBuilder/QueryBuilderContext.js +27 -0
  39. package/lib/queryBuilder/QueryBuilderContextBase.js +35 -0
  40. package/lib/queryBuilder/QueryBuilderOperationSupport.js +558 -0
  41. package/lib/queryBuilder/QueryBuilderUserContext.js +31 -0
  42. package/lib/queryBuilder/RawBuilder.js +93 -0
  43. package/lib/queryBuilder/ReferenceBuilder.js +284 -0
  44. package/lib/queryBuilder/RelationExpression.js +431 -0
  45. package/lib/queryBuilder/StaticHookArguments.js +134 -0
  46. package/lib/queryBuilder/ValueBuilder.js +127 -0
  47. package/lib/queryBuilder/expressions.js +151 -0
  48. package/lib/queryBuilder/functions/mysql.js +817 -0
  49. package/lib/queryBuilder/graph/GraphAction.js +69 -0
  50. package/lib/queryBuilder/graph/GraphData.js +14 -0
  51. package/lib/queryBuilder/graph/GraphFetcher.js +191 -0
  52. package/lib/queryBuilder/graph/GraphNodeDbExistence.js +184 -0
  53. package/lib/queryBuilder/graph/GraphOperation.js +27 -0
  54. package/lib/queryBuilder/graph/GraphOptions.js +207 -0
  55. package/lib/queryBuilder/graph/GraphUpsert.js +305 -0
  56. package/lib/queryBuilder/graph/delete/GraphDelete.js +20 -0
  57. package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +74 -0
  58. package/lib/queryBuilder/graph/insert/GraphInsert.js +145 -0
  59. package/lib/queryBuilder/graph/insert/GraphInsertAction.js +225 -0
  60. package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +92 -0
  61. package/lib/queryBuilder/graph/patch/GraphPatch.js +20 -0
  62. package/lib/queryBuilder/graph/patch/GraphPatchAction.js +258 -0
  63. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +31 -0
  64. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +126 -0
  65. package/lib/queryBuilder/join/JoinResultColumn.js +23 -0
  66. package/lib/queryBuilder/join/JoinResultParser.js +119 -0
  67. package/lib/queryBuilder/join/RelationJoiner.js +341 -0
  68. package/lib/queryBuilder/join/TableNode.js +163 -0
  69. package/lib/queryBuilder/join/TableTree.js +85 -0
  70. package/lib/queryBuilder/join/utils.js +25 -0
  71. package/lib/queryBuilder/operations/DelegateOperation.js +141 -0
  72. package/lib/queryBuilder/operations/DeleteOperation.js +51 -0
  73. package/lib/queryBuilder/operations/FindByIdOperation.js +37 -0
  74. package/lib/queryBuilder/operations/FindByIdsOperation.js +29 -0
  75. package/lib/queryBuilder/operations/FindOperation.js +164 -0
  76. package/lib/queryBuilder/operations/FirstOperation.js +27 -0
  77. package/lib/queryBuilder/operations/FromOperation.js +110 -0
  78. package/lib/queryBuilder/operations/InsertAndFetchOperation.js +49 -0
  79. package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +45 -0
  80. package/lib/queryBuilder/operations/InsertGraphOperation.js +108 -0
  81. package/lib/queryBuilder/operations/InsertOperation.js +206 -0
  82. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +46 -0
  83. package/lib/queryBuilder/operations/InstanceFindOperation.js +21 -0
  84. package/lib/queryBuilder/operations/InstanceInsertOperation.js +24 -0
  85. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +59 -0
  86. package/lib/queryBuilder/operations/JoinRelatedOperation.js +103 -0
  87. package/lib/queryBuilder/operations/KnexOperation.js +14 -0
  88. package/lib/queryBuilder/operations/MergeOperation.js +56 -0
  89. package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +192 -0
  90. package/lib/queryBuilder/operations/OnBuildKnexOperation.js +29 -0
  91. package/lib/queryBuilder/operations/OnBuildOperation.js +29 -0
  92. package/lib/queryBuilder/operations/OnErrorOperation.js +29 -0
  93. package/lib/queryBuilder/operations/QueryBuilderOperation.js +297 -0
  94. package/lib/queryBuilder/operations/RangeOperation.js +47 -0
  95. package/lib/queryBuilder/operations/RelateOperation.js +27 -0
  96. package/lib/queryBuilder/operations/ReturningOperation.js +30 -0
  97. package/lib/queryBuilder/operations/RunAfterOperation.js +29 -0
  98. package/lib/queryBuilder/operations/RunBeforeOperation.js +29 -0
  99. package/lib/queryBuilder/operations/UnrelateOperation.js +23 -0
  100. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +80 -0
  101. package/lib/queryBuilder/operations/UpdateOperation.js +155 -0
  102. package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +45 -0
  103. package/lib/queryBuilder/operations/UpsertGraphOperation.js +66 -0
  104. package/lib/queryBuilder/operations/WhereCompositeOperation.js +57 -0
  105. package/lib/queryBuilder/operations/WhereInOperation.js +48 -0
  106. package/lib/queryBuilder/operations/eager/EagerOperation.js +61 -0
  107. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +51 -0
  108. package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +13 -0
  109. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +285 -0
  110. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +36 -0
  111. package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +53 -0
  112. package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +31 -0
  113. package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +31 -0
  114. package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +167 -0
  115. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +164 -0
  116. package/lib/queryBuilder/operations/select/SelectOperation.js +66 -0
  117. package/lib/queryBuilder/operations/select/Selection.js +123 -0
  118. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +117 -0
  119. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +100 -0
  120. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +104 -0
  121. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +886 -0
  122. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs +97 -0
  123. package/lib/queryBuilder/parsers/relationExpressionParser.js +1042 -0
  124. package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +141 -0
  125. package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +22 -0
  126. package/lib/queryBuilder/transformations/QueryTransformation.js +11 -0
  127. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +71 -0
  128. package/lib/queryBuilder/transformations/index.js +15 -0
  129. package/lib/queryBuilder/whereScopes.js +227 -0
  130. package/lib/relations/Relation.js +401 -0
  131. package/lib/relations/RelationDeleteOperation.js +29 -0
  132. package/lib/relations/RelationFindOperation.js +149 -0
  133. package/lib/relations/RelationInsertOperation.js +33 -0
  134. package/lib/relations/RelationOwner.js +236 -0
  135. package/lib/relations/RelationProperty.js +247 -0
  136. package/lib/relations/RelationUpdateOperation.js +30 -0
  137. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +23 -0
  138. package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +44 -0
  139. package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +50 -0
  140. package/lib/relations/belongsToOne/BelongsToOneRelation.js +53 -0
  141. package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +61 -0
  142. package/lib/relations/hasMany/HasManyInsertOperation.js +61 -0
  143. package/lib/relations/hasMany/HasManyRelateOperation.js +64 -0
  144. package/lib/relations/hasMany/HasManyRelation.js +49 -0
  145. package/lib/relations/hasMany/HasManyUnrelateOperation.js +33 -0
  146. package/lib/relations/hasOne/HasOneRelation.js +13 -0
  147. package/lib/relations/hasOneThrough/HasOneThroughRelation.js +13 -0
  148. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +105 -0
  149. package/lib/relations/manyToMany/ManyToManyRelation.js +507 -0
  150. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +36 -0
  151. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +10 -0
  152. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +26 -0
  153. package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +12 -0
  154. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +105 -0
  155. package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +52 -0
  156. package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +81 -0
  157. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +14 -0
  158. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +34 -0
  159. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +16 -0
  160. package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +10 -0
  161. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +82 -0
  162. package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +12 -0
  163. package/lib/transaction.js +96 -0
  164. package/lib/utils/assert.js +25 -0
  165. package/lib/utils/buildUtils.js +21 -0
  166. package/lib/utils/classUtils.js +13 -0
  167. package/lib/utils/clone.js +2329 -0
  168. package/lib/utils/createModifier.js +42 -0
  169. package/lib/utils/deprecate.js +15 -0
  170. package/lib/utils/identifierMapping.js +227 -0
  171. package/lib/utils/internalPropUtils.js +11 -0
  172. package/lib/utils/knexUtils.js +76 -0
  173. package/lib/utils/mixin.js +25 -0
  174. package/lib/utils/mysqlJsonPath.js +55 -0
  175. package/lib/utils/normalizeIds.js +144 -0
  176. package/lib/utils/objectUtils.js +416 -0
  177. package/lib/utils/parseFieldExpression.js +51 -0
  178. package/lib/utils/postgresJsonPath.js +110 -0
  179. package/lib/utils/promiseUtils/after.js +17 -0
  180. package/lib/utils/promiseUtils/afterReturn.js +17 -0
  181. package/lib/utils/promiseUtils/index.js +17 -0
  182. package/lib/utils/promiseUtils/isPromise.js +11 -0
  183. package/lib/utils/promiseUtils/map.js +110 -0
  184. package/lib/utils/promiseUtils/mapAfterAllReturn.js +29 -0
  185. package/lib/utils/promiseUtils/try.js +22 -0
  186. package/lib/utils/resolveModel.js +107 -0
  187. package/lib/utils/tmpColumnUtils.js +16 -0
  188. package/package.json +86 -4
  189. package/typings/objection/index.d.ts +4104 -0
  190. package/typings/objection/relation-expression.d.ts +237 -0
@@ -0,0 +1,285 @@
1
+ 'use strict';
2
+
3
+ const promiseUtils = require('../../../utils/promiseUtils');
4
+
5
+ const { EagerOperation } = require('./EagerOperation');
6
+ const { isMsSql, isOracle, isSqlite } = require('../../../utils/knexUtils');
7
+ const { isObject, asArray, flatten, chunk } = require('../../../utils/objectUtils');
8
+ const { ValidationErrorType } = require('../../../model/ValidationError');
9
+ const { createModifier } = require('../../../utils/createModifier');
10
+ const { RelationDoesNotExistError } = require('../../../model/RelationDoesNotExistError');
11
+ const { RelationOwner } = require('../../../relations/RelationOwner');
12
+
13
+ class WhereInEagerOperation extends EagerOperation {
14
+ constructor(name, opt) {
15
+ super(name, opt);
16
+
17
+ this.relationsToFetch = [];
18
+ this.omitProps = [];
19
+ }
20
+
21
+ batchSize(knex) {
22
+ if (this.graphOptions.maxBatchSize) {
23
+ return this.graphOptions.maxBatchSize;
24
+ } else if (isMsSql(knex)) {
25
+ // On MSSQL the parameter limit is actually 2100, but since I couldn't figure out
26
+ // if the limit is for all parameters in a query or for individual clauses, we set
27
+ // the limit to 2000 to leave 100 parameters for where clauses etc.
28
+ return 2000;
29
+ } else if (isOracle(knex)) {
30
+ return 1000;
31
+ } else if (isSqlite(knex)) {
32
+ // SQLITE_MAX_VARIABLE_NUMBER is 999 by default
33
+ return 999;
34
+ } else {
35
+ // I'm sure there is some kind of limit for other databases too, but let's lower
36
+ // this if someone ever hits those limits.
37
+ return 10000;
38
+ }
39
+ }
40
+
41
+ onBuild(builder) {
42
+ const relationsToFetch = findRelationsToFetch(
43
+ builder.modelClass(),
44
+ this.buildFinalExpression(),
45
+ );
46
+
47
+ const { selectionsToAdd, selectedProps } = findRelationPropsToSelect(builder, relationsToFetch);
48
+
49
+ if (selectionsToAdd.length) {
50
+ builder.select(selectionsToAdd);
51
+ }
52
+
53
+ this.relationsToFetch = relationsToFetch;
54
+ this.omitProps = selectedProps;
55
+ }
56
+
57
+ async onAfter2(builder, result) {
58
+ const modelClass = builder.resultModelClass();
59
+
60
+ if (!result) {
61
+ return result;
62
+ }
63
+
64
+ const models = asArray(result);
65
+
66
+ // Check models to be actual objects, to filter out `count` results (#2397).
67
+ if (!models.length || !isObject(models[0])) {
68
+ return result;
69
+ }
70
+
71
+ await promiseUtils.map(
72
+ this.relationsToFetch,
73
+ (it) => this.fetchRelation(builder, models, it.relation, it.childExpression),
74
+ { concurrency: modelClass.getConcurrency(builder.unsafeKnex()) },
75
+ );
76
+
77
+ const intOpt = builder.internalOptions();
78
+
79
+ if (!this.omitProps.length || intOpt.keepImplicitJoinProps) {
80
+ return result;
81
+ }
82
+
83
+ // Now that relations have been fetched for `models` we can omit the
84
+ // columns that were implicitly selected by this class.
85
+ for (let i = 0, l = result.length; i < l; ++i) {
86
+ const model = result[i];
87
+
88
+ for (let c = 0, lc = this.omitProps.length; c < lc; ++c) {
89
+ modelClass.omitImpl(model, this.omitProps[c]);
90
+ }
91
+ }
92
+
93
+ return result;
94
+ }
95
+
96
+ async fetchRelation(builder, models, relation, expr) {
97
+ const modelClass = builder.resultModelClass();
98
+ const batchSize = this.batchSize(builder.knex());
99
+ const modelBatches = chunk(models, batchSize);
100
+
101
+ const result = await promiseUtils.map(
102
+ modelBatches,
103
+ (batch) => this.fetchRelationBatch(builder, batch, relation, expr),
104
+ {
105
+ concurrency: modelClass.getConcurrency(builder.unsafeKnex()),
106
+ },
107
+ );
108
+
109
+ return flatten(result);
110
+ }
111
+
112
+ fetchRelationBatch(builder, models, relation, expr) {
113
+ if (this.shouldSkipFetched(models, relation, expr)) {
114
+ return this.createSkippedQuery(builder, models, relation, expr);
115
+ }
116
+
117
+ const queryBuilder = this.createRelationQuery(builder, relation, expr);
118
+ const findOperation = relation.find(queryBuilder, RelationOwner.create(models));
119
+
120
+ findOperation.alwaysReturnArray = true;
121
+ findOperation.assignResultToOwner = true;
122
+ findOperation.relationProperty = expr.node.$name;
123
+
124
+ queryBuilder.addOperation(findOperation, []);
125
+
126
+ for (const modifierName of expr.node.$modify) {
127
+ const modifier = createModifier({
128
+ modifier: modifierName,
129
+ modelClass: relation.relatedModelClass,
130
+ modifiers: this.buildFinalModifiers(builder),
131
+ });
132
+
133
+ try {
134
+ modifier(queryBuilder);
135
+ } catch (err) {
136
+ const modelClass = builder.modelClass();
137
+
138
+ if (err instanceof modelClass.ModifierNotFoundError) {
139
+ throw modelClass.createValidationError({
140
+ type: ValidationErrorType.RelationExpression,
141
+ message: `could not find modifier "${modifierName}" for relation "${relation.name}"`,
142
+ });
143
+ } else {
144
+ throw err;
145
+ }
146
+ }
147
+ }
148
+
149
+ return queryBuilder;
150
+ }
151
+
152
+ shouldSkipFetched(models, relation, expr) {
153
+ if (!this.graphOptions.skipFetched) {
154
+ return false;
155
+ }
156
+
157
+ if (models.some((it) => it[expr.node.$name] === undefined)) {
158
+ return false;
159
+ }
160
+
161
+ const relationsToFetch = findRelationsToFetch(relation.relatedModelClass, expr);
162
+ const childModels = getRelatedModels(models, expr);
163
+
164
+ // We can only skip fetching a relation if all already fetched models
165
+ // have all needed relation properties so that we can fetch the next
166
+ // level of relations.
167
+ for (const { relation } of relationsToFetch) {
168
+ const { ownerProp } = relation;
169
+
170
+ for (let c = 0, lc = ownerProp.size; c < lc; ++c) {
171
+ const prop = ownerProp.props[c];
172
+
173
+ for (const model of childModels) {
174
+ if (model[prop] === undefined) {
175
+ return false;
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ return true;
182
+ }
183
+
184
+ createSkippedQuery(builder, models, relation, expr) {
185
+ const childModels = getRelatedModels(models, expr);
186
+
187
+ return relation.relatedModelClass
188
+ .query()
189
+ .childQueryOf(builder)
190
+ .findOptions({ dontCallFindHooks: true })
191
+ .withGraphFetched(expr, this.graphOptions)
192
+ .resolve(childModels);
193
+ }
194
+
195
+ createRelationQuery(builder, relation, expr) {
196
+ return relation.relatedModelClass
197
+ .query()
198
+ .childQueryOf(builder)
199
+ .withGraphFetched(expr, this.graphOptions)
200
+ .modifiers(this.buildFinalModifiers(builder));
201
+ }
202
+
203
+ clone() {
204
+ const clone = super.clone();
205
+
206
+ clone.relationsToFetch = this.relationsToFetch.slice();
207
+ clone.omitProps = this.omitProps.slice();
208
+
209
+ return clone;
210
+ }
211
+ }
212
+
213
+ function findRelationsToFetch(modelClass, eagerExpression) {
214
+ const relationsToFetch = [];
215
+
216
+ try {
217
+ eagerExpression.forEachChildExpression(modelClass, (childExpression, relation) => {
218
+ relationsToFetch.push({
219
+ childExpression,
220
+ relation,
221
+ });
222
+ });
223
+ } catch (err) {
224
+ if (err instanceof RelationDoesNotExistError) {
225
+ throw modelClass.createValidationError({
226
+ type: ValidationErrorType.RelationExpression,
227
+ message: `unknown relation "${err.relationName}" in an eager expression`,
228
+ });
229
+ }
230
+ throw err;
231
+ }
232
+
233
+ return relationsToFetch;
234
+ }
235
+
236
+ function findRelationPropsToSelect(builder, relationsToFetch) {
237
+ const selectionsToAdd = [];
238
+ const selectedProps = [];
239
+
240
+ // Collect columns that need to be selected for the eager fetch
241
+ // to work that are not currently selected.
242
+ for (const { relation } of relationsToFetch) {
243
+ const ownerProp = relation.ownerProp;
244
+
245
+ for (let c = 0, lc = ownerProp.size; c < lc; ++c) {
246
+ const fullCol = ownerProp.ref(builder, c).fullColumn(builder);
247
+ const prop = ownerProp.props[c];
248
+ const col = ownerProp.cols[c];
249
+
250
+ if (!builder.hasSelectionAs(fullCol, col) && selectionsToAdd.indexOf(fullCol) === -1) {
251
+ selectedProps.push(prop);
252
+ selectionsToAdd.push(fullCol);
253
+ }
254
+ }
255
+ }
256
+
257
+ return {
258
+ selectionsToAdd,
259
+ selectedProps,
260
+ };
261
+ }
262
+
263
+ function getRelatedModels(models, expr) {
264
+ const allRelated = [];
265
+
266
+ for (const model of models) {
267
+ const related = model[expr.node.$name];
268
+
269
+ if (related) {
270
+ if (Array.isArray(related)) {
271
+ for (const rel of related) {
272
+ allRelated.push(rel);
273
+ }
274
+ } else {
275
+ allRelated.push(related);
276
+ }
277
+ }
278
+ }
279
+
280
+ return allRelated;
281
+ }
282
+
283
+ module.exports = {
284
+ WhereInEagerOperation,
285
+ };
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ const postgresJsonApi = require('./postgresJsonApi');
4
+ const mysqlJsonApi = require('./mysqlJsonApi');
5
+ const { getJsonDialect } = require('../../../utils/postgresJsonPath');
6
+ const { ObjectionToKnexConvertingOperation } = require('../ObjectionToKnexConvertingOperation');
7
+
8
+ // `whereJsonHasAny` and `whereJsonHasAll`.
9
+ class WhereJsonHasOperation extends ObjectionToKnexConvertingOperation {
10
+ onBuildKnex(knexBuilder, builder) {
11
+ const dialect = getJsonDialect(builder.unsafeKnex(), this.name);
12
+ const args = this.getKnexArgs(builder);
13
+
14
+ const [sql, bindings] =
15
+ dialect === 'mysql'
16
+ ? mysqlJsonApi.whereJsonHas(args[0], this.opt.operator, args[1])
17
+ : postgresJsonApi.whereJsonFieldRightStringArrayOnLeftQuery(
18
+ builder.knex(),
19
+ args[0],
20
+ this.opt.operator,
21
+ args[1],
22
+ );
23
+
24
+ if (this.opt.bool === 'or') {
25
+ knexBuilder = knexBuilder.orWhereRaw(sql, bindings);
26
+ } else {
27
+ knexBuilder = knexBuilder.whereRaw(sql, bindings);
28
+ }
29
+
30
+ return knexBuilder;
31
+ }
32
+ }
33
+
34
+ module.exports = {
35
+ WhereJsonHasOperation,
36
+ };
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ const jsonApi = require('./postgresJsonApi');
4
+ const mysqlJsonApi = require('./mysqlJsonApi');
5
+ const { getJsonDialect } = require('../../../utils/postgresJsonPath');
6
+ const { ObjectionToKnexConvertingOperation } = require('../ObjectionToKnexConvertingOperation');
7
+
8
+ // `whereJsonNotArray` and `whereJsonNotObject`.
9
+ class WhereJsonNotObjectOperation extends ObjectionToKnexConvertingOperation {
10
+ onBuildKnex(knexBuilder, builder) {
11
+ const dialect = getJsonDialect(builder.unsafeKnex(), this.name);
12
+ const fieldExpression = this.getKnexArgs(builder)[0];
13
+
14
+ if (dialect === 'mysql') {
15
+ const [sql, bindings] = mysqlJsonApi.whereJsonNotContains(
16
+ fieldExpression,
17
+ this.opt.compareValue,
18
+ );
19
+
20
+ return this.opt.bool === 'or'
21
+ ? knexBuilder.orWhereRaw(sql, bindings)
22
+ : knexBuilder.whereRaw(sql, bindings);
23
+ }
24
+
25
+ return this.whereJsonNotObject(knexBuilder, builder.knex(), fieldExpression);
26
+ }
27
+
28
+ whereJsonNotObject(knexBuilder, knex, fieldExpression) {
29
+ const innerQuery = (innerQuery) => {
30
+ const builder = jsonApi.whereJsonbRefOnLeftJsonbValOrRefOnRight(
31
+ innerQuery,
32
+ fieldExpression,
33
+ '@>',
34
+ this.opt.compareValue,
35
+ 'not',
36
+ );
37
+
38
+ builder.orWhereRaw(...jsonApi.whereJsonFieldQuery(knex, fieldExpression, 'IS', null));
39
+ };
40
+
41
+ if (this.opt.bool === 'or') {
42
+ knexBuilder = knexBuilder.orWhere(innerQuery);
43
+ } else {
44
+ knexBuilder = knexBuilder.where(innerQuery);
45
+ }
46
+
47
+ return knexBuilder;
48
+ }
49
+ }
50
+
51
+ module.exports = {
52
+ WhereJsonNotObjectOperation,
53
+ };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const postgresJsonApi = require('./postgresJsonApi');
4
+ const mysqlJsonApi = require('./mysqlJsonApi');
5
+ const { getJsonDialect } = require('../../../utils/postgresJsonPath');
6
+ const { ObjectionToKnexConvertingOperation } = require('../ObjectionToKnexConvertingOperation');
7
+
8
+ // `whereJsonSupersetOf`, `whereJsonSubsetOf` and their variants.
9
+ class WhereJsonOperation extends ObjectionToKnexConvertingOperation {
10
+ onBuildKnex(knexBuilder, builder) {
11
+ const dialect = getJsonDialect(builder.unsafeKnex(), this.name);
12
+ const args = this.getKnexArgs(builder);
13
+ const jsonApi =
14
+ dialect === 'mysql'
15
+ ? mysqlJsonApi.whereJsonContains
16
+ : postgresJsonApi.whereJsonbRefOnLeftJsonbValOrRefOnRightRawQueryParams;
17
+ const rawArgs = jsonApi(args[0], this.opt.operator, args[1], this.opt.prefix);
18
+
19
+ if (this.opt.bool === 'or') {
20
+ knexBuilder = knexBuilder.orWhereRaw.apply(knexBuilder, rawArgs);
21
+ } else {
22
+ knexBuilder = knexBuilder.whereRaw.apply(knexBuilder, rawArgs);
23
+ }
24
+
25
+ return knexBuilder;
26
+ }
27
+ }
28
+
29
+ module.exports = {
30
+ WhereJsonOperation,
31
+ };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const mysqlJsonApi = require('./mysqlJsonApi');
4
+ const { getJsonDialect } = require('../../../utils/postgresJsonPath');
5
+ const { ObjectionToKnexConvertingOperation } = require('../ObjectionToKnexConvertingOperation');
6
+
7
+ // `whereJsonOverlaps` and its variants. Postgres has no operator for this.
8
+ class WhereJsonOverlapsOperation extends ObjectionToKnexConvertingOperation {
9
+ onBuildKnex(knexBuilder, builder) {
10
+ const knex = builder.unsafeKnex();
11
+
12
+ if (knex && getJsonDialect(knex, this.name) !== 'mysql') {
13
+ throw new Error(`${this.name} is only supported on mysql.`);
14
+ }
15
+
16
+ const args = this.getKnexArgs(builder);
17
+ const [sql, bindings] = mysqlJsonApi.whereJsonOverlaps(args[0], args[1], this.opt.prefix);
18
+
19
+ if (this.opt.bool === 'or') {
20
+ knexBuilder = knexBuilder.orWhereRaw(sql, bindings);
21
+ } else {
22
+ knexBuilder = knexBuilder.whereRaw(sql, bindings);
23
+ }
24
+
25
+ return knexBuilder;
26
+ }
27
+ }
28
+
29
+ module.exports = {
30
+ WhereJsonOverlapsOperation,
31
+ };
@@ -0,0 +1,167 @@
1
+ 'use strict';
2
+
3
+ const { parseFieldExpression } = require('../../../utils/parseFieldExpression');
4
+ const { asArray, isObject, isPlainObject, isString } = require('../../../utils/objectUtils');
5
+ const { toMySqlJsonPath } = require('../../../utils/mysqlJsonPath');
6
+
7
+ // The MySQL versions of the `whereJson*` methods. They mirror the postgres
8
+ // operators (`@>`, `<@`, `?|`, `?&`) so that the methods find the same rows on
9
+ // both databases.
10
+ //
11
+ // The parts of the queries are `{ sql, bindings, type }` objects. `type` is
12
+ // `'object'` or `'nonObject'` when the JSON type of the part is known.
13
+
14
+ // Joins sql parts: sql`JSON_CONTAINS(${target}, ${candidate})`.
15
+ function sql(strings, ...parts) {
16
+ let text = strings[0];
17
+ const bindings = [];
18
+
19
+ parts.forEach((part, i) => {
20
+ text += part.sql + strings[i + 1];
21
+ bindings.push(...part.bindings);
22
+ });
23
+
24
+ return { sql: text, bindings };
25
+ }
26
+
27
+ function literal(text) {
28
+ return { sql: text, bindings: [] };
29
+ }
30
+
31
+ // A field expression like `Table.column:a.b[0]`.
32
+ function field(expression) {
33
+ const parsed = parseFieldExpression(expression);
34
+
35
+ if (parsed.access.length === 0) {
36
+ return { sql: '??', bindings: [parsed.columnName] };
37
+ } else {
38
+ return {
39
+ sql: 'JSON_EXTRACT(??, ?)',
40
+ bindings: [parsed.columnName, toMySqlJsonPath(parsed.access)],
41
+ };
42
+ }
43
+ }
44
+
45
+ function jsonValue(value) {
46
+ return {
47
+ sql: 'CAST(? AS JSON)',
48
+ bindings: [JSON.stringify(value)],
49
+ type: isPlainObject(value) ? 'object' : 'nonObject',
50
+ };
51
+ }
52
+
53
+ // The right hand side of the methods: a field expression or a JSON value.
54
+ function fieldOrValue(value) {
55
+ if (isString(value)) {
56
+ return field(value);
57
+ } else if (isObject(value)) {
58
+ return jsonValue(value);
59
+ } else {
60
+ throw new Error('Invalid right hand expression.');
61
+ }
62
+ }
63
+
64
+ // `target @> candidate`. On postgres an object is only contained in an object, but
65
+ // MySQL's `JSON_CONTAINS()` also finds objects inside arrays.
66
+ function contains(target, candidate) {
67
+ const containsSql = sql`JSON_CONTAINS(${target}, ${candidate})`;
68
+ let guard = null;
69
+
70
+ if (candidate.type === 'nonObject' || target.type === 'object') {
71
+ guard = null;
72
+ } else if (candidate.type === 'object') {
73
+ guard = target.type === 'nonObject' ? literal('FALSE') : sql`JSON_TYPE(${target}) = 'OBJECT'`;
74
+ } else if (target.type === 'nonObject') {
75
+ guard = sql`JSON_TYPE(${candidate}) <> 'OBJECT'`;
76
+ } else {
77
+ guard = sql`(JSON_TYPE(${candidate}) <> 'OBJECT' OR JSON_TYPE(${target}) = 'OBJECT')`;
78
+ }
79
+
80
+ return guard ? sql`(${containsSql} AND ${guard})` : containsSql;
81
+ }
82
+
83
+ function not(part, prefix) {
84
+ return prefix === 'not' ? sql`NOT ${part}` : part;
85
+ }
86
+
87
+ function toRawArgs(part) {
88
+ return [part.sql, part.bindings];
89
+ }
90
+
91
+ // `whereJsonSupersetOf` (`@>`) and `whereJsonSubsetOf` (`<@`).
92
+ function whereJsonContains(fieldExpression, operator, jsonObjectOrFieldExpression, prefix) {
93
+ const left = field(fieldExpression);
94
+ const right = fieldOrValue(jsonObjectOrFieldExpression);
95
+ const condition = operator === '@>' ? contains(left, right) : contains(right, left);
96
+
97
+ return toRawArgs(not(condition, prefix));
98
+ }
99
+
100
+ // `whereJsonNotArray` and `whereJsonNotObject`. Like on postgres, NULL is neither
101
+ // an array nor an object.
102
+ function whereJsonNotContains(fieldExpression, compareValue) {
103
+ const left = field(fieldExpression);
104
+ return toRawArgs(sql`(NOT ${contains(left, jsonValue(compareValue))} OR ${left} IS NULL)`);
105
+ }
106
+
107
+ // `whereJsonHasAny` (`?|`) and `whereJsonHasAll` (`?&`). Like on postgres, the keys
108
+ // are looked for in the keys of objects, in the string items of arrays and in
109
+ // strings.
110
+ function whereJsonHas(fieldExpression, operator, keys) {
111
+ const left = field(fieldExpression);
112
+ const all = operator === '?&';
113
+
114
+ keys = asArray(keys);
115
+
116
+ for (const key of keys) {
117
+ if (!isString(key)) {
118
+ throw new Error('All keys to find must be strings.');
119
+ }
120
+ }
121
+
122
+ if (keys.length === 0) {
123
+ return toRawArgs(all ? sql`${left} IS NOT NULL` : literal('FALSE'));
124
+ }
125
+
126
+ const paths = {
127
+ sql: keys.map(() => '?').join(', '),
128
+ bindings: keys.map((key) => toMySqlJsonPath([{ type: 'object', ref: key }])),
129
+ };
130
+
131
+ const keysJson = jsonValue(keys);
132
+ const stringChecks = keys.map((key) => ({
133
+ sql: `JSON_UNQUOTE(${left.sql}) = ?`,
134
+ bindings: [...left.bindings, key],
135
+ }));
136
+
137
+ const stringCheck = {
138
+ sql: `(${stringChecks.map((it) => it.sql).join(all ? ' AND ' : ' OR ')})`,
139
+ bindings: stringChecks.flatMap((it) => it.bindings),
140
+ };
141
+
142
+ const objectCheck = sql`JSON_CONTAINS_PATH(${left}, ${literal(all ? "'all'" : "'one'")}, ${paths})`;
143
+ const arrayCheck = all
144
+ ? sql`JSON_CONTAINS(${left}, ${keysJson})`
145
+ : sql`JSON_OVERLAPS(${left}, ${keysJson})`;
146
+
147
+ return toRawArgs(
148
+ sql`(CASE JSON_TYPE(${left}) WHEN 'OBJECT' THEN ${objectCheck} WHEN 'ARRAY' THEN ${arrayCheck} WHEN 'STRING' THEN ${stringCheck} ELSE FALSE END)`,
149
+ );
150
+ }
151
+
152
+ // `whereJsonOverlaps`. A NULL or missing field doesn't overlap anything, so
153
+ // `whereJsonNotOverlaps` finds it.
154
+ function whereJsonOverlaps(fieldExpression, jsonObjectOrFieldExpression, prefix) {
155
+ const overlaps = sql`JSON_OVERLAPS(${field(fieldExpression)}, ${fieldOrValue(
156
+ jsonObjectOrFieldExpression,
157
+ )})`;
158
+
159
+ return toRawArgs(prefix === 'not' ? sql`NOT COALESCE(${overlaps}, FALSE)` : overlaps);
160
+ }
161
+
162
+ module.exports = {
163
+ whereJsonContains,
164
+ whereJsonNotContains,
165
+ whereJsonHas,
166
+ whereJsonOverlaps,
167
+ };