@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,225 @@
1
+ 'use strict';
2
+
3
+ const { GraphAction } = require('../GraphAction');
4
+ const { groupBy, chunk, get, set } = require('../../../utils/objectUtils');
5
+ const { ModelGraphEdge } = require('../../../model/graph/ModelGraphEdge');
6
+ const promiseUtils = require('../../../utils/promiseUtils');
7
+
8
+ /**
9
+ * Inserts a batch of nodes for a GraphInsert.
10
+ *
11
+ * One of these is created for each batch of nodes that can be inserted at once.
12
+ * However, the nodes can have a different table and not all databases support
13
+ * batch inserts, so this class splits the inserts into further sub batches
14
+ * when needed.
15
+ */
16
+ class GraphInsertAction extends GraphAction {
17
+ constructor(graphData, { nodes, dependencies }) {
18
+ super(graphData);
19
+
20
+ // Nodes to insert.
21
+ this.nodes = nodes;
22
+ this.dependencies = dependencies;
23
+ }
24
+
25
+ run(builder) {
26
+ const batches = this._createInsertBatches(builder);
27
+ const concurrency = this._getConcurrency(builder, this.nodes);
28
+
29
+ return promiseUtils.map(batches, (batch) => this._insertBatch(builder, batch), { concurrency });
30
+ }
31
+
32
+ _createInsertBatches(builder) {
33
+ const batches = [];
34
+ const batchSize = this._getBatchSize(builder);
35
+ const nodesByModelClass = groupBy(this.nodes, getModelClass);
36
+
37
+ for (const nodes of nodesByModelClass.values()) {
38
+ for (const nodeBatch of chunk(nodes, batchSize)) {
39
+ batches.push(nodeBatch);
40
+ }
41
+ }
42
+
43
+ return batches;
44
+ }
45
+
46
+ async _insertBatch(parentBuilder, nodes) {
47
+ await this._beforeInsert(nodes);
48
+ await this._insert(parentBuilder, nodes);
49
+ await this._afterInsert(nodes);
50
+ }
51
+
52
+ _beforeInsert(nodes) {
53
+ this._resolveDependencies(nodes);
54
+ this._omitManyToManyExtraProps(nodes);
55
+ this._copyRelationPropsFromCurrentIfNeeded(nodes);
56
+
57
+ return Promise.resolve();
58
+ }
59
+
60
+ _resolveDependencies(nodes) {
61
+ for (const node of nodes) {
62
+ const edges = this.dependencies.get(node);
63
+
64
+ if (edges) {
65
+ for (const edge of edges) {
66
+ // `node` needs `dependencyNode` to have been inserted (and it has been).
67
+ const dependencyNode = edge.getOtherNode(node);
68
+
69
+ this._resolveDependency(dependencyNode, edge);
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ _resolveDependency(dependencyNode, edge) {
76
+ if (edge.type === ModelGraphEdge.Type.Relation && !edge.relation.joinTable) {
77
+ this._resolveRelationDependency(dependencyNode, edge);
78
+ } else if (edge.refType === ModelGraphEdge.ReferenceType.Property) {
79
+ this._resolvePropertyReferenceNode(dependencyNode, edge);
80
+ }
81
+ }
82
+
83
+ _resolveRelationDependency(dependencyNode, edge) {
84
+ const dependentNode = edge.getOtherNode(dependencyNode);
85
+
86
+ let sourceProp;
87
+ let targetProp;
88
+
89
+ if (edge.isOwnerNode(dependencyNode)) {
90
+ sourceProp = edge.relation.ownerProp;
91
+ targetProp = edge.relation.relatedProp;
92
+ } else {
93
+ targetProp = edge.relation.ownerProp;
94
+ sourceProp = edge.relation.relatedProp;
95
+ }
96
+
97
+ this._resolveReferences(dependencyNode);
98
+
99
+ targetProp.forEach((i) => {
100
+ targetProp.setProp(dependentNode.obj, i, sourceProp.getProp(dependencyNode.obj, i));
101
+ });
102
+ }
103
+
104
+ _resolvePropertyReferenceNode(dependencyNode, edge) {
105
+ const dependentNode = edge.getOtherNode(dependencyNode);
106
+
107
+ let sourcePath;
108
+ let targetPath;
109
+
110
+ if (edge.isOwnerNode(dependencyNode)) {
111
+ sourcePath = edge.refOwnerDataPath;
112
+ targetPath = edge.refRelatedDataPath;
113
+ } else {
114
+ targetPath = edge.refOwnerDataPath;
115
+ sourcePath = edge.refRelatedDataPath;
116
+ }
117
+
118
+ const sourceValue = get(dependencyNode.obj, sourcePath);
119
+ const targetValue = get(dependentNode.obj, targetPath);
120
+
121
+ if (targetValue === edge.refMatch) {
122
+ set(dependentNode.obj, targetPath, sourceValue);
123
+ } else {
124
+ set(dependentNode.obj, targetPath, targetValue.replace(edge.refMatch, sourceValue));
125
+ }
126
+ }
127
+
128
+ _omitManyToManyExtraProps(nodes) {
129
+ for (const node of nodes) {
130
+ if (
131
+ node.parentEdge &&
132
+ node.parentEdge.type === ModelGraphEdge.Type.Relation &&
133
+ node.parentEdge.relation.joinTableExtras.length > 0
134
+ ) {
135
+ node.parentEdge.relation.omitExtraProps([node.obj]);
136
+ }
137
+ }
138
+ }
139
+
140
+ _copyRelationPropsFromCurrentIfNeeded(nodes) {
141
+ for (const node of nodes) {
142
+ const currentNode = this.currentGraph.nodeForNode(node);
143
+
144
+ if (!currentNode) {
145
+ continue;
146
+ }
147
+
148
+ for (const edge of node.edges) {
149
+ if (edge.type !== ModelGraphEdge.Type.Relation) {
150
+ continue;
151
+ }
152
+
153
+ const prop = edge.isOwnerNode(node) ? edge.relation.ownerProp : edge.relation.relatedProp;
154
+
155
+ prop.forEach((i) => {
156
+ const value = prop.getProp(node.obj, i);
157
+
158
+ if (value !== undefined) {
159
+ return;
160
+ }
161
+
162
+ prop.setProp(node.obj, i, prop.getProp(currentNode.obj, i));
163
+ });
164
+ }
165
+ }
166
+ }
167
+
168
+ _insert(parentBuilder, nodes) {
169
+ const [{ modelClass }] = nodes;
170
+
171
+ nodes = nodes.filter((node) => {
172
+ return this.graphOptions.shouldInsert(node, this.graphData);
173
+ });
174
+
175
+ for (const node of nodes) {
176
+ delete node.obj[modelClass.uidProp];
177
+ node.obj.$validate(null, { dataPath: node.dataPathKey });
178
+ }
179
+
180
+ if (nodes.length === 0) {
181
+ return;
182
+ }
183
+
184
+ for (const node of nodes) {
185
+ node.userData.inserted = true;
186
+ }
187
+
188
+ return this._runRelationBeforeInsertMethods(parentBuilder, nodes).then(() => {
189
+ return modelClass
190
+ .query()
191
+ .insert(nodes.map((node) => node.obj))
192
+ .childQueryOf(parentBuilder)
193
+ .copyFrom(parentBuilder, GraphAction.ReturningAllSelector)
194
+ .execute();
195
+ });
196
+ }
197
+
198
+ _runRelationBeforeInsertMethods(parentBuilder, nodes) {
199
+ return Promise.all(
200
+ nodes.map((node) => {
201
+ if (node.parentEdge) {
202
+ return node.parentEdge.relation.beforeInsert(node.obj, parentBuilder.context());
203
+ } else {
204
+ return null;
205
+ }
206
+ }),
207
+ );
208
+ }
209
+
210
+ _afterInsert(nodes) {
211
+ for (const node of nodes) {
212
+ for (const refNode of node.referencingNodes) {
213
+ this._resolveDependency(refNode, refNode.parentEdge);
214
+ }
215
+ }
216
+ }
217
+ }
218
+
219
+ function getModelClass(node) {
220
+ return node.modelClass;
221
+ }
222
+
223
+ module.exports = {
224
+ GraphInsertAction,
225
+ };
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ const { GraphAction } = require('../GraphAction');
4
+ const { groupBy, chunk } = require('../../../utils/objectUtils');
5
+ const promiseUtils = require('../../../utils/promiseUtils');
6
+
7
+ class JoinRowGraphInsertAction extends GraphAction {
8
+ constructor(graphData, { nodes }) {
9
+ super(graphData);
10
+ this.nodes = nodes;
11
+ }
12
+
13
+ run(builder) {
14
+ const batches = this._createInsertBatches(builder);
15
+ const concurrency = this._getConcurrency(builder, this.nodes);
16
+
17
+ return promiseUtils.map(batches, (batch) => this._insertBatch(builder, batch), { concurrency });
18
+ }
19
+
20
+ _createInsertBatches(builder) {
21
+ const batches = [];
22
+ const batchSize = this._getBatchSize(builder);
23
+ const nodesByModel = groupBy(this.nodes, (node) => getJoinTableModel(builder, node));
24
+
25
+ for (const [joinTableModelClass, nodes] of nodesByModel.entries()) {
26
+ for (const nodeBatch of chunk(nodes, batchSize)) {
27
+ batches.push(this._createBatch(joinTableModelClass, nodeBatch));
28
+ }
29
+ }
30
+
31
+ return batches;
32
+ }
33
+
34
+ _createBatch(joinTableModelClass, nodes) {
35
+ return nodes
36
+ .filter((node) => {
37
+ return this.graphOptions.shouldRelate(node, this.graphData) || node.userData.inserted;
38
+ })
39
+ .map((node) => ({
40
+ node,
41
+ joinTableModelClass,
42
+ joinTableObj: this._createJoinTableObj(joinTableModelClass, node),
43
+ }));
44
+ }
45
+
46
+ _createJoinTableObj(joinTableModelClass, node) {
47
+ const { parentEdge, parentNode } = node;
48
+ const { relation } = parentEdge;
49
+
50
+ this._resolveReferences(node);
51
+
52
+ return joinTableModelClass.fromJson(
53
+ relation.createJoinModel(relation.ownerProp.getProps(parentNode.obj), node.obj),
54
+ );
55
+ }
56
+
57
+ _insertBatch(parentBuilder, batch) {
58
+ return this._beforeInsert(parentBuilder, batch).then(() => this._insert(parentBuilder, batch));
59
+ }
60
+
61
+ _beforeInsert(parentBuilder, batch) {
62
+ return Promise.all(
63
+ batch.map(({ node, joinTableObj }) => {
64
+ if (node.parentEdge) {
65
+ return node.parentEdge.relation.joinTableBeforeInsert(
66
+ joinTableObj,
67
+ parentBuilder.context(),
68
+ );
69
+ } else {
70
+ return null;
71
+ }
72
+ }),
73
+ );
74
+ }
75
+
76
+ _insert(parentBuilder, batch) {
77
+ if (batch.length > 0) {
78
+ return batch[0].joinTableModelClass
79
+ .query()
80
+ .childQueryOf(parentBuilder)
81
+ .insert(batch.map((it) => it.joinTableObj));
82
+ }
83
+ }
84
+ }
85
+
86
+ function getJoinTableModel(builder, node) {
87
+ return node.parentEdge.relation.getJoinModelClass(builder.unsafeKnex());
88
+ }
89
+
90
+ module.exports = {
91
+ JoinRowGraphInsertAction,
92
+ };
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const { GraphOperation } = require('../GraphOperation');
4
+ const { GraphPatchAction } = require('./GraphPatchAction');
5
+
6
+ class GraphPatch extends GraphOperation {
7
+ createActions() {
8
+ return [
9
+ new GraphPatchAction(this.graphData, {
10
+ nodes: this.graph.nodes.filter((node) =>
11
+ this.graphOptions.shouldPatchOrUpdateIgnoreDisable(node, this.graphData),
12
+ ),
13
+ }),
14
+ ];
15
+ }
16
+ }
17
+
18
+ module.exports = {
19
+ GraphPatch,
20
+ };
@@ -0,0 +1,258 @@
1
+ 'use strict';
2
+
3
+ const { getModel } = require('../../../model/getModel');
4
+ const { GraphAction } = require('../GraphAction');
5
+ const { isInternalProp } = require('../../../utils/internalPropUtils');
6
+ const { difference, isObject, jsonEquals } = require('../../../utils/objectUtils');
7
+ const promiseUtils = require('../../../utils/promiseUtils');
8
+
9
+ class GraphPatchAction extends GraphAction {
10
+ constructor(graphData, { nodes }) {
11
+ super(graphData);
12
+ // Nodes to patch.
13
+ this.nodes = nodes;
14
+ }
15
+
16
+ run(builder) {
17
+ return promiseUtils.map(this.nodes, (node) => this._runForNode(builder, node), {
18
+ concurrency: this._getConcurrency(builder, this.nodes),
19
+ });
20
+ }
21
+
22
+ _runForNode(builder, node) {
23
+ const shouldPatch = this.graphOptions.shouldPatch(node, this.graphData);
24
+ const shouldUpdate = this.graphOptions.shouldUpdate(node, this.graphData);
25
+
26
+ // BelongsToOneRelation inserts and relates change the parent object's
27
+ // properties. That's why we handle them here.
28
+ const changedPropsBecauseOfBelongsToOneInsert = this._handleBelongsToOneInserts(node);
29
+
30
+ // BelongsToOneRelation deletes and unrelates change the parent object's
31
+ // properties. That's why we handle them here.
32
+ const changePropsBecauseOfBelongsToOneDelete = this._handleBelongsToOneDeletes(node);
33
+
34
+ const handleUpdate = () => {
35
+ const { changedProps, unchangedProps } = this._findChanges(node);
36
+ const allProps = [...changedProps, ...unchangedProps];
37
+
38
+ const propsToUpdate = difference(
39
+ shouldPatch || shouldUpdate
40
+ ? changedProps
41
+ : [...changedPropsBecauseOfBelongsToOneInsert, ...changePropsBecauseOfBelongsToOneDelete],
42
+
43
+ // Remove id properties from the props to update. With upsertGraph
44
+ // it never makes sense to change the id.
45
+ node.modelClass.getIdPropertyArray(),
46
+ );
47
+
48
+ const update = propsToUpdate.length > 0;
49
+ if (update) {
50
+ // Don't update the fields that we know not to change.
51
+ node.obj.$omitFromDatabaseJson(difference(allProps, propsToUpdate));
52
+ node.userData.updated = true;
53
+ }
54
+
55
+ return update;
56
+ };
57
+
58
+ const Model = getModel();
59
+ // See if the model defines a beforeUpdate or $beforeUpdate hook. If it does
60
+ // not, we can check for updated properties now and drop out immediately if
61
+ // there is nothing to update. Otherwise, we need to wait for the hook to be
62
+ // called before calling handleUpdate, but only if the node contains changes
63
+ // that aren't id properties (relates). See issues #2233, #2605.
64
+ const hasBeforeUpdate =
65
+ node.obj.constructor.beforeUpdate !== Model.beforeUpdate ||
66
+ node.obj.$beforeUpdate !== Model.prototype.$beforeUpdate;
67
+
68
+ if (
69
+ (hasBeforeUpdate && !this._hasNonIdPropertyChanges(node)) ||
70
+ (!hasBeforeUpdate && !handleUpdate())
71
+ ) {
72
+ return null;
73
+ }
74
+
75
+ delete node.obj[node.modelClass.uidProp];
76
+ delete node.obj[node.modelClass.uidRefProp];
77
+ delete node.obj[node.modelClass.dbRefProp];
78
+
79
+ node.obj.$validate(null, {
80
+ dataPath: node.dataPathKey,
81
+ patch: shouldPatch || (!shouldPatch && !shouldUpdate),
82
+ });
83
+
84
+ const updateBuilder = this._createBuilder(node)
85
+ .childQueryOf(builder, childQueryOptions())
86
+ .copyFrom(builder, GraphAction.ReturningAllSelector);
87
+
88
+ if (hasBeforeUpdate) {
89
+ updateBuilder.internalContext().runBefore.push((result, builder) => {
90
+ // Call handleUpdate in the runBefore hook which runs after the
91
+ // $beforeUpdate hook, allowing it to modify the object before the
92
+ // updated properties are determined. See issue #2233.
93
+ if (hasBeforeUpdate && !handleUpdate()) {
94
+ builder.internalOptions().returnImmediatelyValue = null;
95
+ }
96
+ return result;
97
+ });
98
+ }
99
+
100
+ if (shouldPatch) {
101
+ updateBuilder.patch(node.obj);
102
+ } else {
103
+ updateBuilder.update(node.obj);
104
+ }
105
+
106
+ return updateBuilder.execute().then((result) => {
107
+ if (isObject(result) && result.$isObjectionModel) {
108
+ // Handle returning('*').
109
+ node.obj.$set(result);
110
+ }
111
+
112
+ return result;
113
+ });
114
+ }
115
+
116
+ _handleBelongsToOneInserts(node) {
117
+ const currentNode = this.currentGraph.nodeForNode(node);
118
+ const updatedProps = [];
119
+
120
+ for (const edge of node.edges) {
121
+ if (
122
+ edge.isOwnerNode(node) &&
123
+ edge.relation &&
124
+ edge.relation.isObjectionBelongsToOneRelation &&
125
+ edge.relation.relatedProp.hasProps(edge.relatedNode.obj)
126
+ ) {
127
+ const { relation } = edge;
128
+
129
+ relation.ownerProp.forEach((i) => {
130
+ const currentValue = currentNode && relation.ownerProp.getProp(currentNode.obj, i);
131
+ const relatedValue = relation.relatedProp.getProp(edge.relatedNode.obj, i);
132
+
133
+ if (currentValue != relatedValue) {
134
+ relation.ownerProp.setProp(node.obj, i, relatedValue);
135
+ updatedProps.push(relation.ownerProp.props[i]);
136
+ }
137
+ });
138
+ }
139
+ }
140
+
141
+ return updatedProps;
142
+ }
143
+
144
+ _handleBelongsToOneDeletes(node) {
145
+ const currentNode = this.currentGraph.nodeForNode(node);
146
+ const updatedProps = [];
147
+
148
+ if (!currentNode) {
149
+ return updatedProps;
150
+ }
151
+
152
+ for (const edge of currentNode.edges) {
153
+ if (
154
+ edge.isOwnerNode(currentNode) &&
155
+ edge.relation.isObjectionBelongsToOneRelation &&
156
+ node.obj[edge.relation.name] === null &&
157
+ this.graphOptions.shouldDeleteOrUnrelate(edge.relatedNode, this.graphData)
158
+ ) {
159
+ const { relation } = edge;
160
+
161
+ relation.ownerProp.forEach((i) => {
162
+ const currentValue = relation.ownerProp.getProp(currentNode.obj, i);
163
+
164
+ if (currentValue != null) {
165
+ relation.ownerProp.setProp(node.obj, i, null);
166
+ updatedProps.push(relation.ownerProp.props[i]);
167
+ }
168
+ });
169
+ }
170
+ }
171
+
172
+ return updatedProps;
173
+ }
174
+
175
+ _findChanges(node) {
176
+ const obj = node.obj;
177
+ const currentNode = this.currentGraph.nodeForNode(node);
178
+ const currentObj = (currentNode && currentNode.obj) || {};
179
+ const relationNames = node.modelClass.getRelationNames();
180
+
181
+ const unchangedProps = [];
182
+ const changedProps = [];
183
+
184
+ for (const prop of Object.keys(obj)) {
185
+ if (isInternalProp(prop) || relationNames.includes(prop)) {
186
+ continue;
187
+ }
188
+
189
+ const value = obj[prop];
190
+ const currentValue = currentObj[prop];
191
+
192
+ // If the current object doesn't have the property, we have to assume
193
+ // it changes (we cannot know if it will). If the object does have the
194
+ // property, we test non-strict equality. See issue #732.
195
+ if (currentValue === undefined || !nonStrictEquals(currentValue, value)) {
196
+ changedProps.push(prop);
197
+ } else {
198
+ unchangedProps.push(prop);
199
+ }
200
+ }
201
+
202
+ // We cannot know if the query properties cause changes to the values.
203
+ // We must assume that they do.
204
+ if (obj.$$queryProps) {
205
+ changedProps.push(...Object.keys(obj.$$queryProps));
206
+ }
207
+
208
+ return {
209
+ changedProps,
210
+ unchangedProps,
211
+ };
212
+ }
213
+
214
+ _hasNonIdPropertyChanges(node) {
215
+ const idProps = node.modelClass.getIdPropertyArray();
216
+ return this._findChanges(node).changedProps.some((prop) => !idProps.includes(prop));
217
+ }
218
+
219
+ _createBuilder(node) {
220
+ if (node.parentEdge && !node.parentEdge.relation.isObjectionHasManyRelation) {
221
+ return this._createRelatedBuilder(node);
222
+ } else {
223
+ return this._createRootBuilder(node);
224
+ }
225
+ }
226
+
227
+ _createRelatedBuilder(node) {
228
+ return node.parentNode.obj
229
+ .$relatedQuery(node.parentEdge.relation.name)
230
+ .findById(node.obj.$id());
231
+ }
232
+
233
+ _createRootBuilder(node) {
234
+ const currentNode = this.currentGraph.nodeForNode(node);
235
+ const currentObj = currentNode && currentNode.obj;
236
+
237
+ return currentObj ? currentObj.$query() : node.obj.$query();
238
+ }
239
+ }
240
+
241
+ function childQueryOptions() {
242
+ return {
243
+ fork: true,
244
+ isInternalQuery: true,
245
+ };
246
+ }
247
+
248
+ function nonStrictEquals(val1, val2) {
249
+ if (val1 == val2) {
250
+ return true;
251
+ }
252
+
253
+ return jsonEquals(val1, val2);
254
+ }
255
+
256
+ module.exports = {
257
+ GraphPatchAction,
258
+ };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const { GraphOperation } = require('../GraphOperation');
4
+ const { GraphRecursiveUpsertAction } = require('./GraphRecursiveUpsertAction');
5
+
6
+ class GraphRecursiveUpsert extends GraphOperation {
7
+ createActions() {
8
+ return [
9
+ new GraphRecursiveUpsertAction(this.graphData, {
10
+ nodes: this.graph.nodes.filter((node) => {
11
+ const shouldRelate = this.graphOptions.shouldRelate(node, this.graphData);
12
+ return shouldRelate && hasRelations(node.obj);
13
+ }),
14
+ }),
15
+ ];
16
+ }
17
+ }
18
+
19
+ function hasRelations(obj) {
20
+ for (const relationName of obj.constructor.getRelationNames()) {
21
+ if (obj[relationName] !== undefined) {
22
+ return true;
23
+ }
24
+ }
25
+
26
+ return false;
27
+ }
28
+
29
+ module.exports = {
30
+ GraphRecursiveUpsert,
31
+ };