objection 3.9.1 → 4.0.0-rc.1

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.
Files changed (248) hide show
  1. package/lib/dbErrors/LICENSE +21 -0
  2. package/lib/dbErrors/errorCodes/mssql.js +19 -0
  3. package/lib/dbErrors/errorCodes/mysql.js +805 -0
  4. package/lib/dbErrors/errorCodes/postgres.js +241 -0
  5. package/lib/dbErrors/errors/CheckViolationError.js +10 -0
  6. package/lib/dbErrors/errors/ConstraintViolationError.js +3 -0
  7. package/lib/dbErrors/errors/DBError.js +9 -0
  8. package/lib/dbErrors/errors/DataError.js +3 -0
  9. package/lib/dbErrors/errors/ForeignKeyViolationError.js +11 -0
  10. package/lib/dbErrors/errors/NotNullViolationError.js +12 -0
  11. package/lib/dbErrors/errors/UniqueViolationError.js +12 -0
  12. package/lib/dbErrors/index.js +59 -0
  13. package/lib/dbErrors/parsers/mssql/DBError/CheckViolationError/parser.js +28 -0
  14. package/lib/dbErrors/parsers/mssql/DBError/DataError/parser.js +25 -0
  15. package/lib/dbErrors/parsers/mssql/DBError/ForeignKeyViolationError/parser.js +39 -0
  16. package/lib/dbErrors/parsers/mssql/DBError/NotNullViolationError/parser.js +30 -0
  17. package/lib/dbErrors/parsers/mssql/DBError/UniqueViolationError/parser.js +53 -0
  18. package/lib/dbErrors/parsers/mssql/DBError/parser.js +51 -0
  19. package/lib/dbErrors/parsers/mssql/DBError/util.js +16 -0
  20. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +43 -0
  21. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/NotNullViolationError/parser.js +29 -0
  22. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/UniqueViolationError/parser.js +27 -0
  23. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/parser.js +23 -0
  24. package/lib/dbErrors/parsers/mysql/DBError/DataError/parser.js +24 -0
  25. package/lib/dbErrors/parsers/mysql/DBError/parser.js +28 -0
  26. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/CheckViolationError/parser.js +18 -0
  27. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +18 -0
  28. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/NotNullViolationError/parser.js +18 -0
  29. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/UniqueViolationError/parser.js +40 -0
  30. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/parser.js +25 -0
  31. package/lib/dbErrors/parsers/postgres/DBError/DataError/parser.js +16 -0
  32. package/lib/dbErrors/parsers/postgres/DBError/parser.js +27 -0
  33. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/CheckViolationError/parser.js +20 -0
  34. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +20 -0
  35. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/NotNullViolationError/parser.js +27 -0
  36. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/UniqueViolationError/parser.js +28 -0
  37. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/parser.js +24 -0
  38. package/lib/dbErrors/parsers/sqlite/DBError/parser.js +23 -0
  39. package/lib/dbErrors/utils/sqlState.js +3 -0
  40. package/lib/initialize.js +1 -7
  41. package/lib/model/AjvValidator.js +51 -117
  42. package/lib/model/Model.js +84 -59
  43. package/lib/model/ModifierNotFoundError.js +1 -7
  44. package/lib/model/NotFoundError.js +1 -7
  45. package/lib/model/RelationDoesNotExistError.js +1 -7
  46. package/lib/model/ValidationError.js +3 -10
  47. package/lib/model/Validator.js +1 -7
  48. package/lib/model/getModel.js +7 -6
  49. package/lib/model/inheritModel.js +1 -7
  50. package/lib/model/modelBindKnex.js +4 -10
  51. package/lib/model/modelClone.js +5 -11
  52. package/lib/model/modelColPropMap.js +3 -10
  53. package/lib/model/modelId.js +3 -10
  54. package/lib/model/modelJsonAttributes.js +35 -16
  55. package/lib/model/modelParseRelations.js +2 -8
  56. package/lib/model/modelQueryProps.js +11 -23
  57. package/lib/model/modelSet.js +89 -41
  58. package/lib/model/modelTableMetadata.js +4 -11
  59. package/lib/model/modelToJson.js +6 -13
  60. package/lib/model/modelUtils.js +14 -13
  61. package/lib/model/modelValidate.js +3 -9
  62. package/lib/model/modelValues.js +5 -14
  63. package/lib/model/modelVisitor.js +1 -7
  64. package/lib/objection.js +33 -71
  65. package/lib/queryBuilder/FunctionBuilder.js +4 -11
  66. package/lib/queryBuilder/InternalOptions.js +4 -9
  67. package/lib/queryBuilder/JoinBuilder.js +5 -8
  68. package/lib/queryBuilder/QueryBuilder.js +126 -95
  69. package/lib/queryBuilder/QueryBuilderBase.js +41 -123
  70. package/lib/queryBuilder/QueryBuilderContext.js +2 -8
  71. package/lib/queryBuilder/QueryBuilderContextBase.js +2 -8
  72. package/lib/queryBuilder/QueryBuilderOperationSupport.js +7 -21
  73. package/lib/queryBuilder/QueryBuilderUserContext.js +1 -7
  74. package/lib/queryBuilder/RawBuilder.js +5 -13
  75. package/lib/queryBuilder/ReferenceBuilder.js +20 -48
  76. package/lib/queryBuilder/RelationExpression.js +18 -16
  77. package/lib/queryBuilder/StaticHookArguments.js +74 -16
  78. package/lib/queryBuilder/ValueBuilder.js +11 -13
  79. package/lib/queryBuilder/getJoinBuilder.js +10 -0
  80. package/lib/queryBuilder/graph/GraphLoader.js +304 -0
  81. package/lib/queryBuilder/graph/GraphNode.js +235 -0
  82. package/lib/queryBuilder/graph/GraphOptions.js +55 -248
  83. package/lib/queryBuilder/graph/GraphPlan.js +395 -0
  84. package/lib/queryBuilder/graph/GraphUpsert.js +466 -414
  85. package/lib/queryBuilder/graph/GraphWriter.js +1263 -0
  86. package/lib/queryBuilder/join/JoinResultColumn.js +1 -7
  87. package/lib/queryBuilder/join/JoinResultParser.js +6 -11
  88. package/lib/queryBuilder/join/RelationJoiner.js +66 -84
  89. package/lib/queryBuilder/join/TableNode.js +2 -8
  90. package/lib/queryBuilder/join/TableTree.js +3 -9
  91. package/lib/queryBuilder/join/utils.js +5 -15
  92. package/lib/queryBuilder/jsonDialects/JsonDialect.js +51 -0
  93. package/lib/queryBuilder/jsonDialects/MySqlJsonDialect.js +81 -0
  94. package/lib/queryBuilder/jsonDialects/PostgresJsonDialect.js +45 -0
  95. package/lib/queryBuilder/jsonDialects/SqliteJsonDialect.js +67 -0
  96. package/lib/queryBuilder/jsonDialects/index.js +44 -0
  97. package/lib/queryBuilder/operations/DelegateOperation.js +2 -8
  98. package/lib/queryBuilder/operations/DeleteOperation.js +10 -39
  99. package/lib/queryBuilder/operations/FindByIdOperation.js +7 -12
  100. package/lib/queryBuilder/operations/FindByIdsOperation.js +4 -8
  101. package/lib/queryBuilder/operations/FindOperation.js +14 -34
  102. package/lib/queryBuilder/operations/FirstOperation.js +6 -10
  103. package/lib/queryBuilder/operations/FromOperation.js +3 -9
  104. package/lib/queryBuilder/operations/InsertAndFetchOperation.js +13 -12
  105. package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +4 -10
  106. package/lib/queryBuilder/operations/InsertGraphOperation.js +7 -18
  107. package/lib/queryBuilder/operations/InsertOperation.js +118 -45
  108. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +5 -12
  109. package/lib/queryBuilder/operations/InstanceFindOperation.js +4 -11
  110. package/lib/queryBuilder/operations/InstanceInsertOperation.js +2 -8
  111. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +7 -14
  112. package/lib/queryBuilder/operations/JoinRelatedOperation.js +5 -11
  113. package/lib/queryBuilder/operations/KnexOperation.js +2 -8
  114. package/lib/queryBuilder/operations/LimitPerParentOperation.js +7 -69
  115. package/lib/queryBuilder/operations/MergeOperation.js +5 -11
  116. package/lib/queryBuilder/operations/NoneOperation.js +2 -8
  117. package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +20 -49
  118. package/lib/queryBuilder/operations/OnBuildKnexOperation.js +2 -8
  119. package/lib/queryBuilder/operations/OnBuildOperation.js +2 -8
  120. package/lib/queryBuilder/operations/OnErrorOperation.js +2 -8
  121. package/lib/queryBuilder/operations/QueryBuilderOperation.js +1 -7
  122. package/lib/queryBuilder/operations/RangeOperation.js +11 -26
  123. package/lib/queryBuilder/operations/RelateOperation.js +2 -8
  124. package/lib/queryBuilder/operations/ReturningOperation.js +3 -9
  125. package/lib/queryBuilder/operations/RunAfterOperation.js +2 -12
  126. package/lib/queryBuilder/operations/RunBeforeOperation.js +2 -8
  127. package/lib/queryBuilder/operations/UnrelateOperation.js +2 -8
  128. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +14 -17
  129. package/lib/queryBuilder/operations/UpdateOperation.js +32 -132
  130. package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +8 -11
  131. package/lib/queryBuilder/operations/UpsertGraphOperation.js +6 -12
  132. package/lib/queryBuilder/operations/WhereCompositeOperation.js +3 -9
  133. package/lib/queryBuilder/operations/WhereGroupOperation.js +2 -8
  134. package/lib/queryBuilder/operations/eager/EagerOperation.js +3 -9
  135. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +72 -10
  136. package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +2 -8
  137. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +23 -44
  138. package/lib/queryBuilder/operations/fieldExpressions.js +122 -0
  139. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +20 -0
  140. package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +44 -0
  141. package/lib/queryBuilder/operations/jsonApi/WhereJsonTypeOperation.js +14 -0
  142. package/lib/queryBuilder/operations/select/SelectOperation.js +11 -21
  143. package/lib/queryBuilder/operations/select/Selection.js +19 -17
  144. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +4 -10
  145. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +4 -10
  146. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +4 -10
  147. package/lib/queryBuilder/operations/windowOrder.js +56 -0
  148. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +3 -7
  149. package/lib/queryBuilder/parsers/relationExpressionParser.js +42 -43
  150. package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +39 -36
  151. package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +2 -8
  152. package/lib/queryBuilder/transformations/QueryTransformation.js +1 -7
  153. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +3 -9
  154. package/lib/queryBuilder/transformations/index.js +3 -11
  155. package/lib/queryBuilder/whereGroups.js +9 -24
  156. package/lib/queryBuilder/withoutOrder.js +4 -11
  157. package/lib/relations/Relation.js +47 -28
  158. package/lib/relations/RelationDeleteOperation.js +4 -14
  159. package/lib/relations/RelationFindOperation.js +119 -90
  160. package/lib/relations/RelationInsertOperation.js +2 -8
  161. package/lib/relations/RelationOwner.js +5 -11
  162. package/lib/relations/RelationProperty.js +4 -10
  163. package/lib/relations/RelationUpdateOperation.js +4 -14
  164. package/lib/relations/RelationWriteResultOperation.js +15 -0
  165. package/lib/relations/belongsToMany/BelongsToManyDeleteOperation.js +23 -0
  166. package/lib/relations/belongsToMany/BelongsToManyFindOperation.js +35 -0
  167. package/lib/relations/belongsToMany/BelongsToManyInsertOperation.js +28 -0
  168. package/lib/relations/belongsToMany/BelongsToManyRelateOperation.js +16 -0
  169. package/lib/relations/belongsToMany/BelongsToManyRelation.js +276 -0
  170. package/lib/relations/belongsToMany/BelongsToManyUnrelateOperation.js +11 -0
  171. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +2 -8
  172. package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +6 -11
  173. package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +3 -9
  174. package/lib/relations/belongsToOne/BelongsToOneRelation.js +8 -19
  175. package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +53 -27
  176. package/lib/relations/hasMany/HasManyInsertOperation.js +5 -10
  177. package/lib/relations/hasMany/HasManyRelateOperation.js +5 -11
  178. package/lib/relations/hasMany/HasManyRelation.js +9 -11
  179. package/lib/relations/hasMany/HasManyUnrelateOperation.js +3 -9
  180. package/lib/relations/hasOne/HasOneRelation.js +2 -8
  181. package/lib/relations/hasOneThrough/HasOneThroughRelation.js +2 -8
  182. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +10 -15
  183. package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +2 -8
  184. package/lib/relations/manyToMany/ManyToManyRelation.js +82 -50
  185. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +2 -8
  186. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +5 -9
  187. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +2 -8
  188. package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +3 -9
  189. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +20 -59
  190. package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +6 -11
  191. package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +4 -10
  192. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +3 -9
  193. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +5 -9
  194. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +3 -9
  195. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +3 -9
  196. package/lib/relations/manyToMany/update/ManyToManyUpdateMySqlOperation.js +3 -9
  197. package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +5 -9
  198. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +64 -72
  199. package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +3 -9
  200. package/lib/transaction.js +3 -9
  201. package/lib/utils/assert.js +3 -11
  202. package/lib/utils/buildUtils.js +2 -8
  203. package/lib/utils/classUtils.js +1 -7
  204. package/lib/utils/createModifier.js +2 -8
  205. package/lib/utils/identifierMapping.js +23 -27
  206. package/lib/utils/internalPropUtils.js +1 -7
  207. package/lib/utils/knexUtils.js +84 -40
  208. package/lib/utils/mixin.js +3 -10
  209. package/lib/utils/normalizeIds.js +2 -8
  210. package/lib/utils/objectUtils.js +41 -60
  211. package/lib/utils/parseFieldExpression.js +16 -16
  212. package/lib/utils/promiseUtils/after.js +2 -8
  213. package/lib/utils/promiseUtils/afterReturn.js +2 -8
  214. package/lib/utils/promiseUtils/index.js +7 -16
  215. package/lib/utils/promiseUtils/isPromise.js +2 -8
  216. package/lib/utils/promiseUtils/map.js +2 -8
  217. package/lib/utils/promiseUtils/mapAfterAllReturn.js +2 -8
  218. package/lib/utils/promiseUtils/try.js +2 -8
  219. package/lib/utils/resolveModel.js +10 -17
  220. package/lib/utils/tmpColumnUtils.js +2 -9
  221. package/lib/utils/warnings.js +2 -14
  222. package/package.json +25 -17
  223. package/typings/objection/index.d.ts +3217 -2179
  224. package/lib/.eslintrc.json +0 -8
  225. package/lib/model/graph/ModelGraph.js +0 -80
  226. package/lib/model/graph/ModelGraphBuilder.js +0 -241
  227. package/lib/model/graph/ModelGraphEdge.js +0 -52
  228. package/lib/model/graph/ModelGraphNode.js +0 -238
  229. package/lib/queryBuilder/graph/GraphAction.js +0 -69
  230. package/lib/queryBuilder/graph/GraphData.js +0 -53
  231. package/lib/queryBuilder/graph/GraphFetcher.js +0 -197
  232. package/lib/queryBuilder/graph/GraphNodeDbExistence.js +0 -184
  233. package/lib/queryBuilder/graph/GraphOperation.js +0 -27
  234. package/lib/queryBuilder/graph/delete/GraphDelete.js +0 -73
  235. package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +0 -88
  236. package/lib/queryBuilder/graph/insert/DeferredRelationGraphInsertAction.js +0 -58
  237. package/lib/queryBuilder/graph/insert/GraphInsert.js +0 -296
  238. package/lib/queryBuilder/graph/insert/GraphInsertAction.js +0 -233
  239. package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +0 -92
  240. package/lib/queryBuilder/graph/patch/GraphPatch.js +0 -20
  241. package/lib/queryBuilder/graph/patch/GraphPatchAction.js +0 -289
  242. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +0 -31
  243. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +0 -126
  244. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasPostgresOperation.js +0 -30
  245. package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectPostgresOperation.js +0 -37
  246. package/lib/queryBuilder/operations/jsonApi/WhereJsonPostgresOperation.js +0 -62
  247. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +0 -192
  248. package/lib/utils/wrapDbError.js +0 -64
@@ -0,0 +1,276 @@
1
+ import { Relation } from '../Relation.js';
2
+ import { BelongsToManyFindOperation } from './BelongsToManyFindOperation.js';
3
+ import { BelongsToManyInsertOperation } from './BelongsToManyInsertOperation.js';
4
+ import { BelongsToManyDeleteOperation } from './BelongsToManyDeleteOperation.js';
5
+ import { BelongsToManyRelateOperation } from './BelongsToManyRelateOperation.js';
6
+ import { BelongsToManyUnrelateOperation } from './BelongsToManyUnrelateOperation.js';
7
+
8
+ import { raw } from '../../queryBuilder/RawBuilder.js';
9
+ import { ref } from '../../queryBuilder/ReferenceBuilder.js';
10
+ import { addWhereGroup } from '../../queryBuilder/whereGroups.js';
11
+ import { isOwnerModelClassQuery } from '../RelationOwner.js';
12
+ import { valuesKey } from '../../model/modelValues.js';
13
+ import { isPostgres, isMySql, isSqlite, isMsSql, whereInKeys } from '../../utils/knexUtils.js';
14
+ import { jsonEquals } from '../../utils/objectUtils.js';
15
+ import * as promiseUtils from '../../utils/promiseUtils/index.js';
16
+
17
+ // The alias of the owner rows in `findQuery()` when the owners are given as ids
18
+ // or as a query:
19
+ const ownersAlias = 'objectiontmpowners';
20
+
21
+ export class BelongsToManyRelation extends Relation {
22
+ // The owner holds the keys, in an array.
23
+ get keySide() {
24
+ return 'owner';
25
+ }
26
+
27
+ setMapping(mapping) {
28
+ const retVal = super.setMapping(mapping);
29
+
30
+ if (this.ownerProp.size !== 1) {
31
+ throw this.createError('composite keys are not supported.');
32
+ }
33
+
34
+ return retVal;
35
+ }
36
+
37
+ // The keys of the related models that `owner` holds in its array:
38
+ getKeys(owner) {
39
+ const keys = this.ownerProp.getProp(owner, 0);
40
+
41
+ if (keys == null) {
42
+ return [];
43
+ } else if (Array.isArray(keys)) {
44
+ return keys.filter((key) => key != null);
45
+ } else {
46
+ throw this.createError(
47
+ `the join property "${this.ownerProp.propDescription(0)}" must hold an array of keys, ` +
48
+ `not ${JSON.stringify(keys)}. Add it to the jsonAttributes of ${this.ownerModelClass.name} ` +
49
+ 'if it holds a JSON array.',
50
+ );
51
+ }
52
+ }
53
+
54
+ // Remove the duplicate keys, keeping the first of each:
55
+ normalizeKeys(keys) {
56
+ const seen = new Set();
57
+
58
+ return keys.filter((key) => {
59
+ const id = valuesKey([key]);
60
+ const isNew = !seen.has(id);
61
+ seen.add(id);
62
+ return isNew;
63
+ });
64
+ }
65
+
66
+ // Remove the keys in `remove` from `keys` and add the ones in `add`, leaving
67
+ // out empty keys. Keys that are already there keep their place and value:
68
+ changeKeys(keys, { add = [], remove = [] }) {
69
+ const removedKeys = new Set(remove.map((key) => valuesKey([key])));
70
+
71
+ return this.normalizeKeys([
72
+ ...keys.filter((key) => !removedKeys.has(valuesKey([key]))),
73
+ ...add.filter((key) => key != null),
74
+ ]);
75
+ }
76
+
77
+ // Create the patch that writes `keys` to the array of the `owner` row: only
78
+ // the array inside a JSON column, unless the column is empty.
79
+ createKeysPatch(owner, keys) {
80
+ const patch = {};
81
+
82
+ if (owner[this.ownerProp.props[0]] == null) {
83
+ this.ownerProp.setProp(patch, 0, keys);
84
+ } else {
85
+ this.ownerProp.patch(patch, 0, keys);
86
+ }
87
+
88
+ return patch;
89
+ }
90
+
91
+ findQuery(builder, owner) {
92
+ const relatedRef = this.relatedProp.ref(builder, 0);
93
+
94
+ addWhereGroup(builder, () => {
95
+ if (owner.isReference) {
96
+ builder.where(containsKey(builder, owner.reference[0], relatedRef));
97
+ } else if (owner.isModels) {
98
+ const keys = this.normalizeKeys(owner.modelArray.flatMap((model) => this.getKeys(model)));
99
+ // Empty arrays are normal here, so we don't resolve the query early,
100
+ // to keep the result of updates, deletes and aggregates.
101
+ if (keys.length) {
102
+ whereInKeys(builder, [relatedRef], keys);
103
+ } else {
104
+ builder.where(false);
105
+ }
106
+ } else if (owner.isIdentifiers || owner.isQueryBuilder) {
107
+ // Look for the related rows in the arrays of the owner rows. The owner
108
+ // query becomes a derived table, so that its `limit()` keeps applying
109
+ // to the owners. The arrays are selected under the name of the owner
110
+ // column, also for JSON field expressions.
111
+ const owners = owner.getProps(this);
112
+ const keysColumn = this.ownerProp.cols[0];
113
+
114
+ if (isOwnerModelClassQuery(owners, this)) {
115
+ owners.clearSelect().select(this.ownerProp.ref(owners, 0).as(keysColumn));
116
+ }
117
+
118
+ builder.whereExists(
119
+ this.ownerModelClass
120
+ .query()
121
+ .childQueryOf(builder)
122
+ .select(1)
123
+ .from(owners.as(ownersAlias))
124
+ .where(containsKey(builder, ref(`${ownersAlias}.${keysColumn}`), relatedRef)),
125
+ );
126
+ } else {
127
+ builder.where(false).resolve([]);
128
+ }
129
+ });
130
+
131
+ return this.applyModify(builder);
132
+ }
133
+
134
+ // @override
135
+ joinOn(builder, join, { relatedTableAlias, ownerTable }) {
136
+ const relatedRef = this.relatedProp.ref(builder, 0).table(relatedTableAlias);
137
+ const keysRef = this.ownerProp.ref(builder, 0).table(ownerTable);
138
+
139
+ join.on(containsKey(builder, keysRef, relatedRef));
140
+ }
141
+
142
+ find(_, owner) {
143
+ return new BelongsToManyFindOperation('find', {
144
+ relation: this,
145
+ owner,
146
+ });
147
+ }
148
+
149
+ insert(_, owner) {
150
+ return new BelongsToManyInsertOperation('insert', {
151
+ relation: this,
152
+ owner,
153
+ });
154
+ }
155
+
156
+ delete(_, owner) {
157
+ return new BelongsToManyDeleteOperation('delete', {
158
+ relation: this,
159
+ owner,
160
+ });
161
+ }
162
+
163
+ relate(_, owner) {
164
+ return new BelongsToManyRelateOperation('relate', {
165
+ relation: this,
166
+ owner,
167
+ });
168
+ }
169
+
170
+ unrelate(_, owner) {
171
+ return new BelongsToManyUnrelateOperation('unrelate', {
172
+ relation: this,
173
+ owner,
174
+ });
175
+ }
176
+
177
+ // Find the keys of the related rows that `builder` filters, for `unrelate()`
178
+ // and `delete()`. Like all internal reads of the relation, it calls no find
179
+ // hooks.
180
+ findKeys(builder, owner) {
181
+ const query = this.relatedModelClass
182
+ .query()
183
+ .childQueryOf(builder)
184
+ .findOptions({ dontCallFindHooks: true });
185
+
186
+ return query
187
+ .copyFrom(builder, builder.constructor.JoinSelector)
188
+ .copyFrom(builder, builder.constructor.WhereSelector)
189
+ .modify((query) => this.findQuery(query, owner))
190
+ .select(this.relatedProp.ref(query, 0))
191
+ .runAfter((related) => related.map((rel) => this.relatedProp.getProp(rel, 0)));
192
+ }
193
+
194
+ // Add and remove keys in the arrays of the owner rows, and in the owner
195
+ // models. Resolves to the number of owner rows that changed. The arrays are
196
+ // read without the find hooks and written back by separate queries.
197
+ updateKeys(builder, owner, { add = [], remove = [] }) {
198
+ const { ownerModelClass, ownerProp } = this;
199
+ const idProp = ownerModelClass.getIdRelationProperty();
200
+ const ownerIds = owner.getProps(this, idProp);
201
+ const query = ownerModelClass
202
+ .query()
203
+ .childQueryOf(builder)
204
+ .findOptions({ dontCallFindHooks: true });
205
+ const idRefs = idProp.refs(query);
206
+
207
+ const updateRow = async (row) => {
208
+ const keys = this.getKeys(row);
209
+ const newKeys = this.changeKeys(keys, { add, remove });
210
+ const isChanged = !jsonEquals(keys, newKeys);
211
+
212
+ if (isChanged) {
213
+ const patch = this.createKeysPatch(row, newKeys);
214
+
215
+ await ownerModelClass.query().childQueryOf(builder).patch(patch).findById(row.$id());
216
+ }
217
+
218
+ if (owner.isModels) {
219
+ const id = row.$idKey();
220
+
221
+ for (const model of owner.modelArray) {
222
+ if (model.$hasId() && model.$idKey() === id) {
223
+ ownerProp.setProp(model, 0, newKeys);
224
+ }
225
+ }
226
+ }
227
+
228
+ return isChanged;
229
+ };
230
+
231
+ // Select the whole column of the arrays, also for JSON field expressions:
232
+ return query
233
+ .select([...idRefs, `${query.tableRefFor(ownerModelClass)}.${ownerProp.cols[0]}`])
234
+ .modify((query) => {
235
+ if (ownerIds) {
236
+ query.whereInComposite(idRefs, ownerIds);
237
+ } else {
238
+ query.resolve([]);
239
+ }
240
+ })
241
+ .runAfter(async (rows) => {
242
+ const results = await promiseUtils.map(rows, updateRow, {
243
+ concurrency: ownerModelClass.getConcurrency(builder.unsafeKnex()),
244
+ });
245
+
246
+ return results.filter(Boolean).length;
247
+ });
248
+ }
249
+ }
250
+
251
+ Object.defineProperties(BelongsToManyRelation.prototype, {
252
+ isObjectionBelongsToManyRelation: {
253
+ enumerable: false,
254
+ writable: false,
255
+ value: true,
256
+ },
257
+ });
258
+
259
+ // The condition for the array of keys in `keysRef` to contain the key in
260
+ // `keyRef`. Postgres converts both to jsonb, which supports json, jsonb and
261
+ // native array columns alike.
262
+ function containsKey(builder, keysRef, keyRef) {
263
+ const knex = builder.knex();
264
+
265
+ if (isPostgres(knex)) {
266
+ return raw('to_jsonb(?) @> to_jsonb(?)', [keysRef, keyRef]);
267
+ } else if (isMySql(knex)) {
268
+ return raw('? member of (?)', [keyRef, keysRef]);
269
+ } else if (isSqlite(knex)) {
270
+ return raw('exists (select 1 from json_each(?) where value = ?)', [keysRef, keyRef]);
271
+ } else if (isMsSql(knex)) {
272
+ return raw('exists (select 1 from openjson(?) where value = ?)', [keysRef, keyRef]);
273
+ } else {
274
+ throw new Error(`Unsupported database for BelongsToManyRelation: '${knex.client.dialect}'.`);
275
+ }
276
+ }
@@ -0,0 +1,11 @@
1
+ import { UnrelateOperation } from '../../queryBuilder/operations/UnrelateOperation.js';
2
+
3
+ export class BelongsToManyUnrelateOperation extends UnrelateOperation {
4
+ // Remove the keys of the related rows that the query finds, so that its
5
+ // `where` clauses and the relation's `modify` filter the related rows.
6
+ queryExecutor(builder) {
7
+ return this.relation
8
+ .findKeys(builder, this.owner)
9
+ .runAfter((keys) => this.relation.updateKeys(builder, this.owner, { remove: keys }));
10
+ }
11
+ }
@@ -1,8 +1,6 @@
1
- 'use strict';
1
+ import { RelationDeleteOperation } from '../RelationDeleteOperation.js';
2
2
 
3
- const { RelationDeleteOperation } = require('../RelationDeleteOperation');
4
-
5
- class BelongsToOneDeleteOperation extends RelationDeleteOperation {
3
+ export class BelongsToOneDeleteOperation extends RelationDeleteOperation {
6
4
  onAfter1(builder, result) {
7
5
  // A query resolved without executing it, e.g. with `none()`, deleted
8
6
  // nothing, so the owners are left untouched.
@@ -19,7 +17,3 @@ class BelongsToOneDeleteOperation extends RelationDeleteOperation {
19
17
  return result;
20
18
  }
21
19
  }
22
-
23
- module.exports = {
24
- BelongsToOneDeleteOperation,
25
- };
@@ -1,10 +1,9 @@
1
- 'use strict';
1
+ import { RelationInsertOperation } from '../RelationInsertOperation.js';
2
+ import { BelongsToOneRelateOperation } from './BelongsToOneRelateOperation.js';
3
+ import { InsertGraphOperation } from '../../queryBuilder/operations/InsertGraphOperation.js';
4
+ import { assignRelated } from '../../model/modelSet.js';
2
5
 
3
- const { RelationInsertOperation } = require('../RelationInsertOperation');
4
- const { BelongsToOneRelateOperation } = require('./BelongsToOneRelateOperation');
5
- const { InsertGraphOperation } = require('../../queryBuilder/operations/InsertGraphOperation');
6
-
7
- class BelongsToOneInsertOperation extends RelationInsertOperation {
6
+ export class BelongsToOneInsertOperation extends RelationInsertOperation {
8
7
  onAdd(builder, args) {
9
8
  const retVal = super.onAdd(builder, args);
10
9
 
@@ -33,7 +32,7 @@ class BelongsToOneInsertOperation extends RelationInsertOperation {
33
32
 
34
33
  if (this.assignResultToOwner && this.owner.isModels) {
35
34
  for (const owner of this.owner.modelArray) {
36
- owner.$setRelated(this.relation, inserted);
35
+ assignRelated(owner, this.relation, inserted);
37
36
  }
38
37
  }
39
38
 
@@ -43,7 +42,3 @@ class BelongsToOneInsertOperation extends RelationInsertOperation {
43
42
  return inserted;
44
43
  }
45
44
  }
46
-
47
- module.exports = {
48
- BelongsToOneInsertOperation,
49
- };
@@ -1,9 +1,7 @@
1
- 'use strict';
1
+ import { normalizeIds } from '../../utils/normalizeIds.js';
2
+ import { RelateOperation } from '../../queryBuilder/operations/RelateOperation.js';
2
3
 
3
- const { normalizeIds } = require('../../utils/normalizeIds');
4
- const { RelateOperation } = require('../../queryBuilder/operations/RelateOperation');
5
-
6
- class BelongsToOneRelateOperation extends RelateOperation {
4
+ export class BelongsToOneRelateOperation extends RelateOperation {
7
5
  onAdd(_, args) {
8
6
  this.input = args[0];
9
7
  this.ids = normalizeIds(args[0], this.relation.relatedProp, { arrayOutput: true });
@@ -49,7 +47,3 @@ function assertOneId(ids) {
49
47
  throw new Error('can only relate one model to a BelongsToOneRelation');
50
48
  }
51
49
  }
52
-
53
- module.exports = {
54
- BelongsToOneRelateOperation,
55
- };
@@ -1,23 +1,16 @@
1
- 'use strict';
1
+ import { Relation } from '../Relation.js';
2
+ import { BelongsToOneInsertOperation } from './BelongsToOneInsertOperation.js';
3
+ import { BelongsToOneDeleteOperation } from './BelongsToOneDeleteOperation.js';
4
+ import { BelongsToOneRelateOperation } from './BelongsToOneRelateOperation.js';
5
+ import { BelongsToOneUnrelateOperation } from './BelongsToOneUnrelateOperation.js';
2
6
 
3
- const { Relation } = require('../Relation');
4
- const { BelongsToOneInsertOperation } = require('./BelongsToOneInsertOperation');
5
- const { BelongsToOneDeleteOperation } = require('./BelongsToOneDeleteOperation');
6
- const { BelongsToOneRelateOperation } = require('./BelongsToOneRelateOperation');
7
- const { BelongsToOneUnrelateOperation } = require('./BelongsToOneUnrelateOperation');
8
-
9
- class BelongsToOneRelation extends Relation {
7
+ export class BelongsToOneRelation extends Relation {
10
8
  isOneToOne() {
11
9
  return true;
12
10
  }
13
11
 
14
- // The foreign key is unset when no related model was ever assigned, e.g. on
15
- // models returned by `insert()` or created with `fromJson()`, where no
16
- // related model is the right result. This can't be told apart from a
17
- // foreign key left out of a partial `select()`. Foreign keys that are part
18
- // of the id are always expected to be set (#132).
19
- isOptionalOwnerProp(index) {
20
- return !this.ownerModelClass.getIdPropertyArray().includes(this.ownerProp.props[index]);
12
+ get keySide() {
13
+ return 'owner';
21
14
  }
22
15
 
23
16
  insert(_, owner) {
@@ -56,7 +49,3 @@ Object.defineProperties(BelongsToOneRelation.prototype, {
56
49
  value: true,
57
50
  },
58
51
  });
59
-
60
- module.exports = {
61
- BelongsToOneRelation,
62
- };
@@ -1,8 +1,6 @@
1
- 'use strict';
1
+ import { UnrelateOperation } from '../../queryBuilder/operations/UnrelateOperation.js';
2
2
 
3
- const { UnrelateOperation } = require('../../queryBuilder/operations/UnrelateOperation');
4
-
5
- class BelongsToOneUnrelateOperation extends UnrelateOperation {
3
+ export class BelongsToOneUnrelateOperation extends UnrelateOperation {
6
4
  onAdd() {
7
5
  const ids = new Array(this.relation.ownerProp.size);
8
6
 
@@ -15,47 +13,75 @@ class BelongsToOneUnrelateOperation extends UnrelateOperation {
15
13
  }
16
14
 
17
15
  queryExecutor(builder) {
16
+ const { relation, owner } = this;
17
+ const { ownerProp, ownerModelClass } = relation;
18
18
  const patch = {};
19
- const ownerProp = this.relation.ownerProp;
20
19
 
21
20
  ownerProp.forEach((i) => {
22
- const relatedValue = this.ids[0][i];
23
-
24
- if (this.owner.isModels) {
25
- for (const owner of this.owner.modelArray) {
26
- ownerProp.setProp(owner, i, relatedValue);
27
- }
28
- }
29
-
30
- ownerProp.patch(patch, i, relatedValue);
21
+ ownerProp.patch(patch, i, this.ids[0][i]);
31
22
  });
32
23
 
33
- const ownerIdProp = this.relation.ownerModelClass.getIdRelationProperty();
24
+ const ownerIdProp = ownerModelClass.getIdRelationProperty();
34
25
  const ownerRefs = ownerIdProp.refs(builder);
35
- const ownerIds = this.owner.getProps(this.relation, ownerIdProp);
26
+ const ownerIds = owner.getProps(relation, ownerIdProp);
27
+ const isFiltered = !!relation.modify || builder.has(builder.constructor.WhereSelector);
28
+
29
+ const clearOwnerKeys = (owners) => {
30
+ for (const owner of owners) {
31
+ ownerProp.forEach((i) => ownerProp.setProp(owner, i, null));
32
+ }
33
+ };
36
34
 
37
- const query = this.relation.ownerModelClass
35
+ const query = ownerModelClass
38
36
  .query()
39
37
  .childQueryOf(builder)
40
38
  .patch(patch)
41
39
  .whereInComposite(ownerRefs, ownerIds);
42
40
 
43
- // We are creating a query to the related items. So any `where` statements
44
- // must filter the *related* items, not the root query above, which is actually
45
- // for the owners.
46
- if (builder.has(builder.constructor.WhereSelector)) {
41
+ if (!isFiltered) {
42
+ if (owner.isModels) {
43
+ clearOwnerKeys(owner.modelArray);
44
+ }
45
+ } else {
46
+ // We are creating a query to the related items. So any `where` statements
47
+ // and the relation's `modify` must filter the *related* items, not the
48
+ // root query above, which is actually for the owners.
47
49
  query.whereExists(
48
- this.relation.ownerModelClass
49
- .relatedQuery(this.relation.name)
50
+ ownerModelClass
51
+ .relatedQuery(relation.name)
52
+ .childQueryOf(builder)
50
53
  .copyFrom(builder, builder.constructor.JoinSelector)
51
54
  .copyFrom(builder, builder.constructor.WhereSelector),
52
55
  );
56
+
57
+ if (owner.isModels) {
58
+ // Only clear the keys of the owner models whose related row matched.
59
+ // Unless all or none did, read back which ones the query unrelated:
60
+ query.runAfter(async (numUnrelated) => {
61
+ if (numUnrelated === ownerIds.length) {
62
+ clearOwnerKeys(owner.modelArray);
63
+ } else if (numUnrelated > 0) {
64
+ const keysQuery = ownerModelClass
65
+ .query()
66
+ .childQueryOf(builder)
67
+ .findOptions({ dontCallFindHooks: true });
68
+ const table = keysQuery.tableRefFor(ownerModelClass);
69
+ // Select the whole columns, also for JSON field expressions:
70
+ const rows = await keysQuery
71
+ .select([...ownerRefs, ...ownerProp.cols.map((col) => `${table}.${col}`)])
72
+ .whereInComposite(ownerRefs, ownerIds);
73
+ const unrelatedIds = new Set(
74
+ rows.filter((row) => !ownerProp.hasProps(row)).map((row) => row.$idKey()),
75
+ );
76
+
77
+ clearOwnerKeys(owner.modelArray.filter((owner) => unrelatedIds.has(owner.$idKey())));
78
+ }
79
+
80
+ return numUnrelated;
81
+ });
82
+ }
53
83
  }
54
84
 
55
85
  return query;
56
86
  }
57
87
  }
58
-
59
- module.exports = {
60
- BelongsToOneUnrelateOperation,
61
- };
@@ -1,9 +1,8 @@
1
- 'use strict';
1
+ import { RelationInsertOperation } from '../RelationInsertOperation.js';
2
+ import { removeOverriddenQueryProps } from '../../model/modelQueryProps.js';
3
+ import { assignRelated } from '../../model/modelSet.js';
2
4
 
3
- const { RelationInsertOperation } = require('../RelationInsertOperation');
4
- const { removeOverriddenQueryProps } = require('../../model/modelQueryProps');
5
-
6
- class HasManyInsertOperation extends RelationInsertOperation {
5
+ export class HasManyInsertOperation extends RelationInsertOperation {
7
6
  onAdd(builder, args) {
8
7
  const retVal = super.onAdd(builder, args);
9
8
  assertOwnerIsSingleItem(this.owner, this.relation);
@@ -32,7 +31,7 @@ class HasManyInsertOperation extends RelationInsertOperation {
32
31
 
33
32
  if (this.owner.isModels) {
34
33
  for (const owner of this.owner.modelArray) {
35
- owner.$appendRelated(this.relation, inserted);
34
+ assignRelated(owner, this.relation, inserted, { append: true });
36
35
  }
37
36
  }
38
37
 
@@ -59,7 +58,3 @@ function assertOwnerIsSingleItem(owner, relation) {
59
58
  );
60
59
  }
61
60
  }
62
-
63
- module.exports = {
64
- HasManyInsertOperation,
65
- };
@@ -1,10 +1,8 @@
1
- 'use strict';
1
+ import { normalizeIds } from '../../utils/normalizeIds.js';
2
+ import { RelateOperation } from '../../queryBuilder/operations/RelateOperation.js';
3
+ import { addWhereGroup } from '../../queryBuilder/whereGroups.js';
2
4
 
3
- const { normalizeIds } = require('../../utils/normalizeIds');
4
- const { RelateOperation } = require('../../queryBuilder/operations/RelateOperation');
5
- const { addWhereGroup } = require('../../queryBuilder/whereGroups');
6
-
7
- class HasManyRelateOperation extends RelateOperation {
5
+ export class HasManyRelateOperation extends RelateOperation {
8
6
  onAdd(_, args) {
9
7
  this.input = args[0];
10
8
 
@@ -33,7 +31,7 @@ class HasManyRelateOperation extends RelateOperation {
33
31
  .copyFrom(builder, builder.constructor.JoinSelector)
34
32
  .copyFrom(builder, builder.constructor.WhereSelector)
35
33
  .modify((query) => {
36
- addWhereGroup(query, () => query.findByIds(this.ids));
34
+ query.findByIds(this.ids);
37
35
  addWhereGroup(query, () => query.modify(this.relation.modify));
38
36
  });
39
37
  }
@@ -58,7 +56,3 @@ function assertOwnerIsSingleItem(owner, relation) {
58
56
  );
59
57
  }
60
58
  }
61
-
62
- module.exports = {
63
- HasManyRelateOperation,
64
- };
@@ -1,11 +1,13 @@
1
- 'use strict';
2
-
3
- const { Relation } = require('../Relation');
4
- const { HasManyInsertOperation } = require('./HasManyInsertOperation');
5
- const { HasManyRelateOperation } = require('./HasManyRelateOperation');
6
- const { HasManyUnrelateOperation } = require('./HasManyUnrelateOperation');
1
+ import { Relation } from '../Relation.js';
2
+ import { HasManyInsertOperation } from './HasManyInsertOperation.js';
3
+ import { HasManyRelateOperation } from './HasManyRelateOperation.js';
4
+ import { HasManyUnrelateOperation } from './HasManyUnrelateOperation.js';
5
+
6
+ export class HasManyRelation extends Relation {
7
+ get keySide() {
8
+ return 'related';
9
+ }
7
10
 
8
- class HasManyRelation extends Relation {
9
11
  insert(_, owner) {
10
12
  return new HasManyInsertOperation('insert', {
11
13
  relation: this,
@@ -43,7 +45,3 @@ Object.defineProperties(HasManyRelation.prototype, {
43
45
  value: true,
44
46
  },
45
47
  });
46
-
47
- module.exports = {
48
- HasManyRelation,
49
- };
@@ -1,9 +1,7 @@
1
- 'use strict';
1
+ import { UnrelateOperation } from '../../queryBuilder/operations/UnrelateOperation.js';
2
+ import { addWhereGroup } from '../../queryBuilder/whereGroups.js';
2
3
 
3
- const { UnrelateOperation } = require('../../queryBuilder/operations/UnrelateOperation');
4
- const { addWhereGroup } = require('../../queryBuilder/whereGroups');
5
-
6
- class HasManyUnrelateOperation extends UnrelateOperation {
4
+ export class HasManyUnrelateOperation extends UnrelateOperation {
7
5
  queryExecutor(builder) {
8
6
  const patch = {};
9
7
  const relatedProp = this.relation.relatedProp;
@@ -26,7 +24,3 @@ class HasManyUnrelateOperation extends UnrelateOperation {
26
24
  });
27
25
  }
28
26
  }
29
-
30
- module.exports = {
31
- HasManyUnrelateOperation,
32
- };
@@ -1,13 +1,7 @@
1
- 'use strict';
1
+ import { HasManyRelation } from '../hasMany/HasManyRelation.js';
2
2
 
3
- const { HasManyRelation } = require('../hasMany/HasManyRelation');
4
-
5
- class HasOneRelation extends HasManyRelation {
3
+ export class HasOneRelation extends HasManyRelation {
6
4
  isOneToOne() {
7
5
  return true;
8
6
  }
9
7
  }
10
-
11
- module.exports = {
12
- HasOneRelation,
13
- };
@@ -1,13 +1,7 @@
1
- 'use strict';
1
+ import { ManyToManyRelation } from '../manyToMany/ManyToManyRelation.js';
2
2
 
3
- const { ManyToManyRelation } = require('../manyToMany/ManyToManyRelation');
4
-
5
- class HasOneThroughRelation extends ManyToManyRelation {
3
+ export class HasOneThroughRelation extends ManyToManyRelation {
6
4
  isOneToOne() {
7
5
  return true;
8
6
  }
9
7
  }
10
-
11
- module.exports = {
12
- HasOneThroughRelation,
13
- };