oak-domain 4.2.3 → 4.2.4
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/lib/base-app-domain/ActionDefDict.d.ts +8 -8
- package/lib/base-app-domain/Oper/Schema.d.ts +158 -158
- package/lib/base-app-domain/Oper/Storage.js +56 -56
- package/lib/base-app-domain/User/Action.d.ts +11 -11
- package/lib/base-app-domain/User/Action.js +12 -12
- package/lib/base-app-domain/UserEntityGrant/Action.d.ts +5 -5
- package/lib/base-app-domain/UserEntityGrant/Action.js +5 -5
- package/lib/compiler/schemalBuilder.js +4167 -4167
- package/lib/entities/Oper.d.ts +12 -12
- package/lib/entities/Oper.js +36 -36
- package/lib/entities/User.d.ts +18 -18
- package/lib/entities/User.js +32 -32
- package/lib/index.d.ts +1 -1
- package/lib/index.js +3 -3
- package/lib/store/AsyncRowStore.d.ts +66 -66
- package/lib/store/CascadeStore.d.ts +109 -109
- package/lib/store/CascadeStore.js +1728 -1728
- package/lib/store/RelationAuth.js +1209 -1209
- package/lib/store/TriggerExecutor.js +468 -468
- package/lib/store/actionDef.js +278 -278
- package/lib/store/checker.js +487 -487
- package/lib/store/relation.d.ts +12 -12
- package/lib/store/relation.js +74 -74
- package/lib/triggers/index.d.ts +5 -5
- package/lib/triggers/index.js +28 -28
- package/lib/types/Configuration.d.ts +42 -42
- package/lib/types/Configuration.js +3 -3
- package/lib/types/Connector.d.ts +39 -39
- package/lib/types/Entity.d.ts +209 -209
- package/lib/types/Sync.d.ts +74 -68
- package/lib/types/Sync.js +9 -9
- package/lib/types/index.d.ts +27 -27
- package/lib/types/index.js +30 -30
- package/lib/utils/SimpleConnector.d.ts +81 -81
- package/lib/utils/SimpleConnector.js +217 -217
- package/lib/utils/assert.d.ts +5 -5
- package/lib/utils/projection.d.ts +4 -4
- package/lib/utils/relationPath.d.ts +31 -31
- package/lib/utils/relationPath.js +202 -202
- package/package.json +51 -51
- package/src/entities/ActionAuth.ts +41 -41
- package/src/entities/I18n.ts +45 -45
- package/src/entities/Modi.ts +69 -69
- package/src/entities/ModiEntity.ts +26 -26
- package/src/entities/Oper.ts +48 -48
- package/src/entities/OperEntity.ts +27 -27
- package/src/entities/Path.ts +43 -43
- package/src/entities/Relation.ts +43 -43
- package/src/entities/RelationAuth.ts +44 -44
- package/src/entities/User.ts +48 -48
- package/src/entities/UserEntityClaim.ts +29 -29
- package/src/entities/UserEntityGrant.ts +24 -24
- package/src/entities/UserRelation.ts +50 -50
|
@@ -1,1728 +1,1728 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CascadeStore = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
|
-
const Entity_1 = require("../types/Entity");
|
|
7
|
-
const RowStore_1 = require("../types/RowStore");
|
|
8
|
-
const filter_1 = require("./filter");
|
|
9
|
-
const relation_1 = require("./relation");
|
|
10
|
-
const types_1 = require("../types");
|
|
11
|
-
const lodash_1 = require("../utils/lodash");
|
|
12
|
-
const AsyncRowStore_1 = require("./AsyncRowStore");
|
|
13
|
-
const filter_2 = require("./filter");
|
|
14
|
-
const uuid_1 = require("../utils/uuid");
|
|
15
|
-
const entities_1 = require("../compiler/entities");
|
|
16
|
-
/**这个用来处理级联的select和update,对不同能力的 */
|
|
17
|
-
class CascadeStore extends RowStore_1.RowStore {
|
|
18
|
-
constructor(storageSchema) {
|
|
19
|
-
super(storageSchema);
|
|
20
|
-
}
|
|
21
|
-
selectionRewriters = [];
|
|
22
|
-
operationRewriters = [];
|
|
23
|
-
async reinforceSelectionAsync(entity, selection, context, option, isAggr) {
|
|
24
|
-
if (!isAggr && !selection.distinct) {
|
|
25
|
-
this.reinforceSelectionInner(entity, selection, context);
|
|
26
|
-
}
|
|
27
|
-
const rewriterPromises = this.selectionRewriters.map(ele => ele(this.getSchema(), entity, selection, context, option, isAggr));
|
|
28
|
-
if (rewriterPromises.length > 0) {
|
|
29
|
-
await Promise.all(rewriterPromises);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
reinforceSelectionSync(entity, selection, context, option, isAggr) {
|
|
33
|
-
if (!isAggr && !selection.distinct) {
|
|
34
|
-
this.reinforceSelectionInner(entity, selection, context);
|
|
35
|
-
}
|
|
36
|
-
this.selectionRewriters.forEach(ele => {
|
|
37
|
-
const result = ele(this.getSchema(), entity, selection, context, option, isAggr);
|
|
38
|
-
(0, assert_1.default)(!(result instanceof Promise));
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
reinforceSelectionInner(entity, selection, context) {
|
|
42
|
-
const { filter, data, sorter } = selection;
|
|
43
|
-
const assignNecessaryProjectionAttrs = (projectionNode, attrs) => {
|
|
44
|
-
attrs.forEach((attr) => {
|
|
45
|
-
if (!projectionNode.hasOwnProperty(attr)) {
|
|
46
|
-
Object.assign(projectionNode, {
|
|
47
|
-
[attr]: 1,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
};
|
|
52
|
-
const checkFilterNode = (entity2, filterNode, projectionNode, toBeAssignNode, filterNodeDict) => {
|
|
53
|
-
const necessaryAttrs = ['id'];
|
|
54
|
-
for (const attr in filterNode) {
|
|
55
|
-
if (attr === '#id') {
|
|
56
|
-
(0, assert_1.default)(!filterNodeDict[filterNode[attr]], `projection中结点的id有重复, ${filterNode[attr]}`);
|
|
57
|
-
Object.assign(filterNodeDict, {
|
|
58
|
-
[filterNode[attr]]: projectionNode,
|
|
59
|
-
});
|
|
60
|
-
if (toBeAssignNode[filterNode[attr]]) {
|
|
61
|
-
assignNecessaryProjectionAttrs(projectionNode, toBeAssignNode[filterNode[attr]]);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
else if (['$and', '$or'].includes(attr)) {
|
|
65
|
-
for (const node of filterNode[attr]) {
|
|
66
|
-
checkFilterNode(entity2, node, projectionNode, toBeAssignNode, filterNodeDict);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else if (attr === '$not') {
|
|
70
|
-
checkFilterNode(entity2, filterNode[attr], projectionNode, toBeAssignNode, filterNodeDict);
|
|
71
|
-
}
|
|
72
|
-
else if (attr === '$text') {
|
|
73
|
-
// 全文检索首先要有fulltext索引,其次要把fulltext的相关属性加到projection里
|
|
74
|
-
const { indexes } = this.getSchema()[entity2];
|
|
75
|
-
const fulltextIndex = indexes.find(ele => ele.config && ele.config.type === 'fulltext');
|
|
76
|
-
const { attributes } = fulltextIndex;
|
|
77
|
-
necessaryAttrs.push(...(attributes.map(ele => ele.name)));
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
|
|
81
|
-
const exprResult = (0, types_1.getAttrRefInExpression)(filterNode[attr]);
|
|
82
|
-
for (const nodeName in exprResult) {
|
|
83
|
-
if (nodeName === '#current') {
|
|
84
|
-
assignNecessaryProjectionAttrs(projectionNode, exprResult[nodeName]);
|
|
85
|
-
}
|
|
86
|
-
else if (filterNodeDict[nodeName]) {
|
|
87
|
-
assignNecessaryProjectionAttrs(filterNodeDict[nodeName], exprResult[nodeName]);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
if (toBeAssignNode[nodeName]) {
|
|
91
|
-
toBeAssignNode[nodeName].push(...exprResult[nodeName]);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
Object.assign(toBeAssignNode, {
|
|
95
|
-
[nodeName]: exprResult[nodeName],
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
const rel = this.judgeRelation(entity2, attr);
|
|
103
|
-
if (rel === 1) {
|
|
104
|
-
necessaryAttrs.push(attr);
|
|
105
|
-
}
|
|
106
|
-
else if (rel === 2) {
|
|
107
|
-
// entity/entityId反指
|
|
108
|
-
necessaryAttrs.push('entity', 'entityId');
|
|
109
|
-
if (!projectionNode[attr]) {
|
|
110
|
-
Object.assign(projectionNode, {
|
|
111
|
-
[attr]: {
|
|
112
|
-
id: 1,
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
checkFilterNode(attr, filterNode[attr], projectionNode[attr], toBeAssignNode, filterNodeDict);
|
|
117
|
-
}
|
|
118
|
-
else if (typeof rel === 'string') {
|
|
119
|
-
necessaryAttrs.push(`${attr}Id`);
|
|
120
|
-
if (!projectionNode[attr]) {
|
|
121
|
-
Object.assign(projectionNode, {
|
|
122
|
-
[attr]: {
|
|
123
|
-
id: 1,
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
checkFilterNode(rel, filterNode[attr], projectionNode[attr], toBeAssignNode, filterNodeDict);
|
|
128
|
-
}
|
|
129
|
-
else if (rel instanceof Array) {
|
|
130
|
-
// 子查询,暂时不处理
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
assignNecessaryProjectionAttrs(projectionNode, necessaryAttrs);
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
const checkSorterNode = (entity2, sorterNode, projectionNode) => {
|
|
138
|
-
const checkSortAttr = (e2, sortAttr, projNode) => {
|
|
139
|
-
const necessaryAttrs = [];
|
|
140
|
-
for (const attr in sortAttr) {
|
|
141
|
-
const rel = this.judgeRelation(e2, attr);
|
|
142
|
-
if (typeof rel === 'number' && [0, 1].includes(rel)) {
|
|
143
|
-
necessaryAttrs.push(attr);
|
|
144
|
-
}
|
|
145
|
-
else if (typeof rel === 'string') {
|
|
146
|
-
if (!projNode[attr]) {
|
|
147
|
-
Object.assign(projNode, {
|
|
148
|
-
[attr]: {},
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
(0, assert_1.default)(typeof sortAttr[attr] === 'object');
|
|
152
|
-
checkSortAttr(rel, sortAttr[attr], projNode[attr]);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
(0, assert_1.default)(rel === 2);
|
|
156
|
-
if (!projNode[attr]) {
|
|
157
|
-
Object.assign(projNode, {
|
|
158
|
-
[attr]: {},
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
(0, assert_1.default)(typeof sortAttr[attr] === 'object');
|
|
162
|
-
checkSortAttr(attr, sortAttr[attr], projNode[attr]);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
sorterNode.forEach((node) => {
|
|
167
|
-
const { $attr } = node;
|
|
168
|
-
checkSortAttr(entity2, $attr, projectionNode);
|
|
169
|
-
});
|
|
170
|
-
};
|
|
171
|
-
let relevantIds = [];
|
|
172
|
-
if (filter) {
|
|
173
|
-
const toBeAssignNode = {}; // 用来记录在表达式中涉及到的结点
|
|
174
|
-
// filter当中所关联到的属性必须在projection中
|
|
175
|
-
const filterNodeDict = {};
|
|
176
|
-
checkFilterNode(entity, filter, data, toBeAssignNode, filterNodeDict);
|
|
177
|
-
relevantIds = (0, filter_2.getRelevantIds)(filter);
|
|
178
|
-
}
|
|
179
|
-
// sorter也得取了,前端需要处理排序
|
|
180
|
-
if (sorter) {
|
|
181
|
-
checkSorterNode(entity, sorter, data);
|
|
182
|
-
}
|
|
183
|
-
const toBeAssignNode2 = {}; // 用来记录在表达式中涉及到的结点
|
|
184
|
-
const projectionNodeDict = {};
|
|
185
|
-
const checkProjectionNode = (entity2, projectionNode) => {
|
|
186
|
-
const necessaryAttrs = ['id', '$$createAt$$', '$$updateAt$$']; // 有的页面依赖于其它页面取数据,有时两个页面的filter的差异会导致有一个加createAt,有一个不加,此时可能产生前台取数据不完整的异常。先统一加上
|
|
187
|
-
for (const attr in projectionNode) {
|
|
188
|
-
if (attr === '#id') {
|
|
189
|
-
(0, assert_1.default)(!projectionNodeDict[projectionNode[attr]], `projection中结点的id有重复, ${projectionNode[attr]}`);
|
|
190
|
-
Object.assign(projectionNodeDict, {
|
|
191
|
-
[projectionNode[attr]]: projectionNode,
|
|
192
|
-
});
|
|
193
|
-
if (toBeAssignNode2[projectionNode[attr]]) {
|
|
194
|
-
assignNecessaryProjectionAttrs(projectionNode, toBeAssignNode2[projectionNode[attr]]);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
|
|
199
|
-
const exprResult = (0, types_1.getAttrRefInExpression)(projectionNode[attr]);
|
|
200
|
-
for (const nodeName in exprResult) {
|
|
201
|
-
if (nodeName === '#current') {
|
|
202
|
-
assignNecessaryProjectionAttrs(projectionNode, exprResult[nodeName]);
|
|
203
|
-
}
|
|
204
|
-
else if (projectionNodeDict[nodeName]) {
|
|
205
|
-
assignNecessaryProjectionAttrs(projectionNodeDict[nodeName], exprResult[nodeName]);
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
if (toBeAssignNode2[nodeName]) {
|
|
209
|
-
toBeAssignNode2[nodeName].push(...exprResult[nodeName]);
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
Object.assign(toBeAssignNode2, {
|
|
213
|
-
[nodeName]: exprResult[nodeName],
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
const rel = (0, relation_1.judgeRelation)(this.getSchema(), entity2, attr);
|
|
221
|
-
if (rel === 1) {
|
|
222
|
-
necessaryAttrs.push(attr);
|
|
223
|
-
}
|
|
224
|
-
else if (rel === 2) {
|
|
225
|
-
// entity/entityId反指
|
|
226
|
-
necessaryAttrs.push('entity', 'entityId');
|
|
227
|
-
checkProjectionNode(attr, projectionNode[attr]);
|
|
228
|
-
}
|
|
229
|
-
else if (typeof rel === 'string') {
|
|
230
|
-
necessaryAttrs.push(`${attr}Id`);
|
|
231
|
-
checkProjectionNode(rel, projectionNode[attr]);
|
|
232
|
-
}
|
|
233
|
-
else if (rel instanceof Array && !attr.endsWith('$$aggr')) {
|
|
234
|
-
const { data, filter } = projectionNode[attr];
|
|
235
|
-
if (rel[1]) {
|
|
236
|
-
assignNecessaryProjectionAttrs(data, [rel[1]]);
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
assignNecessaryProjectionAttrs(data, ['entity', 'entityId']);
|
|
240
|
-
}
|
|
241
|
-
this.reinforceSelectionInner(rel[0], projectionNode[attr], context);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
assignNecessaryProjectionAttrs(projectionNode, necessaryAttrs);
|
|
246
|
-
}
|
|
247
|
-
// 如果对象中指向一对多的Modi,此时加上指向Modi的projection
|
|
248
|
-
if (this.getSchema()[entity2].toModi) {
|
|
249
|
-
Object.assign(projectionNode, {
|
|
250
|
-
modi$entity: {
|
|
251
|
-
$entity: 'modi',
|
|
252
|
-
data: {
|
|
253
|
-
id: 1,
|
|
254
|
-
targetEntity: 1,
|
|
255
|
-
entity: 1,
|
|
256
|
-
entityId: 1,
|
|
257
|
-
action: 1,
|
|
258
|
-
iState: 1,
|
|
259
|
-
data: 1,
|
|
260
|
-
filter: 1,
|
|
261
|
-
$$createAt$$: 1,
|
|
262
|
-
$$updateAt$$: 1,
|
|
263
|
-
},
|
|
264
|
-
filter: {
|
|
265
|
-
iState: 'active',
|
|
266
|
-
},
|
|
267
|
-
}
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
// 如果对象上有relation关系,在此将本用户相关的relation和actionAuth全部取出
|
|
271
|
-
// 还要将actionAuth上没有relation关系但destEntity为本对象的行也全部取出,这些是指向userId的可能路径
|
|
272
|
-
// 放在这里有点怪异,暂先这样
|
|
273
|
-
if (context instanceof AsyncRowStore_1.AsyncContext) {
|
|
274
|
-
const userId = context.getCurrentUserId(true);
|
|
275
|
-
if (userId && !entities_1.SYSTEM_RESERVE_ENTITIES.includes(entity2)) {
|
|
276
|
-
if (this.getSchema()[entity2].relation && !projectionNode.userRelation$entity) {
|
|
277
|
-
Object.assign(projectionNode, {
|
|
278
|
-
userRelation$entity: {
|
|
279
|
-
$entity: 'userRelation',
|
|
280
|
-
data: {
|
|
281
|
-
id: 1,
|
|
282
|
-
entity: 1,
|
|
283
|
-
entityId: 1,
|
|
284
|
-
userId: 1,
|
|
285
|
-
relationId: 1,
|
|
286
|
-
relation: {
|
|
287
|
-
id: 1,
|
|
288
|
-
name: 1,
|
|
289
|
-
display: 1,
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
filter: {
|
|
293
|
-
userId,
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
checkProjectionNode(entity, data);
|
|
302
|
-
if (!sorter && relevantIds.length === 0) {
|
|
303
|
-
// 如果没有sorter,就给予一个按createAt逆序的sorter
|
|
304
|
-
Object.assign(selection, {
|
|
305
|
-
sorter: [
|
|
306
|
-
{
|
|
307
|
-
$attr: {
|
|
308
|
-
$$createAt$$: 1,
|
|
309
|
-
},
|
|
310
|
-
$direction: 'desc',
|
|
311
|
-
}
|
|
312
|
-
]
|
|
313
|
-
});
|
|
314
|
-
Object.assign(data, {
|
|
315
|
-
$$createAt$$: 1,
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
async reinforceOperation(entity, operation, context, option) {
|
|
320
|
-
await Promise.all(this.operationRewriters.map(ele => ele(this.getSchema(), entity, operation, context, option)));
|
|
321
|
-
}
|
|
322
|
-
registerOperationRewriter(rewriter) {
|
|
323
|
-
this.operationRewriters.push(rewriter);
|
|
324
|
-
}
|
|
325
|
-
registerSelectionRewriter(rewriter) {
|
|
326
|
-
this.selectionRewriters.push(rewriter);
|
|
327
|
-
}
|
|
328
|
-
destructCascadeSelect(entity, projection2, context, cascadeSelectFn, aggregateFn, option) {
|
|
329
|
-
const cascadeSelectionFns = [];
|
|
330
|
-
const supportMtoJoin = this.supportManyToOneJoin();
|
|
331
|
-
const { toModi } = this.getSchema()[entity];
|
|
332
|
-
(0, assert_1.default)(typeof projection2 === 'object');
|
|
333
|
-
for (const attr in projection2) {
|
|
334
|
-
const relation = (0, relation_1.judgeRelation)(this.storageSchema, entity, attr);
|
|
335
|
-
if (relation === 1 || relation == 0) {
|
|
336
|
-
}
|
|
337
|
-
else if (relation === 2) {
|
|
338
|
-
// 基于entity/entityId的多对一
|
|
339
|
-
(0, assert_1.default)(typeof projection2[attr] === 'object');
|
|
340
|
-
if (supportMtoJoin) {
|
|
341
|
-
cascadeSelectionFns.push((result) => {
|
|
342
|
-
if (!toModi) {
|
|
343
|
-
result.forEach((ele) => {
|
|
344
|
-
if (ele.entity === attr) {
|
|
345
|
-
(0, assert_1.default)(ele.entityId);
|
|
346
|
-
if (!ele[attr]) {
|
|
347
|
-
throw new types_1.OakRowUnexistedException([{
|
|
348
|
-
entity: attr,
|
|
349
|
-
selection: {
|
|
350
|
-
data: projection2[attr],
|
|
351
|
-
filter: {
|
|
352
|
-
id: ele.entityId,
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}]);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
const { projection: subProjection, cascadeSelectionFns: subCascadeSelectionFns, } = this.destructCascadeSelect(attr, projection2[attr], context, cascadeSelectFn, aggregateFn, option);
|
|
362
|
-
subCascadeSelectionFns.forEach(ele => cascadeSelectionFns.push((result) => {
|
|
363
|
-
return ele(result.map(ele2 => ele2[attr]).filter(ele2 => !!ele2));
|
|
364
|
-
}));
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
cascadeSelectionFns.push((result) => {
|
|
368
|
-
const entityIds = (0, lodash_1.uniq)(result.filter(ele => ele.entity === attr).map(ele => {
|
|
369
|
-
(0, assert_1.default)(ele.entityId !== null);
|
|
370
|
-
return ele.entityId;
|
|
371
|
-
}));
|
|
372
|
-
const dealWithSubRows = (subRows) => {
|
|
373
|
-
(0, assert_1.default)(subRows.length <= entityIds.length);
|
|
374
|
-
if (subRows.length < entityIds.length && !toModi) {
|
|
375
|
-
// 后台不允许数据不一致
|
|
376
|
-
if (context instanceof AsyncRowStore_1.AsyncContext || !option.ignoreAttrMiss) {
|
|
377
|
-
throw new types_1.OakRowUnexistedException([{
|
|
378
|
-
entity: attr,
|
|
379
|
-
selection: {
|
|
380
|
-
data: projection2[attr],
|
|
381
|
-
filter: {
|
|
382
|
-
id: {
|
|
383
|
-
$in: entityIds
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
},
|
|
387
|
-
}]);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
result.forEach((ele) => {
|
|
391
|
-
if (ele.entity === attr) {
|
|
392
|
-
const subRow = subRows.find(ele2 => ele2.id === ele.entityId);
|
|
393
|
-
if (subRow) {
|
|
394
|
-
Object.assign(ele, {
|
|
395
|
-
[attr]: subRow,
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
else {
|
|
399
|
-
Object.assign(ele, {
|
|
400
|
-
[attr]: null,
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
};
|
|
406
|
-
if (entityIds.length > 0) {
|
|
407
|
-
const subRows = cascadeSelectFn.call(this, attr, {
|
|
408
|
-
data: projection2[attr],
|
|
409
|
-
filter: {
|
|
410
|
-
id: {
|
|
411
|
-
$in: entityIds
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
|
-
}, context, option);
|
|
415
|
-
if (subRows instanceof Promise) {
|
|
416
|
-
return subRows.then((subRowss) => dealWithSubRows(subRowss));
|
|
417
|
-
}
|
|
418
|
-
else {
|
|
419
|
-
dealWithSubRows(subRows);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
else if (typeof relation === 'string') {
|
|
426
|
-
(0, assert_1.default)(typeof projection2[attr] === 'object');
|
|
427
|
-
if (supportMtoJoin) {
|
|
428
|
-
if (!toModi) {
|
|
429
|
-
// 如果不是modi,要保证外键没有空指针
|
|
430
|
-
cascadeSelectionFns.push((result) => {
|
|
431
|
-
if (!toModi) {
|
|
432
|
-
result.forEach((ele) => {
|
|
433
|
-
if (ele[`${attr}Id`] && !ele[attr]) {
|
|
434
|
-
throw new types_1.OakRowUnexistedException([{
|
|
435
|
-
entity: relation,
|
|
436
|
-
selection: {
|
|
437
|
-
data: projection2[attr],
|
|
438
|
-
filter: {
|
|
439
|
-
id: ele[`${attr}Id`],
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}]);
|
|
443
|
-
}
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
const { projection: subProjection, cascadeSelectionFns: subCascadeSelectionFns, } = this.destructCascadeSelect(relation, projection2[attr], context, cascadeSelectFn, aggregateFn, option);
|
|
449
|
-
subCascadeSelectionFns.forEach(ele => cascadeSelectionFns.push((result) => {
|
|
450
|
-
return ele(result.map(ele2 => ele2[attr]).filter(ele2 => !!ele2));
|
|
451
|
-
}));
|
|
452
|
-
}
|
|
453
|
-
else {
|
|
454
|
-
cascadeSelectionFns.push((result) => {
|
|
455
|
-
const ids = (0, lodash_1.uniq)(result.filter(ele => !!(ele[`${attr}Id`])).map(ele => ele[`${attr}Id`]));
|
|
456
|
-
const dealWithSubRows = (subRows) => {
|
|
457
|
-
(0, assert_1.default)(subRows.length <= ids.length);
|
|
458
|
-
if (subRows.length < ids.length && !toModi) {
|
|
459
|
-
if (context instanceof AsyncRowStore_1.AsyncContext || !option.ignoreAttrMiss) {
|
|
460
|
-
throw new types_1.OakRowUnexistedException([{
|
|
461
|
-
entity: relation,
|
|
462
|
-
selection: {
|
|
463
|
-
data: projection2[attr],
|
|
464
|
-
filter: {
|
|
465
|
-
id: {
|
|
466
|
-
$in: ids
|
|
467
|
-
},
|
|
468
|
-
},
|
|
469
|
-
}
|
|
470
|
-
}]);
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
result.forEach((ele) => {
|
|
474
|
-
if (ele[`${attr}Id`]) {
|
|
475
|
-
const subRow = subRows.find(ele2 => ele2.id === ele[`${attr}Id`]);
|
|
476
|
-
if (subRow) {
|
|
477
|
-
Object.assign(ele, {
|
|
478
|
-
[attr]: subRow,
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
else {
|
|
482
|
-
Object.assign(ele, {
|
|
483
|
-
[attr]: null,
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
else {
|
|
488
|
-
Object.assign(ele, {
|
|
489
|
-
[attr]: null,
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
});
|
|
493
|
-
};
|
|
494
|
-
if (ids.length > 0) {
|
|
495
|
-
const subRows = cascadeSelectFn.call(this, relation, {
|
|
496
|
-
data: projection2[attr],
|
|
497
|
-
filter: {
|
|
498
|
-
id: {
|
|
499
|
-
$in: ids
|
|
500
|
-
},
|
|
501
|
-
},
|
|
502
|
-
}, context, option);
|
|
503
|
-
if (subRows instanceof Promise) {
|
|
504
|
-
return subRows.then((subRowss) => dealWithSubRows(subRowss));
|
|
505
|
-
}
|
|
506
|
-
dealWithSubRows(subRows);
|
|
507
|
-
}
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
else {
|
|
512
|
-
(0, assert_1.default)(relation instanceof Array);
|
|
513
|
-
const { data: subProjection, filter: subFilter, indexFrom, count, sorter: subSorter, total, randomRange } = projection2[attr];
|
|
514
|
-
const [entity2, foreignKey] = relation;
|
|
515
|
-
const isAggr = attr.endsWith('$$aggr');
|
|
516
|
-
const otmAggrFn = (result) => {
|
|
517
|
-
const aggrResults = result.map(async (row) => {
|
|
518
|
-
const filter2 = foreignKey ? (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
519
|
-
[foreignKey]: row.id,
|
|
520
|
-
}, subFilter]) : (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
521
|
-
entity,
|
|
522
|
-
entityId: row.id,
|
|
523
|
-
}, subFilter]);
|
|
524
|
-
const aggrResult = aggregateFn.call(this, entity2, {
|
|
525
|
-
data: subProjection,
|
|
526
|
-
filter: filter2,
|
|
527
|
-
sorter: subSorter,
|
|
528
|
-
indexFrom,
|
|
529
|
-
count
|
|
530
|
-
}, context, option);
|
|
531
|
-
(0, assert_1.default)(aggrResult instanceof Promise);
|
|
532
|
-
const aggrResultResult = await aggrResult;
|
|
533
|
-
return Object.assign(row, {
|
|
534
|
-
[attr]: aggrResultResult,
|
|
535
|
-
});
|
|
536
|
-
});
|
|
537
|
-
if (aggrResults.length > 0) {
|
|
538
|
-
return Promise.all(aggrResults).then(() => undefined);
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
/** 若一对多子查询没有indexFrom和count,可以优化成组查 */
|
|
542
|
-
const otmGroupFn = (result) => {
|
|
543
|
-
const dealWithSubRows = (subRows) => {
|
|
544
|
-
// 这里如果result只有一行,则把返回结果直接置上,不对比外键值
|
|
545
|
-
// 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理
|
|
546
|
-
// 看不懂了,应该不需要这个if了,不知道怎么重现测试 by Xc 20230906
|
|
547
|
-
if (result.length === 1) {
|
|
548
|
-
Object.assign(result[0], {
|
|
549
|
-
[attr]: subRows,
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
result.forEach((ele) => {
|
|
554
|
-
const subRowss = subRows.filter((ele2) => {
|
|
555
|
-
if (foreignKey) {
|
|
556
|
-
return ele2[foreignKey] === ele.id;
|
|
557
|
-
}
|
|
558
|
-
return ele2.entityId === ele.id;
|
|
559
|
-
});
|
|
560
|
-
(0, assert_1.default)(subRowss);
|
|
561
|
-
Object.assign(ele, {
|
|
562
|
-
[attr]: subRowss,
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
};
|
|
567
|
-
const ids = result.map(ele => ele.id);
|
|
568
|
-
if (ids.length > 0) {
|
|
569
|
-
const filter2 = foreignKey ? (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
570
|
-
[foreignKey]: {
|
|
571
|
-
$in: ids,
|
|
572
|
-
},
|
|
573
|
-
}, subFilter]) : (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
574
|
-
entity,
|
|
575
|
-
entityId: {
|
|
576
|
-
$in: ids,
|
|
577
|
-
},
|
|
578
|
-
}, subFilter]);
|
|
579
|
-
const subRows = cascadeSelectFn.call(this, entity2, {
|
|
580
|
-
data: subProjection,
|
|
581
|
-
filter: filter2,
|
|
582
|
-
sorter: subSorter,
|
|
583
|
-
total,
|
|
584
|
-
}, context, option);
|
|
585
|
-
if (subRows instanceof Promise) {
|
|
586
|
-
return subRows.then((subRowss) => dealWithSubRows(subRowss));
|
|
587
|
-
}
|
|
588
|
-
dealWithSubRows(subRows);
|
|
589
|
-
}
|
|
590
|
-
};
|
|
591
|
-
/** 若一对多子查询有indexFrom和count,只能单行去连接 */
|
|
592
|
-
const otmSingleFn = (result) => {
|
|
593
|
-
const dealWithSubRows2 = (row, subRows) => {
|
|
594
|
-
Object.assign(row, {
|
|
595
|
-
[attr]: subRows,
|
|
596
|
-
});
|
|
597
|
-
};
|
|
598
|
-
if (result.length > 0) {
|
|
599
|
-
const getSubRows = result.map((row) => {
|
|
600
|
-
const filter2 = foreignKey ? (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
601
|
-
[foreignKey]: row.id,
|
|
602
|
-
}, subFilter]) : (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
603
|
-
entity,
|
|
604
|
-
entityId: row.id,
|
|
605
|
-
}, subFilter]);
|
|
606
|
-
const subRows = cascadeSelectFn.call(this, entity2, {
|
|
607
|
-
data: subProjection,
|
|
608
|
-
filter: filter2,
|
|
609
|
-
sorter: subSorter,
|
|
610
|
-
indexFrom,
|
|
611
|
-
count,
|
|
612
|
-
total,
|
|
613
|
-
randomRange,
|
|
614
|
-
}, context, option);
|
|
615
|
-
if (subRows instanceof Promise) {
|
|
616
|
-
return subRows.then((subRowss) => dealWithSubRows2(row, subRowss));
|
|
617
|
-
}
|
|
618
|
-
return dealWithSubRows2(row, subRows);
|
|
619
|
-
});
|
|
620
|
-
if (getSubRows[0] instanceof Promise) {
|
|
621
|
-
return Promise.all(getSubRows).then(() => undefined);
|
|
622
|
-
}
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
};
|
|
626
|
-
if (isAggr) {
|
|
627
|
-
(context instanceof AsyncRowStore_1.AsyncContext) && cascadeSelectionFns.push(result => otmAggrFn(result));
|
|
628
|
-
}
|
|
629
|
-
else {
|
|
630
|
-
cascadeSelectionFns.push((result) => {
|
|
631
|
-
if (typeof indexFrom === 'number') {
|
|
632
|
-
(0, assert_1.default)(typeof count === 'number' && count > 0);
|
|
633
|
-
return otmSingleFn(result);
|
|
634
|
-
}
|
|
635
|
-
return otmGroupFn(result);
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
return {
|
|
641
|
-
projection: projection2,
|
|
642
|
-
cascadeSelectionFns,
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
/**
|
|
646
|
-
* 级联更新
|
|
647
|
-
* A --> B
|
|
648
|
-
多对一:A CREATE/B CREATE,B data的主键赋到A的data上
|
|
649
|
-
A CREATE/B UPDATE,B filter的主键来自A的data
|
|
650
|
-
A UPDATE/B CREATE,B data的主键赋到A的data上
|
|
651
|
-
A UPDATE/B UPDATE,B filter的主键来自A的row
|
|
652
|
-
A UPDATE/B REMOVE,B filter的主键来自A的row
|
|
653
|
-
A REMOVE/B UPDATE,B filter的主键来自A的row
|
|
654
|
-
A REMOVE/B REMOVE,B filter的主键来自A的row
|
|
655
|
-
|
|
656
|
-
一对多:A CREATE/B CREATE,A data上的主键赋到B的data上
|
|
657
|
-
A CREATE/B UPDATE,A data上的主键赋到B的data上
|
|
658
|
-
A UPDATE/B CREATE,A filter上的主键赋到B的data上(一定是带主键的filter)
|
|
659
|
-
A UPDATE/B UPDATE,A filter上的主键赋到B的filter上(一定是带主键的filter)
|
|
660
|
-
A UPDATE/B REMOVE,A filter上的主键赋到B的filter上(一定是带主键的filter)
|
|
661
|
-
A REMOVE/B UPDATE,A filter上的主键赋到B的filter上(且B关于A的外键清空)
|
|
662
|
-
A REMOVE/B REMOVE,A filter上的主键赋到B的filter上
|
|
663
|
-
*
|
|
664
|
-
* 延时更新,
|
|
665
|
-
* A(业务级别的申请对象) ---> B(业务级别需要更新的对象)
|
|
666
|
-
* 两者必须通过entity/entityId关联
|
|
667
|
-
* 此时需要把对B的更新记录成一条新插入的Modi对象,并将A上的entity/entityId指向该对象(新生成的Modi对象的id与此operation的id保持一致)
|
|
668
|
-
* @param entity
|
|
669
|
-
* @param action
|
|
670
|
-
* @param data
|
|
671
|
-
* @param context
|
|
672
|
-
* @param option
|
|
673
|
-
* @param result
|
|
674
|
-
* @param filter
|
|
675
|
-
* @returns
|
|
676
|
-
*/
|
|
677
|
-
destructCascadeUpdate(entity, action, data, context, option, cascadeUpdate, filter, bornAt) {
|
|
678
|
-
const modiAttr = this.getSchema()[entity].toModi;
|
|
679
|
-
const option2 = Object.assign({}, option);
|
|
680
|
-
const opData = {};
|
|
681
|
-
const beforeFns = [];
|
|
682
|
-
const afterFns = [];
|
|
683
|
-
if (modiAttr && action !== 'remove') {
|
|
684
|
-
// create/update具有modi对象的对象,对其子对象的update行为全部是create modi对象(缓存动作)
|
|
685
|
-
// delete此对象,所有的modi子对象应该通过触发器作废,这个目前先通过系统的trigger来实现
|
|
686
|
-
(0, assert_1.default)(!option2.modiParentId && !option2.modiParentEntity);
|
|
687
|
-
if (action === 'create') {
|
|
688
|
-
option2.modiParentId = data.id;
|
|
689
|
-
option2.modiParentEntity = entity;
|
|
690
|
-
}
|
|
691
|
-
else if (filter?.id && typeof filter.id === 'string') {
|
|
692
|
-
// 如果是对toModi对象进行cascadeUpdate操作,必然带有id,如果没有则认为不是modi相关的操作
|
|
693
|
-
// 批量通过或者拒绝applyment应该就会出现
|
|
694
|
-
option2.modiParentId = filter.id;
|
|
695
|
-
option2.modiParentEntity = entity;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
for (const attr in data) {
|
|
699
|
-
const relation = (0, relation_1.judgeRelation)(this.storageSchema, entity, attr, !!bornAt);
|
|
700
|
-
if (relation === 1) {
|
|
701
|
-
Object.assign(opData, {
|
|
702
|
-
[attr]: data[attr],
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
else if (relation === 2) {
|
|
706
|
-
// 基于entity/entityId的many-to-one
|
|
707
|
-
const operationMto = data[attr];
|
|
708
|
-
const { action: actionMto, data: dataMto, filter: filterMto } = operationMto;
|
|
709
|
-
if (actionMto === 'create') {
|
|
710
|
-
Object.assign(opData, {
|
|
711
|
-
entityId: dataMto.id,
|
|
712
|
-
entity: attr,
|
|
713
|
-
});
|
|
714
|
-
}
|
|
715
|
-
else if (action === 'create') {
|
|
716
|
-
const { entityId: fkId, entity } = data;
|
|
717
|
-
(0, assert_1.default)(typeof fkId === 'string' || entity === attr);
|
|
718
|
-
if (filterMto?.id) {
|
|
719
|
-
// 若已有id则不用处理,否则会干扰modi的后续判断(会根据filter来判断对象id,如果判断不出来去查实际的对象,但实际的对象其实还未创建好)
|
|
720
|
-
(0, assert_1.default)(filterMto.id === fkId);
|
|
721
|
-
}
|
|
722
|
-
else {
|
|
723
|
-
// A中data的entityId作为B中filter的主键
|
|
724
|
-
Object.assign(operationMto, {
|
|
725
|
-
filter: (0, filter_1.combineFilters)(attr, this.getSchema(), [{
|
|
726
|
-
id: fkId,
|
|
727
|
-
}, filterMto]),
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
else {
|
|
732
|
-
// 剩下三种情况都是B中的filter的id来自A中row的entityId
|
|
733
|
-
(0, assert_1.default)(!data.hasOwnProperty('entityId') && !data.hasOwnProperty('entity'));
|
|
734
|
-
if (filterMto?.id) {
|
|
735
|
-
// 若已有id则不用处理,否则会干扰modi的后续判断(会根据filter来判断对象id,如果判断不出来去查实际的对象,但实际的对象其实还未创建好)
|
|
736
|
-
(0, assert_1.default)(typeof filterMto.id === 'string');
|
|
737
|
-
}
|
|
738
|
-
else if (filter.entity === attr && filter.entityId) {
|
|
739
|
-
Object.assign(operationMto, {
|
|
740
|
-
filter: (0, filter_1.combineFilters)(attr, this.getSchema(), [{
|
|
741
|
-
id: filter.entityId,
|
|
742
|
-
}, filterMto]),
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
else if (filter[attr]) {
|
|
746
|
-
Object.assign(operationMto, {
|
|
747
|
-
filter: (0, filter_1.combineFilters)(attr, this.getSchema(), [filter[attr], filterMto]),
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
else {
|
|
751
|
-
// A中data的entityId作为B中filter的主键
|
|
752
|
-
Object.assign(operationMto, {
|
|
753
|
-
filter: (0, filter_1.combineFilters)(attr, this.getSchema(), [{
|
|
754
|
-
[`${entity}$entity`]: {
|
|
755
|
-
filter,
|
|
756
|
-
}
|
|
757
|
-
}, filterMto]),
|
|
758
|
-
});
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
beforeFns.push(() => cascadeUpdate.call(this, attr, operationMto, context, option2));
|
|
762
|
-
}
|
|
763
|
-
else if (typeof relation === 'string') {
|
|
764
|
-
// 基于attr的外键的many-to-one
|
|
765
|
-
const operationMto = data[attr];
|
|
766
|
-
const { action: actionMto, data: dataMto, filter: filterMto } = operationMto;
|
|
767
|
-
if (actionMto === 'create') {
|
|
768
|
-
Object.assign(opData, {
|
|
769
|
-
[`${attr}Id`]: dataMto.id,
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
else if (action === 'create') {
|
|
773
|
-
const { [`${attr}Id`]: fkId } = data;
|
|
774
|
-
(0, assert_1.default)(typeof fkId === 'string');
|
|
775
|
-
if (filterMto?.id) {
|
|
776
|
-
// 若已有id则不用处理,否则会干扰modi的后续判断(会根据filter来判断对象id,如果判断不出来去查实际的对象,但实际的对象其实还未创建好)
|
|
777
|
-
(0, assert_1.default)(filterMto.id === fkId);
|
|
778
|
-
}
|
|
779
|
-
else {
|
|
780
|
-
// A中data的entityId作为B中filter的主键
|
|
781
|
-
Object.assign(operationMto, {
|
|
782
|
-
filter: (0, filter_1.combineFilters)(relation, this.getSchema(), [filterMto, {
|
|
783
|
-
id: fkId,
|
|
784
|
-
}]),
|
|
785
|
-
});
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
else {
|
|
789
|
-
(0, assert_1.default)(!data.hasOwnProperty(`${attr}Id`));
|
|
790
|
-
if (filterMto?.id) {
|
|
791
|
-
// 若已有id则不用处理,否则会干扰modi的后续判断(会根据filter来判断对象id,如果判断不出来去查实际的对象,但实际的对象其实还未创建好)
|
|
792
|
-
(0, assert_1.default)(typeof filterMto.id === 'string');
|
|
793
|
-
}
|
|
794
|
-
else if (filter[`${attr}Id`]) {
|
|
795
|
-
Object.assign(operationMto, {
|
|
796
|
-
filter: (0, filter_1.combineFilters)(relation, this.getSchema(), [filterMto, {
|
|
797
|
-
id: filter[`${attr}Id`],
|
|
798
|
-
}]),
|
|
799
|
-
});
|
|
800
|
-
}
|
|
801
|
-
else if (filter[attr]) {
|
|
802
|
-
Object.assign(operationMto, {
|
|
803
|
-
filter: (0, filter_1.combineFilters)(relation, this.getSchema(), [filterMto, filter[attr]]),
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
else {
|
|
807
|
-
// A中data的attrId作为B中filter的主键
|
|
808
|
-
Object.assign(operationMto, {
|
|
809
|
-
filter: (0, filter_1.combineFilters)(relation, this.getSchema(), [filterMto, {
|
|
810
|
-
[`${entity}$${attr}`]: filter
|
|
811
|
-
}]),
|
|
812
|
-
});
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
beforeFns.push(() => cascadeUpdate.call(this, relation, operationMto, context, option2));
|
|
816
|
-
}
|
|
817
|
-
else if (relation instanceof Array) {
|
|
818
|
-
const [entityOtm, foreignKey] = relation;
|
|
819
|
-
const otmOperations = data[attr];
|
|
820
|
-
const dealWithOneToMany = (otm) => {
|
|
821
|
-
const { action: actionOtm, data: dataOtm, filter: filterOtm } = otm;
|
|
822
|
-
if (!foreignKey) {
|
|
823
|
-
// 基于entity/entityId的one-to-many
|
|
824
|
-
if (action === 'create') {
|
|
825
|
-
const { id } = data;
|
|
826
|
-
if (dataOtm instanceof Array) {
|
|
827
|
-
dataOtm.forEach(ele => Object.assign(ele, {
|
|
828
|
-
entity,
|
|
829
|
-
entityId: id,
|
|
830
|
-
}));
|
|
831
|
-
}
|
|
832
|
-
else {
|
|
833
|
-
Object.assign(dataOtm, {
|
|
834
|
-
entity,
|
|
835
|
-
entityId: id,
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
else if (actionOtm === 'create') {
|
|
840
|
-
// 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作
|
|
841
|
-
// todo 这个假设对watcher等后台行为可能不成立,等遇到create/create一对多的case再完善
|
|
842
|
-
const { id } = filter;
|
|
843
|
-
(0, assert_1.default)(typeof id === 'string');
|
|
844
|
-
if (dataOtm instanceof Array) {
|
|
845
|
-
dataOtm.forEach(ele => Object.assign(ele, {
|
|
846
|
-
entity,
|
|
847
|
-
entityId: id,
|
|
848
|
-
}));
|
|
849
|
-
}
|
|
850
|
-
else {
|
|
851
|
-
Object.assign(dataOtm, {
|
|
852
|
-
entity,
|
|
853
|
-
entityId: id,
|
|
854
|
-
});
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
else {
|
|
858
|
-
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
|
859
|
-
if (filter) {
|
|
860
|
-
if (filter.id && Object.keys(filter).length === 1) {
|
|
861
|
-
Object.assign(otm, {
|
|
862
|
-
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
863
|
-
entity,
|
|
864
|
-
entityId: filter.id,
|
|
865
|
-
}, filterOtm]),
|
|
866
|
-
});
|
|
867
|
-
}
|
|
868
|
-
else {
|
|
869
|
-
Object.assign(otm, {
|
|
870
|
-
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
871
|
-
[entity]: filter,
|
|
872
|
-
}, filterOtm]),
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
else {
|
|
877
|
-
Object.assign(otm, {
|
|
878
|
-
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
879
|
-
entity,
|
|
880
|
-
entityId: {
|
|
881
|
-
$exists: true,
|
|
882
|
-
}
|
|
883
|
-
}, filterOtm])
|
|
884
|
-
});
|
|
885
|
-
}
|
|
886
|
-
if (action === 'remove' && actionOtm === 'update') {
|
|
887
|
-
Object.assign(dataOtm, {
|
|
888
|
-
entity: null,
|
|
889
|
-
entityId: null,
|
|
890
|
-
});
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
else {
|
|
895
|
-
// 基于foreignKey的one-to-many
|
|
896
|
-
if (action === 'create') {
|
|
897
|
-
const { id } = data;
|
|
898
|
-
if (dataOtm instanceof Array) {
|
|
899
|
-
dataOtm.forEach(ele => Object.assign(ele, {
|
|
900
|
-
[foreignKey]: id,
|
|
901
|
-
}));
|
|
902
|
-
}
|
|
903
|
-
else {
|
|
904
|
-
Object.assign(dataOtm, {
|
|
905
|
-
[foreignKey]: id,
|
|
906
|
-
});
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
else if (actionOtm === 'create') {
|
|
910
|
-
// 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作
|
|
911
|
-
// todo 这个假设在后台可能不成立,等遇到了再说
|
|
912
|
-
const { id } = filter;
|
|
913
|
-
(0, assert_1.default)(typeof id === 'string');
|
|
914
|
-
if (dataOtm instanceof Array) {
|
|
915
|
-
dataOtm.forEach(ele => Object.assign(ele, {
|
|
916
|
-
[foreignKey]: id,
|
|
917
|
-
}));
|
|
918
|
-
}
|
|
919
|
-
else {
|
|
920
|
-
Object.assign(dataOtm, {
|
|
921
|
-
[foreignKey]: id,
|
|
922
|
-
});
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
else {
|
|
926
|
-
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
|
927
|
-
if (filter) {
|
|
928
|
-
if (filter.id && Object.keys(filter).length === 1) {
|
|
929
|
-
Object.assign(otm, {
|
|
930
|
-
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
931
|
-
[foreignKey]: filter.id,
|
|
932
|
-
}, filterOtm]),
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
else {
|
|
936
|
-
Object.assign(otm, {
|
|
937
|
-
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
938
|
-
[foreignKey.slice(0, foreignKey.length - 2)]: filter,
|
|
939
|
-
}, filterOtm]),
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
else {
|
|
944
|
-
Object.assign(otm, {
|
|
945
|
-
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
946
|
-
[foreignKey]: {
|
|
947
|
-
$exists: true,
|
|
948
|
-
},
|
|
949
|
-
}, filterOtm]),
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
if (action === 'remove' && actionOtm === 'update') {
|
|
953
|
-
Object.assign(dataOtm, {
|
|
954
|
-
[foreignKey]: null,
|
|
955
|
-
});
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
// 一对多的依赖应该后建,否则中间会出现空指针,导致checker等出错
|
|
960
|
-
afterFns.push(() => cascadeUpdate.call(this, entityOtm, otm, context, option2));
|
|
961
|
-
};
|
|
962
|
-
if (otmOperations instanceof Array) {
|
|
963
|
-
for (const oper of otmOperations) {
|
|
964
|
-
dealWithOneToMany(oper);
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
else {
|
|
968
|
-
dealWithOneToMany(otmOperations);
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
else {
|
|
972
|
-
console.warn(`接收到不合法的属性「${attr}」`);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
return {
|
|
976
|
-
data: opData,
|
|
977
|
-
beforeFns,
|
|
978
|
-
afterFns,
|
|
979
|
-
};
|
|
980
|
-
}
|
|
981
|
-
// 对插入的数据,没有初始值的属性置null
|
|
982
|
-
preProcessDataCreated(entity, data) {
|
|
983
|
-
const now = Date.now();
|
|
984
|
-
const { attributes } = this.getSchema()[entity];
|
|
985
|
-
const processSingle = (data2) => {
|
|
986
|
-
for (const key in attributes) {
|
|
987
|
-
if (data2[key] === undefined) {
|
|
988
|
-
Object.assign(data2, {
|
|
989
|
-
[key]: null,
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
Object.assign(data2, {
|
|
994
|
-
[Entity_1.CreateAtAttribute]: now,
|
|
995
|
-
[Entity_1.UpdateAtAttribute]: now,
|
|
996
|
-
[Entity_1.DeleteAtAttribute]: null,
|
|
997
|
-
});
|
|
998
|
-
};
|
|
999
|
-
if (data instanceof Array) {
|
|
1000
|
-
data.forEach(ele => processSingle(ele));
|
|
1001
|
-
}
|
|
1002
|
-
else {
|
|
1003
|
-
processSingle(data);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
// 对更新的数据,去掉所有的undefined属性
|
|
1007
|
-
preProcessDataUpdated(data) {
|
|
1008
|
-
const undefinedKeys = Object.keys(data).filter(ele => data[ele] === undefined);
|
|
1009
|
-
undefinedKeys.forEach(ele => (0, lodash_1.unset)(data, ele));
|
|
1010
|
-
}
|
|
1011
|
-
judgeRelation(entity, attr) {
|
|
1012
|
-
return (0, relation_1.judgeRelation)(this.storageSchema, entity, attr);
|
|
1013
|
-
}
|
|
1014
|
-
/**
|
|
1015
|
-
* 和具体的update过程无关的例程放在这里,包括对later动作的处理、对oper的记录以及对record的收集等
|
|
1016
|
-
* @param entity
|
|
1017
|
-
* @param operation
|
|
1018
|
-
* @param context
|
|
1019
|
-
* @param option
|
|
1020
|
-
*/
|
|
1021
|
-
async doUpdateSingleRowAsync(entity, operation, context, option) {
|
|
1022
|
-
const { data, action, id: operId, filter, bornAt } = operation;
|
|
1023
|
-
const now = Date.now();
|
|
1024
|
-
switch (action) {
|
|
1025
|
-
case 'create': {
|
|
1026
|
-
if (option.modiParentEntity && !['modi', 'modiEntity', 'oper', 'operEntity'].includes(entity)) {
|
|
1027
|
-
// 变成对modi的插入
|
|
1028
|
-
(0, assert_1.default)(option.modiParentId);
|
|
1029
|
-
const modiCreate = {
|
|
1030
|
-
id: 'dummy',
|
|
1031
|
-
action: 'create',
|
|
1032
|
-
data: {
|
|
1033
|
-
id: operId,
|
|
1034
|
-
targetEntity: entity,
|
|
1035
|
-
action,
|
|
1036
|
-
entity: option.modiParentEntity,
|
|
1037
|
-
entityId: option.modiParentId,
|
|
1038
|
-
filter: {
|
|
1039
|
-
id: data.id, //这里记录这个filter是为了后面update的时候直接在其上面update,参见本函数后半段关于modiUpsert相关的优化
|
|
1040
|
-
},
|
|
1041
|
-
data,
|
|
1042
|
-
iState: 'active',
|
|
1043
|
-
},
|
|
1044
|
-
};
|
|
1045
|
-
const closeRootMode = context.openRootMode();
|
|
1046
|
-
await this.cascadeUpdateAsync('modi', modiCreate, context, option);
|
|
1047
|
-
closeRootMode();
|
|
1048
|
-
return {
|
|
1049
|
-
'modi': {
|
|
1050
|
-
create: 1,
|
|
1051
|
-
}
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
else {
|
|
1055
|
-
this.preProcessDataCreated(entity, data);
|
|
1056
|
-
let result = 0;
|
|
1057
|
-
const createInner = async (operation2) => {
|
|
1058
|
-
try {
|
|
1059
|
-
await this.updateAbjointRowAsync(entity, operation2, context, option);
|
|
1060
|
-
}
|
|
1061
|
-
catch (e) {
|
|
1062
|
-
/* 这段代码是处理插入时有重复的行,现在看有问题,等实际需求出现再写
|
|
1063
|
-
if (e instanceof OakCongruentRowExists) {
|
|
1064
|
-
if (option.allowExists) {
|
|
1065
|
-
// 如果允许存在,对已存在行进行update,剩下的行继续insert
|
|
1066
|
-
const congruentRow = e.getData() as ED[T]['OpSchema'];
|
|
1067
|
-
if (data instanceof Array) {
|
|
1068
|
-
const rest = data.filter(
|
|
1069
|
-
ele => ele.id !== congruentRow.id
|
|
1070
|
-
);
|
|
1071
|
-
if (rest.length === data.length) {
|
|
1072
|
-
throw e;
|
|
1073
|
-
}
|
|
1074
|
-
const result2 = await this.updateAbjointRow(
|
|
1075
|
-
entity,
|
|
1076
|
-
Object.assign({}, operation, {
|
|
1077
|
-
data: rest,
|
|
1078
|
-
}),
|
|
1079
|
-
context,
|
|
1080
|
-
option
|
|
1081
|
-
);
|
|
1082
|
-
|
|
1083
|
-
const row = data.find(
|
|
1084
|
-
ele => ele.id === congruentRow.id
|
|
1085
|
-
);
|
|
1086
|
-
const updateData = omit(row, ['id', '$$createAt$$']);
|
|
1087
|
-
const result3 = await this.updateAbjointRow(
|
|
1088
|
-
entity,
|
|
1089
|
-
{
|
|
1090
|
-
id: await generateNewId(),
|
|
1091
|
-
action: 'update',
|
|
1092
|
-
data: updateData,
|
|
1093
|
-
filter: {
|
|
1094
|
-
id: congruentRow.id,
|
|
1095
|
-
} as any,
|
|
1096
|
-
},
|
|
1097
|
-
context,
|
|
1098
|
-
option
|
|
1099
|
-
);
|
|
1100
|
-
|
|
1101
|
-
return result2 + result3;
|
|
1102
|
-
}
|
|
1103
|
-
else {
|
|
1104
|
-
if (data.id !== congruentRow.id) {
|
|
1105
|
-
throw e;
|
|
1106
|
-
}
|
|
1107
|
-
const updateData = omit(data, ['id', '$$createAt$$']);
|
|
1108
|
-
const result2 = await this.updateAbjointRow(
|
|
1109
|
-
entity,
|
|
1110
|
-
{
|
|
1111
|
-
id: await generateNewId(),
|
|
1112
|
-
action: 'update',
|
|
1113
|
-
data: updateData,
|
|
1114
|
-
filter: {
|
|
1115
|
-
id: congruentRow.id,
|
|
1116
|
-
} as any,
|
|
1117
|
-
},
|
|
1118
|
-
context,
|
|
1119
|
-
option
|
|
1120
|
-
);
|
|
1121
|
-
return result2;
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
} */
|
|
1125
|
-
throw e;
|
|
1126
|
-
}
|
|
1127
|
-
};
|
|
1128
|
-
if (data instanceof Array) {
|
|
1129
|
-
result = data.length;
|
|
1130
|
-
const multipleCreate = this.supportMultipleCreate();
|
|
1131
|
-
if (multipleCreate) {
|
|
1132
|
-
await createInner(operation);
|
|
1133
|
-
}
|
|
1134
|
-
else {
|
|
1135
|
-
for (const d of data) {
|
|
1136
|
-
const createSingleOper = {
|
|
1137
|
-
id: 'any',
|
|
1138
|
-
action: 'create',
|
|
1139
|
-
data: d,
|
|
1140
|
-
};
|
|
1141
|
-
await createInner(createSingleOper);
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
else {
|
|
1146
|
-
result = 1;
|
|
1147
|
-
await createInner(operation);
|
|
1148
|
-
}
|
|
1149
|
-
if (!option.dontCollect) {
|
|
1150
|
-
context.saveOpRecord(entity, operation);
|
|
1151
|
-
}
|
|
1152
|
-
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity)) {
|
|
1153
|
-
// 按照框架要求生成Oper和OperEntity这两个内置的对象
|
|
1154
|
-
(0, assert_1.default)(operId);
|
|
1155
|
-
const operatorId = context.getCurrentUserId(true);
|
|
1156
|
-
if (operatorId) {
|
|
1157
|
-
const createOper = {
|
|
1158
|
-
id: 'dummy',
|
|
1159
|
-
action: 'create',
|
|
1160
|
-
data: {
|
|
1161
|
-
id: operId,
|
|
1162
|
-
action,
|
|
1163
|
-
data,
|
|
1164
|
-
operatorId,
|
|
1165
|
-
targetEntity: entity,
|
|
1166
|
-
bornAt,
|
|
1167
|
-
operEntity$oper: data instanceof Array ? {
|
|
1168
|
-
id: 'dummy',
|
|
1169
|
-
action: 'create',
|
|
1170
|
-
data: await Promise.all(data.map(async (ele) => ({
|
|
1171
|
-
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1172
|
-
entityId: ele.id,
|
|
1173
|
-
entity: entity,
|
|
1174
|
-
}))),
|
|
1175
|
-
} : [{
|
|
1176
|
-
id: 'dummy',
|
|
1177
|
-
action: 'create',
|
|
1178
|
-
data: {
|
|
1179
|
-
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1180
|
-
entityId: data.id,
|
|
1181
|
-
entity: entity,
|
|
1182
|
-
},
|
|
1183
|
-
}],
|
|
1184
|
-
},
|
|
1185
|
-
};
|
|
1186
|
-
const closeRootMode = context.openRootMode();
|
|
1187
|
-
await this.cascadeUpdateAsync('oper', createOper, context, {
|
|
1188
|
-
dontCollect: true,
|
|
1189
|
-
});
|
|
1190
|
-
closeRootMode();
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
return {
|
|
1194
|
-
[entity]: {
|
|
1195
|
-
['create']: result,
|
|
1196
|
-
}
|
|
1197
|
-
};
|
|
1198
|
-
}
|
|
1199
|
-
}
|
|
1200
|
-
default: {
|
|
1201
|
-
// 这里要优化一下,显式的对id的update/remove不要去查了,节省数据库层的性能(如果这些row是建立在一个create的modi上也查不到)
|
|
1202
|
-
const ids = (0, filter_2.getRelevantIds)(filter);
|
|
1203
|
-
if (ids.length === 0) {
|
|
1204
|
-
const selection = {
|
|
1205
|
-
data: {
|
|
1206
|
-
id: 1,
|
|
1207
|
-
},
|
|
1208
|
-
filter: operation.filter,
|
|
1209
|
-
indexFrom: operation.indexFrom,
|
|
1210
|
-
count: operation.count,
|
|
1211
|
-
};
|
|
1212
|
-
const rows = await this.selectAbjointRowAsync(entity, selection, context, {
|
|
1213
|
-
dontCollect: true,
|
|
1214
|
-
});
|
|
1215
|
-
ids.push(...(rows.map(ele => ele.id)));
|
|
1216
|
-
}
|
|
1217
|
-
if (data) {
|
|
1218
|
-
this.preProcessDataUpdated(data);
|
|
1219
|
-
}
|
|
1220
|
-
if (option.modiParentEntity && !['modi', 'modiEntity'].includes(entity)) {
|
|
1221
|
-
// 延时更新,变成对modi的插入
|
|
1222
|
-
// 变成对modi的插入
|
|
1223
|
-
// 优化,这里如果是对同一个targetEntity反复update,则变成对最后一条create/update的modi进行update,以避免发布文章这样的需求时产生过多的modi
|
|
1224
|
-
let modiUpsert;
|
|
1225
|
-
if (action !== 'remove') {
|
|
1226
|
-
const upsertModis = await this.selectAbjointRowAsync('modi', {
|
|
1227
|
-
data: {
|
|
1228
|
-
id: 1,
|
|
1229
|
-
data: 1,
|
|
1230
|
-
},
|
|
1231
|
-
filter: {
|
|
1232
|
-
targetEntity: entity,
|
|
1233
|
-
action: {
|
|
1234
|
-
$in: ['create', 'update'],
|
|
1235
|
-
},
|
|
1236
|
-
entity: option.modiParentEntity,
|
|
1237
|
-
entityId: option.modiParentId,
|
|
1238
|
-
iState: 'active',
|
|
1239
|
-
filter: ids.length > 0 ? {
|
|
1240
|
-
id: {
|
|
1241
|
-
$in: ids,
|
|
1242
|
-
},
|
|
1243
|
-
} : filter,
|
|
1244
|
-
},
|
|
1245
|
-
sorter: [
|
|
1246
|
-
{
|
|
1247
|
-
$attr: {
|
|
1248
|
-
$$createAt$$: 1,
|
|
1249
|
-
},
|
|
1250
|
-
$direction: 'desc',
|
|
1251
|
-
}
|
|
1252
|
-
],
|
|
1253
|
-
indexFrom: 0,
|
|
1254
|
-
count: 1,
|
|
1255
|
-
}, context, option);
|
|
1256
|
-
if (upsertModis.length > 0) {
|
|
1257
|
-
const { data: originData, id: originId } = upsertModis[0];
|
|
1258
|
-
modiUpsert = {
|
|
1259
|
-
id: 'dummy',
|
|
1260
|
-
action: 'update',
|
|
1261
|
-
data: {
|
|
1262
|
-
data: Object.assign({}, originData, data),
|
|
1263
|
-
},
|
|
1264
|
-
filter: {
|
|
1265
|
-
id: originId,
|
|
1266
|
-
}
|
|
1267
|
-
};
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
if (!modiUpsert) {
|
|
1271
|
-
modiUpsert = {
|
|
1272
|
-
id: 'dummy',
|
|
1273
|
-
action: 'create',
|
|
1274
|
-
data: {
|
|
1275
|
-
id: operId,
|
|
1276
|
-
targetEntity: entity,
|
|
1277
|
-
entity: option.modiParentEntity,
|
|
1278
|
-
entityId: option.modiParentId,
|
|
1279
|
-
action,
|
|
1280
|
-
data,
|
|
1281
|
-
iState: 'active',
|
|
1282
|
-
filter,
|
|
1283
|
-
},
|
|
1284
|
-
};
|
|
1285
|
-
if (ids.length > 0) {
|
|
1286
|
-
modiUpsert.data.modiEntity$modi = {
|
|
1287
|
-
id: 'dummy',
|
|
1288
|
-
action: 'create',
|
|
1289
|
-
data: await Promise.all(ids.map(async (id) => ({
|
|
1290
|
-
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1291
|
-
entity: entity,
|
|
1292
|
-
entityId: id,
|
|
1293
|
-
}))),
|
|
1294
|
-
};
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
const closeRootMode = context.openRootMode();
|
|
1298
|
-
await this.cascadeUpdateAsync('modi', modiUpsert, context, option);
|
|
1299
|
-
closeRootMode();
|
|
1300
|
-
return {
|
|
1301
|
-
modi: {
|
|
1302
|
-
['create']: 1,
|
|
1303
|
-
},
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
else {
|
|
1307
|
-
const createOper = async () => {
|
|
1308
|
-
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity) && ids.length > 0) {
|
|
1309
|
-
// 按照框架要求生成Oper和OperEntity这两个内置的对象
|
|
1310
|
-
(0, assert_1.default)(operId);
|
|
1311
|
-
const operatorId = context.getCurrentUserId(true);
|
|
1312
|
-
const createOper = {
|
|
1313
|
-
id: 'dummy',
|
|
1314
|
-
action: 'create',
|
|
1315
|
-
data: {
|
|
1316
|
-
id: operId,
|
|
1317
|
-
action,
|
|
1318
|
-
data,
|
|
1319
|
-
targetEntity: entity,
|
|
1320
|
-
bornAt,
|
|
1321
|
-
operatorId,
|
|
1322
|
-
operEntity$oper: {
|
|
1323
|
-
id: 'dummy',
|
|
1324
|
-
action: 'create',
|
|
1325
|
-
data: await Promise.all(ids.map(async (ele) => ({
|
|
1326
|
-
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1327
|
-
entityId: ele,
|
|
1328
|
-
entity: entity,
|
|
1329
|
-
})))
|
|
1330
|
-
},
|
|
1331
|
-
},
|
|
1332
|
-
};
|
|
1333
|
-
const closeRootMode = context.openRootMode();
|
|
1334
|
-
await this.cascadeUpdateAsync('oper', createOper, context, {
|
|
1335
|
-
dontCollect: true,
|
|
1336
|
-
});
|
|
1337
|
-
closeRootMode();
|
|
1338
|
-
}
|
|
1339
|
-
};
|
|
1340
|
-
if (action === 'remove') {
|
|
1341
|
-
if (!option.dontCollect) {
|
|
1342
|
-
context.saveOpRecord(entity, {
|
|
1343
|
-
id: operId,
|
|
1344
|
-
action,
|
|
1345
|
-
data: {},
|
|
1346
|
-
filter: {
|
|
1347
|
-
id: {
|
|
1348
|
-
$in: ids,
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
});
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
else {
|
|
1355
|
-
const updateAttrCount = Object.keys(data).length;
|
|
1356
|
-
if (updateAttrCount > 0) {
|
|
1357
|
-
// 优化一下,如果不更新任何属性,则不实际执行
|
|
1358
|
-
Object.assign(data, {
|
|
1359
|
-
[Entity_1.UpdateAtAttribute]: now,
|
|
1360
|
-
});
|
|
1361
|
-
if (!option.dontCollect) {
|
|
1362
|
-
context.saveOpRecord(entity, {
|
|
1363
|
-
id: operId,
|
|
1364
|
-
action,
|
|
1365
|
-
data: data,
|
|
1366
|
-
filter: {
|
|
1367
|
-
id: {
|
|
1368
|
-
$in: ids,
|
|
1369
|
-
}
|
|
1370
|
-
},
|
|
1371
|
-
});
|
|
1372
|
-
}
|
|
1373
|
-
}
|
|
1374
|
-
else if (action !== 'update') {
|
|
1375
|
-
// 如果不是update动作而是用户自定义的动作,这里还是要记录oper
|
|
1376
|
-
await createOper();
|
|
1377
|
-
return {};
|
|
1378
|
-
}
|
|
1379
|
-
else {
|
|
1380
|
-
return {};
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
await this.updateAbjointRowAsync(entity, operation, context, option);
|
|
1384
|
-
await createOper();
|
|
1385
|
-
return {
|
|
1386
|
-
[entity]: {
|
|
1387
|
-
[action]: ids.length,
|
|
1388
|
-
}
|
|
1389
|
-
};
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
doUpdateSingleRow(entity, operation, context, option) {
|
|
1395
|
-
const { data, action, id: operId, filter } = operation;
|
|
1396
|
-
const now = Date.now();
|
|
1397
|
-
switch (action) {
|
|
1398
|
-
case 'create': {
|
|
1399
|
-
this.preProcessDataCreated(entity, data);
|
|
1400
|
-
let result = 0;
|
|
1401
|
-
const createInner = (operation2) => {
|
|
1402
|
-
try {
|
|
1403
|
-
result += this.updateAbjointRow(entity, operation2, context, option);
|
|
1404
|
-
}
|
|
1405
|
-
catch (e) {
|
|
1406
|
-
throw e;
|
|
1407
|
-
}
|
|
1408
|
-
};
|
|
1409
|
-
if (data instanceof Array) {
|
|
1410
|
-
const multipleCreate = this.supportMultipleCreate();
|
|
1411
|
-
if (multipleCreate) {
|
|
1412
|
-
createInner(operation);
|
|
1413
|
-
}
|
|
1414
|
-
else {
|
|
1415
|
-
for (const d of data) {
|
|
1416
|
-
const createSingleOper = {
|
|
1417
|
-
id: 'any',
|
|
1418
|
-
action: 'create',
|
|
1419
|
-
data: d,
|
|
1420
|
-
};
|
|
1421
|
-
createInner(createSingleOper);
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
else {
|
|
1426
|
-
createInner(operation);
|
|
1427
|
-
}
|
|
1428
|
-
return result;
|
|
1429
|
-
}
|
|
1430
|
-
default: {
|
|
1431
|
-
if (action === 'remove') {
|
|
1432
|
-
}
|
|
1433
|
-
else {
|
|
1434
|
-
const updateAttrCount = Object.keys(data).length;
|
|
1435
|
-
if (updateAttrCount > 0) {
|
|
1436
|
-
// 优化一下,如果不更新任何属性,则不实际执行
|
|
1437
|
-
Object.assign(data, {
|
|
1438
|
-
$$updateAt$$: now,
|
|
1439
|
-
});
|
|
1440
|
-
this.preProcessDataUpdated(data);
|
|
1441
|
-
}
|
|
1442
|
-
else {
|
|
1443
|
-
return 0;
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
return this.updateAbjointRow(entity, operation, context, option);
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
cascadeUpdate(entity, operation, context, option) {
|
|
1451
|
-
const { action, data, filter, id, bornAt } = operation;
|
|
1452
|
-
let opData;
|
|
1453
|
-
const wholeBeforeFns = [];
|
|
1454
|
-
const wholeAfterFns = [];
|
|
1455
|
-
const result = {};
|
|
1456
|
-
if (['create', 'create-l'].includes(action) && data instanceof Array) {
|
|
1457
|
-
opData = [];
|
|
1458
|
-
for (const d of data) {
|
|
1459
|
-
const { data: od, beforeFns, afterFns } = this.destructCascadeUpdate(entity, action, d, context, option, this.cascadeUpdate);
|
|
1460
|
-
opData.push(od);
|
|
1461
|
-
wholeBeforeFns.push(...beforeFns);
|
|
1462
|
-
wholeAfterFns.push(...afterFns);
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
else {
|
|
1466
|
-
const { data: od, beforeFns, afterFns } = this.destructCascadeUpdate(entity, action, data, context, option, this.cascadeUpdate, filter);
|
|
1467
|
-
opData = od;
|
|
1468
|
-
wholeBeforeFns.push(...beforeFns);
|
|
1469
|
-
wholeAfterFns.push(...afterFns);
|
|
1470
|
-
}
|
|
1471
|
-
const operation2 = Object.assign({}, operation, {
|
|
1472
|
-
data: opData,
|
|
1473
|
-
});
|
|
1474
|
-
for (const before of wholeBeforeFns) {
|
|
1475
|
-
before();
|
|
1476
|
-
}
|
|
1477
|
-
const count = this.doUpdateSingleRow(entity, operation2, context, option);
|
|
1478
|
-
for (const after of wholeAfterFns) {
|
|
1479
|
-
after();
|
|
1480
|
-
}
|
|
1481
|
-
return result;
|
|
1482
|
-
}
|
|
1483
|
-
/**
|
|
1484
|
-
*
|
|
1485
|
-
* @param entity
|
|
1486
|
-
* @param operation
|
|
1487
|
-
* @param context
|
|
1488
|
-
* @param option
|
|
1489
|
-
*/
|
|
1490
|
-
async cascadeUpdateAsync(entity, operation, context, option) {
|
|
1491
|
-
const { action, data, filter, id, bornAt } = operation;
|
|
1492
|
-
let opData;
|
|
1493
|
-
const wholeBeforeFns = [];
|
|
1494
|
-
const wholeAfterFns = [];
|
|
1495
|
-
if (['create', 'create-l'].includes(action) && data instanceof Array) {
|
|
1496
|
-
opData = [];
|
|
1497
|
-
for (const d of data) {
|
|
1498
|
-
const { data: od, beforeFns, afterFns } = this.destructCascadeUpdate(entity, action, d, context, option, this.cascadeUpdateAsync, undefined, bornAt);
|
|
1499
|
-
opData.push(od);
|
|
1500
|
-
wholeBeforeFns.push(...beforeFns);
|
|
1501
|
-
wholeAfterFns.push(...afterFns);
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
else {
|
|
1505
|
-
const { data: od, beforeFns, afterFns } = this.destructCascadeUpdate(entity, action, data, context, option, this.cascadeUpdateAsync, filter, bornAt);
|
|
1506
|
-
opData = od;
|
|
1507
|
-
wholeBeforeFns.push(...beforeFns);
|
|
1508
|
-
wholeAfterFns.push(...afterFns);
|
|
1509
|
-
}
|
|
1510
|
-
const operation2 = Object.assign({}, operation, {
|
|
1511
|
-
data: opData,
|
|
1512
|
-
});
|
|
1513
|
-
let result = {};
|
|
1514
|
-
for (const before of wholeBeforeFns) {
|
|
1515
|
-
const result2 = await before();
|
|
1516
|
-
result = this.mergeMultipleResults([result, result2]);
|
|
1517
|
-
}
|
|
1518
|
-
const resultMe = await this.doUpdateSingleRowAsync(entity, operation2, context, option);
|
|
1519
|
-
result = this.mergeMultipleResults([result, resultMe]);
|
|
1520
|
-
for (const after of wholeAfterFns) {
|
|
1521
|
-
const result2 = await after();
|
|
1522
|
-
result = this.mergeMultipleResults([result, result2]);
|
|
1523
|
-
}
|
|
1524
|
-
return result;
|
|
1525
|
-
}
|
|
1526
|
-
cascadeSelect(entity, selection, context, option) {
|
|
1527
|
-
const { data, filter, indexFrom, count, sorter, distinct } = selection;
|
|
1528
|
-
const { projection, cascadeSelectionFns } = this.destructCascadeSelect(entity, data, context, this.cascadeSelect, this.aggregateSync, option);
|
|
1529
|
-
const rows = this.selectAbjointRow(entity, {
|
|
1530
|
-
data: projection,
|
|
1531
|
-
filter,
|
|
1532
|
-
indexFrom,
|
|
1533
|
-
count,
|
|
1534
|
-
sorter,
|
|
1535
|
-
distinct
|
|
1536
|
-
}, context, option);
|
|
1537
|
-
if (cascadeSelectionFns.length > 0) {
|
|
1538
|
-
const ruException = [];
|
|
1539
|
-
cascadeSelectionFns.forEach(ele => {
|
|
1540
|
-
try {
|
|
1541
|
-
ele(rows);
|
|
1542
|
-
}
|
|
1543
|
-
catch (e) {
|
|
1544
|
-
if (e instanceof types_1.OakRowUnexistedException) {
|
|
1545
|
-
const rows = e.getRows();
|
|
1546
|
-
ruException.push(...rows);
|
|
1547
|
-
}
|
|
1548
|
-
else {
|
|
1549
|
-
throw e;
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
});
|
|
1553
|
-
if (ruException.length > 0) {
|
|
1554
|
-
throw new types_1.OakRowUnexistedException(ruException);
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
return rows;
|
|
1558
|
-
}
|
|
1559
|
-
/**
|
|
1560
|
-
* 将一次查询的结果集加入result
|
|
1561
|
-
* todo 如果是supportMtoOJoin,这里还要解构(未充分测试)
|
|
1562
|
-
* @param entity
|
|
1563
|
-
* @param rows
|
|
1564
|
-
* @param context
|
|
1565
|
-
*/
|
|
1566
|
-
addToResultSelections(entity, rows, context) {
|
|
1567
|
-
if (this.supportManyToOneJoin()) {
|
|
1568
|
-
// 这里的外键连接有可能为空,需要使用所有的行的attr的并集来测试
|
|
1569
|
-
const attrs = (0, lodash_1.uniq)(rows.map(ele => Object.keys(ele)).flat());
|
|
1570
|
-
const attrsToPick = [];
|
|
1571
|
-
for (const attr of attrs) {
|
|
1572
|
-
const data = {};
|
|
1573
|
-
const rel = this.judgeRelation(entity, attr);
|
|
1574
|
-
if (rel === 2) {
|
|
1575
|
-
this.addToResultSelections(attr, rows.map(ele => ele[attr]).filter(ele => !!ele), context);
|
|
1576
|
-
}
|
|
1577
|
-
else if (typeof rel === 'string') {
|
|
1578
|
-
this.addToResultSelections(rel, rows.map(ele => ele[attr]).filter(ele => !!ele), context);
|
|
1579
|
-
}
|
|
1580
|
-
else if (rel instanceof Array) {
|
|
1581
|
-
this.addToResultSelections(rel[0], rows.map(ele => ele[attr]).reduce((prev, current) => prev.concat(current), []), context);
|
|
1582
|
-
}
|
|
1583
|
-
else {
|
|
1584
|
-
attrsToPick.push(attr);
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
const originRows = rows.map(ele => (0, lodash_1.pick)(ele, attrsToPick));
|
|
1588
|
-
this.addSingleRowToResultSelections(entity, originRows, context);
|
|
1589
|
-
}
|
|
1590
|
-
else {
|
|
1591
|
-
this.addSingleRowToResultSelections(entity, rows, context);
|
|
1592
|
-
}
|
|
1593
|
-
}
|
|
1594
|
-
addSingleRowToResultSelections(entity, rows, context) {
|
|
1595
|
-
const { opRecords } = context;
|
|
1596
|
-
let lastOperation = opRecords[opRecords.length - 1];
|
|
1597
|
-
if (lastOperation && lastOperation.a === 's') {
|
|
1598
|
-
const entityBranch = lastOperation.d[entity];
|
|
1599
|
-
if (entityBranch) {
|
|
1600
|
-
rows.forEach((row) => {
|
|
1601
|
-
if (row && row.id) { // 如果没有row.id就不加入结果集了
|
|
1602
|
-
const { id } = row;
|
|
1603
|
-
if (!entityBranch[id]) {
|
|
1604
|
-
Object.assign(entityBranch, {
|
|
1605
|
-
[id]: (0, lodash_1.cloneDeep)(row),
|
|
1606
|
-
});
|
|
1607
|
-
}
|
|
1608
|
-
else {
|
|
1609
|
-
Object.assign(entityBranch[id], (0, lodash_1.cloneDeep)(row));
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
});
|
|
1613
|
-
return;
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
else {
|
|
1617
|
-
lastOperation = {
|
|
1618
|
-
a: 's',
|
|
1619
|
-
d: {},
|
|
1620
|
-
};
|
|
1621
|
-
opRecords.push(lastOperation);
|
|
1622
|
-
}
|
|
1623
|
-
const entityBranch = {};
|
|
1624
|
-
rows.forEach((row) => {
|
|
1625
|
-
if (row) {
|
|
1626
|
-
const { id } = row;
|
|
1627
|
-
Object.assign(entityBranch, {
|
|
1628
|
-
[id]: (0, lodash_1.cloneDeep)(row),
|
|
1629
|
-
});
|
|
1630
|
-
}
|
|
1631
|
-
});
|
|
1632
|
-
Object.assign(lastOperation.d, {
|
|
1633
|
-
[entity]: entityBranch,
|
|
1634
|
-
});
|
|
1635
|
-
}
|
|
1636
|
-
async cascadeSelectAsync(entity, selection, context, option) {
|
|
1637
|
-
const { data, filter, indexFrom, count, sorter, total, randomRange, distinct } = selection;
|
|
1638
|
-
const { projection, cascadeSelectionFns } = this.destructCascadeSelect(entity, data, context, this.cascadeSelectAsync, this.aggregateAsync, option);
|
|
1639
|
-
const rows2 = await this.selectAbjointRowAsync(entity, {
|
|
1640
|
-
data: projection,
|
|
1641
|
-
filter,
|
|
1642
|
-
indexFrom,
|
|
1643
|
-
distinct,
|
|
1644
|
-
count: randomRange || count,
|
|
1645
|
-
sorter
|
|
1646
|
-
}, context, option);
|
|
1647
|
-
// 处理随机取值
|
|
1648
|
-
let rows = !randomRange ? rows2 : [];
|
|
1649
|
-
if (randomRange) {
|
|
1650
|
-
const possibility = count / rows2.length;
|
|
1651
|
-
let reduced = rows2.length - count;
|
|
1652
|
-
rows = rows2.filter(() => {
|
|
1653
|
-
const rand = Math.random();
|
|
1654
|
-
if (rand > possibility && reduced) {
|
|
1655
|
-
reduced--;
|
|
1656
|
-
return false;
|
|
1657
|
-
}
|
|
1658
|
-
return true;
|
|
1659
|
-
});
|
|
1660
|
-
}
|
|
1661
|
-
if (!option.dontCollect) {
|
|
1662
|
-
this.addToResultSelections(entity, rows, context);
|
|
1663
|
-
}
|
|
1664
|
-
if (cascadeSelectionFns.length > 0) {
|
|
1665
|
-
const ruException = [];
|
|
1666
|
-
await Promise.all(cascadeSelectionFns.map(async (ele) => {
|
|
1667
|
-
try {
|
|
1668
|
-
await ele(rows);
|
|
1669
|
-
}
|
|
1670
|
-
catch (e) {
|
|
1671
|
-
if (e instanceof types_1.OakRowUnexistedException) {
|
|
1672
|
-
const rows = e.getRows();
|
|
1673
|
-
ruException.push(...rows);
|
|
1674
|
-
}
|
|
1675
|
-
else {
|
|
1676
|
-
throw e;
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
}));
|
|
1680
|
-
if (ruException.length > 0) {
|
|
1681
|
-
throw new types_1.OakRowUnexistedException(ruException);
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
if (total) {
|
|
1685
|
-
const total2 = await this.countAsync(entity, {
|
|
1686
|
-
filter: selection.filter,
|
|
1687
|
-
count: total,
|
|
1688
|
-
}, context, option);
|
|
1689
|
-
Object.assign(rows, {
|
|
1690
|
-
'#total': total2,
|
|
1691
|
-
});
|
|
1692
|
-
}
|
|
1693
|
-
return rows;
|
|
1694
|
-
}
|
|
1695
|
-
async aggregateAsync(entity, aggregation, context, option) {
|
|
1696
|
-
await this.reinforceSelectionAsync(entity, aggregation, context, option, true);
|
|
1697
|
-
return this.aggregateAbjointRowAsync(entity, aggregation, context, option);
|
|
1698
|
-
}
|
|
1699
|
-
aggregateSync(entity, aggregation, context, option) {
|
|
1700
|
-
this.reinforceSelectionSync(entity, aggregation, context, option, true);
|
|
1701
|
-
return this.aggregateAbjointRowSync(entity, aggregation, context, option);
|
|
1702
|
-
}
|
|
1703
|
-
async selectAsync(entity, selection, context, option) {
|
|
1704
|
-
await this.reinforceSelectionAsync(entity, selection, context, option);
|
|
1705
|
-
return this.cascadeSelectAsync(entity, selection, context, option);
|
|
1706
|
-
}
|
|
1707
|
-
selectSync(entity, selection, context, option) {
|
|
1708
|
-
this.reinforceSelectionSync(entity, selection, context, option);
|
|
1709
|
-
return this.cascadeSelect(entity, selection, context, option);
|
|
1710
|
-
}
|
|
1711
|
-
operateSync(entity, operation, context, option) {
|
|
1712
|
-
//this.reinforceOperation(entity, operation); // 感觉前台可以无视?
|
|
1713
|
-
return this.cascadeUpdate(entity, operation, context, option);
|
|
1714
|
-
}
|
|
1715
|
-
async operateAsync(entity, operation, context, option) {
|
|
1716
|
-
await this.reinforceOperation(entity, operation, context, option);
|
|
1717
|
-
return this.cascadeUpdateAsync(entity, operation, context, option);
|
|
1718
|
-
}
|
|
1719
|
-
countSync(entity, selection, context, option) {
|
|
1720
|
-
this.reinforceSelectionSync(entity, selection, context, option, true); // 这样写可能有问题的,虽然能跳过本地的projection补全,但如果有更多的selectionRewriter注入可能会出问题。by Xc 20231220
|
|
1721
|
-
return this.countAbjointRow(entity, selection, context, option);
|
|
1722
|
-
}
|
|
1723
|
-
countAsync(entity, selection, context, option) {
|
|
1724
|
-
this.reinforceSelectionAsync(entity, selection, context, option, true); // 这样写可能有问题的,虽然能跳过本地的projection补全,但如果有更多的selectionRewriter注入可能会出问题。by Xc 20231220
|
|
1725
|
-
return this.countAbjointRowAsync(entity, selection, context, option);
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
exports.CascadeStore = CascadeStore;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CascadeStore = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const assert_1 = tslib_1.__importDefault(require("assert"));
|
|
6
|
+
const Entity_1 = require("../types/Entity");
|
|
7
|
+
const RowStore_1 = require("../types/RowStore");
|
|
8
|
+
const filter_1 = require("./filter");
|
|
9
|
+
const relation_1 = require("./relation");
|
|
10
|
+
const types_1 = require("../types");
|
|
11
|
+
const lodash_1 = require("../utils/lodash");
|
|
12
|
+
const AsyncRowStore_1 = require("./AsyncRowStore");
|
|
13
|
+
const filter_2 = require("./filter");
|
|
14
|
+
const uuid_1 = require("../utils/uuid");
|
|
15
|
+
const entities_1 = require("../compiler/entities");
|
|
16
|
+
/**这个用来处理级联的select和update,对不同能力的 */
|
|
17
|
+
class CascadeStore extends RowStore_1.RowStore {
|
|
18
|
+
constructor(storageSchema) {
|
|
19
|
+
super(storageSchema);
|
|
20
|
+
}
|
|
21
|
+
selectionRewriters = [];
|
|
22
|
+
operationRewriters = [];
|
|
23
|
+
async reinforceSelectionAsync(entity, selection, context, option, isAggr) {
|
|
24
|
+
if (!isAggr && !selection.distinct) {
|
|
25
|
+
this.reinforceSelectionInner(entity, selection, context);
|
|
26
|
+
}
|
|
27
|
+
const rewriterPromises = this.selectionRewriters.map(ele => ele(this.getSchema(), entity, selection, context, option, isAggr));
|
|
28
|
+
if (rewriterPromises.length > 0) {
|
|
29
|
+
await Promise.all(rewriterPromises);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
reinforceSelectionSync(entity, selection, context, option, isAggr) {
|
|
33
|
+
if (!isAggr && !selection.distinct) {
|
|
34
|
+
this.reinforceSelectionInner(entity, selection, context);
|
|
35
|
+
}
|
|
36
|
+
this.selectionRewriters.forEach(ele => {
|
|
37
|
+
const result = ele(this.getSchema(), entity, selection, context, option, isAggr);
|
|
38
|
+
(0, assert_1.default)(!(result instanceof Promise));
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
reinforceSelectionInner(entity, selection, context) {
|
|
42
|
+
const { filter, data, sorter } = selection;
|
|
43
|
+
const assignNecessaryProjectionAttrs = (projectionNode, attrs) => {
|
|
44
|
+
attrs.forEach((attr) => {
|
|
45
|
+
if (!projectionNode.hasOwnProperty(attr)) {
|
|
46
|
+
Object.assign(projectionNode, {
|
|
47
|
+
[attr]: 1,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const checkFilterNode = (entity2, filterNode, projectionNode, toBeAssignNode, filterNodeDict) => {
|
|
53
|
+
const necessaryAttrs = ['id'];
|
|
54
|
+
for (const attr in filterNode) {
|
|
55
|
+
if (attr === '#id') {
|
|
56
|
+
(0, assert_1.default)(!filterNodeDict[filterNode[attr]], `projection中结点的id有重复, ${filterNode[attr]}`);
|
|
57
|
+
Object.assign(filterNodeDict, {
|
|
58
|
+
[filterNode[attr]]: projectionNode,
|
|
59
|
+
});
|
|
60
|
+
if (toBeAssignNode[filterNode[attr]]) {
|
|
61
|
+
assignNecessaryProjectionAttrs(projectionNode, toBeAssignNode[filterNode[attr]]);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (['$and', '$or'].includes(attr)) {
|
|
65
|
+
for (const node of filterNode[attr]) {
|
|
66
|
+
checkFilterNode(entity2, node, projectionNode, toBeAssignNode, filterNodeDict);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (attr === '$not') {
|
|
70
|
+
checkFilterNode(entity2, filterNode[attr], projectionNode, toBeAssignNode, filterNodeDict);
|
|
71
|
+
}
|
|
72
|
+
else if (attr === '$text') {
|
|
73
|
+
// 全文检索首先要有fulltext索引,其次要把fulltext的相关属性加到projection里
|
|
74
|
+
const { indexes } = this.getSchema()[entity2];
|
|
75
|
+
const fulltextIndex = indexes.find(ele => ele.config && ele.config.type === 'fulltext');
|
|
76
|
+
const { attributes } = fulltextIndex;
|
|
77
|
+
necessaryAttrs.push(...(attributes.map(ele => ele.name)));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
|
|
81
|
+
const exprResult = (0, types_1.getAttrRefInExpression)(filterNode[attr]);
|
|
82
|
+
for (const nodeName in exprResult) {
|
|
83
|
+
if (nodeName === '#current') {
|
|
84
|
+
assignNecessaryProjectionAttrs(projectionNode, exprResult[nodeName]);
|
|
85
|
+
}
|
|
86
|
+
else if (filterNodeDict[nodeName]) {
|
|
87
|
+
assignNecessaryProjectionAttrs(filterNodeDict[nodeName], exprResult[nodeName]);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
if (toBeAssignNode[nodeName]) {
|
|
91
|
+
toBeAssignNode[nodeName].push(...exprResult[nodeName]);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
Object.assign(toBeAssignNode, {
|
|
95
|
+
[nodeName]: exprResult[nodeName],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const rel = this.judgeRelation(entity2, attr);
|
|
103
|
+
if (rel === 1) {
|
|
104
|
+
necessaryAttrs.push(attr);
|
|
105
|
+
}
|
|
106
|
+
else if (rel === 2) {
|
|
107
|
+
// entity/entityId反指
|
|
108
|
+
necessaryAttrs.push('entity', 'entityId');
|
|
109
|
+
if (!projectionNode[attr]) {
|
|
110
|
+
Object.assign(projectionNode, {
|
|
111
|
+
[attr]: {
|
|
112
|
+
id: 1,
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
checkFilterNode(attr, filterNode[attr], projectionNode[attr], toBeAssignNode, filterNodeDict);
|
|
117
|
+
}
|
|
118
|
+
else if (typeof rel === 'string') {
|
|
119
|
+
necessaryAttrs.push(`${attr}Id`);
|
|
120
|
+
if (!projectionNode[attr]) {
|
|
121
|
+
Object.assign(projectionNode, {
|
|
122
|
+
[attr]: {
|
|
123
|
+
id: 1,
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
checkFilterNode(rel, filterNode[attr], projectionNode[attr], toBeAssignNode, filterNodeDict);
|
|
128
|
+
}
|
|
129
|
+
else if (rel instanceof Array) {
|
|
130
|
+
// 子查询,暂时不处理
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
assignNecessaryProjectionAttrs(projectionNode, necessaryAttrs);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const checkSorterNode = (entity2, sorterNode, projectionNode) => {
|
|
138
|
+
const checkSortAttr = (e2, sortAttr, projNode) => {
|
|
139
|
+
const necessaryAttrs = [];
|
|
140
|
+
for (const attr in sortAttr) {
|
|
141
|
+
const rel = this.judgeRelation(e2, attr);
|
|
142
|
+
if (typeof rel === 'number' && [0, 1].includes(rel)) {
|
|
143
|
+
necessaryAttrs.push(attr);
|
|
144
|
+
}
|
|
145
|
+
else if (typeof rel === 'string') {
|
|
146
|
+
if (!projNode[attr]) {
|
|
147
|
+
Object.assign(projNode, {
|
|
148
|
+
[attr]: {},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
(0, assert_1.default)(typeof sortAttr[attr] === 'object');
|
|
152
|
+
checkSortAttr(rel, sortAttr[attr], projNode[attr]);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
(0, assert_1.default)(rel === 2);
|
|
156
|
+
if (!projNode[attr]) {
|
|
157
|
+
Object.assign(projNode, {
|
|
158
|
+
[attr]: {},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
(0, assert_1.default)(typeof sortAttr[attr] === 'object');
|
|
162
|
+
checkSortAttr(attr, sortAttr[attr], projNode[attr]);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
sorterNode.forEach((node) => {
|
|
167
|
+
const { $attr } = node;
|
|
168
|
+
checkSortAttr(entity2, $attr, projectionNode);
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
let relevantIds = [];
|
|
172
|
+
if (filter) {
|
|
173
|
+
const toBeAssignNode = {}; // 用来记录在表达式中涉及到的结点
|
|
174
|
+
// filter当中所关联到的属性必须在projection中
|
|
175
|
+
const filterNodeDict = {};
|
|
176
|
+
checkFilterNode(entity, filter, data, toBeAssignNode, filterNodeDict);
|
|
177
|
+
relevantIds = (0, filter_2.getRelevantIds)(filter);
|
|
178
|
+
}
|
|
179
|
+
// sorter也得取了,前端需要处理排序
|
|
180
|
+
if (sorter) {
|
|
181
|
+
checkSorterNode(entity, sorter, data);
|
|
182
|
+
}
|
|
183
|
+
const toBeAssignNode2 = {}; // 用来记录在表达式中涉及到的结点
|
|
184
|
+
const projectionNodeDict = {};
|
|
185
|
+
const checkProjectionNode = (entity2, projectionNode) => {
|
|
186
|
+
const necessaryAttrs = ['id', '$$createAt$$', '$$updateAt$$']; // 有的页面依赖于其它页面取数据,有时两个页面的filter的差异会导致有一个加createAt,有一个不加,此时可能产生前台取数据不完整的异常。先统一加上
|
|
187
|
+
for (const attr in projectionNode) {
|
|
188
|
+
if (attr === '#id') {
|
|
189
|
+
(0, assert_1.default)(!projectionNodeDict[projectionNode[attr]], `projection中结点的id有重复, ${projectionNode[attr]}`);
|
|
190
|
+
Object.assign(projectionNodeDict, {
|
|
191
|
+
[projectionNode[attr]]: projectionNode,
|
|
192
|
+
});
|
|
193
|
+
if (toBeAssignNode2[projectionNode[attr]]) {
|
|
194
|
+
assignNecessaryProjectionAttrs(projectionNode, toBeAssignNode2[projectionNode[attr]]);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
if (attr.toLowerCase().startsWith(types_1.EXPRESSION_PREFIX)) {
|
|
199
|
+
const exprResult = (0, types_1.getAttrRefInExpression)(projectionNode[attr]);
|
|
200
|
+
for (const nodeName in exprResult) {
|
|
201
|
+
if (nodeName === '#current') {
|
|
202
|
+
assignNecessaryProjectionAttrs(projectionNode, exprResult[nodeName]);
|
|
203
|
+
}
|
|
204
|
+
else if (projectionNodeDict[nodeName]) {
|
|
205
|
+
assignNecessaryProjectionAttrs(projectionNodeDict[nodeName], exprResult[nodeName]);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
if (toBeAssignNode2[nodeName]) {
|
|
209
|
+
toBeAssignNode2[nodeName].push(...exprResult[nodeName]);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
Object.assign(toBeAssignNode2, {
|
|
213
|
+
[nodeName]: exprResult[nodeName],
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
const rel = (0, relation_1.judgeRelation)(this.getSchema(), entity2, attr);
|
|
221
|
+
if (rel === 1) {
|
|
222
|
+
necessaryAttrs.push(attr);
|
|
223
|
+
}
|
|
224
|
+
else if (rel === 2) {
|
|
225
|
+
// entity/entityId反指
|
|
226
|
+
necessaryAttrs.push('entity', 'entityId');
|
|
227
|
+
checkProjectionNode(attr, projectionNode[attr]);
|
|
228
|
+
}
|
|
229
|
+
else if (typeof rel === 'string') {
|
|
230
|
+
necessaryAttrs.push(`${attr}Id`);
|
|
231
|
+
checkProjectionNode(rel, projectionNode[attr]);
|
|
232
|
+
}
|
|
233
|
+
else if (rel instanceof Array && !attr.endsWith('$$aggr')) {
|
|
234
|
+
const { data, filter } = projectionNode[attr];
|
|
235
|
+
if (rel[1]) {
|
|
236
|
+
assignNecessaryProjectionAttrs(data, [rel[1]]);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
assignNecessaryProjectionAttrs(data, ['entity', 'entityId']);
|
|
240
|
+
}
|
|
241
|
+
this.reinforceSelectionInner(rel[0], projectionNode[attr], context);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
assignNecessaryProjectionAttrs(projectionNode, necessaryAttrs);
|
|
246
|
+
}
|
|
247
|
+
// 如果对象中指向一对多的Modi,此时加上指向Modi的projection
|
|
248
|
+
if (this.getSchema()[entity2].toModi) {
|
|
249
|
+
Object.assign(projectionNode, {
|
|
250
|
+
modi$entity: {
|
|
251
|
+
$entity: 'modi',
|
|
252
|
+
data: {
|
|
253
|
+
id: 1,
|
|
254
|
+
targetEntity: 1,
|
|
255
|
+
entity: 1,
|
|
256
|
+
entityId: 1,
|
|
257
|
+
action: 1,
|
|
258
|
+
iState: 1,
|
|
259
|
+
data: 1,
|
|
260
|
+
filter: 1,
|
|
261
|
+
$$createAt$$: 1,
|
|
262
|
+
$$updateAt$$: 1,
|
|
263
|
+
},
|
|
264
|
+
filter: {
|
|
265
|
+
iState: 'active',
|
|
266
|
+
},
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
// 如果对象上有relation关系,在此将本用户相关的relation和actionAuth全部取出
|
|
271
|
+
// 还要将actionAuth上没有relation关系但destEntity为本对象的行也全部取出,这些是指向userId的可能路径
|
|
272
|
+
// 放在这里有点怪异,暂先这样
|
|
273
|
+
if (context instanceof AsyncRowStore_1.AsyncContext) {
|
|
274
|
+
const userId = context.getCurrentUserId(true);
|
|
275
|
+
if (userId && !entities_1.SYSTEM_RESERVE_ENTITIES.includes(entity2)) {
|
|
276
|
+
if (this.getSchema()[entity2].relation && !projectionNode.userRelation$entity) {
|
|
277
|
+
Object.assign(projectionNode, {
|
|
278
|
+
userRelation$entity: {
|
|
279
|
+
$entity: 'userRelation',
|
|
280
|
+
data: {
|
|
281
|
+
id: 1,
|
|
282
|
+
entity: 1,
|
|
283
|
+
entityId: 1,
|
|
284
|
+
userId: 1,
|
|
285
|
+
relationId: 1,
|
|
286
|
+
relation: {
|
|
287
|
+
id: 1,
|
|
288
|
+
name: 1,
|
|
289
|
+
display: 1,
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
filter: {
|
|
293
|
+
userId,
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
checkProjectionNode(entity, data);
|
|
302
|
+
if (!sorter && relevantIds.length === 0) {
|
|
303
|
+
// 如果没有sorter,就给予一个按createAt逆序的sorter
|
|
304
|
+
Object.assign(selection, {
|
|
305
|
+
sorter: [
|
|
306
|
+
{
|
|
307
|
+
$attr: {
|
|
308
|
+
$$createAt$$: 1,
|
|
309
|
+
},
|
|
310
|
+
$direction: 'desc',
|
|
311
|
+
}
|
|
312
|
+
]
|
|
313
|
+
});
|
|
314
|
+
Object.assign(data, {
|
|
315
|
+
$$createAt$$: 1,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
async reinforceOperation(entity, operation, context, option) {
|
|
320
|
+
await Promise.all(this.operationRewriters.map(ele => ele(this.getSchema(), entity, operation, context, option)));
|
|
321
|
+
}
|
|
322
|
+
registerOperationRewriter(rewriter) {
|
|
323
|
+
this.operationRewriters.push(rewriter);
|
|
324
|
+
}
|
|
325
|
+
registerSelectionRewriter(rewriter) {
|
|
326
|
+
this.selectionRewriters.push(rewriter);
|
|
327
|
+
}
|
|
328
|
+
destructCascadeSelect(entity, projection2, context, cascadeSelectFn, aggregateFn, option) {
|
|
329
|
+
const cascadeSelectionFns = [];
|
|
330
|
+
const supportMtoJoin = this.supportManyToOneJoin();
|
|
331
|
+
const { toModi } = this.getSchema()[entity];
|
|
332
|
+
(0, assert_1.default)(typeof projection2 === 'object');
|
|
333
|
+
for (const attr in projection2) {
|
|
334
|
+
const relation = (0, relation_1.judgeRelation)(this.storageSchema, entity, attr);
|
|
335
|
+
if (relation === 1 || relation == 0) {
|
|
336
|
+
}
|
|
337
|
+
else if (relation === 2) {
|
|
338
|
+
// 基于entity/entityId的多对一
|
|
339
|
+
(0, assert_1.default)(typeof projection2[attr] === 'object');
|
|
340
|
+
if (supportMtoJoin) {
|
|
341
|
+
cascadeSelectionFns.push((result) => {
|
|
342
|
+
if (!toModi) {
|
|
343
|
+
result.forEach((ele) => {
|
|
344
|
+
if (ele.entity === attr) {
|
|
345
|
+
(0, assert_1.default)(ele.entityId);
|
|
346
|
+
if (!ele[attr]) {
|
|
347
|
+
throw new types_1.OakRowUnexistedException([{
|
|
348
|
+
entity: attr,
|
|
349
|
+
selection: {
|
|
350
|
+
data: projection2[attr],
|
|
351
|
+
filter: {
|
|
352
|
+
id: ele.entityId,
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}]);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const { projection: subProjection, cascadeSelectionFns: subCascadeSelectionFns, } = this.destructCascadeSelect(attr, projection2[attr], context, cascadeSelectFn, aggregateFn, option);
|
|
362
|
+
subCascadeSelectionFns.forEach(ele => cascadeSelectionFns.push((result) => {
|
|
363
|
+
return ele(result.map(ele2 => ele2[attr]).filter(ele2 => !!ele2));
|
|
364
|
+
}));
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
cascadeSelectionFns.push((result) => {
|
|
368
|
+
const entityIds = (0, lodash_1.uniq)(result.filter(ele => ele.entity === attr).map(ele => {
|
|
369
|
+
(0, assert_1.default)(ele.entityId !== null);
|
|
370
|
+
return ele.entityId;
|
|
371
|
+
}));
|
|
372
|
+
const dealWithSubRows = (subRows) => {
|
|
373
|
+
(0, assert_1.default)(subRows.length <= entityIds.length);
|
|
374
|
+
if (subRows.length < entityIds.length && !toModi) {
|
|
375
|
+
// 后台不允许数据不一致
|
|
376
|
+
if (context instanceof AsyncRowStore_1.AsyncContext || !option.ignoreAttrMiss) {
|
|
377
|
+
throw new types_1.OakRowUnexistedException([{
|
|
378
|
+
entity: attr,
|
|
379
|
+
selection: {
|
|
380
|
+
data: projection2[attr],
|
|
381
|
+
filter: {
|
|
382
|
+
id: {
|
|
383
|
+
$in: entityIds
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
}]);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
result.forEach((ele) => {
|
|
391
|
+
if (ele.entity === attr) {
|
|
392
|
+
const subRow = subRows.find(ele2 => ele2.id === ele.entityId);
|
|
393
|
+
if (subRow) {
|
|
394
|
+
Object.assign(ele, {
|
|
395
|
+
[attr]: subRow,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
Object.assign(ele, {
|
|
400
|
+
[attr]: null,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
};
|
|
406
|
+
if (entityIds.length > 0) {
|
|
407
|
+
const subRows = cascadeSelectFn.call(this, attr, {
|
|
408
|
+
data: projection2[attr],
|
|
409
|
+
filter: {
|
|
410
|
+
id: {
|
|
411
|
+
$in: entityIds
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
}, context, option);
|
|
415
|
+
if (subRows instanceof Promise) {
|
|
416
|
+
return subRows.then((subRowss) => dealWithSubRows(subRowss));
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
dealWithSubRows(subRows);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
else if (typeof relation === 'string') {
|
|
426
|
+
(0, assert_1.default)(typeof projection2[attr] === 'object');
|
|
427
|
+
if (supportMtoJoin) {
|
|
428
|
+
if (!toModi) {
|
|
429
|
+
// 如果不是modi,要保证外键没有空指针
|
|
430
|
+
cascadeSelectionFns.push((result) => {
|
|
431
|
+
if (!toModi) {
|
|
432
|
+
result.forEach((ele) => {
|
|
433
|
+
if (ele[`${attr}Id`] && !ele[attr]) {
|
|
434
|
+
throw new types_1.OakRowUnexistedException([{
|
|
435
|
+
entity: relation,
|
|
436
|
+
selection: {
|
|
437
|
+
data: projection2[attr],
|
|
438
|
+
filter: {
|
|
439
|
+
id: ele[`${attr}Id`],
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}]);
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
const { projection: subProjection, cascadeSelectionFns: subCascadeSelectionFns, } = this.destructCascadeSelect(relation, projection2[attr], context, cascadeSelectFn, aggregateFn, option);
|
|
449
|
+
subCascadeSelectionFns.forEach(ele => cascadeSelectionFns.push((result) => {
|
|
450
|
+
return ele(result.map(ele2 => ele2[attr]).filter(ele2 => !!ele2));
|
|
451
|
+
}));
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
cascadeSelectionFns.push((result) => {
|
|
455
|
+
const ids = (0, lodash_1.uniq)(result.filter(ele => !!(ele[`${attr}Id`])).map(ele => ele[`${attr}Id`]));
|
|
456
|
+
const dealWithSubRows = (subRows) => {
|
|
457
|
+
(0, assert_1.default)(subRows.length <= ids.length);
|
|
458
|
+
if (subRows.length < ids.length && !toModi) {
|
|
459
|
+
if (context instanceof AsyncRowStore_1.AsyncContext || !option.ignoreAttrMiss) {
|
|
460
|
+
throw new types_1.OakRowUnexistedException([{
|
|
461
|
+
entity: relation,
|
|
462
|
+
selection: {
|
|
463
|
+
data: projection2[attr],
|
|
464
|
+
filter: {
|
|
465
|
+
id: {
|
|
466
|
+
$in: ids
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
}
|
|
470
|
+
}]);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
result.forEach((ele) => {
|
|
474
|
+
if (ele[`${attr}Id`]) {
|
|
475
|
+
const subRow = subRows.find(ele2 => ele2.id === ele[`${attr}Id`]);
|
|
476
|
+
if (subRow) {
|
|
477
|
+
Object.assign(ele, {
|
|
478
|
+
[attr]: subRow,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
Object.assign(ele, {
|
|
483
|
+
[attr]: null,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
else {
|
|
488
|
+
Object.assign(ele, {
|
|
489
|
+
[attr]: null,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
};
|
|
494
|
+
if (ids.length > 0) {
|
|
495
|
+
const subRows = cascadeSelectFn.call(this, relation, {
|
|
496
|
+
data: projection2[attr],
|
|
497
|
+
filter: {
|
|
498
|
+
id: {
|
|
499
|
+
$in: ids
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
}, context, option);
|
|
503
|
+
if (subRows instanceof Promise) {
|
|
504
|
+
return subRows.then((subRowss) => dealWithSubRows(subRowss));
|
|
505
|
+
}
|
|
506
|
+
dealWithSubRows(subRows);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
(0, assert_1.default)(relation instanceof Array);
|
|
513
|
+
const { data: subProjection, filter: subFilter, indexFrom, count, sorter: subSorter, total, randomRange } = projection2[attr];
|
|
514
|
+
const [entity2, foreignKey] = relation;
|
|
515
|
+
const isAggr = attr.endsWith('$$aggr');
|
|
516
|
+
const otmAggrFn = (result) => {
|
|
517
|
+
const aggrResults = result.map(async (row) => {
|
|
518
|
+
const filter2 = foreignKey ? (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
519
|
+
[foreignKey]: row.id,
|
|
520
|
+
}, subFilter]) : (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
521
|
+
entity,
|
|
522
|
+
entityId: row.id,
|
|
523
|
+
}, subFilter]);
|
|
524
|
+
const aggrResult = aggregateFn.call(this, entity2, {
|
|
525
|
+
data: subProjection,
|
|
526
|
+
filter: filter2,
|
|
527
|
+
sorter: subSorter,
|
|
528
|
+
indexFrom,
|
|
529
|
+
count
|
|
530
|
+
}, context, option);
|
|
531
|
+
(0, assert_1.default)(aggrResult instanceof Promise);
|
|
532
|
+
const aggrResultResult = await aggrResult;
|
|
533
|
+
return Object.assign(row, {
|
|
534
|
+
[attr]: aggrResultResult,
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
if (aggrResults.length > 0) {
|
|
538
|
+
return Promise.all(aggrResults).then(() => undefined);
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
/** 若一对多子查询没有indexFrom和count,可以优化成组查 */
|
|
542
|
+
const otmGroupFn = (result) => {
|
|
543
|
+
const dealWithSubRows = (subRows) => {
|
|
544
|
+
// 这里如果result只有一行,则把返回结果直接置上,不对比外键值
|
|
545
|
+
// 这样做的原因是有的对象的filter会被改写掉(userId),只能临时这样处理
|
|
546
|
+
// 看不懂了,应该不需要这个if了,不知道怎么重现测试 by Xc 20230906
|
|
547
|
+
if (result.length === 1) {
|
|
548
|
+
Object.assign(result[0], {
|
|
549
|
+
[attr]: subRows,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
result.forEach((ele) => {
|
|
554
|
+
const subRowss = subRows.filter((ele2) => {
|
|
555
|
+
if (foreignKey) {
|
|
556
|
+
return ele2[foreignKey] === ele.id;
|
|
557
|
+
}
|
|
558
|
+
return ele2.entityId === ele.id;
|
|
559
|
+
});
|
|
560
|
+
(0, assert_1.default)(subRowss);
|
|
561
|
+
Object.assign(ele, {
|
|
562
|
+
[attr]: subRowss,
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
const ids = result.map(ele => ele.id);
|
|
568
|
+
if (ids.length > 0) {
|
|
569
|
+
const filter2 = foreignKey ? (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
570
|
+
[foreignKey]: {
|
|
571
|
+
$in: ids,
|
|
572
|
+
},
|
|
573
|
+
}, subFilter]) : (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
574
|
+
entity,
|
|
575
|
+
entityId: {
|
|
576
|
+
$in: ids,
|
|
577
|
+
},
|
|
578
|
+
}, subFilter]);
|
|
579
|
+
const subRows = cascadeSelectFn.call(this, entity2, {
|
|
580
|
+
data: subProjection,
|
|
581
|
+
filter: filter2,
|
|
582
|
+
sorter: subSorter,
|
|
583
|
+
total,
|
|
584
|
+
}, context, option);
|
|
585
|
+
if (subRows instanceof Promise) {
|
|
586
|
+
return subRows.then((subRowss) => dealWithSubRows(subRowss));
|
|
587
|
+
}
|
|
588
|
+
dealWithSubRows(subRows);
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
/** 若一对多子查询有indexFrom和count,只能单行去连接 */
|
|
592
|
+
const otmSingleFn = (result) => {
|
|
593
|
+
const dealWithSubRows2 = (row, subRows) => {
|
|
594
|
+
Object.assign(row, {
|
|
595
|
+
[attr]: subRows,
|
|
596
|
+
});
|
|
597
|
+
};
|
|
598
|
+
if (result.length > 0) {
|
|
599
|
+
const getSubRows = result.map((row) => {
|
|
600
|
+
const filter2 = foreignKey ? (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
601
|
+
[foreignKey]: row.id,
|
|
602
|
+
}, subFilter]) : (0, filter_1.combineFilters)(entity2, this.getSchema(), [{
|
|
603
|
+
entity,
|
|
604
|
+
entityId: row.id,
|
|
605
|
+
}, subFilter]);
|
|
606
|
+
const subRows = cascadeSelectFn.call(this, entity2, {
|
|
607
|
+
data: subProjection,
|
|
608
|
+
filter: filter2,
|
|
609
|
+
sorter: subSorter,
|
|
610
|
+
indexFrom,
|
|
611
|
+
count,
|
|
612
|
+
total,
|
|
613
|
+
randomRange,
|
|
614
|
+
}, context, option);
|
|
615
|
+
if (subRows instanceof Promise) {
|
|
616
|
+
return subRows.then((subRowss) => dealWithSubRows2(row, subRowss));
|
|
617
|
+
}
|
|
618
|
+
return dealWithSubRows2(row, subRows);
|
|
619
|
+
});
|
|
620
|
+
if (getSubRows[0] instanceof Promise) {
|
|
621
|
+
return Promise.all(getSubRows).then(() => undefined);
|
|
622
|
+
}
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
if (isAggr) {
|
|
627
|
+
(context instanceof AsyncRowStore_1.AsyncContext) && cascadeSelectionFns.push(result => otmAggrFn(result));
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
cascadeSelectionFns.push((result) => {
|
|
631
|
+
if (typeof indexFrom === 'number') {
|
|
632
|
+
(0, assert_1.default)(typeof count === 'number' && count > 0);
|
|
633
|
+
return otmSingleFn(result);
|
|
634
|
+
}
|
|
635
|
+
return otmGroupFn(result);
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
return {
|
|
641
|
+
projection: projection2,
|
|
642
|
+
cascadeSelectionFns,
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* 级联更新
|
|
647
|
+
* A --> B
|
|
648
|
+
多对一:A CREATE/B CREATE,B data的主键赋到A的data上
|
|
649
|
+
A CREATE/B UPDATE,B filter的主键来自A的data
|
|
650
|
+
A UPDATE/B CREATE,B data的主键赋到A的data上
|
|
651
|
+
A UPDATE/B UPDATE,B filter的主键来自A的row
|
|
652
|
+
A UPDATE/B REMOVE,B filter的主键来自A的row
|
|
653
|
+
A REMOVE/B UPDATE,B filter的主键来自A的row
|
|
654
|
+
A REMOVE/B REMOVE,B filter的主键来自A的row
|
|
655
|
+
|
|
656
|
+
一对多:A CREATE/B CREATE,A data上的主键赋到B的data上
|
|
657
|
+
A CREATE/B UPDATE,A data上的主键赋到B的data上
|
|
658
|
+
A UPDATE/B CREATE,A filter上的主键赋到B的data上(一定是带主键的filter)
|
|
659
|
+
A UPDATE/B UPDATE,A filter上的主键赋到B的filter上(一定是带主键的filter)
|
|
660
|
+
A UPDATE/B REMOVE,A filter上的主键赋到B的filter上(一定是带主键的filter)
|
|
661
|
+
A REMOVE/B UPDATE,A filter上的主键赋到B的filter上(且B关于A的外键清空)
|
|
662
|
+
A REMOVE/B REMOVE,A filter上的主键赋到B的filter上
|
|
663
|
+
*
|
|
664
|
+
* 延时更新,
|
|
665
|
+
* A(业务级别的申请对象) ---> B(业务级别需要更新的对象)
|
|
666
|
+
* 两者必须通过entity/entityId关联
|
|
667
|
+
* 此时需要把对B的更新记录成一条新插入的Modi对象,并将A上的entity/entityId指向该对象(新生成的Modi对象的id与此operation的id保持一致)
|
|
668
|
+
* @param entity
|
|
669
|
+
* @param action
|
|
670
|
+
* @param data
|
|
671
|
+
* @param context
|
|
672
|
+
* @param option
|
|
673
|
+
* @param result
|
|
674
|
+
* @param filter
|
|
675
|
+
* @returns
|
|
676
|
+
*/
|
|
677
|
+
destructCascadeUpdate(entity, action, data, context, option, cascadeUpdate, filter, bornAt) {
|
|
678
|
+
const modiAttr = this.getSchema()[entity].toModi;
|
|
679
|
+
const option2 = Object.assign({}, option);
|
|
680
|
+
const opData = {};
|
|
681
|
+
const beforeFns = [];
|
|
682
|
+
const afterFns = [];
|
|
683
|
+
if (modiAttr && action !== 'remove') {
|
|
684
|
+
// create/update具有modi对象的对象,对其子对象的update行为全部是create modi对象(缓存动作)
|
|
685
|
+
// delete此对象,所有的modi子对象应该通过触发器作废,这个目前先通过系统的trigger来实现
|
|
686
|
+
(0, assert_1.default)(!option2.modiParentId && !option2.modiParentEntity);
|
|
687
|
+
if (action === 'create') {
|
|
688
|
+
option2.modiParentId = data.id;
|
|
689
|
+
option2.modiParentEntity = entity;
|
|
690
|
+
}
|
|
691
|
+
else if (filter?.id && typeof filter.id === 'string') {
|
|
692
|
+
// 如果是对toModi对象进行cascadeUpdate操作,必然带有id,如果没有则认为不是modi相关的操作
|
|
693
|
+
// 批量通过或者拒绝applyment应该就会出现
|
|
694
|
+
option2.modiParentId = filter.id;
|
|
695
|
+
option2.modiParentEntity = entity;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
for (const attr in data) {
|
|
699
|
+
const relation = (0, relation_1.judgeRelation)(this.storageSchema, entity, attr, !!bornAt);
|
|
700
|
+
if (relation === 1) {
|
|
701
|
+
Object.assign(opData, {
|
|
702
|
+
[attr]: data[attr],
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
else if (relation === 2) {
|
|
706
|
+
// 基于entity/entityId的many-to-one
|
|
707
|
+
const operationMto = data[attr];
|
|
708
|
+
const { action: actionMto, data: dataMto, filter: filterMto } = operationMto;
|
|
709
|
+
if (actionMto === 'create') {
|
|
710
|
+
Object.assign(opData, {
|
|
711
|
+
entityId: dataMto.id,
|
|
712
|
+
entity: attr,
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
else if (action === 'create') {
|
|
716
|
+
const { entityId: fkId, entity } = data;
|
|
717
|
+
(0, assert_1.default)(typeof fkId === 'string' || entity === attr);
|
|
718
|
+
if (filterMto?.id) {
|
|
719
|
+
// 若已有id则不用处理,否则会干扰modi的后续判断(会根据filter来判断对象id,如果判断不出来去查实际的对象,但实际的对象其实还未创建好)
|
|
720
|
+
(0, assert_1.default)(filterMto.id === fkId);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
// A中data的entityId作为B中filter的主键
|
|
724
|
+
Object.assign(operationMto, {
|
|
725
|
+
filter: (0, filter_1.combineFilters)(attr, this.getSchema(), [{
|
|
726
|
+
id: fkId,
|
|
727
|
+
}, filterMto]),
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
// 剩下三种情况都是B中的filter的id来自A中row的entityId
|
|
733
|
+
(0, assert_1.default)(!data.hasOwnProperty('entityId') && !data.hasOwnProperty('entity'));
|
|
734
|
+
if (filterMto?.id) {
|
|
735
|
+
// 若已有id则不用处理,否则会干扰modi的后续判断(会根据filter来判断对象id,如果判断不出来去查实际的对象,但实际的对象其实还未创建好)
|
|
736
|
+
(0, assert_1.default)(typeof filterMto.id === 'string');
|
|
737
|
+
}
|
|
738
|
+
else if (filter.entity === attr && filter.entityId) {
|
|
739
|
+
Object.assign(operationMto, {
|
|
740
|
+
filter: (0, filter_1.combineFilters)(attr, this.getSchema(), [{
|
|
741
|
+
id: filter.entityId,
|
|
742
|
+
}, filterMto]),
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
else if (filter[attr]) {
|
|
746
|
+
Object.assign(operationMto, {
|
|
747
|
+
filter: (0, filter_1.combineFilters)(attr, this.getSchema(), [filter[attr], filterMto]),
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
// A中data的entityId作为B中filter的主键
|
|
752
|
+
Object.assign(operationMto, {
|
|
753
|
+
filter: (0, filter_1.combineFilters)(attr, this.getSchema(), [{
|
|
754
|
+
[`${entity}$entity`]: {
|
|
755
|
+
filter,
|
|
756
|
+
}
|
|
757
|
+
}, filterMto]),
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
beforeFns.push(() => cascadeUpdate.call(this, attr, operationMto, context, option2));
|
|
762
|
+
}
|
|
763
|
+
else if (typeof relation === 'string') {
|
|
764
|
+
// 基于attr的外键的many-to-one
|
|
765
|
+
const operationMto = data[attr];
|
|
766
|
+
const { action: actionMto, data: dataMto, filter: filterMto } = operationMto;
|
|
767
|
+
if (actionMto === 'create') {
|
|
768
|
+
Object.assign(opData, {
|
|
769
|
+
[`${attr}Id`]: dataMto.id,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
else if (action === 'create') {
|
|
773
|
+
const { [`${attr}Id`]: fkId } = data;
|
|
774
|
+
(0, assert_1.default)(typeof fkId === 'string');
|
|
775
|
+
if (filterMto?.id) {
|
|
776
|
+
// 若已有id则不用处理,否则会干扰modi的后续判断(会根据filter来判断对象id,如果判断不出来去查实际的对象,但实际的对象其实还未创建好)
|
|
777
|
+
(0, assert_1.default)(filterMto.id === fkId);
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
// A中data的entityId作为B中filter的主键
|
|
781
|
+
Object.assign(operationMto, {
|
|
782
|
+
filter: (0, filter_1.combineFilters)(relation, this.getSchema(), [filterMto, {
|
|
783
|
+
id: fkId,
|
|
784
|
+
}]),
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
(0, assert_1.default)(!data.hasOwnProperty(`${attr}Id`));
|
|
790
|
+
if (filterMto?.id) {
|
|
791
|
+
// 若已有id则不用处理,否则会干扰modi的后续判断(会根据filter来判断对象id,如果判断不出来去查实际的对象,但实际的对象其实还未创建好)
|
|
792
|
+
(0, assert_1.default)(typeof filterMto.id === 'string');
|
|
793
|
+
}
|
|
794
|
+
else if (filter[`${attr}Id`]) {
|
|
795
|
+
Object.assign(operationMto, {
|
|
796
|
+
filter: (0, filter_1.combineFilters)(relation, this.getSchema(), [filterMto, {
|
|
797
|
+
id: filter[`${attr}Id`],
|
|
798
|
+
}]),
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
else if (filter[attr]) {
|
|
802
|
+
Object.assign(operationMto, {
|
|
803
|
+
filter: (0, filter_1.combineFilters)(relation, this.getSchema(), [filterMto, filter[attr]]),
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
// A中data的attrId作为B中filter的主键
|
|
808
|
+
Object.assign(operationMto, {
|
|
809
|
+
filter: (0, filter_1.combineFilters)(relation, this.getSchema(), [filterMto, {
|
|
810
|
+
[`${entity}$${attr}`]: filter
|
|
811
|
+
}]),
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
beforeFns.push(() => cascadeUpdate.call(this, relation, operationMto, context, option2));
|
|
816
|
+
}
|
|
817
|
+
else if (relation instanceof Array) {
|
|
818
|
+
const [entityOtm, foreignKey] = relation;
|
|
819
|
+
const otmOperations = data[attr];
|
|
820
|
+
const dealWithOneToMany = (otm) => {
|
|
821
|
+
const { action: actionOtm, data: dataOtm, filter: filterOtm } = otm;
|
|
822
|
+
if (!foreignKey) {
|
|
823
|
+
// 基于entity/entityId的one-to-many
|
|
824
|
+
if (action === 'create') {
|
|
825
|
+
const { id } = data;
|
|
826
|
+
if (dataOtm instanceof Array) {
|
|
827
|
+
dataOtm.forEach(ele => Object.assign(ele, {
|
|
828
|
+
entity,
|
|
829
|
+
entityId: id,
|
|
830
|
+
}));
|
|
831
|
+
}
|
|
832
|
+
else {
|
|
833
|
+
Object.assign(dataOtm, {
|
|
834
|
+
entity,
|
|
835
|
+
entityId: id,
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
else if (actionOtm === 'create') {
|
|
840
|
+
// 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作
|
|
841
|
+
// todo 这个假设对watcher等后台行为可能不成立,等遇到create/create一对多的case再完善
|
|
842
|
+
const { id } = filter;
|
|
843
|
+
(0, assert_1.default)(typeof id === 'string');
|
|
844
|
+
if (dataOtm instanceof Array) {
|
|
845
|
+
dataOtm.forEach(ele => Object.assign(ele, {
|
|
846
|
+
entity,
|
|
847
|
+
entityId: id,
|
|
848
|
+
}));
|
|
849
|
+
}
|
|
850
|
+
else {
|
|
851
|
+
Object.assign(dataOtm, {
|
|
852
|
+
entity,
|
|
853
|
+
entityId: id,
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
|
859
|
+
if (filter) {
|
|
860
|
+
if (filter.id && Object.keys(filter).length === 1) {
|
|
861
|
+
Object.assign(otm, {
|
|
862
|
+
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
863
|
+
entity,
|
|
864
|
+
entityId: filter.id,
|
|
865
|
+
}, filterOtm]),
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
Object.assign(otm, {
|
|
870
|
+
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
871
|
+
[entity]: filter,
|
|
872
|
+
}, filterOtm]),
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
Object.assign(otm, {
|
|
878
|
+
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
879
|
+
entity,
|
|
880
|
+
entityId: {
|
|
881
|
+
$exists: true,
|
|
882
|
+
}
|
|
883
|
+
}, filterOtm])
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
if (action === 'remove' && actionOtm === 'update') {
|
|
887
|
+
Object.assign(dataOtm, {
|
|
888
|
+
entity: null,
|
|
889
|
+
entityId: null,
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
// 基于foreignKey的one-to-many
|
|
896
|
+
if (action === 'create') {
|
|
897
|
+
const { id } = data;
|
|
898
|
+
if (dataOtm instanceof Array) {
|
|
899
|
+
dataOtm.forEach(ele => Object.assign(ele, {
|
|
900
|
+
[foreignKey]: id,
|
|
901
|
+
}));
|
|
902
|
+
}
|
|
903
|
+
else {
|
|
904
|
+
Object.assign(dataOtm, {
|
|
905
|
+
[foreignKey]: id,
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
else if (actionOtm === 'create') {
|
|
910
|
+
// 这里先假设A(必是update)的filter上一定有id,否则用户界面上应该设计不出来这样的操作
|
|
911
|
+
// todo 这个假设在后台可能不成立,等遇到了再说
|
|
912
|
+
const { id } = filter;
|
|
913
|
+
(0, assert_1.default)(typeof id === 'string');
|
|
914
|
+
if (dataOtm instanceof Array) {
|
|
915
|
+
dataOtm.forEach(ele => Object.assign(ele, {
|
|
916
|
+
[foreignKey]: id,
|
|
917
|
+
}));
|
|
918
|
+
}
|
|
919
|
+
else {
|
|
920
|
+
Object.assign(dataOtm, {
|
|
921
|
+
[foreignKey]: id,
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
// 这里优化一下,如果filter上有id,直接更新成根据entityId来过滤
|
|
927
|
+
if (filter) {
|
|
928
|
+
if (filter.id && Object.keys(filter).length === 1) {
|
|
929
|
+
Object.assign(otm, {
|
|
930
|
+
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
931
|
+
[foreignKey]: filter.id,
|
|
932
|
+
}, filterOtm]),
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
Object.assign(otm, {
|
|
937
|
+
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
938
|
+
[foreignKey.slice(0, foreignKey.length - 2)]: filter,
|
|
939
|
+
}, filterOtm]),
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
Object.assign(otm, {
|
|
945
|
+
filter: (0, filter_1.combineFilters)(entityOtm, this.getSchema(), [{
|
|
946
|
+
[foreignKey]: {
|
|
947
|
+
$exists: true,
|
|
948
|
+
},
|
|
949
|
+
}, filterOtm]),
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
if (action === 'remove' && actionOtm === 'update') {
|
|
953
|
+
Object.assign(dataOtm, {
|
|
954
|
+
[foreignKey]: null,
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
// 一对多的依赖应该后建,否则中间会出现空指针,导致checker等出错
|
|
960
|
+
afterFns.push(() => cascadeUpdate.call(this, entityOtm, otm, context, option2));
|
|
961
|
+
};
|
|
962
|
+
if (otmOperations instanceof Array) {
|
|
963
|
+
for (const oper of otmOperations) {
|
|
964
|
+
dealWithOneToMany(oper);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
else {
|
|
968
|
+
dealWithOneToMany(otmOperations);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
else {
|
|
972
|
+
console.warn(`接收到不合法的属性「${attr}」`);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
return {
|
|
976
|
+
data: opData,
|
|
977
|
+
beforeFns,
|
|
978
|
+
afterFns,
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
// 对插入的数据,没有初始值的属性置null
|
|
982
|
+
preProcessDataCreated(entity, data) {
|
|
983
|
+
const now = Date.now();
|
|
984
|
+
const { attributes } = this.getSchema()[entity];
|
|
985
|
+
const processSingle = (data2) => {
|
|
986
|
+
for (const key in attributes) {
|
|
987
|
+
if (data2[key] === undefined) {
|
|
988
|
+
Object.assign(data2, {
|
|
989
|
+
[key]: null,
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
Object.assign(data2, {
|
|
994
|
+
[Entity_1.CreateAtAttribute]: now,
|
|
995
|
+
[Entity_1.UpdateAtAttribute]: now,
|
|
996
|
+
[Entity_1.DeleteAtAttribute]: null,
|
|
997
|
+
});
|
|
998
|
+
};
|
|
999
|
+
if (data instanceof Array) {
|
|
1000
|
+
data.forEach(ele => processSingle(ele));
|
|
1001
|
+
}
|
|
1002
|
+
else {
|
|
1003
|
+
processSingle(data);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
// 对更新的数据,去掉所有的undefined属性
|
|
1007
|
+
preProcessDataUpdated(data) {
|
|
1008
|
+
const undefinedKeys = Object.keys(data).filter(ele => data[ele] === undefined);
|
|
1009
|
+
undefinedKeys.forEach(ele => (0, lodash_1.unset)(data, ele));
|
|
1010
|
+
}
|
|
1011
|
+
judgeRelation(entity, attr) {
|
|
1012
|
+
return (0, relation_1.judgeRelation)(this.storageSchema, entity, attr);
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* 和具体的update过程无关的例程放在这里,包括对later动作的处理、对oper的记录以及对record的收集等
|
|
1016
|
+
* @param entity
|
|
1017
|
+
* @param operation
|
|
1018
|
+
* @param context
|
|
1019
|
+
* @param option
|
|
1020
|
+
*/
|
|
1021
|
+
async doUpdateSingleRowAsync(entity, operation, context, option) {
|
|
1022
|
+
const { data, action, id: operId, filter, bornAt } = operation;
|
|
1023
|
+
const now = Date.now();
|
|
1024
|
+
switch (action) {
|
|
1025
|
+
case 'create': {
|
|
1026
|
+
if (option.modiParentEntity && !['modi', 'modiEntity', 'oper', 'operEntity'].includes(entity)) {
|
|
1027
|
+
// 变成对modi的插入
|
|
1028
|
+
(0, assert_1.default)(option.modiParentId);
|
|
1029
|
+
const modiCreate = {
|
|
1030
|
+
id: 'dummy',
|
|
1031
|
+
action: 'create',
|
|
1032
|
+
data: {
|
|
1033
|
+
id: operId,
|
|
1034
|
+
targetEntity: entity,
|
|
1035
|
+
action,
|
|
1036
|
+
entity: option.modiParentEntity,
|
|
1037
|
+
entityId: option.modiParentId,
|
|
1038
|
+
filter: {
|
|
1039
|
+
id: data.id, //这里记录这个filter是为了后面update的时候直接在其上面update,参见本函数后半段关于modiUpsert相关的优化
|
|
1040
|
+
},
|
|
1041
|
+
data,
|
|
1042
|
+
iState: 'active',
|
|
1043
|
+
},
|
|
1044
|
+
};
|
|
1045
|
+
const closeRootMode = context.openRootMode();
|
|
1046
|
+
await this.cascadeUpdateAsync('modi', modiCreate, context, option);
|
|
1047
|
+
closeRootMode();
|
|
1048
|
+
return {
|
|
1049
|
+
'modi': {
|
|
1050
|
+
create: 1,
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
else {
|
|
1055
|
+
this.preProcessDataCreated(entity, data);
|
|
1056
|
+
let result = 0;
|
|
1057
|
+
const createInner = async (operation2) => {
|
|
1058
|
+
try {
|
|
1059
|
+
await this.updateAbjointRowAsync(entity, operation2, context, option);
|
|
1060
|
+
}
|
|
1061
|
+
catch (e) {
|
|
1062
|
+
/* 这段代码是处理插入时有重复的行,现在看有问题,等实际需求出现再写
|
|
1063
|
+
if (e instanceof OakCongruentRowExists) {
|
|
1064
|
+
if (option.allowExists) {
|
|
1065
|
+
// 如果允许存在,对已存在行进行update,剩下的行继续insert
|
|
1066
|
+
const congruentRow = e.getData() as ED[T]['OpSchema'];
|
|
1067
|
+
if (data instanceof Array) {
|
|
1068
|
+
const rest = data.filter(
|
|
1069
|
+
ele => ele.id !== congruentRow.id
|
|
1070
|
+
);
|
|
1071
|
+
if (rest.length === data.length) {
|
|
1072
|
+
throw e;
|
|
1073
|
+
}
|
|
1074
|
+
const result2 = await this.updateAbjointRow(
|
|
1075
|
+
entity,
|
|
1076
|
+
Object.assign({}, operation, {
|
|
1077
|
+
data: rest,
|
|
1078
|
+
}),
|
|
1079
|
+
context,
|
|
1080
|
+
option
|
|
1081
|
+
);
|
|
1082
|
+
|
|
1083
|
+
const row = data.find(
|
|
1084
|
+
ele => ele.id === congruentRow.id
|
|
1085
|
+
);
|
|
1086
|
+
const updateData = omit(row, ['id', '$$createAt$$']);
|
|
1087
|
+
const result3 = await this.updateAbjointRow(
|
|
1088
|
+
entity,
|
|
1089
|
+
{
|
|
1090
|
+
id: await generateNewId(),
|
|
1091
|
+
action: 'update',
|
|
1092
|
+
data: updateData,
|
|
1093
|
+
filter: {
|
|
1094
|
+
id: congruentRow.id,
|
|
1095
|
+
} as any,
|
|
1096
|
+
},
|
|
1097
|
+
context,
|
|
1098
|
+
option
|
|
1099
|
+
);
|
|
1100
|
+
|
|
1101
|
+
return result2 + result3;
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
if (data.id !== congruentRow.id) {
|
|
1105
|
+
throw e;
|
|
1106
|
+
}
|
|
1107
|
+
const updateData = omit(data, ['id', '$$createAt$$']);
|
|
1108
|
+
const result2 = await this.updateAbjointRow(
|
|
1109
|
+
entity,
|
|
1110
|
+
{
|
|
1111
|
+
id: await generateNewId(),
|
|
1112
|
+
action: 'update',
|
|
1113
|
+
data: updateData,
|
|
1114
|
+
filter: {
|
|
1115
|
+
id: congruentRow.id,
|
|
1116
|
+
} as any,
|
|
1117
|
+
},
|
|
1118
|
+
context,
|
|
1119
|
+
option
|
|
1120
|
+
);
|
|
1121
|
+
return result2;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
} */
|
|
1125
|
+
throw e;
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
if (data instanceof Array) {
|
|
1129
|
+
result = data.length;
|
|
1130
|
+
const multipleCreate = this.supportMultipleCreate();
|
|
1131
|
+
if (multipleCreate) {
|
|
1132
|
+
await createInner(operation);
|
|
1133
|
+
}
|
|
1134
|
+
else {
|
|
1135
|
+
for (const d of data) {
|
|
1136
|
+
const createSingleOper = {
|
|
1137
|
+
id: 'any',
|
|
1138
|
+
action: 'create',
|
|
1139
|
+
data: d,
|
|
1140
|
+
};
|
|
1141
|
+
await createInner(createSingleOper);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
else {
|
|
1146
|
+
result = 1;
|
|
1147
|
+
await createInner(operation);
|
|
1148
|
+
}
|
|
1149
|
+
if (!option.dontCollect) {
|
|
1150
|
+
context.saveOpRecord(entity, operation);
|
|
1151
|
+
}
|
|
1152
|
+
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity)) {
|
|
1153
|
+
// 按照框架要求生成Oper和OperEntity这两个内置的对象
|
|
1154
|
+
(0, assert_1.default)(operId);
|
|
1155
|
+
const operatorId = context.getCurrentUserId(true);
|
|
1156
|
+
if (operatorId) {
|
|
1157
|
+
const createOper = {
|
|
1158
|
+
id: 'dummy',
|
|
1159
|
+
action: 'create',
|
|
1160
|
+
data: {
|
|
1161
|
+
id: operId,
|
|
1162
|
+
action,
|
|
1163
|
+
data,
|
|
1164
|
+
operatorId,
|
|
1165
|
+
targetEntity: entity,
|
|
1166
|
+
bornAt,
|
|
1167
|
+
operEntity$oper: data instanceof Array ? {
|
|
1168
|
+
id: 'dummy',
|
|
1169
|
+
action: 'create',
|
|
1170
|
+
data: await Promise.all(data.map(async (ele) => ({
|
|
1171
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1172
|
+
entityId: ele.id,
|
|
1173
|
+
entity: entity,
|
|
1174
|
+
}))),
|
|
1175
|
+
} : [{
|
|
1176
|
+
id: 'dummy',
|
|
1177
|
+
action: 'create',
|
|
1178
|
+
data: {
|
|
1179
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1180
|
+
entityId: data.id,
|
|
1181
|
+
entity: entity,
|
|
1182
|
+
},
|
|
1183
|
+
}],
|
|
1184
|
+
},
|
|
1185
|
+
};
|
|
1186
|
+
const closeRootMode = context.openRootMode();
|
|
1187
|
+
await this.cascadeUpdateAsync('oper', createOper, context, {
|
|
1188
|
+
dontCollect: true,
|
|
1189
|
+
});
|
|
1190
|
+
closeRootMode();
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
return {
|
|
1194
|
+
[entity]: {
|
|
1195
|
+
['create']: result,
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
default: {
|
|
1201
|
+
// 这里要优化一下,显式的对id的update/remove不要去查了,节省数据库层的性能(如果这些row是建立在一个create的modi上也查不到)
|
|
1202
|
+
const ids = (0, filter_2.getRelevantIds)(filter);
|
|
1203
|
+
if (ids.length === 0) {
|
|
1204
|
+
const selection = {
|
|
1205
|
+
data: {
|
|
1206
|
+
id: 1,
|
|
1207
|
+
},
|
|
1208
|
+
filter: operation.filter,
|
|
1209
|
+
indexFrom: operation.indexFrom,
|
|
1210
|
+
count: operation.count,
|
|
1211
|
+
};
|
|
1212
|
+
const rows = await this.selectAbjointRowAsync(entity, selection, context, {
|
|
1213
|
+
dontCollect: true,
|
|
1214
|
+
});
|
|
1215
|
+
ids.push(...(rows.map(ele => ele.id)));
|
|
1216
|
+
}
|
|
1217
|
+
if (data) {
|
|
1218
|
+
this.preProcessDataUpdated(data);
|
|
1219
|
+
}
|
|
1220
|
+
if (option.modiParentEntity && !['modi', 'modiEntity'].includes(entity)) {
|
|
1221
|
+
// 延时更新,变成对modi的插入
|
|
1222
|
+
// 变成对modi的插入
|
|
1223
|
+
// 优化,这里如果是对同一个targetEntity反复update,则变成对最后一条create/update的modi进行update,以避免发布文章这样的需求时产生过多的modi
|
|
1224
|
+
let modiUpsert;
|
|
1225
|
+
if (action !== 'remove') {
|
|
1226
|
+
const upsertModis = await this.selectAbjointRowAsync('modi', {
|
|
1227
|
+
data: {
|
|
1228
|
+
id: 1,
|
|
1229
|
+
data: 1,
|
|
1230
|
+
},
|
|
1231
|
+
filter: {
|
|
1232
|
+
targetEntity: entity,
|
|
1233
|
+
action: {
|
|
1234
|
+
$in: ['create', 'update'],
|
|
1235
|
+
},
|
|
1236
|
+
entity: option.modiParentEntity,
|
|
1237
|
+
entityId: option.modiParentId,
|
|
1238
|
+
iState: 'active',
|
|
1239
|
+
filter: ids.length > 0 ? {
|
|
1240
|
+
id: {
|
|
1241
|
+
$in: ids,
|
|
1242
|
+
},
|
|
1243
|
+
} : filter,
|
|
1244
|
+
},
|
|
1245
|
+
sorter: [
|
|
1246
|
+
{
|
|
1247
|
+
$attr: {
|
|
1248
|
+
$$createAt$$: 1,
|
|
1249
|
+
},
|
|
1250
|
+
$direction: 'desc',
|
|
1251
|
+
}
|
|
1252
|
+
],
|
|
1253
|
+
indexFrom: 0,
|
|
1254
|
+
count: 1,
|
|
1255
|
+
}, context, option);
|
|
1256
|
+
if (upsertModis.length > 0) {
|
|
1257
|
+
const { data: originData, id: originId } = upsertModis[0];
|
|
1258
|
+
modiUpsert = {
|
|
1259
|
+
id: 'dummy',
|
|
1260
|
+
action: 'update',
|
|
1261
|
+
data: {
|
|
1262
|
+
data: Object.assign({}, originData, data),
|
|
1263
|
+
},
|
|
1264
|
+
filter: {
|
|
1265
|
+
id: originId,
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
if (!modiUpsert) {
|
|
1271
|
+
modiUpsert = {
|
|
1272
|
+
id: 'dummy',
|
|
1273
|
+
action: 'create',
|
|
1274
|
+
data: {
|
|
1275
|
+
id: operId,
|
|
1276
|
+
targetEntity: entity,
|
|
1277
|
+
entity: option.modiParentEntity,
|
|
1278
|
+
entityId: option.modiParentId,
|
|
1279
|
+
action,
|
|
1280
|
+
data,
|
|
1281
|
+
iState: 'active',
|
|
1282
|
+
filter,
|
|
1283
|
+
},
|
|
1284
|
+
};
|
|
1285
|
+
if (ids.length > 0) {
|
|
1286
|
+
modiUpsert.data.modiEntity$modi = {
|
|
1287
|
+
id: 'dummy',
|
|
1288
|
+
action: 'create',
|
|
1289
|
+
data: await Promise.all(ids.map(async (id) => ({
|
|
1290
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1291
|
+
entity: entity,
|
|
1292
|
+
entityId: id,
|
|
1293
|
+
}))),
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
const closeRootMode = context.openRootMode();
|
|
1298
|
+
await this.cascadeUpdateAsync('modi', modiUpsert, context, option);
|
|
1299
|
+
closeRootMode();
|
|
1300
|
+
return {
|
|
1301
|
+
modi: {
|
|
1302
|
+
['create']: 1,
|
|
1303
|
+
},
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
else {
|
|
1307
|
+
const createOper = async () => {
|
|
1308
|
+
if (!option.dontCreateOper && !['oper', 'operEntity', 'modiEntity', 'modi'].includes(entity) && ids.length > 0) {
|
|
1309
|
+
// 按照框架要求生成Oper和OperEntity这两个内置的对象
|
|
1310
|
+
(0, assert_1.default)(operId);
|
|
1311
|
+
const operatorId = context.getCurrentUserId(true);
|
|
1312
|
+
const createOper = {
|
|
1313
|
+
id: 'dummy',
|
|
1314
|
+
action: 'create',
|
|
1315
|
+
data: {
|
|
1316
|
+
id: operId,
|
|
1317
|
+
action,
|
|
1318
|
+
data,
|
|
1319
|
+
targetEntity: entity,
|
|
1320
|
+
bornAt,
|
|
1321
|
+
operatorId,
|
|
1322
|
+
operEntity$oper: {
|
|
1323
|
+
id: 'dummy',
|
|
1324
|
+
action: 'create',
|
|
1325
|
+
data: await Promise.all(ids.map(async (ele) => ({
|
|
1326
|
+
id: await (0, uuid_1.generateNewIdAsync)(),
|
|
1327
|
+
entityId: ele,
|
|
1328
|
+
entity: entity,
|
|
1329
|
+
})))
|
|
1330
|
+
},
|
|
1331
|
+
},
|
|
1332
|
+
};
|
|
1333
|
+
const closeRootMode = context.openRootMode();
|
|
1334
|
+
await this.cascadeUpdateAsync('oper', createOper, context, {
|
|
1335
|
+
dontCollect: true,
|
|
1336
|
+
});
|
|
1337
|
+
closeRootMode();
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
if (action === 'remove') {
|
|
1341
|
+
if (!option.dontCollect) {
|
|
1342
|
+
context.saveOpRecord(entity, {
|
|
1343
|
+
id: operId,
|
|
1344
|
+
action,
|
|
1345
|
+
data: {},
|
|
1346
|
+
filter: {
|
|
1347
|
+
id: {
|
|
1348
|
+
$in: ids,
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
else {
|
|
1355
|
+
const updateAttrCount = Object.keys(data).length;
|
|
1356
|
+
if (updateAttrCount > 0) {
|
|
1357
|
+
// 优化一下,如果不更新任何属性,则不实际执行
|
|
1358
|
+
Object.assign(data, {
|
|
1359
|
+
[Entity_1.UpdateAtAttribute]: now,
|
|
1360
|
+
});
|
|
1361
|
+
if (!option.dontCollect) {
|
|
1362
|
+
context.saveOpRecord(entity, {
|
|
1363
|
+
id: operId,
|
|
1364
|
+
action,
|
|
1365
|
+
data: data,
|
|
1366
|
+
filter: {
|
|
1367
|
+
id: {
|
|
1368
|
+
$in: ids,
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
else if (action !== 'update') {
|
|
1375
|
+
// 如果不是update动作而是用户自定义的动作,这里还是要记录oper
|
|
1376
|
+
await createOper();
|
|
1377
|
+
return {};
|
|
1378
|
+
}
|
|
1379
|
+
else {
|
|
1380
|
+
return {};
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
await this.updateAbjointRowAsync(entity, operation, context, option);
|
|
1384
|
+
await createOper();
|
|
1385
|
+
return {
|
|
1386
|
+
[entity]: {
|
|
1387
|
+
[action]: ids.length,
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
doUpdateSingleRow(entity, operation, context, option) {
|
|
1395
|
+
const { data, action, id: operId, filter } = operation;
|
|
1396
|
+
const now = Date.now();
|
|
1397
|
+
switch (action) {
|
|
1398
|
+
case 'create': {
|
|
1399
|
+
this.preProcessDataCreated(entity, data);
|
|
1400
|
+
let result = 0;
|
|
1401
|
+
const createInner = (operation2) => {
|
|
1402
|
+
try {
|
|
1403
|
+
result += this.updateAbjointRow(entity, operation2, context, option);
|
|
1404
|
+
}
|
|
1405
|
+
catch (e) {
|
|
1406
|
+
throw e;
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
if (data instanceof Array) {
|
|
1410
|
+
const multipleCreate = this.supportMultipleCreate();
|
|
1411
|
+
if (multipleCreate) {
|
|
1412
|
+
createInner(operation);
|
|
1413
|
+
}
|
|
1414
|
+
else {
|
|
1415
|
+
for (const d of data) {
|
|
1416
|
+
const createSingleOper = {
|
|
1417
|
+
id: 'any',
|
|
1418
|
+
action: 'create',
|
|
1419
|
+
data: d,
|
|
1420
|
+
};
|
|
1421
|
+
createInner(createSingleOper);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
else {
|
|
1426
|
+
createInner(operation);
|
|
1427
|
+
}
|
|
1428
|
+
return result;
|
|
1429
|
+
}
|
|
1430
|
+
default: {
|
|
1431
|
+
if (action === 'remove') {
|
|
1432
|
+
}
|
|
1433
|
+
else {
|
|
1434
|
+
const updateAttrCount = Object.keys(data).length;
|
|
1435
|
+
if (updateAttrCount > 0) {
|
|
1436
|
+
// 优化一下,如果不更新任何属性,则不实际执行
|
|
1437
|
+
Object.assign(data, {
|
|
1438
|
+
$$updateAt$$: now,
|
|
1439
|
+
});
|
|
1440
|
+
this.preProcessDataUpdated(data);
|
|
1441
|
+
}
|
|
1442
|
+
else {
|
|
1443
|
+
return 0;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
return this.updateAbjointRow(entity, operation, context, option);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
cascadeUpdate(entity, operation, context, option) {
|
|
1451
|
+
const { action, data, filter, id, bornAt } = operation;
|
|
1452
|
+
let opData;
|
|
1453
|
+
const wholeBeforeFns = [];
|
|
1454
|
+
const wholeAfterFns = [];
|
|
1455
|
+
const result = {};
|
|
1456
|
+
if (['create', 'create-l'].includes(action) && data instanceof Array) {
|
|
1457
|
+
opData = [];
|
|
1458
|
+
for (const d of data) {
|
|
1459
|
+
const { data: od, beforeFns, afterFns } = this.destructCascadeUpdate(entity, action, d, context, option, this.cascadeUpdate);
|
|
1460
|
+
opData.push(od);
|
|
1461
|
+
wholeBeforeFns.push(...beforeFns);
|
|
1462
|
+
wholeAfterFns.push(...afterFns);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
else {
|
|
1466
|
+
const { data: od, beforeFns, afterFns } = this.destructCascadeUpdate(entity, action, data, context, option, this.cascadeUpdate, filter);
|
|
1467
|
+
opData = od;
|
|
1468
|
+
wholeBeforeFns.push(...beforeFns);
|
|
1469
|
+
wholeAfterFns.push(...afterFns);
|
|
1470
|
+
}
|
|
1471
|
+
const operation2 = Object.assign({}, operation, {
|
|
1472
|
+
data: opData,
|
|
1473
|
+
});
|
|
1474
|
+
for (const before of wholeBeforeFns) {
|
|
1475
|
+
before();
|
|
1476
|
+
}
|
|
1477
|
+
const count = this.doUpdateSingleRow(entity, operation2, context, option);
|
|
1478
|
+
for (const after of wholeAfterFns) {
|
|
1479
|
+
after();
|
|
1480
|
+
}
|
|
1481
|
+
return result;
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
*
|
|
1485
|
+
* @param entity
|
|
1486
|
+
* @param operation
|
|
1487
|
+
* @param context
|
|
1488
|
+
* @param option
|
|
1489
|
+
*/
|
|
1490
|
+
async cascadeUpdateAsync(entity, operation, context, option) {
|
|
1491
|
+
const { action, data, filter, id, bornAt } = operation;
|
|
1492
|
+
let opData;
|
|
1493
|
+
const wholeBeforeFns = [];
|
|
1494
|
+
const wholeAfterFns = [];
|
|
1495
|
+
if (['create', 'create-l'].includes(action) && data instanceof Array) {
|
|
1496
|
+
opData = [];
|
|
1497
|
+
for (const d of data) {
|
|
1498
|
+
const { data: od, beforeFns, afterFns } = this.destructCascadeUpdate(entity, action, d, context, option, this.cascadeUpdateAsync, undefined, bornAt);
|
|
1499
|
+
opData.push(od);
|
|
1500
|
+
wholeBeforeFns.push(...beforeFns);
|
|
1501
|
+
wholeAfterFns.push(...afterFns);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
else {
|
|
1505
|
+
const { data: od, beforeFns, afterFns } = this.destructCascadeUpdate(entity, action, data, context, option, this.cascadeUpdateAsync, filter, bornAt);
|
|
1506
|
+
opData = od;
|
|
1507
|
+
wholeBeforeFns.push(...beforeFns);
|
|
1508
|
+
wholeAfterFns.push(...afterFns);
|
|
1509
|
+
}
|
|
1510
|
+
const operation2 = Object.assign({}, operation, {
|
|
1511
|
+
data: opData,
|
|
1512
|
+
});
|
|
1513
|
+
let result = {};
|
|
1514
|
+
for (const before of wholeBeforeFns) {
|
|
1515
|
+
const result2 = await before();
|
|
1516
|
+
result = this.mergeMultipleResults([result, result2]);
|
|
1517
|
+
}
|
|
1518
|
+
const resultMe = await this.doUpdateSingleRowAsync(entity, operation2, context, option);
|
|
1519
|
+
result = this.mergeMultipleResults([result, resultMe]);
|
|
1520
|
+
for (const after of wholeAfterFns) {
|
|
1521
|
+
const result2 = await after();
|
|
1522
|
+
result = this.mergeMultipleResults([result, result2]);
|
|
1523
|
+
}
|
|
1524
|
+
return result;
|
|
1525
|
+
}
|
|
1526
|
+
cascadeSelect(entity, selection, context, option) {
|
|
1527
|
+
const { data, filter, indexFrom, count, sorter, distinct } = selection;
|
|
1528
|
+
const { projection, cascadeSelectionFns } = this.destructCascadeSelect(entity, data, context, this.cascadeSelect, this.aggregateSync, option);
|
|
1529
|
+
const rows = this.selectAbjointRow(entity, {
|
|
1530
|
+
data: projection,
|
|
1531
|
+
filter,
|
|
1532
|
+
indexFrom,
|
|
1533
|
+
count,
|
|
1534
|
+
sorter,
|
|
1535
|
+
distinct
|
|
1536
|
+
}, context, option);
|
|
1537
|
+
if (cascadeSelectionFns.length > 0) {
|
|
1538
|
+
const ruException = [];
|
|
1539
|
+
cascadeSelectionFns.forEach(ele => {
|
|
1540
|
+
try {
|
|
1541
|
+
ele(rows);
|
|
1542
|
+
}
|
|
1543
|
+
catch (e) {
|
|
1544
|
+
if (e instanceof types_1.OakRowUnexistedException) {
|
|
1545
|
+
const rows = e.getRows();
|
|
1546
|
+
ruException.push(...rows);
|
|
1547
|
+
}
|
|
1548
|
+
else {
|
|
1549
|
+
throw e;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
if (ruException.length > 0) {
|
|
1554
|
+
throw new types_1.OakRowUnexistedException(ruException);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
return rows;
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* 将一次查询的结果集加入result
|
|
1561
|
+
* todo 如果是supportMtoOJoin,这里还要解构(未充分测试)
|
|
1562
|
+
* @param entity
|
|
1563
|
+
* @param rows
|
|
1564
|
+
* @param context
|
|
1565
|
+
*/
|
|
1566
|
+
addToResultSelections(entity, rows, context) {
|
|
1567
|
+
if (this.supportManyToOneJoin()) {
|
|
1568
|
+
// 这里的外键连接有可能为空,需要使用所有的行的attr的并集来测试
|
|
1569
|
+
const attrs = (0, lodash_1.uniq)(rows.map(ele => Object.keys(ele)).flat());
|
|
1570
|
+
const attrsToPick = [];
|
|
1571
|
+
for (const attr of attrs) {
|
|
1572
|
+
const data = {};
|
|
1573
|
+
const rel = this.judgeRelation(entity, attr);
|
|
1574
|
+
if (rel === 2) {
|
|
1575
|
+
this.addToResultSelections(attr, rows.map(ele => ele[attr]).filter(ele => !!ele), context);
|
|
1576
|
+
}
|
|
1577
|
+
else if (typeof rel === 'string') {
|
|
1578
|
+
this.addToResultSelections(rel, rows.map(ele => ele[attr]).filter(ele => !!ele), context);
|
|
1579
|
+
}
|
|
1580
|
+
else if (rel instanceof Array) {
|
|
1581
|
+
this.addToResultSelections(rel[0], rows.map(ele => ele[attr]).reduce((prev, current) => prev.concat(current), []), context);
|
|
1582
|
+
}
|
|
1583
|
+
else {
|
|
1584
|
+
attrsToPick.push(attr);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
const originRows = rows.map(ele => (0, lodash_1.pick)(ele, attrsToPick));
|
|
1588
|
+
this.addSingleRowToResultSelections(entity, originRows, context);
|
|
1589
|
+
}
|
|
1590
|
+
else {
|
|
1591
|
+
this.addSingleRowToResultSelections(entity, rows, context);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
addSingleRowToResultSelections(entity, rows, context) {
|
|
1595
|
+
const { opRecords } = context;
|
|
1596
|
+
let lastOperation = opRecords[opRecords.length - 1];
|
|
1597
|
+
if (lastOperation && lastOperation.a === 's') {
|
|
1598
|
+
const entityBranch = lastOperation.d[entity];
|
|
1599
|
+
if (entityBranch) {
|
|
1600
|
+
rows.forEach((row) => {
|
|
1601
|
+
if (row && row.id) { // 如果没有row.id就不加入结果集了
|
|
1602
|
+
const { id } = row;
|
|
1603
|
+
if (!entityBranch[id]) {
|
|
1604
|
+
Object.assign(entityBranch, {
|
|
1605
|
+
[id]: (0, lodash_1.cloneDeep)(row),
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
else {
|
|
1609
|
+
Object.assign(entityBranch[id], (0, lodash_1.cloneDeep)(row));
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
else {
|
|
1617
|
+
lastOperation = {
|
|
1618
|
+
a: 's',
|
|
1619
|
+
d: {},
|
|
1620
|
+
};
|
|
1621
|
+
opRecords.push(lastOperation);
|
|
1622
|
+
}
|
|
1623
|
+
const entityBranch = {};
|
|
1624
|
+
rows.forEach((row) => {
|
|
1625
|
+
if (row) {
|
|
1626
|
+
const { id } = row;
|
|
1627
|
+
Object.assign(entityBranch, {
|
|
1628
|
+
[id]: (0, lodash_1.cloneDeep)(row),
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
});
|
|
1632
|
+
Object.assign(lastOperation.d, {
|
|
1633
|
+
[entity]: entityBranch,
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
async cascadeSelectAsync(entity, selection, context, option) {
|
|
1637
|
+
const { data, filter, indexFrom, count, sorter, total, randomRange, distinct } = selection;
|
|
1638
|
+
const { projection, cascadeSelectionFns } = this.destructCascadeSelect(entity, data, context, this.cascadeSelectAsync, this.aggregateAsync, option);
|
|
1639
|
+
const rows2 = await this.selectAbjointRowAsync(entity, {
|
|
1640
|
+
data: projection,
|
|
1641
|
+
filter,
|
|
1642
|
+
indexFrom,
|
|
1643
|
+
distinct,
|
|
1644
|
+
count: randomRange || count,
|
|
1645
|
+
sorter
|
|
1646
|
+
}, context, option);
|
|
1647
|
+
// 处理随机取值
|
|
1648
|
+
let rows = !randomRange ? rows2 : [];
|
|
1649
|
+
if (randomRange) {
|
|
1650
|
+
const possibility = count / rows2.length;
|
|
1651
|
+
let reduced = rows2.length - count;
|
|
1652
|
+
rows = rows2.filter(() => {
|
|
1653
|
+
const rand = Math.random();
|
|
1654
|
+
if (rand > possibility && reduced) {
|
|
1655
|
+
reduced--;
|
|
1656
|
+
return false;
|
|
1657
|
+
}
|
|
1658
|
+
return true;
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
if (!option.dontCollect) {
|
|
1662
|
+
this.addToResultSelections(entity, rows, context);
|
|
1663
|
+
}
|
|
1664
|
+
if (cascadeSelectionFns.length > 0) {
|
|
1665
|
+
const ruException = [];
|
|
1666
|
+
await Promise.all(cascadeSelectionFns.map(async (ele) => {
|
|
1667
|
+
try {
|
|
1668
|
+
await ele(rows);
|
|
1669
|
+
}
|
|
1670
|
+
catch (e) {
|
|
1671
|
+
if (e instanceof types_1.OakRowUnexistedException) {
|
|
1672
|
+
const rows = e.getRows();
|
|
1673
|
+
ruException.push(...rows);
|
|
1674
|
+
}
|
|
1675
|
+
else {
|
|
1676
|
+
throw e;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}));
|
|
1680
|
+
if (ruException.length > 0) {
|
|
1681
|
+
throw new types_1.OakRowUnexistedException(ruException);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
if (total) {
|
|
1685
|
+
const total2 = await this.countAsync(entity, {
|
|
1686
|
+
filter: selection.filter,
|
|
1687
|
+
count: total,
|
|
1688
|
+
}, context, option);
|
|
1689
|
+
Object.assign(rows, {
|
|
1690
|
+
'#total': total2,
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
return rows;
|
|
1694
|
+
}
|
|
1695
|
+
async aggregateAsync(entity, aggregation, context, option) {
|
|
1696
|
+
await this.reinforceSelectionAsync(entity, aggregation, context, option, true);
|
|
1697
|
+
return this.aggregateAbjointRowAsync(entity, aggregation, context, option);
|
|
1698
|
+
}
|
|
1699
|
+
aggregateSync(entity, aggregation, context, option) {
|
|
1700
|
+
this.reinforceSelectionSync(entity, aggregation, context, option, true);
|
|
1701
|
+
return this.aggregateAbjointRowSync(entity, aggregation, context, option);
|
|
1702
|
+
}
|
|
1703
|
+
async selectAsync(entity, selection, context, option) {
|
|
1704
|
+
await this.reinforceSelectionAsync(entity, selection, context, option);
|
|
1705
|
+
return this.cascadeSelectAsync(entity, selection, context, option);
|
|
1706
|
+
}
|
|
1707
|
+
selectSync(entity, selection, context, option) {
|
|
1708
|
+
this.reinforceSelectionSync(entity, selection, context, option);
|
|
1709
|
+
return this.cascadeSelect(entity, selection, context, option);
|
|
1710
|
+
}
|
|
1711
|
+
operateSync(entity, operation, context, option) {
|
|
1712
|
+
//this.reinforceOperation(entity, operation); // 感觉前台可以无视?
|
|
1713
|
+
return this.cascadeUpdate(entity, operation, context, option);
|
|
1714
|
+
}
|
|
1715
|
+
async operateAsync(entity, operation, context, option) {
|
|
1716
|
+
await this.reinforceOperation(entity, operation, context, option);
|
|
1717
|
+
return this.cascadeUpdateAsync(entity, operation, context, option);
|
|
1718
|
+
}
|
|
1719
|
+
countSync(entity, selection, context, option) {
|
|
1720
|
+
this.reinforceSelectionSync(entity, selection, context, option, true); // 这样写可能有问题的,虽然能跳过本地的projection补全,但如果有更多的selectionRewriter注入可能会出问题。by Xc 20231220
|
|
1721
|
+
return this.countAbjointRow(entity, selection, context, option);
|
|
1722
|
+
}
|
|
1723
|
+
countAsync(entity, selection, context, option) {
|
|
1724
|
+
this.reinforceSelectionAsync(entity, selection, context, option, true); // 这样写可能有问题的,虽然能跳过本地的projection补全,但如果有更多的selectionRewriter注入可能会出问题。by Xc 20231220
|
|
1725
|
+
return this.countAbjointRowAsync(entity, selection, context, option);
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
exports.CascadeStore = CascadeStore;
|