@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.
- package/LICENSE +23 -0
- package/README.md +69 -2
- package/lib/.eslintrc.json +8 -0
- package/lib/initialize.js +14 -0
- package/lib/model/AjvValidator.js +325 -0
- package/lib/model/Model.js +942 -0
- package/lib/model/ModifierNotFoundError.js +12 -0
- package/lib/model/NotFoundError.js +23 -0
- package/lib/model/RelationDoesNotExistError.js +14 -0
- package/lib/model/ValidationError.js +54 -0
- package/lib/model/Validator.js +26 -0
- package/lib/model/getModel.js +9 -0
- package/lib/model/graph/ModelGraph.js +80 -0
- package/lib/model/graph/ModelGraphBuilder.js +241 -0
- package/lib/model/graph/ModelGraphEdge.js +52 -0
- package/lib/model/graph/ModelGraphNode.js +240 -0
- package/lib/model/inheritModel.js +28 -0
- package/lib/model/modelBindKnex.js +83 -0
- package/lib/model/modelClone.js +121 -0
- package/lib/model/modelColPropMap.js +34 -0
- package/lib/model/modelId.js +62 -0
- package/lib/model/modelJsonAttributes.js +92 -0
- package/lib/model/modelParseRelations.js +82 -0
- package/lib/model/modelQueryProps.js +147 -0
- package/lib/model/modelSet.js +136 -0
- package/lib/model/modelTableMetadata.js +83 -0
- package/lib/model/modelToJson.js +187 -0
- package/lib/model/modelUtils.js +47 -0
- package/lib/model/modelValidate.js +48 -0
- package/lib/model/modelValues.js +126 -0
- package/lib/model/modelVisitor.js +41 -0
- package/lib/objection.js +114 -0
- package/lib/queryBuilder/FunctionBuilder.js +339 -0
- package/lib/queryBuilder/InternalOptions.js +29 -0
- package/lib/queryBuilder/JoinBuilder.js +157 -0
- package/lib/queryBuilder/QueryBuilder.js +1398 -0
- package/lib/queryBuilder/QueryBuilderBase.js +884 -0
- package/lib/queryBuilder/QueryBuilderContext.js +27 -0
- package/lib/queryBuilder/QueryBuilderContextBase.js +35 -0
- package/lib/queryBuilder/QueryBuilderOperationSupport.js +558 -0
- package/lib/queryBuilder/QueryBuilderUserContext.js +31 -0
- package/lib/queryBuilder/RawBuilder.js +93 -0
- package/lib/queryBuilder/ReferenceBuilder.js +284 -0
- package/lib/queryBuilder/RelationExpression.js +431 -0
- package/lib/queryBuilder/StaticHookArguments.js +134 -0
- package/lib/queryBuilder/ValueBuilder.js +127 -0
- package/lib/queryBuilder/expressions.js +151 -0
- package/lib/queryBuilder/functions/mysql.js +817 -0
- package/lib/queryBuilder/graph/GraphAction.js +69 -0
- package/lib/queryBuilder/graph/GraphData.js +14 -0
- package/lib/queryBuilder/graph/GraphFetcher.js +191 -0
- package/lib/queryBuilder/graph/GraphNodeDbExistence.js +184 -0
- package/lib/queryBuilder/graph/GraphOperation.js +27 -0
- package/lib/queryBuilder/graph/GraphOptions.js +207 -0
- package/lib/queryBuilder/graph/GraphUpsert.js +305 -0
- package/lib/queryBuilder/graph/delete/GraphDelete.js +20 -0
- package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +74 -0
- package/lib/queryBuilder/graph/insert/GraphInsert.js +145 -0
- package/lib/queryBuilder/graph/insert/GraphInsertAction.js +225 -0
- package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +92 -0
- package/lib/queryBuilder/graph/patch/GraphPatch.js +20 -0
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +258 -0
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +31 -0
- package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +126 -0
- package/lib/queryBuilder/join/JoinResultColumn.js +23 -0
- package/lib/queryBuilder/join/JoinResultParser.js +119 -0
- package/lib/queryBuilder/join/RelationJoiner.js +341 -0
- package/lib/queryBuilder/join/TableNode.js +163 -0
- package/lib/queryBuilder/join/TableTree.js +85 -0
- package/lib/queryBuilder/join/utils.js +25 -0
- package/lib/queryBuilder/operations/DelegateOperation.js +141 -0
- package/lib/queryBuilder/operations/DeleteOperation.js +51 -0
- package/lib/queryBuilder/operations/FindByIdOperation.js +37 -0
- package/lib/queryBuilder/operations/FindByIdsOperation.js +29 -0
- package/lib/queryBuilder/operations/FindOperation.js +164 -0
- package/lib/queryBuilder/operations/FirstOperation.js +27 -0
- package/lib/queryBuilder/operations/FromOperation.js +110 -0
- package/lib/queryBuilder/operations/InsertAndFetchOperation.js +49 -0
- package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +45 -0
- package/lib/queryBuilder/operations/InsertGraphOperation.js +108 -0
- package/lib/queryBuilder/operations/InsertOperation.js +206 -0
- package/lib/queryBuilder/operations/InstanceDeleteOperation.js +46 -0
- package/lib/queryBuilder/operations/InstanceFindOperation.js +21 -0
- package/lib/queryBuilder/operations/InstanceInsertOperation.js +24 -0
- package/lib/queryBuilder/operations/InstanceUpdateOperation.js +59 -0
- package/lib/queryBuilder/operations/JoinRelatedOperation.js +103 -0
- package/lib/queryBuilder/operations/KnexOperation.js +14 -0
- package/lib/queryBuilder/operations/MergeOperation.js +56 -0
- package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +192 -0
- package/lib/queryBuilder/operations/OnBuildKnexOperation.js +29 -0
- package/lib/queryBuilder/operations/OnBuildOperation.js +29 -0
- package/lib/queryBuilder/operations/OnErrorOperation.js +29 -0
- package/lib/queryBuilder/operations/QueryBuilderOperation.js +297 -0
- package/lib/queryBuilder/operations/RangeOperation.js +47 -0
- package/lib/queryBuilder/operations/RelateOperation.js +27 -0
- package/lib/queryBuilder/operations/ReturningOperation.js +30 -0
- package/lib/queryBuilder/operations/RunAfterOperation.js +29 -0
- package/lib/queryBuilder/operations/RunBeforeOperation.js +29 -0
- package/lib/queryBuilder/operations/UnrelateOperation.js +23 -0
- package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +80 -0
- package/lib/queryBuilder/operations/UpdateOperation.js +155 -0
- package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +45 -0
- package/lib/queryBuilder/operations/UpsertGraphOperation.js +66 -0
- package/lib/queryBuilder/operations/WhereCompositeOperation.js +57 -0
- package/lib/queryBuilder/operations/WhereInOperation.js +48 -0
- package/lib/queryBuilder/operations/eager/EagerOperation.js +61 -0
- package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +51 -0
- package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +13 -0
- package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +285 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +36 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +53 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +31 -0
- package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +31 -0
- package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +167 -0
- package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +164 -0
- package/lib/queryBuilder/operations/select/SelectOperation.js +66 -0
- package/lib/queryBuilder/operations/select/Selection.js +123 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +117 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +100 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +104 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +886 -0
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs +97 -0
- package/lib/queryBuilder/parsers/relationExpressionParser.js +1042 -0
- package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +141 -0
- package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +22 -0
- package/lib/queryBuilder/transformations/QueryTransformation.js +11 -0
- package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +71 -0
- package/lib/queryBuilder/transformations/index.js +15 -0
- package/lib/queryBuilder/whereScopes.js +227 -0
- package/lib/relations/Relation.js +401 -0
- package/lib/relations/RelationDeleteOperation.js +29 -0
- package/lib/relations/RelationFindOperation.js +149 -0
- package/lib/relations/RelationInsertOperation.js +33 -0
- package/lib/relations/RelationOwner.js +236 -0
- package/lib/relations/RelationProperty.js +247 -0
- package/lib/relations/RelationUpdateOperation.js +30 -0
- package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +23 -0
- package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +44 -0
- package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +50 -0
- package/lib/relations/belongsToOne/BelongsToOneRelation.js +53 -0
- package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +61 -0
- package/lib/relations/hasMany/HasManyInsertOperation.js +61 -0
- package/lib/relations/hasMany/HasManyRelateOperation.js +64 -0
- package/lib/relations/hasMany/HasManyRelation.js +49 -0
- package/lib/relations/hasMany/HasManyUnrelateOperation.js +33 -0
- package/lib/relations/hasOne/HasOneRelation.js +13 -0
- package/lib/relations/hasOneThrough/HasOneThroughRelation.js +13 -0
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +105 -0
- package/lib/relations/manyToMany/ManyToManyRelation.js +507 -0
- package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +36 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +10 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +26 -0
- package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +12 -0
- package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +105 -0
- package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +52 -0
- package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +81 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +14 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +34 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +16 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +10 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +82 -0
- package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +12 -0
- package/lib/transaction.js +96 -0
- package/lib/utils/assert.js +25 -0
- package/lib/utils/buildUtils.js +21 -0
- package/lib/utils/classUtils.js +13 -0
- package/lib/utils/clone.js +2329 -0
- package/lib/utils/createModifier.js +42 -0
- package/lib/utils/deprecate.js +15 -0
- package/lib/utils/identifierMapping.js +227 -0
- package/lib/utils/internalPropUtils.js +11 -0
- package/lib/utils/knexUtils.js +76 -0
- package/lib/utils/mixin.js +25 -0
- package/lib/utils/mysqlJsonPath.js +55 -0
- package/lib/utils/normalizeIds.js +144 -0
- package/lib/utils/objectUtils.js +416 -0
- package/lib/utils/parseFieldExpression.js +51 -0
- package/lib/utils/postgresJsonPath.js +110 -0
- package/lib/utils/promiseUtils/after.js +17 -0
- package/lib/utils/promiseUtils/afterReturn.js +17 -0
- package/lib/utils/promiseUtils/index.js +17 -0
- package/lib/utils/promiseUtils/isPromise.js +11 -0
- package/lib/utils/promiseUtils/map.js +110 -0
- package/lib/utils/promiseUtils/mapAfterAllReturn.js +29 -0
- package/lib/utils/promiseUtils/try.js +22 -0
- package/lib/utils/resolveModel.js +107 -0
- package/lib/utils/tmpColumnUtils.js +16 -0
- package/package.json +86 -4
- package/typings/objection/index.d.ts +4104 -0
- package/typings/objection/relation-expression.d.ts +237 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { ModelGraph } = require('../../model/graph/ModelGraph');
|
|
4
|
+
const { ModelGraphEdge } = require('../../model/graph/ModelGraphEdge');
|
|
5
|
+
const { createNotModelError } = require('../../model/graph/ModelGraphBuilder');
|
|
6
|
+
const { GraphFetcher } = require('../graph/GraphFetcher');
|
|
7
|
+
const { GraphInsert } = require('./insert/GraphInsert');
|
|
8
|
+
const { GraphPatch } = require('./patch/GraphPatch');
|
|
9
|
+
const { GraphDelete } = require('./delete/GraphDelete');
|
|
10
|
+
const { GraphRecursiveUpsert } = require('./recursiveUpsert/GraphRecursiveUpsert');
|
|
11
|
+
const { GraphOptions } = require('./GraphOptions');
|
|
12
|
+
const { ValidationErrorType } = require('../../model/ValidationError');
|
|
13
|
+
const { RelationExpression } = require('../RelationExpression');
|
|
14
|
+
const { GraphNodeDbExistence } = require('./GraphNodeDbExistence');
|
|
15
|
+
const { GraphData } = require('./GraphData');
|
|
16
|
+
const { uniqBy, asArray, isObject } = require('../../utils/objectUtils');
|
|
17
|
+
|
|
18
|
+
class GraphUpsert {
|
|
19
|
+
constructor({ rootModelClass, objects, upsertOptions }) {
|
|
20
|
+
checkCanBeConvertedToModels(rootModelClass, objects);
|
|
21
|
+
|
|
22
|
+
this.objects = rootModelClass.ensureModelArray(objects, GraphUpsert.modelOptions);
|
|
23
|
+
this.isArray = Array.isArray(objects);
|
|
24
|
+
this.upsertOpt = upsertOptions;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static get modelOptions() {
|
|
28
|
+
return { skipValidation: true };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
run(builder) {
|
|
32
|
+
const modelClass = builder.modelClass();
|
|
33
|
+
const graphOptions = new GraphOptions(this.upsertOpt);
|
|
34
|
+
|
|
35
|
+
const graph = ModelGraph.create(modelClass, this.objects);
|
|
36
|
+
assignDbRefsAsRelateProps(graph);
|
|
37
|
+
|
|
38
|
+
return createGraphData(builder, graphOptions, graph)
|
|
39
|
+
.then(checkForErrors(builder))
|
|
40
|
+
.then(pruneGraphs())
|
|
41
|
+
.then(executeOperations(builder))
|
|
42
|
+
.then(returnResult(this.objects, this.isArray));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function checkCanBeConvertedToModels(modelClass, objects) {
|
|
47
|
+
asArray(objects).forEach((obj) => {
|
|
48
|
+
if (!isObject(obj)) {
|
|
49
|
+
throw createNotModelError(modelClass, obj);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function assignDbRefsAsRelateProps(graph) {
|
|
55
|
+
for (const node of graph.nodes) {
|
|
56
|
+
if (!node.parentEdge || !node.parentEdge.relation || !node.isDbReference) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
node.parentEdge.relation.setRelateProp(node.obj, asArray(node.dbReference));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function createGraphData(builder, graphOptions, graph) {
|
|
65
|
+
const currentGraph = await fetchCurrentGraph(builder, graphOptions, graph);
|
|
66
|
+
|
|
67
|
+
const nodeDbExistence = await GraphNodeDbExistence.create({
|
|
68
|
+
builder,
|
|
69
|
+
graph,
|
|
70
|
+
graphOptions,
|
|
71
|
+
currentGraph,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return new GraphData({ graph, currentGraph, graphOptions, nodeDbExistence });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function fetchCurrentGraph(builder, graphOptions, graph) {
|
|
78
|
+
if (graphOptions.isInsertOnly()) {
|
|
79
|
+
return Promise.resolve(ModelGraph.createEmpty());
|
|
80
|
+
} else {
|
|
81
|
+
return GraphFetcher.fetchCurrentGraph({ builder, graph, graphOptions });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Remove branches from the graph that require no operations. For example
|
|
86
|
+
// we never want to do anything for descendant nodes of a node that is
|
|
87
|
+
// deleted or unrelated. We never delete recursively.
|
|
88
|
+
function pruneGraphs() {
|
|
89
|
+
return (graphData) => {
|
|
90
|
+
pruneRelatedBranches(graphData);
|
|
91
|
+
|
|
92
|
+
if (!graphData.graphOptions.isInsertOnly()) {
|
|
93
|
+
pruneDeletedBranches(graphData);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return graphData;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function pruneRelatedBranches(graphData) {
|
|
101
|
+
const relateNodes = graphData.graph.nodes.filter((node) => {
|
|
102
|
+
return (
|
|
103
|
+
!graphData.currentGraph.nodeForNode(node) &&
|
|
104
|
+
!graphData.graphOptions.shouldInsertIgnoreDisable(node, graphData)
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
removeBranchesFromGraph(findRoots(relateNodes), graphData.graph);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function pruneDeletedBranches(graphData) {
|
|
112
|
+
const { graph, currentGraph } = graphData;
|
|
113
|
+
|
|
114
|
+
const deleteNodes = currentGraph.nodes.filter((currentNode) => !graph.nodeForNode(currentNode));
|
|
115
|
+
const roots = findRoots(deleteNodes);
|
|
116
|
+
|
|
117
|
+
// Don't delete relations the current graph doesn't even mention.
|
|
118
|
+
// So if the parent node doesn't even have the relation, it's not
|
|
119
|
+
// supposed to be deleted.
|
|
120
|
+
const rootsNotInRelation = roots.filter((deleteRoot) => {
|
|
121
|
+
if (!deleteRoot.parentNode) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const { relation } = deleteRoot.parentEdge;
|
|
126
|
+
const parentNode = graph.nodeForNode(deleteRoot.parentNode);
|
|
127
|
+
|
|
128
|
+
if (!parentNode) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return parentNode.obj[relation.name] === undefined;
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
removeBranchesFromGraph(roots, currentGraph);
|
|
136
|
+
removeNodesFromGraph(new Set(rootsNotInRelation), currentGraph);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function findRoots(nodes) {
|
|
140
|
+
const nodeSet = new Set(nodes);
|
|
141
|
+
|
|
142
|
+
return uniqBy(
|
|
143
|
+
nodes.filter((node) => {
|
|
144
|
+
let parentNode = node.parentNode;
|
|
145
|
+
|
|
146
|
+
while (parentNode) {
|
|
147
|
+
if (nodeSet.has(parentNode)) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
parentNode = parentNode.parentNode;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return true;
|
|
155
|
+
}),
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function removeBranchesFromGraph(branchRoots, graph) {
|
|
160
|
+
const nodesToRemove = new Set(
|
|
161
|
+
branchRoots.reduce(
|
|
162
|
+
(nodesToRemove, node) => [...nodesToRemove, ...node.descendantRelationNodes],
|
|
163
|
+
[],
|
|
164
|
+
),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
removeNodesFromGraph(nodesToRemove, graph);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function removeNodesFromGraph(nodesToRemove, graph) {
|
|
171
|
+
const edgesToRemove = new Set();
|
|
172
|
+
|
|
173
|
+
for (const node of nodesToRemove) {
|
|
174
|
+
for (const edge of node.edges) {
|
|
175
|
+
const otherNode = edge.getOtherNode(node);
|
|
176
|
+
|
|
177
|
+
if (!nodesToRemove.has(otherNode)) {
|
|
178
|
+
otherNode.removeEdge(edge);
|
|
179
|
+
edgesToRemove.add(edge);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
graph.nodes = graph.nodes.filter((node) => !nodesToRemove.has(node));
|
|
185
|
+
graph.edges = graph.edges.filter((edge) => !edgesToRemove.has(edge));
|
|
186
|
+
|
|
187
|
+
return graph;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function checkForErrors(builder) {
|
|
191
|
+
return (graphData) => {
|
|
192
|
+
checkForNotFoundErrors(graphData, builder);
|
|
193
|
+
checkForUnallowedRelationErrors(graphData, builder);
|
|
194
|
+
checkForUnallowedReferenceErrors(graphData, builder);
|
|
195
|
+
|
|
196
|
+
if (graphData.graphOptions.isInsertOnly()) {
|
|
197
|
+
checkForHasManyRelateErrors(graphData);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return graphData;
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function checkForNotFoundErrors(graphData, builder) {
|
|
205
|
+
const { graphOptions, currentGraph, graph } = graphData;
|
|
206
|
+
|
|
207
|
+
for (const node of graph.nodes) {
|
|
208
|
+
if (
|
|
209
|
+
node.obj.$hasId() &&
|
|
210
|
+
!graphOptions.shouldInsertIgnoreDisable(node, graphData) &&
|
|
211
|
+
!graphOptions.shouldRelateIgnoreDisable(node, graphData) &&
|
|
212
|
+
!currentGraph.nodeForNode(node)
|
|
213
|
+
) {
|
|
214
|
+
if (!node.parentNode) {
|
|
215
|
+
throw node.modelClass.createNotFoundError(builder.context(), {
|
|
216
|
+
message: `root model (id=${node.obj.$id()}) does not exist. If you want to insert it with an id, use the insertMissing option`,
|
|
217
|
+
dataPath: node.dataPath,
|
|
218
|
+
});
|
|
219
|
+
} else {
|
|
220
|
+
throw node.modelClass.createNotFoundError(builder.context(), {
|
|
221
|
+
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`,
|
|
222
|
+
dataPath: node.dataPath,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function checkForUnallowedRelationErrors(graphData, builder) {
|
|
230
|
+
const { graph } = graphData;
|
|
231
|
+
const allowedExpression = builder.allowedGraphExpression();
|
|
232
|
+
|
|
233
|
+
if (allowedExpression) {
|
|
234
|
+
const rootsObjs = graph.nodes.filter((node) => !node.parentEdge).map((node) => node.obj);
|
|
235
|
+
const expression = RelationExpression.fromModelGraph(rootsObjs);
|
|
236
|
+
|
|
237
|
+
if (!allowedExpression.isSubExpression(expression)) {
|
|
238
|
+
throw builder.modelClass().createValidationError({
|
|
239
|
+
type: ValidationErrorType.UnallowedRelation,
|
|
240
|
+
message: 'trying to upsert an unallowed relation',
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function checkForUnallowedReferenceErrors(graphData, builder) {
|
|
247
|
+
const { graph, graphOptions } = graphData;
|
|
248
|
+
|
|
249
|
+
if (graphOptions.allowRefs()) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (graph.edges.some((edge) => edge.type === ModelGraphEdge.Type.Reference)) {
|
|
254
|
+
throw builder.modelClass().createValidationError({
|
|
255
|
+
type: ValidationErrorType.InvalidGraph,
|
|
256
|
+
message:
|
|
257
|
+
'#ref references are not allowed in a graph by default. see the allowRefs insert/upsert graph option',
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function checkForHasManyRelateErrors(graphData) {
|
|
263
|
+
const { graph, graphOptions } = graphData;
|
|
264
|
+
|
|
265
|
+
for (const node of graph.nodes) {
|
|
266
|
+
if (
|
|
267
|
+
graphOptions.shouldRelate(node, graphData) &&
|
|
268
|
+
node.parentEdge.relation.isObjectionHasManyRelation
|
|
269
|
+
) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
'You cannot relate HasManyRelation or HasOneRelation using insertGraph, because those require update operations. Consider using upsertGraph instead.',
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function executeOperations(builder) {
|
|
278
|
+
return (graphData) => {
|
|
279
|
+
const operations = graphData.graphOptions.isInsertOnly()
|
|
280
|
+
? [GraphInsert]
|
|
281
|
+
: [GraphDelete, GraphInsert, GraphPatch, GraphRecursiveUpsert];
|
|
282
|
+
|
|
283
|
+
return operations.reduce((promise, Operation) => {
|
|
284
|
+
const operation = new Operation(graphData);
|
|
285
|
+
const actions = operation.createActions();
|
|
286
|
+
|
|
287
|
+
return promise.then(() => executeActions(builder, actions));
|
|
288
|
+
}, Promise.resolve());
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function executeActions(builder, actions) {
|
|
293
|
+
return actions.reduce(
|
|
294
|
+
(promise, action) => promise.then(() => action.run(builder)),
|
|
295
|
+
Promise.resolve(),
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function returnResult(objects, isArray) {
|
|
300
|
+
return () => (isArray ? objects : objects[0]);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
module.exports = {
|
|
304
|
+
GraphUpsert,
|
|
305
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { GraphOperation } = require('../GraphOperation');
|
|
4
|
+
const { GraphDeleteAction } = require('./GraphDeleteAction');
|
|
5
|
+
|
|
6
|
+
class GraphDelete extends GraphOperation {
|
|
7
|
+
createActions() {
|
|
8
|
+
return [
|
|
9
|
+
new GraphDeleteAction(this.graphData, {
|
|
10
|
+
nodes: this.currentGraph.nodes.filter((currentNode) =>
|
|
11
|
+
this.graphOptions.shouldDeleteOrUnrelate(currentNode, this.graphData),
|
|
12
|
+
),
|
|
13
|
+
}),
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
GraphDelete,
|
|
20
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { GraphAction } = require('../GraphAction');
|
|
4
|
+
const { groupBy } = require('../../../utils/objectUtils');
|
|
5
|
+
const promiseUtils = require('../../../utils/promiseUtils');
|
|
6
|
+
|
|
7
|
+
class GraphDeleteAction extends GraphAction {
|
|
8
|
+
constructor(graphData, { nodes }) {
|
|
9
|
+
super(graphData);
|
|
10
|
+
// Nodes to delete.
|
|
11
|
+
this.nodes = nodes;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
run(builder) {
|
|
15
|
+
const nodesTodelete = this._filterOutBelongsToOneRelationUnrelates(this.nodes);
|
|
16
|
+
const builders = this._createDeleteBuilders(builder, nodesTodelete);
|
|
17
|
+
|
|
18
|
+
return promiseUtils.map(builders, (builder) => builder.execute(), {
|
|
19
|
+
concurrency: this._getConcurrency(builder, nodesTodelete),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
_filterOutBelongsToOneRelationUnrelates(nodes) {
|
|
24
|
+
// `BelongsToOneRelation` unrelate is handled by `GraphPatch` because
|
|
25
|
+
// unrelating a `BelongsToOneRelation` is just a matter of updating
|
|
26
|
+
// one field of the parent node.
|
|
27
|
+
return nodes.filter((node) => {
|
|
28
|
+
return !(
|
|
29
|
+
this.graphOptions.shouldUnrelate(node, this.graphData) &&
|
|
30
|
+
node.parentEdge.relation.isObjectionBelongsToOneRelation
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_createDeleteBuilders(parentBuilder, nodesTodelete) {
|
|
36
|
+
const nodesByRelation = groupBy(nodesTodelete, getRelation);
|
|
37
|
+
const builders = [];
|
|
38
|
+
|
|
39
|
+
nodesByRelation.forEach((nodes, relation) => {
|
|
40
|
+
const nodesByParent = groupBy(nodes, getParent);
|
|
41
|
+
|
|
42
|
+
nodesByParent.forEach((nodes, parentNode) => {
|
|
43
|
+
const shouldUnrelate = this.graphOptions.shouldUnrelate(nodes[0], this.graphData);
|
|
44
|
+
|
|
45
|
+
const builder = parentNode.obj.$relatedQuery(relation.name).childQueryOf(parentBuilder);
|
|
46
|
+
|
|
47
|
+
if (!relation.isObjectionBelongsToOneRelation) {
|
|
48
|
+
// This is useless in case of BelongsToOneRelation.
|
|
49
|
+
builder.findByIds(nodes.map((node) => node.obj.$id()));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
for (const node of nodes) {
|
|
53
|
+
node.userData.deleted = true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
builders.push(shouldUnrelate ? builder.unrelate() : builder.delete());
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return builders;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getRelation(node) {
|
|
65
|
+
return node.parentEdge.relation;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getParent(node) {
|
|
69
|
+
return node.parentNode;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
GraphDeleteAction,
|
|
74
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { JoinRowGraphInsertAction } = require('./JoinRowGraphInsertAction');
|
|
4
|
+
const { GraphInsertAction } = require('./GraphInsertAction');
|
|
5
|
+
const { GraphOperation } = require('../GraphOperation');
|
|
6
|
+
const { ModelGraphEdge } = require('../../../model/graph/ModelGraphEdge');
|
|
7
|
+
|
|
8
|
+
class GraphInsert extends GraphOperation {
|
|
9
|
+
constructor(...args) {
|
|
10
|
+
super(...args);
|
|
11
|
+
this.dependencies = this._createDependencyMap();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
createActions() {
|
|
15
|
+
return [...this._createNormalActions(), ...this._createJoinRowActions()];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
_createDependencyMap() {
|
|
19
|
+
const dependencies = new Map();
|
|
20
|
+
|
|
21
|
+
for (const edge of this.graph.edges) {
|
|
22
|
+
if (edge.type == ModelGraphEdge.Type.Relation) {
|
|
23
|
+
this._createRelationDependency(edge, dependencies);
|
|
24
|
+
} else {
|
|
25
|
+
this._createReferenceDependency(edge, dependencies);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return dependencies;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_createRelationDependency(edge, dependencies) {
|
|
33
|
+
if (edge.relation.isObjectionHasManyRelation) {
|
|
34
|
+
// In case of HasManyRelation the related node depends on the owner node
|
|
35
|
+
// because the related node has the foreign key.
|
|
36
|
+
this._addDependency(edge.relatedNode, edge, dependencies);
|
|
37
|
+
} else if (edge.relation.isObjectionBelongsToOneRelation) {
|
|
38
|
+
// In case of BelongsToOneRelation the owner node depends on the related
|
|
39
|
+
// node because the owner node has the foreign key.
|
|
40
|
+
this._addDependency(edge.ownerNode, edge, dependencies);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_createReferenceDependency(edge, dependencies) {
|
|
45
|
+
this._addDependency(edge.ownerNode, edge, dependencies);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
_addDependency(node, edge, dependencies) {
|
|
49
|
+
let edges = dependencies.get(node);
|
|
50
|
+
|
|
51
|
+
if (!edges) {
|
|
52
|
+
edges = [];
|
|
53
|
+
dependencies.set(node, edges);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
edges.push(edge);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_createNormalActions() {
|
|
60
|
+
const handledNodes = new Set();
|
|
61
|
+
const actions = [];
|
|
62
|
+
|
|
63
|
+
while (true) {
|
|
64
|
+
// At this point, don't care if the nodes have already been inserted before
|
|
65
|
+
// given to this class. `GraphInsertAction` will test that and only insert
|
|
66
|
+
// new ones. We need to pass all nodes to `GraphInsertActions` so that we
|
|
67
|
+
// can resolve all dependencies.
|
|
68
|
+
const nodesToInsert = this.graph.nodes.filter((node) => {
|
|
69
|
+
return !this._isHandled(node, handledNodes) && !this._hasDependencies(node, handledNodes);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (nodesToInsert.length === 0) {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
actions.push(
|
|
77
|
+
new GraphInsertAction(this.graphData, {
|
|
78
|
+
nodes: nodesToInsert,
|
|
79
|
+
dependencies: this.dependencies,
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
for (const node of nodesToInsert) {
|
|
84
|
+
this._markHandled(node, handledNodes);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (handledNodes.size !== this.graph.nodes.length) {
|
|
89
|
+
throw new Error('the object graph contains cyclic references');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return actions;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_isHandled(node, handledNodes) {
|
|
96
|
+
return handledNodes.has(node);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
_hasDependencies(node, handledNodes) {
|
|
100
|
+
if (!this.dependencies.has(node)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const edge of this.dependencies.get(node)) {
|
|
105
|
+
const dependencyNode = edge.getOtherNode(node);
|
|
106
|
+
|
|
107
|
+
if (!handledNodes.has(dependencyNode) && !this.currentGraph.nodeForNode(dependencyNode)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
_markHandled(node, handledNodes) {
|
|
116
|
+
handledNodes.add(node);
|
|
117
|
+
|
|
118
|
+
// The referencing nodes are all references that don't
|
|
119
|
+
// represent any real entity. They are simply intermediate nodes
|
|
120
|
+
// that depend on this node. Once this node is handled, we can
|
|
121
|
+
// also mark those nodes as handled as there is nothing to actually
|
|
122
|
+
// insert.
|
|
123
|
+
for (const refNode of node.referencingNodes) {
|
|
124
|
+
this._markHandled(refNode, handledNodes);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
_createJoinRowActions() {
|
|
129
|
+
return [
|
|
130
|
+
new JoinRowGraphInsertAction(this.graphData, {
|
|
131
|
+
nodes: this.graph.nodes.filter((node) => {
|
|
132
|
+
return (
|
|
133
|
+
this.currentGraph.nodeForNode(node) === null &&
|
|
134
|
+
node.parentEdge &&
|
|
135
|
+
node.parentEdge.relation.isObjectionManyToManyRelation
|
|
136
|
+
);
|
|
137
|
+
}),
|
|
138
|
+
}),
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
GraphInsert,
|
|
145
|
+
};
|