@abedmurrar/objection 0.0.0-stage → 4.0.0-beta.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 +105 -2
  3. package/lib/index.mjs +6 -0
  4. package/lib/initialize.js +11 -0
  5. package/lib/model/AjvValidator.js +278 -0
  6. package/lib/model/Model.js +772 -0
  7. package/lib/model/ModifierNotFoundError.js +10 -0
  8. package/lib/model/NotFoundError.js +19 -0
  9. package/lib/model/RelationDoesNotExistError.js +11 -0
  10. package/lib/model/ValidationError.js +46 -0
  11. package/lib/model/Validator.js +20 -0
  12. package/lib/model/getModel.js +7 -0
  13. package/lib/model/graph/ModelGraph.js +60 -0
  14. package/lib/model/graph/ModelGraphBuilder.js +188 -0
  15. package/lib/model/graph/ModelGraphEdge.js +40 -0
  16. package/lib/model/graph/ModelGraphNode.js +201 -0
  17. package/lib/model/inheritModel.js +19 -0
  18. package/lib/model/modelBindKnex.js +69 -0
  19. package/lib/model/modelClone.js +102 -0
  20. package/lib/model/modelColPropMap.js +24 -0
  21. package/lib/model/modelConcurrency.js +24 -0
  22. package/lib/model/modelId.js +57 -0
  23. package/lib/model/modelJsonAttributes.js +72 -0
  24. package/lib/model/modelParseRelations.js +83 -0
  25. package/lib/model/modelQueryProps.js +116 -0
  26. package/lib/model/modelSet.js +116 -0
  27. package/lib/model/modelTableMetadata.js +71 -0
  28. package/lib/model/modelToJson.js +157 -0
  29. package/lib/model/modelUtils.js +40 -0
  30. package/lib/model/modelValidate.js +40 -0
  31. package/lib/model/modelValues.js +113 -0
  32. package/lib/model/modelVisitor.js +34 -0
  33. package/lib/objection.js +89 -0
  34. package/lib/queryBuilder/FunctionBuilder.js +291 -0
  35. package/lib/queryBuilder/InternalOptions.js +24 -0
  36. package/lib/queryBuilder/JoinBuilder.js +124 -0
  37. package/lib/queryBuilder/QueryBuilder.js +1136 -0
  38. package/lib/queryBuilder/QueryBuilderBase.js +666 -0
  39. package/lib/queryBuilder/QueryBuilderContext.js +20 -0
  40. package/lib/queryBuilder/QueryBuilderContextBase.js +26 -0
  41. package/lib/queryBuilder/QueryBuilderOperationSupport.js +484 -0
  42. package/lib/queryBuilder/QueryBuilderUserContext.js +25 -0
  43. package/lib/queryBuilder/RawBuilder.js +78 -0
  44. package/lib/queryBuilder/ReferenceBuilder.js +246 -0
  45. package/lib/queryBuilder/RelationExpression.js +373 -0
  46. package/lib/queryBuilder/StaticHookArguments.js +122 -0
  47. package/lib/queryBuilder/ValueBuilder.js +115 -0
  48. package/lib/queryBuilder/expressions.js +121 -0
  49. package/lib/queryBuilder/functions/mysql.js +762 -0
  50. package/lib/queryBuilder/graph/GraphAction.js +54 -0
  51. package/lib/queryBuilder/graph/GraphData.js +12 -0
  52. package/lib/queryBuilder/graph/GraphFetcher.js +155 -0
  53. package/lib/queryBuilder/graph/GraphNodeDbExistence.js +146 -0
  54. package/lib/queryBuilder/graph/GraphOperation.js +21 -0
  55. package/lib/queryBuilder/graph/GraphOptions.js +165 -0
  56. package/lib/queryBuilder/graph/GraphUpsert.js +231 -0
  57. package/lib/queryBuilder/graph/delete/GraphDelete.js +15 -0
  58. package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +56 -0
  59. package/lib/queryBuilder/graph/insert/GraphInsert.js +116 -0
  60. package/lib/queryBuilder/graph/insert/GraphInsertAction.js +183 -0
  61. package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +70 -0
  62. package/lib/queryBuilder/graph/patch/GraphPatch.js +15 -0
  63. package/lib/queryBuilder/graph/patch/GraphPatchAction.js +205 -0
  64. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +26 -0
  65. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +96 -0
  66. package/lib/queryBuilder/join/JoinResultColumn.js +19 -0
  67. package/lib/queryBuilder/join/JoinResultParser.js +106 -0
  68. package/lib/queryBuilder/join/RelationJoiner.js +258 -0
  69. package/lib/queryBuilder/join/TableNode.js +136 -0
  70. package/lib/queryBuilder/join/TableTree.js +69 -0
  71. package/lib/queryBuilder/join/utils.js +19 -0
  72. package/lib/queryBuilder/operations/DelegateOperation.js +101 -0
  73. package/lib/queryBuilder/operations/DeleteOperation.js +47 -0
  74. package/lib/queryBuilder/operations/FindByIdOperation.js +29 -0
  75. package/lib/queryBuilder/operations/FindByIdsOperation.js +23 -0
  76. package/lib/queryBuilder/operations/FindOperation.js +147 -0
  77. package/lib/queryBuilder/operations/FirstOperation.js +22 -0
  78. package/lib/queryBuilder/operations/FromOperation.js +91 -0
  79. package/lib/queryBuilder/operations/InsertAndFetchOperation.js +38 -0
  80. package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +35 -0
  81. package/lib/queryBuilder/operations/InsertGraphOperation.js +87 -0
  82. package/lib/queryBuilder/operations/InsertOperation.js +173 -0
  83. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +37 -0
  84. package/lib/queryBuilder/operations/InstanceFindOperation.js +17 -0
  85. package/lib/queryBuilder/operations/InstanceInsertOperation.js +20 -0
  86. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +45 -0
  87. package/lib/queryBuilder/operations/JoinRelatedOperation.js +83 -0
  88. package/lib/queryBuilder/operations/KnexOperation.js +11 -0
  89. package/lib/queryBuilder/operations/MergeOperation.js +43 -0
  90. package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +173 -0
  91. package/lib/queryBuilder/operations/OnBuildKnexOperation.js +23 -0
  92. package/lib/queryBuilder/operations/OnBuildOperation.js +23 -0
  93. package/lib/queryBuilder/operations/OnErrorOperation.js +23 -0
  94. package/lib/queryBuilder/operations/QueryBuilderOperation.js +258 -0
  95. package/lib/queryBuilder/operations/RangeOperation.js +37 -0
  96. package/lib/queryBuilder/operations/RelateOperation.js +20 -0
  97. package/lib/queryBuilder/operations/ReturningOperation.js +24 -0
  98. package/lib/queryBuilder/operations/RunAfterOperation.js +23 -0
  99. package/lib/queryBuilder/operations/RunBeforeOperation.js +23 -0
  100. package/lib/queryBuilder/operations/UnrelateOperation.js +18 -0
  101. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +63 -0
  102. package/lib/queryBuilder/operations/UpdateOperation.js +130 -0
  103. package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +35 -0
  104. package/lib/queryBuilder/operations/UpsertGraphOperation.js +47 -0
  105. package/lib/queryBuilder/operations/WhereCompositeOperation.js +48 -0
  106. package/lib/queryBuilder/operations/WhereInOperation.js +46 -0
  107. package/lib/queryBuilder/operations/eager/EagerOperation.js +46 -0
  108. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +41 -0
  109. package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +10 -0
  110. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +227 -0
  111. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +29 -0
  112. package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +37 -0
  113. package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +30 -0
  114. package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +33 -0
  115. package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +155 -0
  116. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +135 -0
  117. package/lib/queryBuilder/operations/select/SelectOperation.js +50 -0
  118. package/lib/queryBuilder/operations/select/Selection.js +112 -0
  119. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +84 -0
  120. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +90 -0
  121. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +95 -0
  122. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +787 -0
  123. package/lib/queryBuilder/parsers/relationExpressionParser.js +930 -0
  124. package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +17 -0
  125. package/lib/queryBuilder/transformations/QueryTransformation.js +9 -0
  126. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +57 -0
  127. package/lib/queryBuilder/transformations/index.js +9 -0
  128. package/lib/queryBuilder/whereScopes.js +199 -0
  129. package/lib/relations/Relation.js +317 -0
  130. package/lib/relations/RelationDeleteOperation.js +23 -0
  131. package/lib/relations/RelationFindOperation.js +116 -0
  132. package/lib/relations/RelationInsertOperation.js +25 -0
  133. package/lib/relations/RelationOwner.js +199 -0
  134. package/lib/relations/RelationProperty.js +207 -0
  135. package/lib/relations/RelationUpdateOperation.js +23 -0
  136. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +18 -0
  137. package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +33 -0
  138. package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +39 -0
  139. package/lib/relations/belongsToOne/BelongsToOneRelation.js +45 -0
  140. package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +46 -0
  141. package/lib/relations/hasMany/HasManyInsertOperation.js +45 -0
  142. package/lib/relations/hasMany/HasManyRelateOperation.js +48 -0
  143. package/lib/relations/hasMany/HasManyRelation.js +41 -0
  144. package/lib/relations/hasMany/HasManyUnrelateOperation.js +26 -0
  145. package/lib/relations/hasOne/HasOneRelation.js +10 -0
  146. package/lib/relations/hasOneThrough/HasOneThroughRelation.js +10 -0
  147. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +82 -0
  148. package/lib/relations/manyToMany/ManyToManyRelation.js +405 -0
  149. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +29 -0
  150. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +8 -0
  151. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +20 -0
  152. package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +8 -0
  153. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +82 -0
  154. package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +40 -0
  155. package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +64 -0
  156. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +11 -0
  157. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +27 -0
  158. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +11 -0
  159. package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +8 -0
  160. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +65 -0
  161. package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +8 -0
  162. package/lib/transaction.js +66 -0
  163. package/lib/utils/assert.js +19 -0
  164. package/lib/utils/buildUtils.js +19 -0
  165. package/lib/utils/classUtils.js +9 -0
  166. package/lib/utils/cloneDeep.js +365 -0
  167. package/lib/utils/createModifier.js +37 -0
  168. package/lib/utils/deprecate.js +11 -0
  169. package/lib/utils/identifierMapping.js +188 -0
  170. package/lib/utils/internalPropUtils.js +7 -0
  171. package/lib/utils/knexUtils.js +81 -0
  172. package/lib/utils/mixin.js +18 -0
  173. package/lib/utils/mysqlJsonPath.js +70 -0
  174. package/lib/utils/normalizeIds.js +127 -0
  175. package/lib/utils/objectUtils.js +314 -0
  176. package/lib/utils/parseFieldExpression.js +73 -0
  177. package/lib/utils/postgresJsonPath.js +96 -0
  178. package/lib/utils/promiseUtils/after.js +14 -0
  179. package/lib/utils/promiseUtils/afterReturn.js +14 -0
  180. package/lib/utils/promiseUtils/index.js +15 -0
  181. package/lib/utils/promiseUtils/isPromise.js +8 -0
  182. package/lib/utils/promiseUtils/map.js +96 -0
  183. package/lib/utils/promiseUtils/mapAfterAllReturn.js +23 -0
  184. package/lib/utils/promiseUtils/try.js +19 -0
  185. package/lib/utils/resolveModel.js +104 -0
  186. package/lib/utils/tmpColumnUtils.js +7 -0
  187. package/package.json +113 -4
  188. package/typings/objection/index.d.mts +362 -0
  189. package/typings/objection/index.d.ts +4131 -0
  190. package/typings/objection/relation-expression.d.ts +237 -0
@@ -0,0 +1,231 @@
1
+ 'use strict';
2
+ const { ModelGraph } = require('../../model/graph/ModelGraph');
3
+ const { ModelGraphEdge } = require('../../model/graph/ModelGraphEdge');
4
+ const { createNotModelError } = require('../../model/graph/ModelGraphBuilder');
5
+ const { GraphFetcher } = require('../graph/GraphFetcher');
6
+ const { GraphInsert } = require('./insert/GraphInsert');
7
+ const { GraphPatch } = require('./patch/GraphPatch');
8
+ const { GraphDelete } = require('./delete/GraphDelete');
9
+ const { GraphRecursiveUpsert } = require('./recursiveUpsert/GraphRecursiveUpsert');
10
+ const { GraphOptions } = require('./GraphOptions');
11
+ const { ValidationErrorType } = require('../../model/ValidationError');
12
+ const { RelationExpression } = require('../RelationExpression');
13
+ const { GraphNodeDbExistence } = require('./GraphNodeDbExistence');
14
+ const { GraphData } = require('./GraphData');
15
+ const { uniqBy, asArray, isObject } = require('../../utils/objectUtils');
16
+ class GraphUpsert {
17
+ constructor({ rootModelClass, objects, upsertOptions }) {
18
+ checkCanBeConvertedToModels(rootModelClass, objects);
19
+ this.objects = rootModelClass.ensureModelArray(objects, GraphUpsert.modelOptions);
20
+ this.isArray = Array.isArray(objects);
21
+ this.upsertOpt = upsertOptions;
22
+ }
23
+ static get modelOptions() {
24
+ return { skipValidation: true };
25
+ }
26
+ run(builder) {
27
+ const modelClass = builder.modelClass();
28
+ const graphOptions = new GraphOptions(this.upsertOpt);
29
+ const graph = ModelGraph.create(modelClass, this.objects);
30
+ assignDbRefsAsRelateProps(graph);
31
+ return createGraphData(builder, graphOptions, graph)
32
+ .then(checkForErrors(builder))
33
+ .then(pruneGraphs())
34
+ .then(executeOperations(builder))
35
+ .then(returnResult(this.objects, this.isArray));
36
+ }
37
+ }
38
+ function checkCanBeConvertedToModels(modelClass, objects) {
39
+ asArray(objects).forEach((obj) => {
40
+ if (!isObject(obj)) {
41
+ throw createNotModelError(modelClass, obj);
42
+ }
43
+ });
44
+ }
45
+ function assignDbRefsAsRelateProps(graph) {
46
+ for (const node of graph.nodes) {
47
+ if (!node.parentEdge || !node.parentEdge.relation || !node.isDbReference) {
48
+ continue;
49
+ }
50
+ node.parentEdge.relation.setRelateProp(node.obj, asArray(node.dbReference));
51
+ }
52
+ }
53
+ async function createGraphData(builder, graphOptions, graph) {
54
+ const currentGraph = await fetchCurrentGraph(builder, graphOptions, graph);
55
+ const nodeDbExistence = await GraphNodeDbExistence.create({
56
+ builder,
57
+ graph,
58
+ graphOptions,
59
+ currentGraph,
60
+ });
61
+ return new GraphData({ graph, currentGraph, graphOptions, nodeDbExistence });
62
+ }
63
+ function fetchCurrentGraph(builder, graphOptions, graph) {
64
+ if (graphOptions.isInsertOnly()) {
65
+ return Promise.resolve(ModelGraph.createEmpty());
66
+ }
67
+ else {
68
+ return GraphFetcher.fetchCurrentGraph({ builder, graph, graphOptions });
69
+ }
70
+ }
71
+ // Remove branches from the graph that require no operations. For example
72
+ // we never want to do anything for descendant nodes of a node that is
73
+ // deleted or unrelated. We never delete recursively.
74
+ function pruneGraphs() {
75
+ return (graphData) => {
76
+ pruneRelatedBranches(graphData);
77
+ if (!graphData.graphOptions.isInsertOnly()) {
78
+ pruneDeletedBranches(graphData);
79
+ }
80
+ return graphData;
81
+ };
82
+ }
83
+ function pruneRelatedBranches(graphData) {
84
+ const relateNodes = graphData.graph.nodes.filter((node) => {
85
+ return (!graphData.currentGraph.nodeForNode(node) &&
86
+ !graphData.graphOptions.shouldInsertIgnoreDisable(node, graphData));
87
+ });
88
+ removeBranchesFromGraph(findRoots(relateNodes), graphData.graph);
89
+ }
90
+ function pruneDeletedBranches(graphData) {
91
+ const { graph, currentGraph } = graphData;
92
+ const deleteNodes = currentGraph.nodes.filter((currentNode) => !graph.nodeForNode(currentNode));
93
+ const roots = findRoots(deleteNodes);
94
+ // Don't delete relations the current graph doesn't even mention.
95
+ // So if the parent node doesn't even have the relation, it's not
96
+ // supposed to be deleted.
97
+ const rootsNotInRelation = roots.filter((deleteRoot) => {
98
+ if (!deleteRoot.parentNode) {
99
+ return false;
100
+ }
101
+ const { relation } = deleteRoot.parentEdge;
102
+ const parentNode = graph.nodeForNode(deleteRoot.parentNode);
103
+ if (!parentNode) {
104
+ return false;
105
+ }
106
+ return parentNode.obj[relation.name] === undefined;
107
+ });
108
+ removeBranchesFromGraph(roots, currentGraph);
109
+ removeNodesFromGraph(new Set(rootsNotInRelation), currentGraph);
110
+ }
111
+ function findRoots(nodes) {
112
+ const nodeSet = new Set(nodes);
113
+ return uniqBy(nodes.filter((node) => {
114
+ let parentNode = node.parentNode;
115
+ while (parentNode) {
116
+ if (nodeSet.has(parentNode)) {
117
+ return false;
118
+ }
119
+ parentNode = parentNode.parentNode;
120
+ }
121
+ return true;
122
+ }));
123
+ }
124
+ function removeBranchesFromGraph(branchRoots, graph) {
125
+ const nodesToRemove = new Set(branchRoots.reduce((nodesToRemove, node) => [...nodesToRemove, ...node.descendantRelationNodes], []));
126
+ removeNodesFromGraph(nodesToRemove, graph);
127
+ }
128
+ function removeNodesFromGraph(nodesToRemove, graph) {
129
+ const edgesToRemove = new Set();
130
+ for (const node of nodesToRemove) {
131
+ for (const edge of node.edges) {
132
+ const otherNode = edge.getOtherNode(node);
133
+ if (!nodesToRemove.has(otherNode)) {
134
+ otherNode.removeEdge(edge);
135
+ edgesToRemove.add(edge);
136
+ }
137
+ }
138
+ }
139
+ graph.nodes = graph.nodes.filter((node) => !nodesToRemove.has(node));
140
+ graph.edges = graph.edges.filter((edge) => !edgesToRemove.has(edge));
141
+ return graph;
142
+ }
143
+ function checkForErrors(builder) {
144
+ return (graphData) => {
145
+ checkForNotFoundErrors(graphData, builder);
146
+ checkForUnallowedRelationErrors(graphData, builder);
147
+ checkForUnallowedReferenceErrors(graphData, builder);
148
+ if (graphData.graphOptions.isInsertOnly()) {
149
+ checkForHasManyRelateErrors(graphData);
150
+ }
151
+ return graphData;
152
+ };
153
+ }
154
+ function checkForNotFoundErrors(graphData, builder) {
155
+ const { graphOptions, currentGraph, graph } = graphData;
156
+ for (const node of graph.nodes) {
157
+ if (node.obj.$hasId() &&
158
+ !graphOptions.shouldInsertIgnoreDisable(node, graphData) &&
159
+ !graphOptions.shouldRelateIgnoreDisable(node, graphData) &&
160
+ !currentGraph.nodeForNode(node)) {
161
+ if (!node.parentNode) {
162
+ throw node.modelClass.createNotFoundError(builder.context(), {
163
+ message: `root model (id=${node.obj.$id()}) does not exist. If you want to insert it with an id, use the insertMissing option`,
164
+ dataPath: node.dataPath,
165
+ });
166
+ }
167
+ else {
168
+ throw node.modelClass.createNotFoundError(builder.context(), {
169
+ message: `model (id=${node.obj.$id()}) is not a child of model (id=${node.parentNode.obj.$id()}). If you want to relate it, use the relate option. If you want to insert it with an id, use the insertMissing option`,
170
+ dataPath: node.dataPath,
171
+ });
172
+ }
173
+ }
174
+ }
175
+ }
176
+ function checkForUnallowedRelationErrors(graphData, builder) {
177
+ const { graph } = graphData;
178
+ const allowedExpression = builder.allowedGraphExpression();
179
+ if (allowedExpression) {
180
+ const rootsObjs = graph.nodes.filter((node) => !node.parentEdge).map((node) => node.obj);
181
+ const expression = RelationExpression.fromModelGraph(rootsObjs);
182
+ if (!allowedExpression.isSubExpression(expression)) {
183
+ throw builder.modelClass().createValidationError({
184
+ type: ValidationErrorType.UnallowedRelation,
185
+ message: 'trying to upsert an unallowed relation',
186
+ });
187
+ }
188
+ }
189
+ }
190
+ function checkForUnallowedReferenceErrors(graphData, builder) {
191
+ const { graph, graphOptions } = graphData;
192
+ if (graphOptions.allowRefs()) {
193
+ return;
194
+ }
195
+ if (graph.edges.some((edge) => edge.type === ModelGraphEdge.Type.Reference)) {
196
+ throw builder.modelClass().createValidationError({
197
+ type: ValidationErrorType.InvalidGraph,
198
+ message: '#ref references are not allowed in a graph by default. see the allowRefs insert/upsert graph option',
199
+ });
200
+ }
201
+ }
202
+ function checkForHasManyRelateErrors(graphData) {
203
+ const { graph, graphOptions } = graphData;
204
+ for (const node of graph.nodes) {
205
+ if (graphOptions.shouldRelate(node, graphData) &&
206
+ node.parentEdge.relation.isObjectionHasManyRelation) {
207
+ throw new Error('You cannot relate HasManyRelation or HasOneRelation using insertGraph, because those require update operations. Consider using upsertGraph instead.');
208
+ }
209
+ }
210
+ }
211
+ function executeOperations(builder) {
212
+ return (graphData) => {
213
+ const operations = graphData.graphOptions.isInsertOnly()
214
+ ? [GraphInsert]
215
+ : [GraphDelete, GraphInsert, GraphPatch, GraphRecursiveUpsert];
216
+ return operations.reduce((promise, Operation) => {
217
+ const operation = new Operation(graphData);
218
+ const actions = operation.createActions();
219
+ return promise.then(() => executeActions(builder, actions));
220
+ }, Promise.resolve());
221
+ };
222
+ }
223
+ function executeActions(builder, actions) {
224
+ return actions.reduce((promise, action) => promise.then(() => action.run(builder)), Promise.resolve());
225
+ }
226
+ function returnResult(objects, isArray) {
227
+ return () => (isArray ? objects : objects[0]);
228
+ }
229
+ module.exports = {
230
+ GraphUpsert,
231
+ };
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+ const { GraphOperation } = require('../GraphOperation');
3
+ const { GraphDeleteAction } = require('./GraphDeleteAction');
4
+ class GraphDelete extends GraphOperation {
5
+ createActions() {
6
+ return [
7
+ new GraphDeleteAction(this.graphData, {
8
+ nodes: this.currentGraph.nodes.filter((currentNode) => this.graphOptions.shouldDeleteOrUnrelate(currentNode, this.graphData)),
9
+ }),
10
+ ];
11
+ }
12
+ }
13
+ module.exports = {
14
+ GraphDelete,
15
+ };
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+ const { GraphAction } = require('../GraphAction');
3
+ const { groupBy } = require('../../../utils/objectUtils');
4
+ const promiseUtils = require('../../../utils/promiseUtils');
5
+ class GraphDeleteAction extends GraphAction {
6
+ constructor(graphData, { nodes }) {
7
+ super(graphData);
8
+ // Nodes to delete.
9
+ this.nodes = nodes;
10
+ }
11
+ run(builder) {
12
+ const nodesTodelete = this._filterOutBelongsToOneRelationUnrelates(this.nodes);
13
+ const builders = this._createDeleteBuilders(builder, nodesTodelete);
14
+ return promiseUtils.map(builders, (builder) => builder.execute(), {
15
+ concurrency: this._getConcurrency(builder, nodesTodelete),
16
+ });
17
+ }
18
+ _filterOutBelongsToOneRelationUnrelates(nodes) {
19
+ // `BelongsToOneRelation` unrelate is handled by `GraphPatch` because
20
+ // unrelating a `BelongsToOneRelation` is just a matter of updating
21
+ // one field of the parent node.
22
+ return nodes.filter((node) => {
23
+ return !(this.graphOptions.shouldUnrelate(node, this.graphData) &&
24
+ node.parentEdge.relation.isObjectionBelongsToOneRelation);
25
+ });
26
+ }
27
+ _createDeleteBuilders(parentBuilder, nodesTodelete) {
28
+ const nodesByRelation = groupBy(nodesTodelete, getRelation);
29
+ const builders = [];
30
+ nodesByRelation.forEach((nodes, relation) => {
31
+ const nodesByParent = groupBy(nodes, getParent);
32
+ nodesByParent.forEach((nodes, parentNode) => {
33
+ const shouldUnrelate = this.graphOptions.shouldUnrelate(nodes[0], this.graphData);
34
+ const builder = parentNode.obj.$relatedQuery(relation.name).childQueryOf(parentBuilder);
35
+ if (!relation.isObjectionBelongsToOneRelation) {
36
+ // This is useless in case of BelongsToOneRelation.
37
+ builder.findByIds(nodes.map((node) => node.obj.$id()));
38
+ }
39
+ for (const node of nodes) {
40
+ node.userData.deleted = true;
41
+ }
42
+ builders.push(shouldUnrelate ? builder.unrelate() : builder.delete());
43
+ });
44
+ });
45
+ return builders;
46
+ }
47
+ }
48
+ function getRelation(node) {
49
+ return node.parentEdge.relation;
50
+ }
51
+ function getParent(node) {
52
+ return node.parentNode;
53
+ }
54
+ module.exports = {
55
+ GraphDeleteAction,
56
+ };
@@ -0,0 +1,116 @@
1
+ 'use strict';
2
+ const { JoinRowGraphInsertAction } = require('./JoinRowGraphInsertAction');
3
+ const { GraphInsertAction } = require('./GraphInsertAction');
4
+ const { GraphOperation } = require('../GraphOperation');
5
+ const { ModelGraphEdge } = require('../../../model/graph/ModelGraphEdge');
6
+ class GraphInsert extends GraphOperation {
7
+ constructor(...args) {
8
+ super(...args);
9
+ this.dependencies = this._createDependencyMap();
10
+ }
11
+ createActions() {
12
+ return [...this._createNormalActions(), ...this._createJoinRowActions()];
13
+ }
14
+ _createDependencyMap() {
15
+ const dependencies = new Map();
16
+ for (const edge of this.graph.edges) {
17
+ if (edge.type == ModelGraphEdge.Type.Relation) {
18
+ this._createRelationDependency(edge, dependencies);
19
+ }
20
+ else {
21
+ this._createReferenceDependency(edge, dependencies);
22
+ }
23
+ }
24
+ return dependencies;
25
+ }
26
+ _createRelationDependency(edge, dependencies) {
27
+ if (edge.relation.isObjectionHasManyRelation) {
28
+ // In case of HasManyRelation the related node depends on the owner node
29
+ // because the related node has the foreign key.
30
+ this._addDependency(edge.relatedNode, edge, dependencies);
31
+ }
32
+ else if (edge.relation.isObjectionBelongsToOneRelation) {
33
+ // In case of BelongsToOneRelation the owner node depends on the related
34
+ // node because the owner node has the foreign key.
35
+ this._addDependency(edge.ownerNode, edge, dependencies);
36
+ }
37
+ }
38
+ _createReferenceDependency(edge, dependencies) {
39
+ this._addDependency(edge.ownerNode, edge, dependencies);
40
+ }
41
+ _addDependency(node, edge, dependencies) {
42
+ let edges = dependencies.get(node);
43
+ if (!edges) {
44
+ edges = [];
45
+ dependencies.set(node, edges);
46
+ }
47
+ edges.push(edge);
48
+ }
49
+ _createNormalActions() {
50
+ const handledNodes = new Set();
51
+ const actions = [];
52
+ while (true) {
53
+ // At this point, don't care if the nodes have already been inserted before
54
+ // given to this class. `GraphInsertAction` will test that and only insert
55
+ // new ones. We need to pass all nodes to `GraphInsertActions` so that we
56
+ // can resolve all dependencies.
57
+ const nodesToInsert = this.graph.nodes.filter((node) => {
58
+ return !this._isHandled(node, handledNodes) && !this._hasDependencies(node, handledNodes);
59
+ });
60
+ if (nodesToInsert.length === 0) {
61
+ break;
62
+ }
63
+ actions.push(new GraphInsertAction(this.graphData, {
64
+ nodes: nodesToInsert,
65
+ dependencies: this.dependencies,
66
+ }));
67
+ for (const node of nodesToInsert) {
68
+ this._markHandled(node, handledNodes);
69
+ }
70
+ }
71
+ if (handledNodes.size !== this.graph.nodes.length) {
72
+ throw new Error('the object graph contains cyclic references');
73
+ }
74
+ return actions;
75
+ }
76
+ _isHandled(node, handledNodes) {
77
+ return handledNodes.has(node);
78
+ }
79
+ _hasDependencies(node, handledNodes) {
80
+ if (!this.dependencies.has(node)) {
81
+ return false;
82
+ }
83
+ for (const edge of this.dependencies.get(node)) {
84
+ const dependencyNode = edge.getOtherNode(node);
85
+ if (!handledNodes.has(dependencyNode) && !this.currentGraph.nodeForNode(dependencyNode)) {
86
+ return true;
87
+ }
88
+ }
89
+ return false;
90
+ }
91
+ _markHandled(node, handledNodes) {
92
+ handledNodes.add(node);
93
+ // The referencing nodes are all references that don't
94
+ // represent any real entity. They are simply intermediate nodes
95
+ // that depend on this node. Once this node is handled, we can
96
+ // also mark those nodes as handled as there is nothing to actually
97
+ // insert.
98
+ for (const refNode of node.referencingNodes) {
99
+ this._markHandled(refNode, handledNodes);
100
+ }
101
+ }
102
+ _createJoinRowActions() {
103
+ return [
104
+ new JoinRowGraphInsertAction(this.graphData, {
105
+ nodes: this.graph.nodes.filter((node) => {
106
+ return (this.currentGraph.nodeForNode(node) === null &&
107
+ node.parentEdge &&
108
+ node.parentEdge.relation.isObjectionManyToManyRelation);
109
+ }),
110
+ }),
111
+ ];
112
+ }
113
+ }
114
+ module.exports = {
115
+ GraphInsert,
116
+ };
@@ -0,0 +1,183 @@
1
+ 'use strict';
2
+ const { GraphAction } = require('../GraphAction');
3
+ const { groupBy, chunk, get, set } = require('../../../utils/objectUtils');
4
+ const { ModelGraphEdge } = require('../../../model/graph/ModelGraphEdge');
5
+ const promiseUtils = require('../../../utils/promiseUtils');
6
+ /**
7
+ * Inserts a batch of nodes for a GraphInsert.
8
+ *
9
+ * One of these is created for each batch of nodes that can be inserted at once.
10
+ * However, the nodes can have a different table and not all databases support
11
+ * batch inserts, so this class splits the inserts into further sub batches
12
+ * when needed.
13
+ */
14
+ class GraphInsertAction extends GraphAction {
15
+ constructor(graphData, { nodes, dependencies }) {
16
+ super(graphData);
17
+ // Nodes to insert.
18
+ this.nodes = nodes;
19
+ this.dependencies = dependencies;
20
+ }
21
+ run(builder) {
22
+ const batches = this._createInsertBatches(builder);
23
+ const concurrency = this._getConcurrency(builder, this.nodes);
24
+ return promiseUtils.map(batches, (batch) => this._insertBatch(builder, batch), { concurrency });
25
+ }
26
+ _createInsertBatches(builder) {
27
+ const batches = [];
28
+ const batchSize = this._getBatchSize(builder);
29
+ const nodesByModelClass = groupBy(this.nodes, getModelClass);
30
+ for (const nodes of nodesByModelClass.values()) {
31
+ for (const nodeBatch of chunk(nodes, batchSize)) {
32
+ batches.push(nodeBatch);
33
+ }
34
+ }
35
+ return batches;
36
+ }
37
+ async _insertBatch(parentBuilder, nodes) {
38
+ await this._beforeInsert(nodes);
39
+ await this._insert(parentBuilder, nodes);
40
+ await this._afterInsert(nodes);
41
+ }
42
+ _beforeInsert(nodes) {
43
+ this._resolveDependencies(nodes);
44
+ this._omitManyToManyExtraProps(nodes);
45
+ this._copyRelationPropsFromCurrentIfNeeded(nodes);
46
+ return Promise.resolve();
47
+ }
48
+ _resolveDependencies(nodes) {
49
+ for (const node of nodes) {
50
+ const edges = this.dependencies.get(node);
51
+ if (edges) {
52
+ for (const edge of edges) {
53
+ // `node` needs `dependencyNode` to have been inserted (and it has been).
54
+ const dependencyNode = edge.getOtherNode(node);
55
+ this._resolveDependency(dependencyNode, edge);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ _resolveDependency(dependencyNode, edge) {
61
+ if (edge.type === ModelGraphEdge.Type.Relation && !edge.relation.joinTable) {
62
+ this._resolveRelationDependency(dependencyNode, edge);
63
+ }
64
+ else if (edge.refType === ModelGraphEdge.ReferenceType.Property) {
65
+ this._resolvePropertyReferenceNode(dependencyNode, edge);
66
+ }
67
+ }
68
+ _resolveRelationDependency(dependencyNode, edge) {
69
+ const dependentNode = edge.getOtherNode(dependencyNode);
70
+ let sourceProp;
71
+ let targetProp;
72
+ if (edge.isOwnerNode(dependencyNode)) {
73
+ sourceProp = edge.relation.ownerProp;
74
+ targetProp = edge.relation.relatedProp;
75
+ }
76
+ else {
77
+ targetProp = edge.relation.ownerProp;
78
+ sourceProp = edge.relation.relatedProp;
79
+ }
80
+ this._resolveReferences(dependencyNode);
81
+ targetProp.forEach((i) => {
82
+ targetProp.setProp(dependentNode.obj, i, sourceProp.getProp(dependencyNode.obj, i));
83
+ });
84
+ }
85
+ _resolvePropertyReferenceNode(dependencyNode, edge) {
86
+ const dependentNode = edge.getOtherNode(dependencyNode);
87
+ let sourcePath;
88
+ let targetPath;
89
+ if (edge.isOwnerNode(dependencyNode)) {
90
+ sourcePath = edge.refOwnerDataPath;
91
+ targetPath = edge.refRelatedDataPath;
92
+ }
93
+ else {
94
+ targetPath = edge.refOwnerDataPath;
95
+ sourcePath = edge.refRelatedDataPath;
96
+ }
97
+ const sourceValue = get(dependencyNode.obj, sourcePath);
98
+ const targetValue = get(dependentNode.obj, targetPath);
99
+ if (targetValue === edge.refMatch) {
100
+ set(dependentNode.obj, targetPath, sourceValue);
101
+ }
102
+ else {
103
+ set(dependentNode.obj, targetPath, targetValue.replace(edge.refMatch, sourceValue));
104
+ }
105
+ }
106
+ _omitManyToManyExtraProps(nodes) {
107
+ for (const node of nodes) {
108
+ if (node.parentEdge &&
109
+ node.parentEdge.type === ModelGraphEdge.Type.Relation &&
110
+ node.parentEdge.relation.joinTableExtras.length > 0) {
111
+ node.parentEdge.relation.omitExtraProps([node.obj]);
112
+ }
113
+ }
114
+ }
115
+ _copyRelationPropsFromCurrentIfNeeded(nodes) {
116
+ for (const node of nodes) {
117
+ const currentNode = this.currentGraph.nodeForNode(node);
118
+ if (!currentNode) {
119
+ continue;
120
+ }
121
+ for (const edge of node.edges) {
122
+ if (edge.type !== ModelGraphEdge.Type.Relation) {
123
+ continue;
124
+ }
125
+ const prop = edge.isOwnerNode(node) ? edge.relation.ownerProp : edge.relation.relatedProp;
126
+ prop.forEach((i) => {
127
+ const value = prop.getProp(node.obj, i);
128
+ if (value !== undefined) {
129
+ return;
130
+ }
131
+ prop.setProp(node.obj, i, prop.getProp(currentNode.obj, i));
132
+ });
133
+ }
134
+ }
135
+ }
136
+ _insert(parentBuilder, nodes) {
137
+ const [{ modelClass }] = nodes;
138
+ nodes = nodes.filter((node) => {
139
+ return this.graphOptions.shouldInsert(node, this.graphData);
140
+ });
141
+ for (const node of nodes) {
142
+ delete node.obj[modelClass.uidProp];
143
+ node.obj.$validate(null, { dataPath: node.dataPathKey });
144
+ }
145
+ if (nodes.length === 0) {
146
+ return;
147
+ }
148
+ for (const node of nodes) {
149
+ node.userData.inserted = true;
150
+ }
151
+ return this._runRelationBeforeInsertMethods(parentBuilder, nodes).then(() => {
152
+ return modelClass
153
+ .query()
154
+ .insert(nodes.map((node) => node.obj))
155
+ .childQueryOf(parentBuilder)
156
+ .copyFrom(parentBuilder, GraphAction.ReturningAllSelector)
157
+ .execute();
158
+ });
159
+ }
160
+ _runRelationBeforeInsertMethods(parentBuilder, nodes) {
161
+ return Promise.all(nodes.map((node) => {
162
+ if (node.parentEdge) {
163
+ return node.parentEdge.relation.beforeInsert(node.obj, parentBuilder.context());
164
+ }
165
+ else {
166
+ return null;
167
+ }
168
+ }));
169
+ }
170
+ _afterInsert(nodes) {
171
+ for (const node of nodes) {
172
+ for (const refNode of node.referencingNodes) {
173
+ this._resolveDependency(refNode, refNode.parentEdge);
174
+ }
175
+ }
176
+ }
177
+ }
178
+ function getModelClass(node) {
179
+ return node.modelClass;
180
+ }
181
+ module.exports = {
182
+ GraphInsertAction,
183
+ };
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+ const { GraphAction } = require('../GraphAction');
3
+ const { groupBy, chunk } = require('../../../utils/objectUtils');
4
+ const promiseUtils = require('../../../utils/promiseUtils');
5
+ class JoinRowGraphInsertAction extends GraphAction {
6
+ constructor(graphData, { nodes }) {
7
+ super(graphData);
8
+ this.nodes = nodes;
9
+ }
10
+ run(builder) {
11
+ const batches = this._createInsertBatches(builder);
12
+ const concurrency = this._getConcurrency(builder, this.nodes);
13
+ return promiseUtils.map(batches, (batch) => this._insertBatch(builder, batch), { concurrency });
14
+ }
15
+ _createInsertBatches(builder) {
16
+ const batches = [];
17
+ const batchSize = this._getBatchSize(builder);
18
+ const nodesByModel = groupBy(this.nodes, (node) => getJoinTableModel(builder, node));
19
+ for (const [joinTableModelClass, nodes] of nodesByModel.entries()) {
20
+ for (const nodeBatch of chunk(nodes, batchSize)) {
21
+ batches.push(this._createBatch(joinTableModelClass, nodeBatch));
22
+ }
23
+ }
24
+ return batches;
25
+ }
26
+ _createBatch(joinTableModelClass, nodes) {
27
+ return nodes
28
+ .filter((node) => {
29
+ return this.graphOptions.shouldRelate(node, this.graphData) || node.userData.inserted;
30
+ })
31
+ .map((node) => ({
32
+ node,
33
+ joinTableModelClass,
34
+ joinTableObj: this._createJoinTableObj(joinTableModelClass, node),
35
+ }));
36
+ }
37
+ _createJoinTableObj(joinTableModelClass, node) {
38
+ const { parentEdge, parentNode } = node;
39
+ const { relation } = parentEdge;
40
+ this._resolveReferences(node);
41
+ return joinTableModelClass.fromJson(relation.createJoinModel(relation.ownerProp.getProps(parentNode.obj), node.obj));
42
+ }
43
+ _insertBatch(parentBuilder, batch) {
44
+ return this._beforeInsert(parentBuilder, batch).then(() => this._insert(parentBuilder, batch));
45
+ }
46
+ _beforeInsert(parentBuilder, batch) {
47
+ return Promise.all(batch.map(({ node, joinTableObj }) => {
48
+ if (node.parentEdge) {
49
+ return node.parentEdge.relation.joinTableBeforeInsert(joinTableObj, parentBuilder.context());
50
+ }
51
+ else {
52
+ return null;
53
+ }
54
+ }));
55
+ }
56
+ _insert(parentBuilder, batch) {
57
+ if (batch.length > 0) {
58
+ return batch[0].joinTableModelClass
59
+ .query()
60
+ .childQueryOf(parentBuilder)
61
+ .insert(batch.map((it) => it.joinTableObj));
62
+ }
63
+ }
64
+ }
65
+ function getJoinTableModel(builder, node) {
66
+ return node.parentEdge.relation.getJoinModelClass(builder.unsafeKnex());
67
+ }
68
+ module.exports = {
69
+ JoinRowGraphInsertAction,
70
+ };
@@ -0,0 +1,15 @@
1
+ 'use strict';
2
+ const { GraphOperation } = require('../GraphOperation');
3
+ const { GraphPatchAction } = require('./GraphPatchAction');
4
+ class GraphPatch extends GraphOperation {
5
+ createActions() {
6
+ return [
7
+ new GraphPatchAction(this.graphData, {
8
+ nodes: this.graph.nodes.filter((node) => this.graphOptions.shouldPatchOrUpdateIgnoreDisable(node, this.graphData)),
9
+ }),
10
+ ];
11
+ }
12
+ }
13
+ module.exports = {
14
+ GraphPatch,
15
+ };