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
@@ -1,9 +1,9 @@
1
- 'use strict';
1
+ import { FindOperation } from '../queryBuilder/operations/FindOperation.js';
2
+ import { RangeOperation } from '../queryBuilder/operations/RangeOperation.js';
3
+ import { asArray } from '../utils/objectUtils.js';
4
+ import { hasAliasSelection } from '../queryBuilder/operations/select/Selection.js';
2
5
 
3
- const { FindOperation } = require('../queryBuilder/operations/FindOperation');
4
- const { warnOnce } = require('../utils/warnings');
5
-
6
- class RelationFindOperation extends FindOperation {
6
+ export class RelationFindOperation extends FindOperation {
7
7
  constructor(name, opt) {
8
8
  super(name, opt);
9
9
 
@@ -13,6 +13,8 @@ class RelationFindOperation extends FindOperation {
13
13
  this.assignResultToOwner = true;
14
14
  this.relationProperty = opt.relationProperty || this.relation.name;
15
15
  this.alias = null;
16
+ // Shared with the clones that execute the query, for `assignResult()`.
17
+ this.ownerKeyByRelated = new Map();
16
18
  }
17
19
 
18
20
  onBuild(builder) {
@@ -20,7 +22,7 @@ class RelationFindOperation extends FindOperation {
20
22
  this.relation.findQuery(builder, this.owner);
21
23
 
22
24
  if (this.owner.isModels) {
23
- this.warnOnInvalidOwnerProps();
25
+ this.checkOwnerProps();
24
26
 
25
27
  if (this.assignResultToOwner) {
26
28
  this.selectMissingJoinColumns(builder);
@@ -29,51 +31,82 @@ class RelationFindOperation extends FindOperation {
29
31
  }
30
32
 
31
33
  onAfter2(_, related) {
32
- const isOneToOne = this.relation.isOneToOne();
33
-
34
34
  if (this.assignResultToOwner && this.owner.isModels) {
35
- const owners = this.owner.modelArray;
36
- const relatedByOwnerId = new Map();
37
-
38
- for (let i = 0, l = related.length; i < l; ++i) {
39
- const rel = related[i];
40
- const key = this.relation.relatedProp.propKey(rel);
41
- let arr = relatedByOwnerId.get(key);
35
+ this.checkRelatedProps(related);
36
+ // Read the owner keys before the join columns that were only selected
37
+ // for them are omitted from the related models.
38
+ for (const rel of related) {
39
+ this.ownerKeyByRelated.set(rel, this.getOwnerKey(rel));
40
+ }
41
+ }
42
42
 
43
- if (!arr) {
44
- arr = [];
45
- relatedByOwnerId.set(key, arr);
46
- }
43
+ return related;
44
+ }
47
45
 
48
- arr.push(rel);
49
- }
46
+ onAfter3(builder, related) {
47
+ const isOneToOne = this.relation.isOneToOne();
50
48
 
51
- this.warnOnMissingRelatedProps(related);
49
+ if (!this.alwaysReturnArray && isOneToOne && related.length <= 1) {
50
+ related = related[0] || undefined;
51
+ }
52
52
 
53
- for (let i = 0, l = owners.length; i < l; ++i) {
54
- const own = owners[i];
55
- const key = this.relation.ownerProp.propKey(own);
56
- const related = relatedByOwnerId.get(key);
53
+ return super.onAfter3(builder, related);
54
+ }
57
55
 
58
- if (isOneToOne) {
59
- own[this.relationProperty] = (related && related[0]) || null;
60
- } else {
61
- own[this.relationProperty] = related || [];
56
+ // Assign what the query returns to the owners, once it has resolved, after its
57
+ // `afterFind()` hook and `runAfter()` callbacks, which may filter or reorder
58
+ // the related models. `range()` and `page()` wrap the models in `{ results, total }`.
59
+ assignResult(builder, result) {
60
+ if (this.assignResultToOwner && this.owner.isModels) {
61
+ const related = asArray(builder.has(RangeOperation) ? result.results : result);
62
+
63
+ for (const rel of related) {
64
+ if (!this.ownerKeyByRelated.has(rel)) {
65
+ throw new Error(
66
+ `Unable to assign the result to relation "${this.relation.name}" of ` +
67
+ `${this.relation.ownerModelClass.name}: afterFind() and runAfter() can only ` +
68
+ 'return the related models that the query found, filtered or reordered.',
69
+ );
62
70
  }
63
71
  }
72
+
73
+ this.assignRelated(related);
64
74
  }
65
75
 
66
- return related;
76
+ return result;
67
77
  }
68
78
 
69
- onAfter3(builder, related) {
79
+ // @overridable
80
+ assignRelated(related) {
70
81
  const isOneToOne = this.relation.isOneToOne();
82
+ const relatedByOwnerKey = new Map();
71
83
 
72
- if (!this.alwaysReturnArray && isOneToOne && related.length <= 1) {
73
- related = related[0] || undefined;
84
+ for (const rel of related) {
85
+ const key = this.ownerKeyByRelated.get(rel);
86
+ let arr = relatedByOwnerKey.get(key);
87
+
88
+ if (!arr) {
89
+ arr = [];
90
+ relatedByOwnerKey.set(key, arr);
91
+ }
92
+
93
+ arr.push(rel);
74
94
  }
75
95
 
76
- return super.onAfter3(builder, related);
96
+ for (const owner of this.owner.modelArray) {
97
+ const related = relatedByOwnerKey.get(this.relation.ownerProp.propKey(owner));
98
+
99
+ if (isOneToOne) {
100
+ owner[this.relationProperty] = (related && related[0]) || null;
101
+ } else {
102
+ owner[this.relationProperty] = related || [];
103
+ }
104
+ }
105
+ }
106
+
107
+ // The key of the owner that a related model belongs to:
108
+ getOwnerKey(related) {
109
+ return this.relation.relatedProp.propKey(related);
77
110
  }
78
111
 
79
112
  // The expressions of the result rows in `table` that hold the keys of their
@@ -84,74 +117,69 @@ class RelationFindOperation extends FindOperation {
84
117
  .map((ref) => ref.table(table).toKnexRaw(builder));
85
118
  }
86
119
 
87
- warnOnInvalidOwnerProps() {
88
- this.warnOnMissingOwnerProps();
89
-
90
- if (this.assignResultToOwner) {
91
- this.warnOnArrayOwnerProps();
92
- }
93
- }
94
-
95
- // Owner models without the join columns, e.g. loaded with a partial
96
- // `select()`, silently get no related models (#1832). Only `undefined`
97
- // is reported, as `null` is a legit value for a missing relation, and
98
- // owner props that may legitimately be unset are skipped (#132).
99
- // TODO: Consider throwing an error instead in objection 4.0 (#272).
100
- warnOnMissingOwnerProps() {
101
- const { ownerProp } = this.relation;
120
+ checkOwnerProps() {
121
+ const { name, ownerModelClass, ownerProp } = this.relation;
122
+ const owners = this.owner.modelArray;
102
123
 
103
124
  for (let c = 0, lc = ownerProp.size; c < lc; ++c) {
104
- if (this.relation.isOptionalOwnerProp(c)) {
105
- continue;
106
- }
107
-
108
- if (this.owner.modelArray.some((owner) => ownerProp.getProp(owner, c) === undefined)) {
109
- warnOnce(
110
- `Fetching relation "${this.relation.name}" of ${this.relation.ownerModelClass.name}: ` +
111
- `some owner models are missing the join property "${ownerProp.propDescription(c)}", ` +
112
- 'so no related models are found for them. Select the column in the owner query.',
125
+ // Owner models loaded without the join columns, e.g. with a partial
126
+ // `select()`, would silently get no related models (#1832). Models built
127
+ // in memory, e.g. returned by `insert()`, may not have them yet (#132).
128
+ // Check the columns, not the values of JSON field expressions in them,
129
+ // which a JSON document may leave out.
130
+ const prop = ownerProp.props[c];
131
+
132
+ if (owners.some((owner) => owner.$$isFromDatabase && owner[prop] === undefined)) {
133
+ throw new Error(
134
+ `Unable to fetch relation "${name}" of ${ownerModelClass.name}: some owner models ` +
135
+ 'loaded from the database are missing the join property ' +
136
+ `"${ownerProp.propDescription(c)}". Select the column in the owner query.`,
113
137
  );
114
138
  }
115
139
  }
140
+
141
+ if (this.assignResultToOwner) {
142
+ this.checkOwnerKeys();
143
+ }
116
144
  }
117
145
 
118
- // Owner join properties holding an array of keys, e.g. a JSON array column,
119
- // aren't supported: The query finds the related models, but they can't be
120
- // assigned to their owners, so all owners silently get no related models
121
- // (#2667). `$relatedQuery()` without assigning the results isn't affected.
122
- warnOnArrayOwnerProps() {
123
- const { ownerProp, joinTable } = this.relation;
124
- // Relations through a join table already are the alternative to arrays.
125
- const advice = joinTable
126
- ? 'Map the relation from a column that holds a single key instead.'
127
- : 'Use a ManyToManyRelation through a join table instead.';
146
+ // Related models can't be assigned to owner props holding an array of keys,
147
+ // e.g. a JSON array column (#2667). `$relatedQuery()` without assigning the
148
+ // results isn't affected.
149
+ // @overridable
150
+ checkOwnerKeys() {
151
+ const { name, ownerModelClass, ownerProp, joinTable } = this.relation;
152
+ const owners = this.owner.modelArray;
128
153
 
129
154
  for (let c = 0, lc = ownerProp.size; c < lc; ++c) {
130
- if (this.owner.modelArray.some((owner) => Array.isArray(ownerProp.getProp(owner, c)))) {
131
- warnOnce(
132
- `Fetching relation "${this.relation.name}" of ${this.relation.ownerModelClass.name}: ` +
133
- `the join property "${ownerProp.propDescription(c)}" of some owner models holds ` +
134
- 'an array, which is not supported, so no related models are found for them. ' +
135
- advice,
155
+ if (owners.some((owner) => Array.isArray(ownerProp.getProp(owner, c)))) {
156
+ throw new Error(
157
+ `Unable to fetch relation "${name}" of ${ownerModelClass.name}: the join property ` +
158
+ `"${ownerProp.propDescription(c)}" of some owner models holds an array, which is ` +
159
+ `not supported. ${
160
+ joinTable
161
+ ? 'Map the relation from a column that holds a single key instead.'
162
+ : 'Use a BelongsToManyRelation or a ManyToManyRelation through a join table instead.'
163
+ }`,
136
164
  );
137
165
  }
138
166
  }
139
167
  }
140
168
 
141
169
  // Related models without the join properties can't be assigned to their
142
- // owners and are silently dropped (#2258). This happens for example when the
143
- // relation mapping uses column names but the rows come back with different
144
- // property names, e.g. snake_case mappings with `knexSnakeCaseMappers`.
145
- warnOnMissingRelatedProps(related) {
146
- const { relatedProp } = this.relation;
170
+ // owners (#2258). This happens for example when the relation mapping uses
171
+ // column names but the rows come back with different property names, e.g.
172
+ // snake_case mappings with `knexSnakeCaseMappers`.
173
+ checkRelatedProps(related) {
174
+ const { name, ownerModelClass, relatedProp } = this.relation;
147
175
 
148
176
  for (let c = 0, lc = relatedProp.size; c < lc; ++c) {
149
177
  if (related.some((rel) => relatedProp.getProp(rel, c) === undefined)) {
150
- warnOnce(
151
- `Fetching relation "${this.relation.name}" of ${this.relation.ownerModelClass.name}: ` +
152
- `some related models are missing the join property "${relatedProp.propDescription(c)}", ` +
153
- 'so they are not assigned to any owner. Check that the relation mapping uses the ' +
154
- 'property names of the models, e.g. camelCase names when using knexSnakeCaseMappers.',
178
+ throw new Error(
179
+ `Unable to fetch relation "${name}" of ${ownerModelClass.name}: some related models ` +
180
+ `are missing the join property "${relatedProp.propDescription(c)}". Check that the ` +
181
+ 'relation mapping uses the property names of the models, e.g. camelCase names when ' +
182
+ 'using knexSnakeCaseMappers.',
155
183
  );
156
184
  }
157
185
  }
@@ -166,7 +194,11 @@ class RelationFindOperation extends FindOperation {
166
194
  const prop = relatedProp.props[c];
167
195
  const col = relatedProp.cols[c];
168
196
 
169
- if (!builder.hasSelectionAs(fullCol, col) && addedSelects.indexOf(fullCol) === -1) {
197
+ if (
198
+ !builder.hasSelectionAs(fullCol, col) &&
199
+ !hasAliasSelection(builder, col) &&
200
+ addedSelects.indexOf(fullCol) === -1
201
+ ) {
170
202
  this.omitFromResult(builder, prop);
171
203
  addedSelects.push(fullCol);
172
204
  }
@@ -198,11 +230,8 @@ class RelationFindOperation extends FindOperation {
198
230
  clone.assignResultToOwner = this.assignResultToOwner;
199
231
  clone.relationProperty = this.relationProperty;
200
232
  clone.alias = this.alias;
233
+ clone.ownerKeyByRelated = this.ownerKeyByRelated;
201
234
 
202
235
  return clone;
203
236
  }
204
237
  }
205
-
206
- module.exports = {
207
- RelationFindOperation,
208
- };
@@ -1,8 +1,6 @@
1
- 'use strict';
1
+ import { InsertOperation } from '../queryBuilder/operations/InsertOperation.js';
2
2
 
3
- const { InsertOperation } = require('../queryBuilder/operations/InsertOperation');
4
-
5
- class RelationInsertOperation extends InsertOperation {
3
+ export class RelationInsertOperation extends InsertOperation {
6
4
  constructor(name, opt) {
7
5
  super(name, opt);
8
6
 
@@ -39,7 +37,3 @@ class RelationInsertOperation extends InsertOperation {
39
37
  return clone;
40
38
  }
41
39
  }
42
-
43
- module.exports = {
44
- RelationInsertOperation,
45
- };
@@ -1,8 +1,6 @@
1
- 'use strict';
2
-
3
- const { isObject, asArray, asSingle, uniqBy } = require('../utils/objectUtils');
4
- const { normalizeIds } = require('../utils/normalizeIds');
5
- const { valuesKey } = require('../model/modelValues');
1
+ import { isObject, asArray, asSingle, uniqBy } from '../utils/objectUtils.js';
2
+ import { normalizeIds } from '../utils/normalizeIds.js';
3
+ import { valuesKey } from '../model/modelValues.js';
6
4
 
7
5
  const Type = {
8
6
  Models: 'Models',
@@ -11,7 +9,7 @@ const Type = {
11
9
  Identifiers: 'Identifiers',
12
10
  };
13
11
 
14
- class RelationOwner {
12
+ export class RelationOwner {
15
13
  constructor(owner) {
16
14
  this.owner = owner;
17
15
  this.type = detectType(owner);
@@ -224,10 +222,6 @@ function isIdProp(relationProp) {
224
222
  return idProp.props.every((prop, i) => prop === relationProp.props[i]);
225
223
  }
226
224
 
227
- function isOwnerModelClassQuery(query, relation) {
225
+ export function isOwnerModelClassQuery(query, relation) {
228
226
  return query.modelClass().getTableName() === relation.ownerModelClass.getTableName();
229
227
  }
230
-
231
- module.exports = {
232
- RelationOwner,
233
- };
@@ -1,8 +1,6 @@
1
- 'use strict';
2
-
3
- const { asArray, isObject, uniqBy, get, set } = require('../utils/objectUtils');
4
- const { ref: createRef } = require('../queryBuilder/ReferenceBuilder');
5
- const { valuesKey } = require('../model/modelValues');
1
+ import { asArray, isObject, uniqBy, get, set } from '../utils/objectUtils.js';
2
+ import { ref as createRef } from '../queryBuilder/ReferenceBuilder.js';
3
+ import { valuesKey } from '../model/modelValues.js';
6
4
 
7
5
  class ModelNotFoundError extends Error {
8
6
  constructor(tableName) {
@@ -25,7 +23,7 @@ class InvalidReferenceError extends Error {
25
23
  // A relation property can be a single column, an array of columns
26
24
  // (composite key) a json column reference, an array of json column
27
25
  // references or any combination of the above.
28
- class RelationProperty {
26
+ export class RelationProperty {
29
27
  // references must be a reference string like `Table.column:maybe.some.json[1].path`.
30
28
  // or an array of such references (composite key).
31
29
  //
@@ -235,7 +233,3 @@ function createPatcher(ref, path) {
235
233
  return (patch, value) => (patch[ref.expression] = value);
236
234
  }
237
235
  }
238
-
239
- module.exports = {
240
- RelationProperty,
241
- };
@@ -1,9 +1,6 @@
1
- 'use strict';
1
+ import { UpdateOperation } from '../queryBuilder/operations/UpdateOperation.js';
2
2
 
3
- const { UpdateOperation } = require('../queryBuilder/operations/UpdateOperation');
4
- const { RelationFindOperation } = require('./RelationFindOperation');
5
-
6
- class RelationUpdateOperation extends UpdateOperation {
3
+ export class RelationUpdateOperation extends UpdateOperation {
7
4
  constructor(name, opt) {
8
5
  super(name, opt);
9
6
 
@@ -17,14 +14,7 @@ class RelationUpdateOperation extends UpdateOperation {
17
14
  this.relation.findQuery(builder, this.owner);
18
15
  }
19
16
 
20
- toFindOperation() {
21
- return new RelationFindOperation('find', {
22
- relation: this.relation,
23
- owner: this.owner,
24
- });
17
+ toFindOperation(builder) {
18
+ return this.relation.find(builder, this.owner);
25
19
  }
26
20
  }
27
-
28
- module.exports = {
29
- RelationUpdateOperation,
30
- };
@@ -0,0 +1,15 @@
1
+ import { QueryBuilderOperation } from '../queryBuilder/operations/QueryBuilderOperation.js';
2
+
3
+ // Resolves the rows that writes through `$relatedQuery()` of a to-one relation
4
+ // return with `returning()` like its finds: one model, or `undefined`.
5
+ export class RelationWriteResultOperation extends QueryBuilderOperation {
6
+ onAfter3(_, result) {
7
+ return Array.isArray(result) && result.length <= 1 ? result[0] : result;
8
+ }
9
+
10
+ // Find queries of the write, e.g. `asFindQuery()` in hooks, resolve to all
11
+ // rows that it writes.
12
+ toFindOperation() {
13
+ return null;
14
+ }
15
+ }
@@ -0,0 +1,23 @@
1
+ import { RelationDeleteOperation } from '../RelationDeleteOperation.js';
2
+
3
+ export class BelongsToManyDeleteOperation extends RelationDeleteOperation {
4
+ // Find the keys of the rows to delete first, to remove them from the owners
5
+ // after the delete.
6
+ async onBefore2(builder, result) {
7
+ result = await super.onBefore2(builder, result);
8
+
9
+ if (builder.isExecutable()) {
10
+ this.keys = await this.relation.findKeys(builder, this.owner);
11
+ }
12
+
13
+ return result;
14
+ }
15
+
16
+ async onAfter1(builder, result) {
17
+ if (this.keys) {
18
+ await this.relation.updateKeys(builder, this.owner, { remove: this.keys });
19
+ }
20
+
21
+ return result;
22
+ }
23
+ }
@@ -0,0 +1,35 @@
1
+ import { RelationFindOperation } from '../RelationFindOperation.js';
2
+ import { valuesKey } from '../../model/modelValues.js';
3
+ import { groupBy } from '../../utils/objectUtils.js';
4
+
5
+ export class BelongsToManyFindOperation extends RelationFindOperation {
6
+ // Arrays of keys are what the owner props hold here, see `getKeys()`.
7
+ // @override
8
+ checkOwnerKeys() {}
9
+
10
+ // A related model belongs to every owner that holds its key. They keep the
11
+ // order of the query, not the order of the keys in the arrays.
12
+ // @override
13
+ assignRelated(related) {
14
+ const indexByRelated = new Map(related.map((rel, index) => [rel, index]));
15
+ const relatedByKey = groupBy(related, (rel) => this.ownerKeyByRelated.get(rel));
16
+
17
+ for (const owner of this.owner.modelArray) {
18
+ const keys = new Set(this.relation.getKeys(owner).map((key) => valuesKey([key])));
19
+
20
+ owner[this.relationProperty] = [...keys]
21
+ .flatMap((key) => relatedByKey.get(key) || [])
22
+ .sort((a, b) => indexByRelated.get(a) - indexByRelated.get(b));
23
+ }
24
+ }
25
+
26
+ // The related models of all owners come back in one list, without a column
27
+ // to partition them by owner.
28
+ // @override
29
+ ownerKeys() {
30
+ throw this.relation.createError(
31
+ 'limit(), offset() and first() are not supported for each owner. Pass ' +
32
+ '`limitPerParent: false` to the graph options to limit all owners together.',
33
+ );
34
+ }
35
+ }
@@ -0,0 +1,28 @@
1
+ import { RelationInsertOperation } from '../RelationInsertOperation.js';
2
+ import { InsertGraphOperation } from '../../queryBuilder/operations/InsertGraphOperation.js';
3
+ import { assignRelated } from '../../model/modelSet.js';
4
+
5
+ export class BelongsToManyInsertOperation extends RelationInsertOperation {
6
+ async onAfter1(builder, ret) {
7
+ const inserted = await super.onAfter1(builder, ret);
8
+
9
+ // A query resolved without executing it, e.g. with `resolve()`, inserted
10
+ // nothing. `insertGraph()` resolves the query itself, but always inserts
11
+ // the graph.
12
+ if (!builder.isExecutable() && !builder.has(InsertGraphOperation)) {
13
+ return inserted;
14
+ }
15
+
16
+ if (this.assignResultToOwner && this.owner.isModels) {
17
+ for (const owner of this.owner.modelArray) {
18
+ assignRelated(owner, this.relation, inserted, { append: true });
19
+ }
20
+ }
21
+
22
+ await this.relation.updateKeys(builder, this.owner, {
23
+ add: this.models.map((model) => this.relation.relatedProp.getProp(model, 0)),
24
+ });
25
+
26
+ return inserted;
27
+ }
28
+ }
@@ -0,0 +1,16 @@
1
+ import { normalizeIds } from '../../utils/normalizeIds.js';
2
+ import { RelateOperation } from '../../queryBuilder/operations/RelateOperation.js';
3
+
4
+ export class BelongsToManyRelateOperation extends RelateOperation {
5
+ onAdd(_, args) {
6
+ this.input = args[0];
7
+ this.ids = normalizeIds(args[0], this.relation.relatedProp, { arrayOutput: true });
8
+ return true;
9
+ }
10
+
11
+ queryExecutor(builder) {
12
+ return this.relation.updateKeys(builder, this.owner, {
13
+ add: this.ids.map(([key]) => key),
14
+ });
15
+ }
16
+ }