oak-domain/lib/schema/CascadeStore.js

366 lines
15 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 = {};
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,
});
(0, lodash_1.assign)(manyToOneOnEntity, {
[attr]: 1,
});
}
else if (typeof relation === 'string') {
// 基于属性的多对一
(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, selection, context, params);
for (const row of rows) {
for (const attr in manyToOne) {
const row2 = await this.cascadeSelect(manyToOne[attr], {
data: data[attr],
filter: {
id: row[`${attr}Id`],
}
}, context, params);
(0, lodash_1.assign)(row, {
[attr]: row2[0],
});
}
for (const attr in manyToOneOnEntity) {
if (row.entity === attr) {
const row2 = await this.cascadeSelect(attr, {
data: data[attr],
filter: {
id: row[`entityId`],
}
}, context, params);
(0, lodash_1.assign)(row, {
[attr]: row2[0],
});
}
}
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,
});
}
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 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 = {};
if (action === 'create' && data instanceof Array) {
for (const dataEle of data) {
await this.cascadeUpdate(entity, {
action,
data: dataEle,
}, context, params);
}
return;
}
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 } = data2;
(0, assert_1.default)(typeof fkId === 'string');
(0, lodash_1.assign)(operationMto, {
filter: (0, filter_1.addFilterSegment)({
id: fkId,
}), filterMto,
});
(0, lodash_1.assign)(opData, {
entity: attr,
});
}
else {
(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),
});
}
await this.cascadeUpdate(attr, operationMto, context, params);
}
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,
}
},
}),
});
}
await this.cascadeUpdate(relation, operationMto, context, params);
}
else {
const operationOtm = data2[attr];
const { action: actionOtm, data: dataOtm, filter: filterOtm } = operationOtm;
(0, assert_1.default)(relation instanceof Array);
const [entityOtm, foreignKey] = relation;
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') {
// 这里先假设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 {
(0, lodash_1.assign)(operationOtm, {
filter: (0, filter_1.addFilterSegment)({
entity,
entityId: {
$in: {
entity,
data: {
id: 1,
},
filter,
}
}
}, 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') {
// 这里先假设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 {
(0, lodash_1.assign)(operationOtm, {
filter: (0, filter_1.addFilterSegment)({
[foreignKey]: {
$in: {
entity,
data: {
id: 1,
},
filter,
}
}
}, filterOtm),
});
if (action === 'remove' && actionOtm === 'update') {
(0, lodash_1.assign)(dataOtm, {
[foreignKey]: null,
});
}
}
}
await this.cascadeUpdate(entityOtm, operationOtm, context, params);
}
}
const operation2 = (0, lodash_1.assign)({}, operation, {
data: opData,
});
await this.updateAbjointRow(entity, operation2, context, params);
}
}
exports.CascadeStore = CascadeStore;