@strapi/database 5.12.1 → 5.12.2
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/dist/connection.js +43 -0
- package/dist/connection.js.map +1 -0
- package/dist/connection.mjs +41 -0
- package/dist/connection.mjs.map +1 -0
- package/dist/dialects/dialect.js +54 -0
- package/dist/dialects/dialect.js.map +1 -0
- package/dist/dialects/dialect.mjs +52 -0
- package/dist/dialects/dialect.mjs.map +1 -0
- package/dist/dialects/index.js +44 -0
- package/dist/dialects/index.js.map +1 -0
- package/dist/dialects/index.mjs +42 -0
- package/dist/dialects/index.mjs.map +1 -0
- package/dist/dialects/mysql/constants.js +8 -0
- package/dist/dialects/mysql/constants.js.map +1 -0
- package/dist/dialects/mysql/constants.mjs +5 -0
- package/dist/dialects/mysql/constants.mjs.map +1 -0
- package/dist/dialects/mysql/database-inspector.js +35 -0
- package/dist/dialects/mysql/database-inspector.js.map +1 -0
- package/dist/dialects/mysql/database-inspector.mjs +33 -0
- package/dist/dialects/mysql/database-inspector.mjs.map +1 -0
- package/dist/dialects/mysql/index.js +75 -0
- package/dist/dialects/mysql/index.js.map +1 -0
- package/dist/dialects/mysql/index.mjs +73 -0
- package/dist/dialects/mysql/index.mjs.map +1 -0
- package/dist/dialects/mysql/schema-inspector.js +297 -0
- package/dist/dialects/mysql/schema-inspector.js.map +1 -0
- package/dist/dialects/mysql/schema-inspector.mjs +295 -0
- package/dist/dialects/mysql/schema-inspector.mjs.map +1 -0
- package/dist/dialects/postgresql/index.js +62 -0
- package/dist/dialects/postgresql/index.js.map +1 -0
- package/dist/dialects/postgresql/index.mjs +60 -0
- package/dist/dialects/postgresql/index.mjs.map +1 -0
- package/dist/dialects/postgresql/schema-inspector.js +316 -0
- package/dist/dialects/postgresql/schema-inspector.js.map +1 -0
- package/dist/dialects/postgresql/schema-inspector.mjs +314 -0
- package/dist/dialects/postgresql/schema-inspector.mjs.map +1 -0
- package/dist/dialects/sqlite/index.js +82 -0
- package/dist/dialects/sqlite/index.js.map +1 -0
- package/dist/dialects/sqlite/index.mjs +80 -0
- package/dist/dialects/sqlite/index.mjs.map +1 -0
- package/dist/dialects/sqlite/schema-inspector.js +211 -0
- package/dist/dialects/sqlite/schema-inspector.js.map +1 -0
- package/dist/dialects/sqlite/schema-inspector.mjs +209 -0
- package/dist/dialects/sqlite/schema-inspector.mjs.map +1 -0
- package/dist/entity-manager/entity-repository.js +139 -0
- package/dist/entity-manager/entity-repository.js.map +1 -0
- package/dist/entity-manager/entity-repository.mjs +137 -0
- package/dist/entity-manager/entity-repository.mjs.map +1 -0
- package/dist/entity-manager/index.js +1186 -0
- package/dist/entity-manager/index.js.map +1 -0
- package/dist/entity-manager/index.mjs +1184 -0
- package/dist/entity-manager/index.mjs.map +1 -0
- package/dist/entity-manager/morph-relations.js +73 -0
- package/dist/entity-manager/morph-relations.js.map +1 -0
- package/dist/entity-manager/morph-relations.mjs +69 -0
- package/dist/entity-manager/morph-relations.mjs.map +1 -0
- package/dist/entity-manager/regular-relations.js +247 -0
- package/dist/entity-manager/regular-relations.js.map +1 -0
- package/dist/entity-manager/regular-relations.mjs +242 -0
- package/dist/entity-manager/regular-relations.mjs.map +1 -0
- package/dist/entity-manager/relations-orderer.js +221 -0
- package/dist/entity-manager/relations-orderer.js.map +1 -0
- package/dist/entity-manager/relations-orderer.mjs +218 -0
- package/dist/entity-manager/relations-orderer.mjs.map +1 -0
- package/dist/errors/database.js +13 -0
- package/dist/errors/database.js.map +1 -0
- package/dist/errors/database.mjs +11 -0
- package/dist/errors/database.mjs.map +1 -0
- package/dist/errors/index.js +18 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/index.mjs +7 -0
- package/dist/errors/index.mjs.map +1 -0
- package/dist/errors/invalid-date.js +13 -0
- package/dist/errors/invalid-date.js.map +1 -0
- package/dist/errors/invalid-date.mjs +11 -0
- package/dist/errors/invalid-date.mjs.map +1 -0
- package/dist/errors/invalid-datetime.js +13 -0
- package/dist/errors/invalid-datetime.js.map +1 -0
- package/dist/errors/invalid-datetime.mjs +11 -0
- package/dist/errors/invalid-datetime.mjs.map +1 -0
- package/dist/errors/invalid-relation.js +13 -0
- package/dist/errors/invalid-relation.js.map +1 -0
- package/dist/errors/invalid-relation.mjs +11 -0
- package/dist/errors/invalid-relation.mjs.map +1 -0
- package/dist/errors/invalid-time.js +13 -0
- package/dist/errors/invalid-time.js.map +1 -0
- package/dist/errors/invalid-time.mjs +11 -0
- package/dist/errors/invalid-time.mjs.map +1 -0
- package/dist/errors/not-null.js +17 -0
- package/dist/errors/not-null.js.map +1 -0
- package/dist/errors/not-null.mjs +15 -0
- package/dist/errors/not-null.mjs.map +1 -0
- package/dist/fields/biginteger.js +9 -0
- package/dist/fields/biginteger.js.map +1 -0
- package/dist/fields/biginteger.mjs +7 -0
- package/dist/fields/biginteger.mjs.map +1 -0
- package/dist/fields/boolean.js +48 -0
- package/dist/fields/boolean.js.map +1 -0
- package/dist/fields/boolean.mjs +46 -0
- package/dist/fields/boolean.mjs.map +1 -0
- package/dist/fields/date.js +16 -0
- package/dist/fields/date.js.map +1 -0
- package/dist/fields/date.mjs +14 -0
- package/dist/fields/date.mjs.map +1 -0
- package/dist/fields/datetime.js +37 -0
- package/dist/fields/datetime.js.map +1 -0
- package/dist/fields/datetime.mjs +16 -0
- package/dist/fields/datetime.mjs.map +1 -0
- package/dist/fields/field.js +16 -0
- package/dist/fields/field.js.map +1 -0
- package/dist/fields/field.mjs +14 -0
- package/dist/fields/field.mjs.map +1 -0
- package/dist/fields/index.js +45 -0
- package/dist/fields/index.js.map +1 -0
- package/dist/fields/index.mjs +43 -0
- package/dist/fields/index.mjs.map +1 -0
- package/dist/fields/json.js +36 -0
- package/dist/fields/json.js.map +1 -0
- package/dist/fields/json.mjs +34 -0
- package/dist/fields/json.mjs.map +1 -0
- package/dist/fields/number.js +20 -0
- package/dist/fields/number.js.map +1 -0
- package/dist/fields/number.mjs +18 -0
- package/dist/fields/number.mjs.map +1 -0
- package/dist/fields/shared/parsers.js +91 -0
- package/dist/fields/shared/parsers.js.map +1 -0
- package/dist/fields/shared/parsers.mjs +68 -0
- package/dist/fields/shared/parsers.mjs.map +1 -0
- package/dist/fields/string.js +16 -0
- package/dist/fields/string.js.map +1 -0
- package/dist/fields/string.mjs +14 -0
- package/dist/fields/string.mjs.map +1 -0
- package/dist/fields/time.js +17 -0
- package/dist/fields/time.js.map +1 -0
- package/dist/fields/time.mjs +15 -0
- package/dist/fields/time.mjs.map +1 -0
- package/dist/fields/timestamp.js +37 -0
- package/dist/fields/timestamp.js.map +1 -0
- package/dist/fields/timestamp.mjs +16 -0
- package/dist/fields/timestamp.mjs.map +1 -0
- package/dist/index.js +33 -8569
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +16 -8532
- package/dist/index.mjs.map +1 -1
- package/dist/lifecycles/index.js +73 -0
- package/dist/lifecycles/index.js.map +1 -0
- package/dist/lifecycles/index.mjs +71 -0
- package/dist/lifecycles/index.mjs.map +1 -0
- package/dist/lifecycles/subscribers/index.js +10 -0
- package/dist/lifecycles/subscribers/index.js.map +1 -0
- package/dist/lifecycles/subscribers/index.mjs +8 -0
- package/dist/lifecycles/subscribers/index.mjs.map +1 -0
- package/dist/lifecycles/subscribers/models-lifecycles.js +13 -0
- package/dist/lifecycles/subscribers/models-lifecycles.js.map +1 -0
- package/dist/lifecycles/subscribers/models-lifecycles.mjs +11 -0
- package/dist/lifecycles/subscribers/models-lifecycles.mjs.map +1 -0
- package/dist/lifecycles/subscribers/timestamps.js +55 -0
- package/dist/lifecycles/subscribers/timestamps.js.map +1 -0
- package/dist/lifecycles/subscribers/timestamps.mjs +53 -0
- package/dist/lifecycles/subscribers/timestamps.mjs.map +1 -0
- package/dist/metadata/index.js +24 -0
- package/dist/metadata/index.js.map +1 -0
- package/dist/metadata/index.mjs +16 -0
- package/dist/metadata/index.mjs.map +1 -0
- package/dist/metadata/metadata.js +100 -0
- package/dist/metadata/metadata.js.map +1 -0
- package/dist/metadata/metadata.mjs +98 -0
- package/dist/metadata/metadata.mjs.map +1 -0
- package/dist/metadata/relations.js +545 -0
- package/dist/metadata/relations.js.map +1 -0
- package/dist/metadata/relations.mjs +536 -0
- package/dist/metadata/relations.mjs.map +1 -0
- package/dist/migrations/common.js +8 -0
- package/dist/migrations/common.js.map +1 -0
- package/dist/migrations/common.mjs +6 -0
- package/dist/migrations/common.mjs.map +1 -0
- package/dist/migrations/index.js +39 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/index.mjs +37 -0
- package/dist/migrations/index.mjs.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.js +179 -0
- package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.js.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.mjs +177 -0
- package/dist/migrations/internal-migrations/5.0.0-01-convert-identifiers-long-than-max-length.mjs.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-02-document-id.js +125 -0
- package/dist/migrations/internal-migrations/5.0.0-02-document-id.js.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-02-document-id.mjs +123 -0
- package/dist/migrations/internal-migrations/5.0.0-02-document-id.mjs.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-03-locale.js +41 -0
- package/dist/migrations/internal-migrations/5.0.0-03-locale.js.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-03-locale.mjs +39 -0
- package/dist/migrations/internal-migrations/5.0.0-03-locale.mjs.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-04-published-at.js +45 -0
- package/dist/migrations/internal-migrations/5.0.0-04-published-at.js.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-04-published-at.mjs +43 -0
- package/dist/migrations/internal-migrations/5.0.0-04-published-at.mjs.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-05-drop-slug-unique-index.js +43 -0
- package/dist/migrations/internal-migrations/5.0.0-05-drop-slug-unique-index.js.map +1 -0
- package/dist/migrations/internal-migrations/5.0.0-05-drop-slug-unique-index.mjs +41 -0
- package/dist/migrations/internal-migrations/5.0.0-05-drop-slug-unique-index.mjs.map +1 -0
- package/dist/migrations/internal-migrations/index.js +26 -0
- package/dist/migrations/internal-migrations/index.js.map +1 -0
- package/dist/migrations/internal-migrations/index.mjs +24 -0
- package/dist/migrations/internal-migrations/index.mjs.map +1 -0
- package/dist/migrations/internal.js +63 -0
- package/dist/migrations/internal.js.map +1 -0
- package/dist/migrations/internal.mjs +61 -0
- package/dist/migrations/internal.mjs.map +1 -0
- package/dist/migrations/logger.js +24 -0
- package/dist/migrations/logger.js.map +1 -0
- package/dist/migrations/logger.mjs +22 -0
- package/dist/migrations/logger.mjs.map +1 -0
- package/dist/migrations/storage.js +39 -0
- package/dist/migrations/storage.js.map +1 -0
- package/dist/migrations/storage.mjs +37 -0
- package/dist/migrations/storage.mjs.map +1 -0
- package/dist/migrations/users.js +87 -0
- package/dist/migrations/users.js.map +1 -0
- package/dist/migrations/users.mjs +85 -0
- package/dist/migrations/users.mjs.map +1 -0
- package/dist/query/helpers/join.js +127 -0
- package/dist/query/helpers/join.js.map +1 -0
- package/dist/query/helpers/join.mjs +122 -0
- package/dist/query/helpers/join.mjs.map +1 -0
- package/dist/query/helpers/order-by.js +167 -0
- package/dist/query/helpers/order-by.js.map +1 -0
- package/dist/query/helpers/order-by.mjs +163 -0
- package/dist/query/helpers/order-by.mjs.map +1 -0
- package/dist/query/helpers/populate/apply.js +592 -0
- package/dist/query/helpers/populate/apply.js.map +1 -0
- package/dist/query/helpers/populate/apply.mjs +590 -0
- package/dist/query/helpers/populate/apply.mjs.map +1 -0
- package/dist/query/helpers/populate/process.js +92 -0
- package/dist/query/helpers/populate/process.js.map +1 -0
- package/dist/query/helpers/populate/process.mjs +90 -0
- package/dist/query/helpers/populate/process.mjs.map +1 -0
- package/dist/query/helpers/search.js +67 -0
- package/dist/query/helpers/search.js.map +1 -0
- package/dist/query/helpers/search.mjs +65 -0
- package/dist/query/helpers/search.mjs.map +1 -0
- package/dist/query/helpers/streams/readable.js +131 -0
- package/dist/query/helpers/streams/readable.js.map +1 -0
- package/dist/query/helpers/streams/readable.mjs +129 -0
- package/dist/query/helpers/streams/readable.mjs.map +1 -0
- package/dist/query/helpers/transform.js +77 -0
- package/dist/query/helpers/transform.js.map +1 -0
- package/dist/query/helpers/transform.mjs +73 -0
- package/dist/query/helpers/transform.mjs.map +1 -0
- package/dist/query/helpers/where.js +372 -0
- package/dist/query/helpers/where.js.map +1 -0
- package/dist/query/helpers/where.mjs +369 -0
- package/dist/query/helpers/where.mjs.map +1 -0
- package/dist/query/query-builder.js +507 -0
- package/dist/query/query-builder.js.map +1 -0
- package/dist/query/query-builder.mjs +505 -0
- package/dist/query/query-builder.mjs.map +1 -0
- package/dist/repairs/index.js +13 -0
- package/dist/repairs/index.js.map +1 -0
- package/dist/repairs/index.mjs +11 -0
- package/dist/repairs/index.mjs.map +1 -0
- package/dist/repairs/operations/remove-orphan-morph-types.js +54 -0
- package/dist/repairs/operations/remove-orphan-morph-types.js.map +1 -0
- package/dist/repairs/operations/remove-orphan-morph-types.mjs +52 -0
- package/dist/repairs/operations/remove-orphan-morph-types.mjs.map +1 -0
- package/dist/schema/builder.js +354 -0
- package/dist/schema/builder.js.map +1 -0
- package/dist/schema/builder.mjs +352 -0
- package/dist/schema/builder.mjs.map +1 -0
- package/dist/schema/diff.js +379 -0
- package/dist/schema/diff.js.map +1 -0
- package/dist/schema/diff.mjs +377 -0
- package/dist/schema/diff.mjs.map +1 -0
- package/dist/schema/index.js +93 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/index.mjs +91 -0
- package/dist/schema/index.mjs.map +1 -0
- package/dist/schema/schema.js +266 -0
- package/dist/schema/schema.js.map +1 -0
- package/dist/schema/schema.mjs +264 -0
- package/dist/schema/schema.mjs.map +1 -0
- package/dist/schema/storage.js +58 -0
- package/dist/schema/storage.js.map +1 -0
- package/dist/schema/storage.mjs +56 -0
- package/dist/schema/storage.mjs.map +1 -0
- package/dist/transaction-context.js +65 -0
- package/dist/transaction-context.js.map +1 -0
- package/dist/transaction-context.mjs +63 -0
- package/dist/transaction-context.mjs.map +1 -0
- package/dist/utils/async-curry.js +19 -0
- package/dist/utils/async-curry.js.map +1 -0
- package/dist/utils/async-curry.mjs +17 -0
- package/dist/utils/async-curry.mjs.map +1 -0
- package/dist/utils/identifiers/hash.js +30 -0
- package/dist/utils/identifiers/hash.js.map +1 -0
- package/dist/utils/identifiers/hash.mjs +28 -0
- package/dist/utils/identifiers/hash.mjs.map +1 -0
- package/dist/utils/identifiers/index.js +414 -0
- package/dist/utils/identifiers/index.js.map +1 -0
- package/dist/utils/identifiers/index.mjs +411 -0
- package/dist/utils/identifiers/index.mjs.map +1 -0
- package/dist/utils/knex.js +21 -0
- package/dist/utils/knex.js.map +1 -0
- package/dist/utils/knex.mjs +18 -0
- package/dist/utils/knex.mjs.map +1 -0
- package/dist/utils/types.js +51 -0
- package/dist/utils/types.js.map +1 -0
- package/dist/utils/types.mjs +44 -0
- package/dist/utils/types.mjs.map +1 -0
- package/dist/validations/index.js +12 -0
- package/dist/validations/index.js.map +1 -0
- package/dist/validations/index.mjs +10 -0
- package/dist/validations/index.mjs.map +1 -0
- package/dist/validations/relations/bidirectional.js +64 -0
- package/dist/validations/relations/bidirectional.js.map +1 -0
- package/dist/validations/relations/bidirectional.mjs +62 -0
- package/dist/validations/relations/bidirectional.mjs.map +1 -0
- package/dist/validations/relations/index.js +13 -0
- package/dist/validations/relations/index.js.map +1 -0
- package/dist/validations/relations/index.mjs +11 -0
- package/dist/validations/relations/index.mjs.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,1184 @@
|
|
|
1
|
+
import { pick, isPlainObject, isArray, isEmpty, has, isNil, uniqBy, isNull, compact, differenceWith, isEqual, difference, map, castArray, isObject, isString, isInteger, isNumber, isUndefined, uniqWith } from 'lodash/fp';
|
|
2
|
+
import { isScalarAttribute, isRelationalAttribute } from '../utils/types.mjs';
|
|
3
|
+
import { createField } from '../fields/index.mjs';
|
|
4
|
+
import createQueryBuilder from '../query/query-builder.mjs';
|
|
5
|
+
import { createRepository } from './entity-repository.mjs';
|
|
6
|
+
import { encodePolymorphicRelation, deleteRelatedMorphOneRelationsAfterMorphToManyUpdate, encodePolymorphicId } from './morph-relations.mjs';
|
|
7
|
+
import { isBidirectional, isOneToAny, hasOrderColumn, hasInverseOrderColumn, isAnyToOne } from '../metadata/relations.mjs';
|
|
8
|
+
import '../utils/identifiers/index.mjs';
|
|
9
|
+
import { deletePreviousOneToAnyRelations, deleteRelations, cleanOrderColumns, deletePreviousAnyToOneRelations } from './regular-relations.mjs';
|
|
10
|
+
import { relationsOrderer } from './relations-orderer.mjs';
|
|
11
|
+
|
|
12
|
+
const isRecord = (value)=>isObject(value) && !isNil(value);
|
|
13
|
+
const toId = (value)=>{
|
|
14
|
+
if (isRecord(value) && 'id' in value && isValidId(value.id)) {
|
|
15
|
+
return value.id;
|
|
16
|
+
}
|
|
17
|
+
if (isValidId(value)) {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`Invalid id, expected a string or integer, got ${JSON.stringify(value)}`);
|
|
21
|
+
};
|
|
22
|
+
const toIds = (value)=>castArray(value || []).map(toId);
|
|
23
|
+
const isValidId = (value)=>isString(value) || isInteger(value);
|
|
24
|
+
const isValidObjectId = (value)=>isRecord(value) && 'id' in value && isValidId(value.id);
|
|
25
|
+
const toIdArray = (data)=>{
|
|
26
|
+
const array = castArray(data).filter((datum)=>!isNil(datum)).map((datum)=>{
|
|
27
|
+
// if it is a string or an integer return an obj with id = to datum
|
|
28
|
+
if (isValidId(datum)) {
|
|
29
|
+
return {
|
|
30
|
+
id: datum,
|
|
31
|
+
__pivot: {}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// if it is an object check it has at least a valid id
|
|
35
|
+
if (!isValidObjectId(datum)) {
|
|
36
|
+
throw new Error(`Invalid id, expected a string or integer, got ${datum}`);
|
|
37
|
+
}
|
|
38
|
+
return datum;
|
|
39
|
+
});
|
|
40
|
+
return uniqWith(isEqual, array);
|
|
41
|
+
};
|
|
42
|
+
const toAssocs = (data)=>{
|
|
43
|
+
if (isArray(data) || isString(data) || isNumber(data) || isNull(data) || isRecord(data) && 'id' in data) {
|
|
44
|
+
return {
|
|
45
|
+
set: isNull(data) ? data : toIdArray(data)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (data?.set) {
|
|
49
|
+
return {
|
|
50
|
+
set: isNull(data.set) ? data.set : toIdArray(data.set)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
options: {
|
|
55
|
+
strict: data?.options?.strict
|
|
56
|
+
},
|
|
57
|
+
connect: toIdArray(data?.connect).map((elm)=>({
|
|
58
|
+
id: elm.id,
|
|
59
|
+
position: elm.position ? elm.position : {
|
|
60
|
+
end: true
|
|
61
|
+
},
|
|
62
|
+
__pivot: elm.__pivot ?? {},
|
|
63
|
+
__type: elm.__type
|
|
64
|
+
})),
|
|
65
|
+
disconnect: toIdArray(data?.disconnect)
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
const processData = (metadata, data = {}, { withDefaults = false } = {})=>{
|
|
69
|
+
const { attributes } = metadata;
|
|
70
|
+
const obj = {};
|
|
71
|
+
for (const attributeName of Object.keys(attributes)){
|
|
72
|
+
const attribute = attributes[attributeName];
|
|
73
|
+
if (isScalarAttribute(attribute)) {
|
|
74
|
+
const field = createField(attribute);
|
|
75
|
+
if (isUndefined(data[attributeName])) {
|
|
76
|
+
if (!isUndefined(attribute.default) && withDefaults) {
|
|
77
|
+
if (typeof attribute.default === 'function') {
|
|
78
|
+
obj[attributeName] = attribute.default();
|
|
79
|
+
} else {
|
|
80
|
+
obj[attributeName] = attribute.default;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if ('validate' in field && typeof field.validate === 'function' && data[attributeName] !== null) {
|
|
86
|
+
field.validate(data[attributeName]);
|
|
87
|
+
}
|
|
88
|
+
const val = data[attributeName] === null ? null : field.toDB(data[attributeName]);
|
|
89
|
+
obj[attributeName] = val;
|
|
90
|
+
}
|
|
91
|
+
if (isRelationalAttribute(attribute)) {
|
|
92
|
+
// oneToOne & manyToOne
|
|
93
|
+
if ('joinColumn' in attribute && attribute.joinColumn && attribute.owner) {
|
|
94
|
+
const joinColumnName = attribute.joinColumn.name;
|
|
95
|
+
// allow setting to null
|
|
96
|
+
const attrValue = !isUndefined(data[attributeName]) ? data[attributeName] : data[joinColumnName];
|
|
97
|
+
if (isNull(attrValue)) {
|
|
98
|
+
obj[joinColumnName] = attrValue;
|
|
99
|
+
} else if (!isUndefined(attrValue)) {
|
|
100
|
+
obj[joinColumnName] = toId(attrValue);
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if ('morphColumn' in attribute && attribute.morphColumn && attribute.owner) {
|
|
105
|
+
const { idColumn, typeColumn, typeField = '__type' } = attribute.morphColumn;
|
|
106
|
+
const value = data[attributeName];
|
|
107
|
+
if (value === null) {
|
|
108
|
+
Object.assign(obj, {
|
|
109
|
+
[idColumn.name]: null,
|
|
110
|
+
[typeColumn.name]: null
|
|
111
|
+
});
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (!isUndefined(value)) {
|
|
115
|
+
if (!has('id', value) || !has(typeField, value)) {
|
|
116
|
+
throw new Error(`Expects properties ${typeField} an id to make a morph association`);
|
|
117
|
+
}
|
|
118
|
+
Object.assign(obj, {
|
|
119
|
+
[idColumn.name]: value.id,
|
|
120
|
+
[typeColumn.name]: value[typeField]
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return obj;
|
|
127
|
+
};
|
|
128
|
+
const createEntityManager = (db)=>{
|
|
129
|
+
const repoMap = {};
|
|
130
|
+
return {
|
|
131
|
+
async findOne (uid, params) {
|
|
132
|
+
const states = await db.lifecycles.run('beforeFindOne', uid, {
|
|
133
|
+
params
|
|
134
|
+
});
|
|
135
|
+
const result = await this.createQueryBuilder(uid).init(params).first().execute();
|
|
136
|
+
await db.lifecycles.run('afterFindOne', uid, {
|
|
137
|
+
params,
|
|
138
|
+
result
|
|
139
|
+
}, states);
|
|
140
|
+
return result;
|
|
141
|
+
},
|
|
142
|
+
// should we name it findOne because people are used to it ?
|
|
143
|
+
async findMany (uid, params) {
|
|
144
|
+
const states = await db.lifecycles.run('beforeFindMany', uid, {
|
|
145
|
+
params
|
|
146
|
+
});
|
|
147
|
+
const result = await this.createQueryBuilder(uid).init(params).execute();
|
|
148
|
+
await db.lifecycles.run('afterFindMany', uid, {
|
|
149
|
+
params,
|
|
150
|
+
result
|
|
151
|
+
}, states);
|
|
152
|
+
return result;
|
|
153
|
+
},
|
|
154
|
+
async count (uid, params = {}) {
|
|
155
|
+
const states = await db.lifecycles.run('beforeCount', uid, {
|
|
156
|
+
params
|
|
157
|
+
});
|
|
158
|
+
const res = await this.createQueryBuilder(uid).init(pick([
|
|
159
|
+
'_q',
|
|
160
|
+
'where',
|
|
161
|
+
'filters'
|
|
162
|
+
], params)).count().first().execute();
|
|
163
|
+
const result = Number(res.count);
|
|
164
|
+
await db.lifecycles.run('afterCount', uid, {
|
|
165
|
+
params,
|
|
166
|
+
result
|
|
167
|
+
}, states);
|
|
168
|
+
return result;
|
|
169
|
+
},
|
|
170
|
+
async create (uid, params = {}) {
|
|
171
|
+
const states = await db.lifecycles.run('beforeCreate', uid, {
|
|
172
|
+
params
|
|
173
|
+
});
|
|
174
|
+
const metadata = db.metadata.get(uid);
|
|
175
|
+
const { data } = params;
|
|
176
|
+
if (!isPlainObject(data)) {
|
|
177
|
+
throw new Error('Create expects a data object');
|
|
178
|
+
}
|
|
179
|
+
const dataToInsert = processData(metadata, data, {
|
|
180
|
+
withDefaults: true
|
|
181
|
+
});
|
|
182
|
+
const res = await this.createQueryBuilder(uid).insert(dataToInsert).execute();
|
|
183
|
+
const id = isRecord(res[0]) ? res[0].id : res[0];
|
|
184
|
+
const trx = await strapi.db.transaction();
|
|
185
|
+
try {
|
|
186
|
+
await this.attachRelations(uid, id, data, {
|
|
187
|
+
transaction: trx.get()
|
|
188
|
+
});
|
|
189
|
+
await trx.commit();
|
|
190
|
+
} catch (e) {
|
|
191
|
+
await trx.rollback();
|
|
192
|
+
await this.createQueryBuilder(uid).where({
|
|
193
|
+
id
|
|
194
|
+
}).delete().execute();
|
|
195
|
+
throw e;
|
|
196
|
+
}
|
|
197
|
+
// TODO: in case there is no select or populate specified return the inserted data ?
|
|
198
|
+
// TODO: do not trigger the findOne lifecycles ?
|
|
199
|
+
const result = await this.findOne(uid, {
|
|
200
|
+
where: {
|
|
201
|
+
id
|
|
202
|
+
},
|
|
203
|
+
select: params.select,
|
|
204
|
+
populate: params.populate,
|
|
205
|
+
filters: params.filters
|
|
206
|
+
});
|
|
207
|
+
await db.lifecycles.run('afterCreate', uid, {
|
|
208
|
+
params,
|
|
209
|
+
result
|
|
210
|
+
}, states);
|
|
211
|
+
return result;
|
|
212
|
+
},
|
|
213
|
+
// TODO: where do we handle relation processing for many queries ?
|
|
214
|
+
async createMany (uid, params = {}) {
|
|
215
|
+
const states = await db.lifecycles.run('beforeCreateMany', uid, {
|
|
216
|
+
params
|
|
217
|
+
});
|
|
218
|
+
const metadata = db.metadata.get(uid);
|
|
219
|
+
const { data } = params;
|
|
220
|
+
if (!isArray(data)) {
|
|
221
|
+
throw new Error('CreateMany expects data to be an array');
|
|
222
|
+
}
|
|
223
|
+
const dataToInsert = data.map((datum)=>processData(metadata, datum, {
|
|
224
|
+
withDefaults: true
|
|
225
|
+
}));
|
|
226
|
+
if (isEmpty(dataToInsert)) {
|
|
227
|
+
throw new Error('Nothing to insert');
|
|
228
|
+
}
|
|
229
|
+
const createdEntries = await this.createQueryBuilder(uid).insert(dataToInsert).execute();
|
|
230
|
+
const result = {
|
|
231
|
+
count: data.length,
|
|
232
|
+
ids: createdEntries.map((entry)=>typeof entry === 'object' ? entry?.id : entry)
|
|
233
|
+
};
|
|
234
|
+
await db.lifecycles.run('afterCreateMany', uid, {
|
|
235
|
+
params,
|
|
236
|
+
result
|
|
237
|
+
}, states);
|
|
238
|
+
return result;
|
|
239
|
+
},
|
|
240
|
+
async update (uid, params = {}) {
|
|
241
|
+
const states = await db.lifecycles.run('beforeUpdate', uid, {
|
|
242
|
+
params
|
|
243
|
+
});
|
|
244
|
+
const metadata = db.metadata.get(uid);
|
|
245
|
+
const { where, data } = params;
|
|
246
|
+
if (!isPlainObject(data)) {
|
|
247
|
+
throw new Error('Update requires a data object');
|
|
248
|
+
}
|
|
249
|
+
if (isEmpty(where)) {
|
|
250
|
+
throw new Error('Update requires a where parameter');
|
|
251
|
+
}
|
|
252
|
+
const entity = await this.createQueryBuilder(uid).select('*').where(where).first().execute({
|
|
253
|
+
mapResults: false
|
|
254
|
+
});
|
|
255
|
+
if (!entity) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const { id } = entity;
|
|
259
|
+
const dataToUpdate = processData(metadata, data);
|
|
260
|
+
if (!isEmpty(dataToUpdate)) {
|
|
261
|
+
await this.createQueryBuilder(uid).where({
|
|
262
|
+
id
|
|
263
|
+
}).update(dataToUpdate).execute();
|
|
264
|
+
}
|
|
265
|
+
const trx = await strapi.db.transaction();
|
|
266
|
+
try {
|
|
267
|
+
await this.updateRelations(uid, id, data, {
|
|
268
|
+
transaction: trx.get()
|
|
269
|
+
});
|
|
270
|
+
await trx.commit();
|
|
271
|
+
} catch (e) {
|
|
272
|
+
await trx.rollback();
|
|
273
|
+
await this.createQueryBuilder(uid).where({
|
|
274
|
+
id
|
|
275
|
+
}).update(entity).execute();
|
|
276
|
+
throw e;
|
|
277
|
+
}
|
|
278
|
+
// TODO: do not trigger the findOne lifecycles ?
|
|
279
|
+
const result = await this.findOne(uid, {
|
|
280
|
+
where: {
|
|
281
|
+
id
|
|
282
|
+
},
|
|
283
|
+
select: params.select,
|
|
284
|
+
populate: params.populate,
|
|
285
|
+
filters: params.filters
|
|
286
|
+
});
|
|
287
|
+
await db.lifecycles.run('afterUpdate', uid, {
|
|
288
|
+
params,
|
|
289
|
+
result
|
|
290
|
+
}, states);
|
|
291
|
+
return result;
|
|
292
|
+
},
|
|
293
|
+
// TODO: where do we handle relation processing for many queries ?
|
|
294
|
+
async updateMany (uid, params = {}) {
|
|
295
|
+
const states = await db.lifecycles.run('beforeUpdateMany', uid, {
|
|
296
|
+
params
|
|
297
|
+
});
|
|
298
|
+
const metadata = db.metadata.get(uid);
|
|
299
|
+
const { where, data } = params;
|
|
300
|
+
const dataToUpdate = processData(metadata, data);
|
|
301
|
+
if (isEmpty(dataToUpdate)) {
|
|
302
|
+
throw new Error('Update requires data');
|
|
303
|
+
}
|
|
304
|
+
const updatedRows = await this.createQueryBuilder(uid).where(where).update(dataToUpdate).execute();
|
|
305
|
+
const result = {
|
|
306
|
+
count: updatedRows
|
|
307
|
+
};
|
|
308
|
+
await db.lifecycles.run('afterUpdateMany', uid, {
|
|
309
|
+
params,
|
|
310
|
+
result
|
|
311
|
+
}, states);
|
|
312
|
+
return result;
|
|
313
|
+
},
|
|
314
|
+
async delete (uid, params = {}) {
|
|
315
|
+
const states = await db.lifecycles.run('beforeDelete', uid, {
|
|
316
|
+
params
|
|
317
|
+
});
|
|
318
|
+
const { where, select, populate } = params;
|
|
319
|
+
if (isEmpty(where)) {
|
|
320
|
+
throw new Error('Delete requires a where parameter');
|
|
321
|
+
}
|
|
322
|
+
// TODO: do not trigger the findOne lifecycles ?
|
|
323
|
+
const entity = await this.findOne(uid, {
|
|
324
|
+
select: select && [
|
|
325
|
+
'id'
|
|
326
|
+
].concat(select),
|
|
327
|
+
where,
|
|
328
|
+
populate
|
|
329
|
+
});
|
|
330
|
+
if (!entity) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
const { id } = entity;
|
|
334
|
+
await this.createQueryBuilder(uid).where({
|
|
335
|
+
id
|
|
336
|
+
}).delete().execute();
|
|
337
|
+
const trx = await strapi.db.transaction();
|
|
338
|
+
try {
|
|
339
|
+
await this.deleteRelations(uid, id, {
|
|
340
|
+
transaction: trx.get()
|
|
341
|
+
});
|
|
342
|
+
await trx.commit();
|
|
343
|
+
} catch (e) {
|
|
344
|
+
await trx.rollback();
|
|
345
|
+
throw e;
|
|
346
|
+
}
|
|
347
|
+
await db.lifecycles.run('afterDelete', uid, {
|
|
348
|
+
params,
|
|
349
|
+
result: entity
|
|
350
|
+
}, states);
|
|
351
|
+
return entity;
|
|
352
|
+
},
|
|
353
|
+
// TODO: where do we handle relation processing for many queries ?
|
|
354
|
+
async deleteMany (uid, params = {}) {
|
|
355
|
+
const states = await db.lifecycles.run('beforeDeleteMany', uid, {
|
|
356
|
+
params
|
|
357
|
+
});
|
|
358
|
+
const { where } = params;
|
|
359
|
+
const deletedRows = await this.createQueryBuilder(uid).where(where).delete().execute({
|
|
360
|
+
mapResults: false
|
|
361
|
+
});
|
|
362
|
+
const result = {
|
|
363
|
+
count: deletedRows
|
|
364
|
+
};
|
|
365
|
+
await db.lifecycles.run('afterDeleteMany', uid, {
|
|
366
|
+
params,
|
|
367
|
+
result
|
|
368
|
+
}, states);
|
|
369
|
+
return result;
|
|
370
|
+
},
|
|
371
|
+
/**
|
|
372
|
+
* Attach relations to a new entity
|
|
373
|
+
*/ async attachRelations (uid, id, data, options) {
|
|
374
|
+
const { attributes } = db.metadata.get(uid);
|
|
375
|
+
const { transaction: trx } = options ?? {};
|
|
376
|
+
for (const attributeName of Object.keys(attributes)){
|
|
377
|
+
const attribute = attributes[attributeName];
|
|
378
|
+
const isValidLink = has(attributeName, data) && !isNil(data[attributeName]);
|
|
379
|
+
if (attribute.type !== 'relation' || !isValidLink) {
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
const cleanRelationData = toAssocs(data[attributeName]);
|
|
383
|
+
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
384
|
+
/**
|
|
385
|
+
* morphOne and morphMany relations
|
|
386
|
+
*/ const { target, morphBy } = attribute;
|
|
387
|
+
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
|
388
|
+
if (targetAttribute.type !== 'relation') {
|
|
389
|
+
throw new Error(`Expected target attribute ${target}.${morphBy} to be a relation attribute`);
|
|
390
|
+
}
|
|
391
|
+
if (targetAttribute.relation === 'morphToOne') {
|
|
392
|
+
// set columns
|
|
393
|
+
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
394
|
+
const relId = toId(cleanRelationData.set?.[0]);
|
|
395
|
+
await this.createQueryBuilder(target).update({
|
|
396
|
+
[idColumn.name]: id,
|
|
397
|
+
[typeColumn.name]: uid
|
|
398
|
+
}).where({
|
|
399
|
+
id: relId
|
|
400
|
+
}).transacting(trx).execute();
|
|
401
|
+
} else if (targetAttribute.relation === 'morphToMany') {
|
|
402
|
+
const { joinTable } = targetAttribute;
|
|
403
|
+
const { joinColumn, morphColumn } = joinTable;
|
|
404
|
+
const { idColumn, typeColumn } = morphColumn;
|
|
405
|
+
if (isEmpty(cleanRelationData.set)) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
const rows = cleanRelationData.set?.map((data, idx)=>{
|
|
409
|
+
return {
|
|
410
|
+
[joinColumn.name]: data.id,
|
|
411
|
+
[idColumn.name]: id,
|
|
412
|
+
[typeColumn.name]: uid,
|
|
413
|
+
...'on' in joinTable && joinTable.on || {},
|
|
414
|
+
...data.__pivot || {},
|
|
415
|
+
order: idx + 1,
|
|
416
|
+
field: attributeName
|
|
417
|
+
};
|
|
418
|
+
}) ?? [];
|
|
419
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
420
|
+
}
|
|
421
|
+
continue;
|
|
422
|
+
} else if (attribute.relation === 'morphToOne') {
|
|
423
|
+
continue;
|
|
424
|
+
} else if (attribute.relation === 'morphToMany') {
|
|
425
|
+
/**
|
|
426
|
+
* morphToMany
|
|
427
|
+
*/ const { joinTable } = attribute;
|
|
428
|
+
const { joinColumn, morphColumn } = joinTable;
|
|
429
|
+
const { idColumn, typeColumn, typeField = '__type' } = morphColumn;
|
|
430
|
+
if (isEmpty(cleanRelationData.set) && isEmpty(cleanRelationData.connect)) {
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
// set happens before connect/disconnect
|
|
434
|
+
const dataset = cleanRelationData.set || cleanRelationData.connect || [];
|
|
435
|
+
const rows = dataset.map((data, idx)=>({
|
|
436
|
+
[joinColumn.name]: id,
|
|
437
|
+
[idColumn.name]: data.id,
|
|
438
|
+
[typeColumn.name]: data[typeField],
|
|
439
|
+
...'on' in joinTable && joinTable.on || {},
|
|
440
|
+
...data.__pivot || {},
|
|
441
|
+
order: idx + 1
|
|
442
|
+
}));
|
|
443
|
+
const orderMap = relationsOrderer([], morphColumn.idColumn.name, 'order', true // Always make a strict connect when inserting
|
|
444
|
+
).connect(// Merge id & __type to get a single id key
|
|
445
|
+
dataset.map(encodePolymorphicRelation({
|
|
446
|
+
idColumn: 'id',
|
|
447
|
+
typeColumn: typeField
|
|
448
|
+
}))).get()// set the order based on the order of the ids
|
|
449
|
+
.reduce((acc, rel, idx)=>({
|
|
450
|
+
...acc,
|
|
451
|
+
[rel.id]: idx + 1
|
|
452
|
+
}), {});
|
|
453
|
+
rows.forEach((row)=>{
|
|
454
|
+
const rowId = row[morphColumn.idColumn.name];
|
|
455
|
+
const rowType = row[morphColumn.typeColumn.name];
|
|
456
|
+
const encodedId = encodePolymorphicId(rowId, rowType);
|
|
457
|
+
row.order = orderMap[encodedId];
|
|
458
|
+
});
|
|
459
|
+
// delete previous relations
|
|
460
|
+
await deleteRelatedMorphOneRelationsAfterMorphToManyUpdate(rows, {
|
|
461
|
+
uid,
|
|
462
|
+
attributeName,
|
|
463
|
+
joinTable,
|
|
464
|
+
db,
|
|
465
|
+
transaction: trx
|
|
466
|
+
});
|
|
467
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
if ('joinColumn' in attribute && attribute.joinColumn && attribute.owner) {
|
|
471
|
+
const relIdsToAdd = toIds(cleanRelationData.set);
|
|
472
|
+
if (attribute.relation === 'oneToOne' && isBidirectional(attribute) && relIdsToAdd.length) {
|
|
473
|
+
await this.createQueryBuilder(uid).where({
|
|
474
|
+
[attribute.joinColumn.name]: relIdsToAdd,
|
|
475
|
+
id: {
|
|
476
|
+
$ne: id
|
|
477
|
+
}
|
|
478
|
+
}).update({
|
|
479
|
+
[attribute.joinColumn.name]: null
|
|
480
|
+
}).transacting(trx).execute();
|
|
481
|
+
}
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
// oneToOne oneToMany on the non owning side
|
|
485
|
+
if ('joinColumn' in attribute && attribute.joinColumn && !attribute.owner) {
|
|
486
|
+
// need to set the column on the target
|
|
487
|
+
const { target } = attribute;
|
|
488
|
+
// TODO: check it is an id & the entity exists (will throw due to FKs otherwise so not a big pbl in SQL)
|
|
489
|
+
const relIdsToAdd = toIds(cleanRelationData.set);
|
|
490
|
+
await this.createQueryBuilder(target).where({
|
|
491
|
+
[attribute.joinColumn.referencedColumn]: id
|
|
492
|
+
}).update({
|
|
493
|
+
[attribute.joinColumn.referencedColumn]: null
|
|
494
|
+
}).transacting(trx).execute();
|
|
495
|
+
await this.createQueryBuilder(target).update({
|
|
496
|
+
[attribute.joinColumn.referencedColumn]: id
|
|
497
|
+
})// NOTE: works if it is an array or a single id
|
|
498
|
+
.where({
|
|
499
|
+
id: relIdsToAdd
|
|
500
|
+
}).transacting(trx).execute();
|
|
501
|
+
}
|
|
502
|
+
if ('joinTable' in attribute && attribute.joinTable) {
|
|
503
|
+
// need to set the column on the target
|
|
504
|
+
const { joinTable } = attribute;
|
|
505
|
+
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable;
|
|
506
|
+
const relsToAdd = (cleanRelationData.set || cleanRelationData.connect) ?? [];
|
|
507
|
+
const relIdsToadd = toIds(relsToAdd);
|
|
508
|
+
if (isBidirectional(attribute) && isOneToAny(attribute)) {
|
|
509
|
+
await deletePreviousOneToAnyRelations({
|
|
510
|
+
id,
|
|
511
|
+
attribute,
|
|
512
|
+
relIdsToadd,
|
|
513
|
+
db,
|
|
514
|
+
transaction: trx
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
// prepare new relations to insert
|
|
518
|
+
const insert = uniqBy('id', relsToAdd).map((data)=>{
|
|
519
|
+
return {
|
|
520
|
+
[joinColumn.name]: id,
|
|
521
|
+
[inverseJoinColumn.name]: data.id,
|
|
522
|
+
...'on' in joinTable && joinTable.on || {},
|
|
523
|
+
...data.__pivot || {}
|
|
524
|
+
};
|
|
525
|
+
});
|
|
526
|
+
// add order value
|
|
527
|
+
if (cleanRelationData.set && hasOrderColumn(attribute)) {
|
|
528
|
+
insert.forEach((data, idx)=>{
|
|
529
|
+
data[orderColumnName] = idx + 1;
|
|
530
|
+
});
|
|
531
|
+
} else if (cleanRelationData.connect && hasOrderColumn(attribute)) {
|
|
532
|
+
// use position attributes to calculate order
|
|
533
|
+
const orderMap = relationsOrderer([], inverseJoinColumn.name, joinTable.orderColumnName, true // Always make an strict connect when inserting
|
|
534
|
+
).connect(relsToAdd).get()// set the order based on the order of the ids
|
|
535
|
+
.reduce((acc, rel, idx)=>({
|
|
536
|
+
...acc,
|
|
537
|
+
[rel.id]: idx
|
|
538
|
+
}), {});
|
|
539
|
+
insert.forEach((row)=>{
|
|
540
|
+
row[orderColumnName] = orderMap[row[inverseJoinColumn.name]];
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
// add inv_order value
|
|
544
|
+
if (hasInverseOrderColumn(attribute)) {
|
|
545
|
+
const maxResults = await db.getConnection().select(inverseJoinColumn.name).max(inverseOrderColumnName, {
|
|
546
|
+
as: 'max'
|
|
547
|
+
}).whereIn(inverseJoinColumn.name, relIdsToadd).where(joinTable.on || {}).groupBy(inverseJoinColumn.name).from(joinTable.name).transacting(trx);
|
|
548
|
+
const maxMap = maxResults.reduce((acc, res)=>Object.assign(acc, {
|
|
549
|
+
[res[inverseJoinColumn.name]]: res.max
|
|
550
|
+
}), {});
|
|
551
|
+
insert.forEach((rel)=>{
|
|
552
|
+
rel[inverseOrderColumnName] = (maxMap[rel[inverseJoinColumn.name]] || 0) + 1;
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
if (insert.length === 0) {
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
// insert new relations
|
|
559
|
+
await this.createQueryBuilder(joinTable.name).insert(insert).transacting(trx).execute();
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
/**
|
|
564
|
+
* Updates relations of an existing entity
|
|
565
|
+
*/ // TODO: check relation exists (handled by FKs except for polymorphics)
|
|
566
|
+
async updateRelations (uid, id, data, options) {
|
|
567
|
+
const { attributes } = db.metadata.get(uid);
|
|
568
|
+
const { transaction: trx } = options ?? {};
|
|
569
|
+
for (const attributeName of Object.keys(attributes)){
|
|
570
|
+
const attribute = attributes[attributeName];
|
|
571
|
+
if (attribute.type !== 'relation' || !has(attributeName, data)) {
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
const cleanRelationData = toAssocs(data[attributeName]);
|
|
575
|
+
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
576
|
+
const { target, morphBy } = attribute;
|
|
577
|
+
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
|
578
|
+
if (targetAttribute.type === 'relation' && targetAttribute.relation === 'morphToOne') {
|
|
579
|
+
// set columns
|
|
580
|
+
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
581
|
+
// update instead of deleting because the relation is directly on the entity table
|
|
582
|
+
// and not in a join table
|
|
583
|
+
await this.createQueryBuilder(target).update({
|
|
584
|
+
[idColumn.name]: null,
|
|
585
|
+
[typeColumn.name]: null
|
|
586
|
+
}).where({
|
|
587
|
+
[idColumn.name]: id,
|
|
588
|
+
[typeColumn.name]: uid
|
|
589
|
+
}).transacting(trx).execute();
|
|
590
|
+
if (!isNull(cleanRelationData.set)) {
|
|
591
|
+
const relId = toIds(cleanRelationData.set?.[0]);
|
|
592
|
+
await this.createQueryBuilder(target).update({
|
|
593
|
+
[idColumn.name]: id,
|
|
594
|
+
[typeColumn.name]: uid
|
|
595
|
+
}).where({
|
|
596
|
+
id: relId
|
|
597
|
+
}).transacting(trx).execute();
|
|
598
|
+
}
|
|
599
|
+
} else if (targetAttribute.type === 'relation' && targetAttribute.relation === 'morphToMany') {
|
|
600
|
+
const { joinTable } = targetAttribute;
|
|
601
|
+
const { joinColumn, morphColumn } = joinTable;
|
|
602
|
+
const { idColumn, typeColumn } = morphColumn;
|
|
603
|
+
const hasSet = !isEmpty(cleanRelationData.set);
|
|
604
|
+
const hasConnect = !isEmpty(cleanRelationData.connect);
|
|
605
|
+
const hasDisconnect = !isEmpty(cleanRelationData.disconnect);
|
|
606
|
+
// for connect/disconnect without a set, only modify those relations
|
|
607
|
+
if (!hasSet && (hasConnect || hasDisconnect)) {
|
|
608
|
+
// delete disconnects and connects (to prevent duplicates when we add them later)
|
|
609
|
+
const idsToDelete = [
|
|
610
|
+
...cleanRelationData.disconnect || [],
|
|
611
|
+
...cleanRelationData.connect || []
|
|
612
|
+
];
|
|
613
|
+
if (!isEmpty(idsToDelete)) {
|
|
614
|
+
const where = {
|
|
615
|
+
$or: idsToDelete.map((item)=>{
|
|
616
|
+
return {
|
|
617
|
+
[idColumn.name]: id,
|
|
618
|
+
[typeColumn.name]: uid,
|
|
619
|
+
[joinColumn.name]: item.id,
|
|
620
|
+
...joinTable.on || {},
|
|
621
|
+
field: attributeName
|
|
622
|
+
};
|
|
623
|
+
})
|
|
624
|
+
};
|
|
625
|
+
await this.createQueryBuilder(joinTable.name).delete().where(where).transacting(trx).execute();
|
|
626
|
+
}
|
|
627
|
+
// connect relations
|
|
628
|
+
if (hasConnect) {
|
|
629
|
+
// Query database to find the order of the last relation
|
|
630
|
+
const start = await this.createQueryBuilder(joinTable.name).where({
|
|
631
|
+
[idColumn.name]: id,
|
|
632
|
+
[typeColumn.name]: uid,
|
|
633
|
+
...joinTable.on || {},
|
|
634
|
+
...data.__pivot || {}
|
|
635
|
+
}).max('order').first().transacting(trx).execute();
|
|
636
|
+
const startOrder = start?.max || 0;
|
|
637
|
+
const rows = (cleanRelationData.connect ?? []).map((data, idx)=>({
|
|
638
|
+
[joinColumn.name]: data.id,
|
|
639
|
+
[idColumn.name]: id,
|
|
640
|
+
[typeColumn.name]: uid,
|
|
641
|
+
...joinTable.on || {},
|
|
642
|
+
...data.__pivot || {},
|
|
643
|
+
order: startOrder + idx + 1,
|
|
644
|
+
field: attributeName
|
|
645
|
+
}));
|
|
646
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
647
|
+
}
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
// delete all relations
|
|
651
|
+
await this.createQueryBuilder(joinTable.name).delete().where({
|
|
652
|
+
[idColumn.name]: id,
|
|
653
|
+
[typeColumn.name]: uid,
|
|
654
|
+
...joinTable.on || {},
|
|
655
|
+
field: attributeName
|
|
656
|
+
}).transacting(trx).execute();
|
|
657
|
+
if (hasSet) {
|
|
658
|
+
const rows = (cleanRelationData.set ?? []).map((data, idx)=>({
|
|
659
|
+
[joinColumn.name]: data.id,
|
|
660
|
+
[idColumn.name]: id,
|
|
661
|
+
[typeColumn.name]: uid,
|
|
662
|
+
...joinTable.on || {},
|
|
663
|
+
...data.__pivot || {},
|
|
664
|
+
order: idx + 1,
|
|
665
|
+
field: attributeName
|
|
666
|
+
}));
|
|
667
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if (attribute.relation === 'morphToOne') {
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
if (attribute.relation === 'morphToMany') {
|
|
676
|
+
const { joinTable } = attribute;
|
|
677
|
+
const { joinColumn, morphColumn } = joinTable;
|
|
678
|
+
const { idColumn, typeColumn, typeField = '__type' } = morphColumn;
|
|
679
|
+
const hasSet = !isEmpty(cleanRelationData.set);
|
|
680
|
+
const hasConnect = !isEmpty(cleanRelationData.connect);
|
|
681
|
+
const hasDisconnect = !isEmpty(cleanRelationData.disconnect);
|
|
682
|
+
// for connect/disconnect without a set, only modify those relations
|
|
683
|
+
if (!hasSet && (hasConnect || hasDisconnect)) {
|
|
684
|
+
// delete disconnects and connects (to prevent duplicates when we add them later)
|
|
685
|
+
const idsToDelete = [
|
|
686
|
+
...cleanRelationData.disconnect || [],
|
|
687
|
+
...cleanRelationData.connect || []
|
|
688
|
+
];
|
|
689
|
+
const rowsToDelete = [
|
|
690
|
+
...(cleanRelationData.disconnect ?? []).map((data, idx)=>({
|
|
691
|
+
[joinColumn.name]: id,
|
|
692
|
+
[idColumn.name]: data.id,
|
|
693
|
+
[typeColumn.name]: data[typeField],
|
|
694
|
+
...'on' in joinTable && joinTable.on || {},
|
|
695
|
+
...data.__pivot || {},
|
|
696
|
+
order: idx + 1
|
|
697
|
+
})),
|
|
698
|
+
...(cleanRelationData.connect ?? []).map((data, idx)=>({
|
|
699
|
+
[joinColumn.name]: id,
|
|
700
|
+
[idColumn.name]: data.id,
|
|
701
|
+
// @ts-expect-error TODO
|
|
702
|
+
[typeColumn.name]: data[typeField],
|
|
703
|
+
...'on' in joinTable && joinTable.on || {},
|
|
704
|
+
...data.__pivot || {},
|
|
705
|
+
order: idx + 1
|
|
706
|
+
}))
|
|
707
|
+
];
|
|
708
|
+
const adjacentRelations = await this.createQueryBuilder(joinTable.name).where({
|
|
709
|
+
$or: [
|
|
710
|
+
{
|
|
711
|
+
[joinColumn.name]: id,
|
|
712
|
+
[idColumn.name]: {
|
|
713
|
+
$in: compact(cleanRelationData.connect?.map((r)=>r.position?.after || r.position?.before))
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
[joinColumn.name]: id,
|
|
718
|
+
order: this.createQueryBuilder(joinTable.name).max('order').where({
|
|
719
|
+
[joinColumn.name]: id
|
|
720
|
+
}).where(joinTable.on || {}).transacting(trx).getKnexQuery()
|
|
721
|
+
}
|
|
722
|
+
]
|
|
723
|
+
}).where(joinTable.on || {}).transacting(trx).execute();
|
|
724
|
+
if (!isEmpty(idsToDelete)) {
|
|
725
|
+
const where = {
|
|
726
|
+
$or: idsToDelete.map((item)=>{
|
|
727
|
+
return {
|
|
728
|
+
[idColumn.name]: item.id,
|
|
729
|
+
[typeColumn.name]: item[typeField],
|
|
730
|
+
[joinColumn.name]: id,
|
|
731
|
+
...joinTable.on || {}
|
|
732
|
+
};
|
|
733
|
+
})
|
|
734
|
+
};
|
|
735
|
+
// delete previous relations
|
|
736
|
+
await this.createQueryBuilder(joinTable.name).delete().where(where).transacting(trx).execute();
|
|
737
|
+
await deleteRelatedMorphOneRelationsAfterMorphToManyUpdate(rowsToDelete, {
|
|
738
|
+
uid,
|
|
739
|
+
attributeName,
|
|
740
|
+
joinTable,
|
|
741
|
+
db,
|
|
742
|
+
transaction: trx
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
// connect relations
|
|
746
|
+
if (hasConnect) {
|
|
747
|
+
const dataset = cleanRelationData.connect || [];
|
|
748
|
+
const rows = dataset.map((data)=>({
|
|
749
|
+
[joinColumn.name]: id,
|
|
750
|
+
[idColumn.name]: data.id,
|
|
751
|
+
[typeColumn.name]: data[typeField],
|
|
752
|
+
...joinTable.on || {},
|
|
753
|
+
...data.__pivot || {},
|
|
754
|
+
field: attributeName
|
|
755
|
+
}));
|
|
756
|
+
const orderMap = relationsOrderer(// Merge id & __type to get a single id key
|
|
757
|
+
adjacentRelations.map(encodePolymorphicRelation({
|
|
758
|
+
idColumn: idColumn.name,
|
|
759
|
+
typeColumn: typeColumn.name
|
|
760
|
+
})), idColumn.name, 'order', cleanRelationData.options?.strict).connect(// Merge id & __type to get a single id key
|
|
761
|
+
dataset.map(encodePolymorphicRelation({
|
|
762
|
+
idColumn: 'id',
|
|
763
|
+
typeColumn: '__type'
|
|
764
|
+
}))).getOrderMap();
|
|
765
|
+
rows.forEach((row)=>{
|
|
766
|
+
const rowId = row[idColumn.name];
|
|
767
|
+
const rowType = row[typeColumn.name];
|
|
768
|
+
const encodedId = encodePolymorphicId(rowId, rowType);
|
|
769
|
+
row.order = orderMap[encodedId];
|
|
770
|
+
});
|
|
771
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
772
|
+
}
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
if (hasSet) {
|
|
776
|
+
// delete all relations for this entity
|
|
777
|
+
await this.createQueryBuilder(joinTable.name).delete().where({
|
|
778
|
+
[joinColumn.name]: id,
|
|
779
|
+
...joinTable.on || {}
|
|
780
|
+
}).transacting(trx).execute();
|
|
781
|
+
const rows = (cleanRelationData.set ?? []).map((data, idx)=>({
|
|
782
|
+
[joinColumn.name]: id,
|
|
783
|
+
[idColumn.name]: data.id,
|
|
784
|
+
[typeColumn.name]: data[typeField],
|
|
785
|
+
field: attributeName,
|
|
786
|
+
...joinTable.on || {},
|
|
787
|
+
...data.__pivot || {},
|
|
788
|
+
order: idx + 1
|
|
789
|
+
}));
|
|
790
|
+
await deleteRelatedMorphOneRelationsAfterMorphToManyUpdate(rows, {
|
|
791
|
+
uid,
|
|
792
|
+
attributeName,
|
|
793
|
+
joinTable,
|
|
794
|
+
db,
|
|
795
|
+
transaction: trx
|
|
796
|
+
});
|
|
797
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
798
|
+
}
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if ('joinColumn' in attribute && attribute.joinColumn && attribute.owner) {
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
// oneToOne oneToMany on the non owning side.
|
|
805
|
+
// Since it is a join column no need to remove previous relations
|
|
806
|
+
if ('joinColumn' in attribute && attribute.joinColumn && !attribute.owner) {
|
|
807
|
+
// need to set the column on the target
|
|
808
|
+
const { target } = attribute;
|
|
809
|
+
await this.createQueryBuilder(target).where({
|
|
810
|
+
[attribute.joinColumn.referencedColumn]: id
|
|
811
|
+
}).update({
|
|
812
|
+
[attribute.joinColumn.referencedColumn]: null
|
|
813
|
+
}).transacting(trx).execute();
|
|
814
|
+
if (!isNull(cleanRelationData.set)) {
|
|
815
|
+
const relIdsToAdd = toIds(cleanRelationData.set);
|
|
816
|
+
await this.createQueryBuilder(target).where({
|
|
817
|
+
id: relIdsToAdd
|
|
818
|
+
}).update({
|
|
819
|
+
[attribute.joinColumn.referencedColumn]: id
|
|
820
|
+
}).transacting(trx).execute();
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
if (attribute.joinTable) {
|
|
824
|
+
const { joinTable } = attribute;
|
|
825
|
+
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable;
|
|
826
|
+
const select = [
|
|
827
|
+
joinColumn.name,
|
|
828
|
+
inverseJoinColumn.name
|
|
829
|
+
];
|
|
830
|
+
if (hasOrderColumn(attribute)) {
|
|
831
|
+
select.push(orderColumnName);
|
|
832
|
+
}
|
|
833
|
+
if (hasInverseOrderColumn(attribute)) {
|
|
834
|
+
select.push(inverseOrderColumnName);
|
|
835
|
+
}
|
|
836
|
+
// only delete relations
|
|
837
|
+
if (isNull(cleanRelationData.set)) {
|
|
838
|
+
await deleteRelations({
|
|
839
|
+
id,
|
|
840
|
+
attribute,
|
|
841
|
+
db,
|
|
842
|
+
relIdsToDelete: 'all',
|
|
843
|
+
transaction: trx
|
|
844
|
+
});
|
|
845
|
+
} else {
|
|
846
|
+
const isPartialUpdate = !has('set', cleanRelationData);
|
|
847
|
+
let relIdsToaddOrMove;
|
|
848
|
+
if (isPartialUpdate) {
|
|
849
|
+
if (isAnyToOne(attribute)) ;
|
|
850
|
+
relIdsToaddOrMove = toIds(cleanRelationData.connect);
|
|
851
|
+
const relIdsToDelete = toIds(differenceWith(isEqual, cleanRelationData.disconnect, cleanRelationData.connect ?? []));
|
|
852
|
+
if (!isEmpty(relIdsToDelete)) {
|
|
853
|
+
await deleteRelations({
|
|
854
|
+
id,
|
|
855
|
+
attribute,
|
|
856
|
+
db,
|
|
857
|
+
relIdsToDelete,
|
|
858
|
+
transaction: trx
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
if (isEmpty(cleanRelationData.connect)) {
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
// Fetch current relations to handle ordering
|
|
865
|
+
let currentMovingRels = [];
|
|
866
|
+
if (hasOrderColumn(attribute) || hasInverseOrderColumn(attribute)) {
|
|
867
|
+
currentMovingRels = await this.createQueryBuilder(joinTable.name).select(select).where({
|
|
868
|
+
[joinColumn.name]: id,
|
|
869
|
+
[inverseJoinColumn.name]: {
|
|
870
|
+
$in: relIdsToaddOrMove
|
|
871
|
+
}
|
|
872
|
+
}).where(joinTable.on || {}).transacting(trx).execute();
|
|
873
|
+
}
|
|
874
|
+
// prepare relations to insert
|
|
875
|
+
const insert = uniqBy('id', cleanRelationData.connect).map((relToAdd)=>({
|
|
876
|
+
[joinColumn.name]: id,
|
|
877
|
+
[inverseJoinColumn.name]: relToAdd.id,
|
|
878
|
+
...joinTable.on || {},
|
|
879
|
+
...relToAdd.__pivot || {}
|
|
880
|
+
}));
|
|
881
|
+
if (hasOrderColumn(attribute)) {
|
|
882
|
+
// Get all adjacent relations and the one with the highest order
|
|
883
|
+
const adjacentRelations = await this.createQueryBuilder(joinTable.name).where({
|
|
884
|
+
$or: [
|
|
885
|
+
{
|
|
886
|
+
[joinColumn.name]: id,
|
|
887
|
+
[inverseJoinColumn.name]: {
|
|
888
|
+
$in: compact(cleanRelationData.connect?.map((r)=>r.position?.after || r.position?.before))
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
[joinColumn.name]: id,
|
|
893
|
+
[orderColumnName]: this.createQueryBuilder(joinTable.name).max(orderColumnName).where({
|
|
894
|
+
[joinColumn.name]: id
|
|
895
|
+
}).where(joinTable.on || {}).transacting(trx).getKnexQuery()
|
|
896
|
+
}
|
|
897
|
+
]
|
|
898
|
+
}).where(joinTable.on || {}).transacting(trx).execute();
|
|
899
|
+
const orderMap = relationsOrderer(adjacentRelations, inverseJoinColumn.name, joinTable.orderColumnName, cleanRelationData.options?.strict).connect(cleanRelationData.connect ?? []).getOrderMap();
|
|
900
|
+
insert.forEach((row)=>{
|
|
901
|
+
row[orderColumnName] = orderMap[row[inverseJoinColumn.name]];
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
// add inv order value
|
|
905
|
+
if (hasInverseOrderColumn(attribute)) {
|
|
906
|
+
const nonExistingRelsIds = difference(relIdsToaddOrMove, map(inverseJoinColumn.name, currentMovingRels));
|
|
907
|
+
const maxResults = await db.getConnection().select(inverseJoinColumn.name).max(inverseOrderColumnName, {
|
|
908
|
+
as: 'max'
|
|
909
|
+
}).whereIn(inverseJoinColumn.name, nonExistingRelsIds).where(joinTable.on || {}).groupBy(inverseJoinColumn.name).from(joinTable.name).transacting(trx);
|
|
910
|
+
const maxMap = maxResults.reduce((acc, res)=>Object.assign(acc, {
|
|
911
|
+
[res[inverseJoinColumn.name]]: res.max
|
|
912
|
+
}), {});
|
|
913
|
+
insert.forEach((row)=>{
|
|
914
|
+
row[inverseOrderColumnName] = (maxMap[row[inverseJoinColumn.name]] || 0) + 1;
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
// insert rows
|
|
918
|
+
const query = this.createQueryBuilder(joinTable.name).insert(insert).onConflict(joinTable.pivotColumns).transacting(trx);
|
|
919
|
+
if (hasOrderColumn(attribute)) {
|
|
920
|
+
query.merge([
|
|
921
|
+
orderColumnName
|
|
922
|
+
]);
|
|
923
|
+
} else {
|
|
924
|
+
query.ignore();
|
|
925
|
+
}
|
|
926
|
+
await query.execute();
|
|
927
|
+
// remove gap between orders
|
|
928
|
+
await cleanOrderColumns({
|
|
929
|
+
attribute,
|
|
930
|
+
db,
|
|
931
|
+
id,
|
|
932
|
+
transaction: trx
|
|
933
|
+
});
|
|
934
|
+
} else {
|
|
935
|
+
if (isAnyToOne(attribute)) {
|
|
936
|
+
cleanRelationData.set = cleanRelationData.set?.slice(-1);
|
|
937
|
+
}
|
|
938
|
+
// overwrite all relations
|
|
939
|
+
relIdsToaddOrMove = toIds(cleanRelationData.set);
|
|
940
|
+
await deleteRelations({
|
|
941
|
+
id,
|
|
942
|
+
attribute,
|
|
943
|
+
db,
|
|
944
|
+
relIdsToDelete: 'all',
|
|
945
|
+
relIdsToNotDelete: relIdsToaddOrMove,
|
|
946
|
+
transaction: trx
|
|
947
|
+
});
|
|
948
|
+
if (isEmpty(cleanRelationData.set)) {
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
const insert = uniqBy('id', cleanRelationData.set).map((relToAdd)=>({
|
|
952
|
+
[joinColumn.name]: id,
|
|
953
|
+
[inverseJoinColumn.name]: relToAdd.id,
|
|
954
|
+
...joinTable.on || {},
|
|
955
|
+
...relToAdd.__pivot || {}
|
|
956
|
+
}));
|
|
957
|
+
// add order value
|
|
958
|
+
if (hasOrderColumn(attribute)) {
|
|
959
|
+
insert.forEach((row, idx)=>{
|
|
960
|
+
row[orderColumnName] = idx + 1;
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
// add inv order value
|
|
964
|
+
if (hasInverseOrderColumn(attribute)) {
|
|
965
|
+
const existingRels = await this.createQueryBuilder(joinTable.name).select(inverseJoinColumn.name).where({
|
|
966
|
+
[joinColumn.name]: id,
|
|
967
|
+
[inverseJoinColumn.name]: {
|
|
968
|
+
$in: relIdsToaddOrMove
|
|
969
|
+
}
|
|
970
|
+
}).where(joinTable.on || {}).transacting(trx).execute();
|
|
971
|
+
const inverseRelsIds = map(inverseJoinColumn.name, existingRels);
|
|
972
|
+
const nonExistingRelsIds = difference(relIdsToaddOrMove, inverseRelsIds);
|
|
973
|
+
const maxResults = await db.getConnection().select(inverseJoinColumn.name).max(inverseOrderColumnName, {
|
|
974
|
+
as: 'max'
|
|
975
|
+
}).whereIn(inverseJoinColumn.name, nonExistingRelsIds).where(joinTable.on || {}).groupBy(inverseJoinColumn.name).from(joinTable.name).transacting(trx);
|
|
976
|
+
const maxMap = maxResults.reduce((acc, res)=>Object.assign(acc, {
|
|
977
|
+
[res[inverseJoinColumn.name]]: res.max
|
|
978
|
+
}), {});
|
|
979
|
+
insert.forEach((row)=>{
|
|
980
|
+
row[inverseOrderColumnName] = (maxMap[row[inverseJoinColumn.name]] || 0) + 1;
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
// insert rows
|
|
984
|
+
const query = this.createQueryBuilder(joinTable.name).insert(insert).onConflict(joinTable.pivotColumns).transacting(trx);
|
|
985
|
+
if (hasOrderColumn(attribute)) {
|
|
986
|
+
query.merge([
|
|
987
|
+
orderColumnName
|
|
988
|
+
]);
|
|
989
|
+
} else {
|
|
990
|
+
query.ignore();
|
|
991
|
+
}
|
|
992
|
+
await query.execute();
|
|
993
|
+
}
|
|
994
|
+
// Delete the previous relations for oneToAny relations
|
|
995
|
+
if (isBidirectional(attribute) && isOneToAny(attribute)) {
|
|
996
|
+
await deletePreviousOneToAnyRelations({
|
|
997
|
+
id,
|
|
998
|
+
attribute,
|
|
999
|
+
relIdsToadd: relIdsToaddOrMove,
|
|
1000
|
+
db,
|
|
1001
|
+
transaction: trx
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
// Delete the previous relations for anyToOne relations
|
|
1005
|
+
if (isAnyToOne(attribute)) {
|
|
1006
|
+
await deletePreviousAnyToOneRelations({
|
|
1007
|
+
id,
|
|
1008
|
+
attribute,
|
|
1009
|
+
relIdToadd: relIdsToaddOrMove[0],
|
|
1010
|
+
db,
|
|
1011
|
+
transaction: trx
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
},
|
|
1018
|
+
/**
|
|
1019
|
+
* Delete relational associations of an existing entity
|
|
1020
|
+
* This removes associations but doesn't do cascade deletions for components for example. This will be handled on the entity service layer instead
|
|
1021
|
+
* NOTE: Most of the deletion should be handled by ON DELETE CASCADE for dialects that have FKs
|
|
1022
|
+
*
|
|
1023
|
+
* @param {EntityManager} em - entity manager instance
|
|
1024
|
+
* @param {Metadata} metadata - model metadta
|
|
1025
|
+
* @param {ID} id - entity ID
|
|
1026
|
+
*/ async deleteRelations (uid, id, options) {
|
|
1027
|
+
const { attributes } = db.metadata.get(uid);
|
|
1028
|
+
const { transaction: trx } = options ?? {};
|
|
1029
|
+
for (const attributeName of Object.keys(attributes)){
|
|
1030
|
+
const attribute = attributes[attributeName];
|
|
1031
|
+
if (attribute.type !== 'relation') {
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
/*
|
|
1035
|
+
if morphOne | morphMany
|
|
1036
|
+
if morphBy is morphToOne
|
|
1037
|
+
set null
|
|
1038
|
+
if morphBy is morphToOne
|
|
1039
|
+
delete links
|
|
1040
|
+
*/ if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
1041
|
+
const { target, morphBy } = attribute;
|
|
1042
|
+
const targetAttribute = db.metadata.get(target).attributes[morphBy];
|
|
1043
|
+
if (targetAttribute.type === 'relation' && targetAttribute.relation === 'morphToOne') {
|
|
1044
|
+
// set columns
|
|
1045
|
+
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
1046
|
+
await this.createQueryBuilder(target).update({
|
|
1047
|
+
[idColumn.name]: null,
|
|
1048
|
+
[typeColumn.name]: null
|
|
1049
|
+
}).where({
|
|
1050
|
+
[idColumn.name]: id,
|
|
1051
|
+
[typeColumn.name]: uid
|
|
1052
|
+
}).transacting(trx).execute();
|
|
1053
|
+
} else if (targetAttribute.type === 'relation' && targetAttribute.relation === 'morphToMany') {
|
|
1054
|
+
const { joinTable } = targetAttribute;
|
|
1055
|
+
const { morphColumn } = joinTable;
|
|
1056
|
+
const { idColumn, typeColumn } = morphColumn;
|
|
1057
|
+
await this.createQueryBuilder(joinTable.name).delete().where({
|
|
1058
|
+
[idColumn.name]: id,
|
|
1059
|
+
[typeColumn.name]: uid,
|
|
1060
|
+
...joinTable.on || {},
|
|
1061
|
+
field: attributeName
|
|
1062
|
+
}).transacting(trx).execute();
|
|
1063
|
+
}
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
/*
|
|
1067
|
+
if morphToOne
|
|
1068
|
+
nothing to do
|
|
1069
|
+
*/ if (attribute.relation === 'morphToOne') ;
|
|
1070
|
+
/*
|
|
1071
|
+
if morphToMany
|
|
1072
|
+
delete links
|
|
1073
|
+
*/ if (attribute.relation === 'morphToMany') {
|
|
1074
|
+
const { joinTable } = attribute;
|
|
1075
|
+
const { joinColumn } = joinTable;
|
|
1076
|
+
await this.createQueryBuilder(joinTable.name).delete().where({
|
|
1077
|
+
[joinColumn.name]: id,
|
|
1078
|
+
...joinTable.on || {}
|
|
1079
|
+
}).transacting(trx).execute();
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
// do not need to delete links when using foreign keys
|
|
1083
|
+
if (db.dialect.usesForeignKeys()) {
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
// NOTE: we do not remove existing associations with the target as it should handled by unique FKs instead
|
|
1087
|
+
if ('joinColumn' in attribute && attribute.joinColumn && attribute.owner) {
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
// oneToOne oneToMany on the non owning side.
|
|
1091
|
+
if ('joinColumn' in attribute && attribute.joinColumn && !attribute.owner) {
|
|
1092
|
+
// need to set the column on the target
|
|
1093
|
+
const { target } = attribute;
|
|
1094
|
+
await this.createQueryBuilder(target).where({
|
|
1095
|
+
[attribute.joinColumn.referencedColumn]: id
|
|
1096
|
+
}).update({
|
|
1097
|
+
[attribute.joinColumn.referencedColumn]: null
|
|
1098
|
+
}).transacting(trx).execute();
|
|
1099
|
+
}
|
|
1100
|
+
if ('joinTable' in attribute && attribute.joinTable) {
|
|
1101
|
+
await deleteRelations({
|
|
1102
|
+
id,
|
|
1103
|
+
attribute,
|
|
1104
|
+
db,
|
|
1105
|
+
relIdsToDelete: 'all',
|
|
1106
|
+
transaction: trx
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
},
|
|
1111
|
+
// TODO: add lifecycle events
|
|
1112
|
+
async populate (uid, entity, populate) {
|
|
1113
|
+
const entry = await this.findOne(uid, {
|
|
1114
|
+
select: [
|
|
1115
|
+
'id'
|
|
1116
|
+
],
|
|
1117
|
+
where: {
|
|
1118
|
+
id: entity.id
|
|
1119
|
+
},
|
|
1120
|
+
populate
|
|
1121
|
+
});
|
|
1122
|
+
return {
|
|
1123
|
+
...entity,
|
|
1124
|
+
...entry
|
|
1125
|
+
};
|
|
1126
|
+
},
|
|
1127
|
+
// TODO: add lifecycle events
|
|
1128
|
+
async load (uid, entity, fields, populate) {
|
|
1129
|
+
const { attributes } = db.metadata.get(uid);
|
|
1130
|
+
const fieldsArr = castArray(fields);
|
|
1131
|
+
fieldsArr.forEach((field)=>{
|
|
1132
|
+
const attribute = attributes[field];
|
|
1133
|
+
if (!attribute || attribute.type !== 'relation') {
|
|
1134
|
+
throw new Error(`Invalid load. Expected ${field} to be a relational attribute`);
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
const entry = await this.findOne(uid, {
|
|
1138
|
+
select: [
|
|
1139
|
+
'id'
|
|
1140
|
+
],
|
|
1141
|
+
where: {
|
|
1142
|
+
id: entity.id
|
|
1143
|
+
},
|
|
1144
|
+
populate: fieldsArr.reduce((acc, field)=>{
|
|
1145
|
+
acc[field] = populate || true;
|
|
1146
|
+
return acc;
|
|
1147
|
+
}, {})
|
|
1148
|
+
});
|
|
1149
|
+
if (!entry) {
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|
|
1152
|
+
if (Array.isArray(fields)) {
|
|
1153
|
+
return pick(fields, entry);
|
|
1154
|
+
}
|
|
1155
|
+
return entry[fields];
|
|
1156
|
+
},
|
|
1157
|
+
// cascading
|
|
1158
|
+
// aggregations
|
|
1159
|
+
// -> avg
|
|
1160
|
+
// -> min
|
|
1161
|
+
// -> max
|
|
1162
|
+
// -> grouping
|
|
1163
|
+
// formulas
|
|
1164
|
+
// custom queries
|
|
1165
|
+
// utilities
|
|
1166
|
+
// -> map result
|
|
1167
|
+
// -> map input
|
|
1168
|
+
// extra features
|
|
1169
|
+
// -> virtuals
|
|
1170
|
+
// -> private
|
|
1171
|
+
createQueryBuilder (uid) {
|
|
1172
|
+
return createQueryBuilder(uid, db);
|
|
1173
|
+
},
|
|
1174
|
+
getRepository (uid) {
|
|
1175
|
+
if (!repoMap[uid]) {
|
|
1176
|
+
repoMap[uid] = createRepository(uid, db);
|
|
1177
|
+
}
|
|
1178
|
+
return repoMap[uid];
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
export { createEntityManager };
|
|
1184
|
+
//# sourceMappingURL=index.mjs.map
|