oak-domain/lib/store/CascadeStore.js

439 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CascadeStore = void 0;
const assert_1 = __importDefault(require("assert"));
const lodash_1 = require("lodash");
const RowStore_1 = require("../types/RowStore");
const filter_1 = require("./filter");
const relation_1 = require("./relation");
/**这个用来处理级联的select和update对不同能力的 */
class CascadeStore extends RowStore_1.RowStore {
constructor(storageSchema) {
super(storageSchema);
}
async cascadeSelect(entity, selection, context, params) {
const { data } = selection;
const projection = {};
const oneToMany = {};
const oneToManyOnEntity = {};
const manyToOne = {};
const manyToOneOnEntity = {};
const supportMtoJoin = this.supportManyToOneJoin();
for (const attr in data) {
const relation = (0, relation_1.judgeRelation)(this.storageSchema, entity, attr);
if (relation === 1 || relation == 0) {
(0, lodash_1.assign)(projection, {
[attr]: data[attr],
});
}
else if (relation === 2) {
// 基于entity的多对一
(0, lodash_1.assign)(projection, {
entity: 1,
entityId: 1,
});
if (supportMtoJoin) {
(0, lodash_1.assign)(projection, {
[attr]: data[attr],
});
}
else {
(0, lodash_1.assign)(manyToOneOnEntity, {
[attr]: 1,
});
}
}
else if (typeof relation === 'string') {
// 基于属性的多对一
if (supportMtoJoin) {
(0, lodash_1.assign)(projection, {
[attr]: data[attr],
});
}
else {
(0, lodash_1.assign)(projection, {
[`${attr}Id`]: 1,
});
(0, lodash_1.assign)(manyToOne, {
[attr]: relation,
});
}
}
else {
const [entity2, foreignKey] = relation;
if (foreignKey) {
// 基于属性的一对多
(0, lodash_1.assign)(oneToMany, {
[attr]: {
entity: entity2,
foreignKey,
},
});
}
else {
// 基于entity的多对一
(0, lodash_1.assign)(oneToManyOnEntity, {
[attr]: entity2,
});
}
}
}
const rows = await this.selectAbjointRow(entity, (0, lodash_1.assign)({}, selection, {
data: projection,
}), context, params);
await Promise.all(
// manyToOne
(() => {
const attrs = (0, lodash_1.keys)(manyToOne);
if (attrs.length > 0) {
return attrs.map(async (attr) => {
const subRows = await this.cascadeSelect(manyToOne[attr], {
data: data[attr],
filter: {
id: {
$in: rows.map((row) => row[`${attr}Id`])
},
}
}, context, params);
rows.forEach((row) => {
const subRow = subRows.find(ele => ele.id === row[`${attr}Id`]);
(0, lodash_1.assign)(row, {
[attr]: subRow,
});
});
});
}
return [];
})().concat(
// manyToOneOnEntity
(() => {
const attrs = (0, lodash_1.keys)(manyToOneOnEntity);
if (attrs.length > 0) {
return attrs.map(async (attr) => {
const subRows = await this.cascadeSelect(attr, {
data: data[attr],
filter: {
id: {
$in: rows.filter(row => row.entity === attr).map(row => row.entityId)
},
}
}, context, params);
rows.filter(row => row.entity === attr).forEach((row) => {
const subRow = subRows.find(ele => ele.id === row.entityId);
(0, lodash_1.assign)(row, {
[attr]: subRow,
});
});
});
}
return [];
})()).concat((() => {
const attrs = (0, lodash_1.keys)(oneToMany);
if (attrs.length > 0) {
// 必须一行一行的查询否则indexFrom和count无法准确
return rows.map(async (row) => {
for (const attr in oneToMany) {
const { entity: entity2, foreignKey } = oneToMany[attr];
const filter2 = data[attr];
const rows2 = await this.cascadeSelect(entity2, (0, lodash_1.assign)({}, filter2, {
filter: (0, filter_1.addFilterSegment)({
[foreignKey]: row.id,
}, filter2.filter),
}), context, params);
(0, lodash_1.assign)(row, {
[attr]: rows2,
});
}
});
}
return [];
})()).concat((() => {
const attrs = (0, lodash_1.keys)(oneToManyOnEntity);
if (attrs.length > 0) {
// 必须一行一行的查询否则indexFrom和count无法准确
return rows.map(async (row) => {
for (const attr in oneToManyOnEntity) {
const filter2 = data[attr];
const rows2 = await this.cascadeSelect(oneToManyOnEntity[attr], (0, lodash_1.assign)({}, filter2, {
filter: (0, filter_1.addFilterSegment)({
entityId: row.id,
entity,
}, filter2.filter),
}), context, params);
(0, lodash_1.assign)(row, {
[attr]: rows2,
});
}
});
}
return [];
})()));
return rows;
}
/**
* 级联更新
* A --> B
多对一A CREATEB CREATEB data的主键赋到A的data上
A CREATEB UPDATEB filter的主键来自A的data
A UPDATEB CREATEB data的主键赋到A的data上
A UPDATEB UPDATEB filter的主键来自A的row
A UPDATEB REMOVEB filter的主键来自A的row
A REMOVEB UPDATEB filter的主键来自A的row
A REMOVEB REMOVEB filter的主键来自A的row
一对多A CREATEB CREATEA data上的主键赋到B的data上
A CREATEB UPDATEA data上的主键赋到B的data上
A UPDATEB CREATEA filter上的主键赋到B的data上一定是带主键的filter
A UPDATEB UPDATEA filter上的主键赋到B的filter上一定是带主键的filter
A UPDATEB REMOVEA filter上的主键赋到B的filter上一定是带主键的filter
A REMOVEB UPDATEA filter上的主键赋到B的filter上且B关于A的外键清空
A REMOVEB REMOVEA filter上的主键赋到B的filter上
* @param entity
* @param operation
* @param context
* @param params
*/
async cascadeUpdate(entity, operation, context, params) {
const { action, data, filter } = operation;
const opData = {};
const result = {};
if (action === 'create' && data instanceof Array) {
const multipleCreate = this.supportMultipleCreate();
if (multipleCreate) {
return await this.cascadeUpdate(entity, {
action,
data,
}, context, params);
}
else {
for (const dataEle of data) {
const result2 = await this.cascadeUpdate(entity, {
action,
data: dataEle,
}, context, params);
this.mergeOperationResult(result, result2);
}
return result;
}
}
const data2 = data;
for (const attr in data2) {
const relation = (0, relation_1.judgeRelation)(this.storageSchema, entity, attr);
if (relation === 1) {
(0, lodash_1.assign)(opData, {
[attr]: data2[attr],
});
}
else if (relation === 2) {
// 基于entity/entityId的many-to-one
const operationMto = data2[attr];
const { action: actionMto, data: dataMto, filter: filterMto } = operationMto;
if (actionMto === 'create') {
(0, lodash_1.assign)(opData, {
entityId: dataMto.id,
entity: attr,
});
}
else if (action === 'create') {
const { entityId: fkId, entity } = data2;
(0, assert_1.default)(typeof fkId === 'string' || entity === attr); // A中data的entityId作为B中filter的主键
(0, lodash_1.assign)(operationMto, {
filter: (0, filter_1.addFilterSegment)({
id: fkId,
}), filterMto,
});
}
else {
// 剩下三种情况都是B中的filter的id来自A中row的entityId
(0, assert_1.default)(!data2.hasOwnProperty('entityId') && !data2.hasOwnProperty('entity'));
(0, lodash_1.assign)(operationMto, {
filter: (0, filter_1.addFilterSegment)({
id: {
$in: {
entity,
data: {
entityId: 1,
},
filter: (0, filter_1.addFilterSegment)({
entity: attr,
}, filter),
}
},
}, filterMto),
});
}
const result2 = await this.cascadeUpdate(attr, operationMto, context, params);
this.mergeOperationResult(result, result2);
}
else if (typeof relation === 'string') {
// 基于attr的外键的many-to-one
const operationMto = data2[attr];
const { action: actionMto, data: dataMto, filter: filterMto } = operationMto;
if (actionMto === 'create') {
(0, lodash_1.assign)(opData, {
[`${attr}Id`]: dataMto.id,
});
}
else if (action === 'create') {
const { [`${attr}Id`]: fkId } = data2;
(0, assert_1.default)(typeof fkId === 'string');
(0, lodash_1.assign)(operationMto, {
filter: (0, filter_1.addFilterSegment)(filterMto || {}, {
id: fkId,
}),
});
}
else {
(0, assert_1.default)(!data2.hasOwnProperty(`${attr}Id`));
(0, lodash_1.assign)(operationMto, {
filter: (0, filter_1.addFilterSegment)(filterMto || {}, {
id: {
$in: {
entity,
data: {
[`${attr}Id`]: 1,
},
filter,
}
},
}),
});
}
const result2 = await this.cascadeUpdate(relation, operationMto, context, params);
this.mergeOperationResult(result, result2);
}
else {
(0, assert_1.default)(relation instanceof Array);
const [entityOtm, foreignKey] = relation;
const otmOperations = data2[attr];
const dealWithOneToMany = async (otm) => {
const { action: actionOtm, data: dataOtm, filter: filterOtm } = otm;
if (!foreignKey) {
// 基于entity/entityId的one-to-many
if (action === 'create') {
const { id } = data2;
if (dataOtm instanceof Array) {
dataOtm.forEach(ele => (0, lodash_1.assign)(ele, {
entity,
entityId: id,
}));
}
else {
(0, lodash_1.assign)(dataOtm, {
entity,
entityId: id,
});
}
}
else if (actionOtm === 'create') {
// 这里先假设A必是update的filter上一定有id否则用户界面上应该设计不出来这样的操作
const { id } = filter;
(0, assert_1.default)(typeof id === 'string');
if (dataOtm instanceof Array) {
dataOtm.forEach(ele => (0, lodash_1.assign)(ele, {
entity,
entityId: id,
}));
}
else {
(0, lodash_1.assign)(dataOtm, {
entity,
entityId: id,
});
}
}
else {
// 这里先假设A必是update的filter上一定有id否则用户界面上应该设计不出来这样的操作
const { id } = filter;
(0, lodash_1.assign)(otm, {
filter: (0, filter_1.addFilterSegment)({
entity,
entityId: id,
}, filterOtm),
});
if (action === 'remove' && actionOtm === 'update') {
(0, lodash_1.assign)(dataOtm, {
entity: null,
entityId: null,
});
}
}
}
else {
// 基于foreignKey的one-to-many
if (action === 'create') {
const { id } = data2;
if (dataOtm instanceof Array) {
dataOtm.forEach(ele => (0, lodash_1.assign)(ele, {
[foreignKey]: id,
}));
}
else {
(0, lodash_1.assign)(dataOtm, {
[foreignKey]: id,
});
}
}
else if (actionOtm === 'create') {
// 这里先假设A必是update的filter上一定有id否则用户界面上应该设计不出来这样的操作
const { id } = filter;
(0, assert_1.default)(typeof id === 'string');
if (dataOtm instanceof Array) {
dataOtm.forEach(ele => (0, lodash_1.assign)(ele, {
[foreignKey]: id,
}));
}
else {
(0, lodash_1.assign)(dataOtm, {
[foreignKey]: id,
});
}
}
else {
// 这里先假设A必是update的filter上一定有id否则用户界面上应该设计不出来这样的操作
const { id } = filter;
(0, lodash_1.assign)(otm, {
filter: (0, filter_1.addFilterSegment)({
[foreignKey]: id,
}, filterOtm),
});
if (action === 'remove' && actionOtm === 'update') {
(0, lodash_1.assign)(dataOtm, {
[foreignKey]: null,
});
}
}
}
const result2 = await this.cascadeUpdate(entityOtm, otm, context, params);
this.mergeOperationResult(result, result2);
};
if (otmOperations instanceof Array) {
for (const oper of otmOperations) {
await dealWithOneToMany(oper);
}
}
else {
await dealWithOneToMany(otmOperations);
}
}
}
const operation2 = (0, lodash_1.assign)({}, operation, {
data: opData,
});
const count = await this.updateAbjointRow(entity, operation2, context, params);
this.mergeOperationResult(result, {
[entity]: {
[operation2.action]: count,
}
});
return result;
}
judgeRelation(entity, attr) {
return (0, relation_1.judgeRelation)(this.storageSchema, entity, attr);
}
}
exports.CascadeStore = CascadeStore;