@abedmurrar/objection 0.0.0-stage → 4.0.0-alpha.0

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 (190) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +69 -2
  3. package/lib/.eslintrc.json +8 -0
  4. package/lib/initialize.js +14 -0
  5. package/lib/model/AjvValidator.js +325 -0
  6. package/lib/model/Model.js +942 -0
  7. package/lib/model/ModifierNotFoundError.js +12 -0
  8. package/lib/model/NotFoundError.js +23 -0
  9. package/lib/model/RelationDoesNotExistError.js +14 -0
  10. package/lib/model/ValidationError.js +54 -0
  11. package/lib/model/Validator.js +26 -0
  12. package/lib/model/getModel.js +9 -0
  13. package/lib/model/graph/ModelGraph.js +80 -0
  14. package/lib/model/graph/ModelGraphBuilder.js +241 -0
  15. package/lib/model/graph/ModelGraphEdge.js +52 -0
  16. package/lib/model/graph/ModelGraphNode.js +240 -0
  17. package/lib/model/inheritModel.js +28 -0
  18. package/lib/model/modelBindKnex.js +83 -0
  19. package/lib/model/modelClone.js +121 -0
  20. package/lib/model/modelColPropMap.js +34 -0
  21. package/lib/model/modelId.js +62 -0
  22. package/lib/model/modelJsonAttributes.js +92 -0
  23. package/lib/model/modelParseRelations.js +82 -0
  24. package/lib/model/modelQueryProps.js +147 -0
  25. package/lib/model/modelSet.js +136 -0
  26. package/lib/model/modelTableMetadata.js +83 -0
  27. package/lib/model/modelToJson.js +187 -0
  28. package/lib/model/modelUtils.js +47 -0
  29. package/lib/model/modelValidate.js +48 -0
  30. package/lib/model/modelValues.js +126 -0
  31. package/lib/model/modelVisitor.js +41 -0
  32. package/lib/objection.js +114 -0
  33. package/lib/queryBuilder/FunctionBuilder.js +339 -0
  34. package/lib/queryBuilder/InternalOptions.js +29 -0
  35. package/lib/queryBuilder/JoinBuilder.js +157 -0
  36. package/lib/queryBuilder/QueryBuilder.js +1398 -0
  37. package/lib/queryBuilder/QueryBuilderBase.js +884 -0
  38. package/lib/queryBuilder/QueryBuilderContext.js +27 -0
  39. package/lib/queryBuilder/QueryBuilderContextBase.js +35 -0
  40. package/lib/queryBuilder/QueryBuilderOperationSupport.js +558 -0
  41. package/lib/queryBuilder/QueryBuilderUserContext.js +31 -0
  42. package/lib/queryBuilder/RawBuilder.js +93 -0
  43. package/lib/queryBuilder/ReferenceBuilder.js +284 -0
  44. package/lib/queryBuilder/RelationExpression.js +431 -0
  45. package/lib/queryBuilder/StaticHookArguments.js +134 -0
  46. package/lib/queryBuilder/ValueBuilder.js +127 -0
  47. package/lib/queryBuilder/expressions.js +151 -0
  48. package/lib/queryBuilder/functions/mysql.js +817 -0
  49. package/lib/queryBuilder/graph/GraphAction.js +69 -0
  50. package/lib/queryBuilder/graph/GraphData.js +14 -0
  51. package/lib/queryBuilder/graph/GraphFetcher.js +191 -0
  52. package/lib/queryBuilder/graph/GraphNodeDbExistence.js +184 -0
  53. package/lib/queryBuilder/graph/GraphOperation.js +27 -0
  54. package/lib/queryBuilder/graph/GraphOptions.js +207 -0
  55. package/lib/queryBuilder/graph/GraphUpsert.js +305 -0
  56. package/lib/queryBuilder/graph/delete/GraphDelete.js +20 -0
  57. package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +74 -0
  58. package/lib/queryBuilder/graph/insert/GraphInsert.js +145 -0
  59. package/lib/queryBuilder/graph/insert/GraphInsertAction.js +225 -0
  60. package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +92 -0
  61. package/lib/queryBuilder/graph/patch/GraphPatch.js +20 -0
  62. package/lib/queryBuilder/graph/patch/GraphPatchAction.js +258 -0
  63. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +31 -0
  64. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +126 -0
  65. package/lib/queryBuilder/join/JoinResultColumn.js +23 -0
  66. package/lib/queryBuilder/join/JoinResultParser.js +119 -0
  67. package/lib/queryBuilder/join/RelationJoiner.js +341 -0
  68. package/lib/queryBuilder/join/TableNode.js +163 -0
  69. package/lib/queryBuilder/join/TableTree.js +85 -0
  70. package/lib/queryBuilder/join/utils.js +25 -0
  71. package/lib/queryBuilder/operations/DelegateOperation.js +141 -0
  72. package/lib/queryBuilder/operations/DeleteOperation.js +51 -0
  73. package/lib/queryBuilder/operations/FindByIdOperation.js +37 -0
  74. package/lib/queryBuilder/operations/FindByIdsOperation.js +29 -0
  75. package/lib/queryBuilder/operations/FindOperation.js +164 -0
  76. package/lib/queryBuilder/operations/FirstOperation.js +27 -0
  77. package/lib/queryBuilder/operations/FromOperation.js +110 -0
  78. package/lib/queryBuilder/operations/InsertAndFetchOperation.js +49 -0
  79. package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +45 -0
  80. package/lib/queryBuilder/operations/InsertGraphOperation.js +108 -0
  81. package/lib/queryBuilder/operations/InsertOperation.js +206 -0
  82. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +46 -0
  83. package/lib/queryBuilder/operations/InstanceFindOperation.js +21 -0
  84. package/lib/queryBuilder/operations/InstanceInsertOperation.js +24 -0
  85. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +59 -0
  86. package/lib/queryBuilder/operations/JoinRelatedOperation.js +103 -0
  87. package/lib/queryBuilder/operations/KnexOperation.js +14 -0
  88. package/lib/queryBuilder/operations/MergeOperation.js +56 -0
  89. package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +192 -0
  90. package/lib/queryBuilder/operations/OnBuildKnexOperation.js +29 -0
  91. package/lib/queryBuilder/operations/OnBuildOperation.js +29 -0
  92. package/lib/queryBuilder/operations/OnErrorOperation.js +29 -0
  93. package/lib/queryBuilder/operations/QueryBuilderOperation.js +297 -0
  94. package/lib/queryBuilder/operations/RangeOperation.js +47 -0
  95. package/lib/queryBuilder/operations/RelateOperation.js +27 -0
  96. package/lib/queryBuilder/operations/ReturningOperation.js +30 -0
  97. package/lib/queryBuilder/operations/RunAfterOperation.js +29 -0
  98. package/lib/queryBuilder/operations/RunBeforeOperation.js +29 -0
  99. package/lib/queryBuilder/operations/UnrelateOperation.js +23 -0
  100. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +80 -0
  101. package/lib/queryBuilder/operations/UpdateOperation.js +155 -0
  102. package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +45 -0
  103. package/lib/queryBuilder/operations/UpsertGraphOperation.js +66 -0
  104. package/lib/queryBuilder/operations/WhereCompositeOperation.js +57 -0
  105. package/lib/queryBuilder/operations/WhereInOperation.js +48 -0
  106. package/lib/queryBuilder/operations/eager/EagerOperation.js +61 -0
  107. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +51 -0
  108. package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +13 -0
  109. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +285 -0
  110. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +36 -0
  111. package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +53 -0
  112. package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +31 -0
  113. package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +31 -0
  114. package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +167 -0
  115. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +164 -0
  116. package/lib/queryBuilder/operations/select/SelectOperation.js +66 -0
  117. package/lib/queryBuilder/operations/select/Selection.js +123 -0
  118. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +117 -0
  119. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +100 -0
  120. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +104 -0
  121. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +886 -0
  122. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs +97 -0
  123. package/lib/queryBuilder/parsers/relationExpressionParser.js +1042 -0
  124. package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +141 -0
  125. package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +22 -0
  126. package/lib/queryBuilder/transformations/QueryTransformation.js +11 -0
  127. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +71 -0
  128. package/lib/queryBuilder/transformations/index.js +15 -0
  129. package/lib/queryBuilder/whereScopes.js +227 -0
  130. package/lib/relations/Relation.js +401 -0
  131. package/lib/relations/RelationDeleteOperation.js +29 -0
  132. package/lib/relations/RelationFindOperation.js +149 -0
  133. package/lib/relations/RelationInsertOperation.js +33 -0
  134. package/lib/relations/RelationOwner.js +236 -0
  135. package/lib/relations/RelationProperty.js +247 -0
  136. package/lib/relations/RelationUpdateOperation.js +30 -0
  137. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +23 -0
  138. package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +44 -0
  139. package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +50 -0
  140. package/lib/relations/belongsToOne/BelongsToOneRelation.js +53 -0
  141. package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +61 -0
  142. package/lib/relations/hasMany/HasManyInsertOperation.js +61 -0
  143. package/lib/relations/hasMany/HasManyRelateOperation.js +64 -0
  144. package/lib/relations/hasMany/HasManyRelation.js +49 -0
  145. package/lib/relations/hasMany/HasManyUnrelateOperation.js +33 -0
  146. package/lib/relations/hasOne/HasOneRelation.js +13 -0
  147. package/lib/relations/hasOneThrough/HasOneThroughRelation.js +13 -0
  148. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +105 -0
  149. package/lib/relations/manyToMany/ManyToManyRelation.js +507 -0
  150. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +36 -0
  151. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +10 -0
  152. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +26 -0
  153. package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +12 -0
  154. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +105 -0
  155. package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +52 -0
  156. package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +81 -0
  157. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +14 -0
  158. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +34 -0
  159. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +16 -0
  160. package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +10 -0
  161. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +82 -0
  162. package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +12 -0
  163. package/lib/transaction.js +96 -0
  164. package/lib/utils/assert.js +25 -0
  165. package/lib/utils/buildUtils.js +21 -0
  166. package/lib/utils/classUtils.js +13 -0
  167. package/lib/utils/clone.js +2329 -0
  168. package/lib/utils/createModifier.js +42 -0
  169. package/lib/utils/deprecate.js +15 -0
  170. package/lib/utils/identifierMapping.js +227 -0
  171. package/lib/utils/internalPropUtils.js +11 -0
  172. package/lib/utils/knexUtils.js +76 -0
  173. package/lib/utils/mixin.js +25 -0
  174. package/lib/utils/mysqlJsonPath.js +55 -0
  175. package/lib/utils/normalizeIds.js +144 -0
  176. package/lib/utils/objectUtils.js +416 -0
  177. package/lib/utils/parseFieldExpression.js +51 -0
  178. package/lib/utils/postgresJsonPath.js +110 -0
  179. package/lib/utils/promiseUtils/after.js +17 -0
  180. package/lib/utils/promiseUtils/afterReturn.js +17 -0
  181. package/lib/utils/promiseUtils/index.js +17 -0
  182. package/lib/utils/promiseUtils/isPromise.js +11 -0
  183. package/lib/utils/promiseUtils/map.js +110 -0
  184. package/lib/utils/promiseUtils/mapAfterAllReturn.js +29 -0
  185. package/lib/utils/promiseUtils/try.js +22 -0
  186. package/lib/utils/resolveModel.js +107 -0
  187. package/lib/utils/tmpColumnUtils.js +16 -0
  188. package/package.json +86 -4
  189. package/typings/objection/index.d.ts +4104 -0
  190. package/typings/objection/relation-expression.d.ts +237 -0
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+
3
+ const { GraphAction } = require('../GraphAction');
4
+ const { groupBy, get, set } = require('../../../utils/objectUtils');
5
+ const { forEachPropertyReference } = require('../../../model/graph/ModelGraphBuilder');
6
+ const promiseUtils = require('../../../utils/promiseUtils');
7
+
8
+ class GraphRecursiveUpsertAction extends GraphAction {
9
+ constructor(graphData, { nodes }) {
10
+ super(graphData);
11
+ // Nodes to upsert.
12
+ this.nodes = nodes;
13
+ }
14
+
15
+ run(builder) {
16
+ const builders = this._createUpsertBuilders(builder, this.nodes);
17
+
18
+ return promiseUtils.map(builders, (builder) => builder.execute(), {
19
+ concurrency: this._getConcurrency(builder, this.nodes),
20
+ });
21
+ }
22
+
23
+ _createUpsertBuilders(parentBuilder, nodesToUpsert) {
24
+ const nodesByRelation = groupBy(nodesToUpsert, getRelation);
25
+ const builders = [];
26
+
27
+ nodesByRelation.forEach((nodes) => {
28
+ const nodesByParent = groupBy(nodes, getParent);
29
+
30
+ nodesByParent.forEach((nodes) => {
31
+ for (const node of nodes) {
32
+ this._resolveReferences(node);
33
+ node.userData.upserted = true;
34
+ }
35
+
36
+ builders.push(
37
+ nodes[0].modelClass
38
+ .query()
39
+ .childQueryOf(parentBuilder)
40
+ .copyFrom(parentBuilder, GraphAction.ReturningAllSelector)
41
+ .upsertGraph(
42
+ nodes.map((node) => node.obj),
43
+ this.graphOptions.rebasedOptions(nodes[0]),
44
+ ),
45
+ );
46
+ });
47
+ });
48
+
49
+ return builders;
50
+ }
51
+
52
+ /**
53
+ * The nodes inside the subgraph we are about to recursively upsert may
54
+ * have references outside that graph that won't be available during the
55
+ * recursive upsertGraph call. This method resolves the references.
56
+ *
57
+ * TODO: This doesn't work if a recursively upserted node refers to
58
+ * a node inside another recursively upsertable graph.
59
+ */
60
+ _resolveReferences(node) {
61
+ node.obj.$traverse((obj) => this._resolveReferencesForObject(obj));
62
+ }
63
+
64
+ _resolveReferencesForObject(obj) {
65
+ this._resolveObjectReference(obj);
66
+ this._resolvePropertyReferences(obj);
67
+ }
68
+
69
+ _resolveObjectReference(obj) {
70
+ const modelClass = obj.constructor;
71
+ const ref = obj[modelClass.uidRefProp];
72
+
73
+ if (!ref) {
74
+ return;
75
+ }
76
+
77
+ const referencedNode = this.graph.nodes.find((it) => it.uid === ref);
78
+
79
+ if (!referencedNode) {
80
+ return;
81
+ }
82
+
83
+ const relationNames = referencedNode.modelClass.getRelationNames();
84
+
85
+ for (const prop of Object.keys(referencedNode.obj)) {
86
+ if (relationNames.includes(prop)) {
87
+ continue;
88
+ }
89
+
90
+ obj[prop] = referencedNode.obj[prop];
91
+ }
92
+
93
+ delete obj[modelClass.uidRefProp];
94
+ }
95
+
96
+ _resolvePropertyReferences(obj) {
97
+ forEachPropertyReference(obj, ({ path, refMatch, ref, refPath }) => {
98
+ const referencedNode = this.graph.nodes.find((it) => it.uid === ref);
99
+
100
+ if (!referencedNode) {
101
+ return;
102
+ }
103
+
104
+ const referencedValue = get(referencedNode.obj, refPath);
105
+ const value = get(obj, path);
106
+
107
+ if (value === refMatch) {
108
+ set(obj, path, referencedValue);
109
+ } else {
110
+ set(obj, path, value.replace(refMatch, referencedValue));
111
+ }
112
+ });
113
+ }
114
+ }
115
+
116
+ function getRelation(node) {
117
+ return node.parentEdge.relation;
118
+ }
119
+
120
+ function getParent(node) {
121
+ return node.parentNode;
122
+ }
123
+
124
+ module.exports = {
125
+ GraphRecursiveUpsertAction,
126
+ };
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ class JoinResultColumn {
4
+ constructor({ columnAlias, tableNode, name }) {
5
+ this.columnAlias = columnAlias;
6
+ this.tableNode = tableNode;
7
+ this.name = name;
8
+ }
9
+
10
+ static create({ tableTree, columnAlias }) {
11
+ const tableNode = tableTree.getNodeForColumnAlias(columnAlias);
12
+
13
+ return new JoinResultColumn({
14
+ columnAlias,
15
+ tableNode,
16
+ name: tableNode.getColumnForColumnAlias(columnAlias),
17
+ });
18
+ }
19
+ }
20
+
21
+ module.exports = {
22
+ JoinResultColumn,
23
+ };
@@ -0,0 +1,119 @@
1
+ 'use strict';
2
+
3
+ const { JoinResultColumn } = require('./JoinResultColumn');
4
+ const { groupBy } = require('../../utils/objectUtils');
5
+
6
+ class JoinResultParser {
7
+ constructor({ tableTree, omitColumnAliases = [] }) {
8
+ this.tableTree = tableTree;
9
+ this.omitColumnAliases = new Set(omitColumnAliases);
10
+
11
+ this.columnsByTableNode = null;
12
+ this.parentMap = null;
13
+ this.rootModels = null;
14
+ }
15
+
16
+ static create(args) {
17
+ return new JoinResultParser(args);
18
+ }
19
+
20
+ parse(flatRows) {
21
+ if (!Array.isArray(flatRows) || flatRows.length === 0) {
22
+ return flatRows;
23
+ }
24
+
25
+ this.columnsByTableNode = this._createColumns(flatRows[0]);
26
+ this.parentMap = new Map();
27
+ this.rootModels = [];
28
+
29
+ for (const flatRow of flatRows) {
30
+ this._parseNode(this.tableTree.rootNode, flatRow);
31
+ }
32
+
33
+ return this.rootModels;
34
+ }
35
+
36
+ _parseNode(tableNode, flatRow, parentModel = null, parentKey = null) {
37
+ const id = tableNode.getIdFromFlatRow(flatRow);
38
+
39
+ if (id === null) {
40
+ return;
41
+ }
42
+
43
+ const key = getKey(parentKey, id, tableNode);
44
+ let model = this.parentMap.get(key);
45
+
46
+ if (!model) {
47
+ model = this._createModel(tableNode, flatRow);
48
+
49
+ this._addToParent(tableNode, model, parentModel);
50
+ this.parentMap.set(key, model);
51
+ }
52
+
53
+ for (const childNode of tableNode.childNodes) {
54
+ this._parseNode(childNode, flatRow, model, key);
55
+ }
56
+ }
57
+
58
+ _createModel(tableNode, flatRow) {
59
+ const row = {};
60
+ const columns = this.columnsByTableNode.get(tableNode);
61
+
62
+ if (columns) {
63
+ for (const column of columns) {
64
+ if (!this.omitColumnAliases.has(column.columnAlias)) {
65
+ row[column.name] = flatRow[column.columnAlias];
66
+ }
67
+ }
68
+ }
69
+
70
+ const model = tableNode.modelClass.fromDatabaseJson(row);
71
+
72
+ for (const childNode of tableNode.childNodes) {
73
+ if (childNode.relation.isOneToOne()) {
74
+ model[childNode.relationProperty] = null;
75
+ } else {
76
+ model[childNode.relationProperty] = [];
77
+ }
78
+ }
79
+
80
+ return model;
81
+ }
82
+
83
+ _addToParent(tableNode, model, parentModel) {
84
+ if (tableNode.parentNode) {
85
+ if (tableNode.relation.isOneToOne()) {
86
+ parentModel[tableNode.relationProperty] = model;
87
+ } else {
88
+ parentModel[tableNode.relationProperty].push(model);
89
+ }
90
+ } else {
91
+ // Root model. Add to root list.
92
+ this.rootModels.push(model);
93
+ }
94
+ }
95
+
96
+ _createColumns(row) {
97
+ const columns = Object.keys(row).map((columnAlias) =>
98
+ JoinResultColumn.create({ tableTree: this.tableTree, columnAlias }),
99
+ );
100
+
101
+ return groupBy(columns, getTableNode);
102
+ }
103
+ }
104
+
105
+ function getTableNode(column) {
106
+ return column.tableNode;
107
+ }
108
+
109
+ function getKey(parentKey, id, tableNode) {
110
+ if (parentKey !== null) {
111
+ return `${parentKey}/${tableNode.relationProperty}/${id}`;
112
+ } else {
113
+ return `/${id}`;
114
+ }
115
+ }
116
+
117
+ module.exports = {
118
+ JoinResultParser,
119
+ };
@@ -0,0 +1,341 @@
1
+ 'use strict';
2
+
3
+ const { uniqBy } = require('../../utils/objectUtils');
4
+ const { Selection } = require('../operations/select/Selection');
5
+ const { createModifier } = require('../../utils/createModifier');
6
+ const { map: mapPromise } = require('../../utils/promiseUtils');
7
+ const { ValidationErrorType } = require('../../model/ValidationError');
8
+
9
+ const { TableTree } = require('./TableTree');
10
+ const { JoinResultParser } = require('./JoinResultParser');
11
+ const { ID_LENGTH_LIMIT } = require('./utils');
12
+
13
+ /**
14
+ * Given a relation expression, builds a query using joins to fetch it.
15
+ */
16
+ class RelationJoiner {
17
+ constructor({ modelClass }) {
18
+ this.rootModelClass = modelClass;
19
+
20
+ // The relation expression to join.
21
+ this.expression = null;
22
+
23
+ // Explicit modifiers for the relation expression.
24
+ this.modifiers = null;
25
+
26
+ this.options = defaultOptions();
27
+ this.tableTree = null;
28
+ this.internalSelections = null;
29
+ }
30
+
31
+ setExpression(expression) {
32
+ if (!this.expression) {
33
+ this.expression = expression;
34
+ }
35
+
36
+ return this;
37
+ }
38
+
39
+ setModifiers(modifiers) {
40
+ if (!this.modifiers) {
41
+ this.modifiers = modifiers;
42
+ }
43
+
44
+ return this;
45
+ }
46
+
47
+ setOptions(options) {
48
+ this.options = Object.assign(this.options, options);
49
+ return this;
50
+ }
51
+
52
+ /**
53
+ * Fetches the column information needed for building the select clauses.
54
+ *
55
+ * This must be called before calling `build(builder, true)`. `build(builder, false)`
56
+ * can be called without this since it doesn't build selects.
57
+ */
58
+ fetchColumnInfo(builder) {
59
+ const tableTree = this._getTableTree(builder);
60
+ const allModelClasses = new Set(tableTree.nodes.map((node) => node.modelClass));
61
+
62
+ return mapPromise(
63
+ Array.from(allModelClasses),
64
+ (modelClass) => modelClass.fetchTableMetadata({ parentBuilder: builder }),
65
+ {
66
+ concurrency: this.rootModelClass.getConcurrency(builder.unsafeKnex()),
67
+ },
68
+ );
69
+ }
70
+
71
+ build(builder, buildSelects = true) {
72
+ const rootTableNode = this._getTableTree(builder).rootNode;
73
+ const userSelectQueries = new Map([[rootTableNode, builder]]);
74
+
75
+ for (const tableNode of rootTableNode.childNodes) {
76
+ this._buildJoinsForNode({ builder, tableNode, userSelectQueries });
77
+ }
78
+
79
+ if (buildSelects) {
80
+ this._buildSelects({ builder, tableNode: rootTableNode, userSelectQueries });
81
+ }
82
+ }
83
+
84
+ parseResult(builder, flatRows) {
85
+ const parser = JoinResultParser.create({
86
+ tableTree: this._getTableTree(builder),
87
+ omitColumnAliases: this.internalSelections.map((it) => it.alias),
88
+ });
89
+
90
+ return parser.parse(flatRows);
91
+ }
92
+
93
+ _getTableTree(builder) {
94
+ if (!this.tableTree) {
95
+ // Create the table tree lazily.
96
+ this.tableTree = TableTree.create({
97
+ rootModelClass: this.rootModelClass,
98
+ rootTableAlias: builder.tableRef(),
99
+ expression: this.expression,
100
+ options: this.options,
101
+ });
102
+ }
103
+
104
+ return this.tableTree;
105
+ }
106
+
107
+ _buildJoinsForNode({ builder, tableNode, userSelectQueries }) {
108
+ const subqueryToJoin = createSubqueryToJoin({
109
+ builder,
110
+ tableNode,
111
+ modifiers: this.modifiers,
112
+ });
113
+
114
+ const userSelectQuery = subqueryToJoin.clone();
115
+
116
+ // relation.join applies the relation modifier that can
117
+ // also contain selects.
118
+ userSelectQuery.modify(tableNode.relation.modify);
119
+
120
+ // Save the query that contains the user specified selects
121
+ // for later use.
122
+ userSelectQueries.set(tableNode, userSelectQuery);
123
+
124
+ tableNode.relation.join(builder, {
125
+ joinOperation: this.options.joinOperation,
126
+
127
+ ownerTable: tableNode.parentNode.alias,
128
+ relatedTableAlias: tableNode.alias,
129
+ joinTableAlias: tableNode.getJoinTableAlias(builder),
130
+
131
+ relatedJoinSelectQuery: ensureIdAndRelationPropsAreSelected({
132
+ builder: subqueryToJoin,
133
+ tableNode,
134
+ }),
135
+ });
136
+
137
+ for (const childNode of tableNode.childNodes) {
138
+ this._buildJoinsForNode({ builder, tableNode: childNode, userSelectQueries });
139
+ }
140
+ }
141
+
142
+ _buildSelects({ builder, tableNode, userSelectQueries }) {
143
+ const { selections, internalSelections } = getSelectionsForNode({
144
+ builder,
145
+ tableNode,
146
+ userSelectQueries,
147
+ });
148
+
149
+ for (const selection of selections) {
150
+ checkAliasLength(tableNode.modelClass, selection.name);
151
+ }
152
+
153
+ // Save the selections that were added internally (not by the user)
154
+ // so that we can later remove the corresponding properties when
155
+ // parsing the result.
156
+ this.internalSelections = internalSelections;
157
+
158
+ builder.select(selectionsToStrings(selections));
159
+ }
160
+ }
161
+
162
+ function defaultOptions() {
163
+ return {
164
+ joinOperation: 'leftJoin',
165
+ minimize: false,
166
+ separator: ':',
167
+ aliases: {},
168
+ };
169
+ }
170
+
171
+ function createSubqueryToJoin({ builder, tableNode, modifiers }) {
172
+ const { relation, expression, modelClass } = tableNode;
173
+ const modifierQuery = modelClass.query().childQueryOf(builder);
174
+
175
+ for (const modifierName of expression.node.$modify) {
176
+ const modifier = createModifier({
177
+ modifier: modifierName,
178
+ modelClass,
179
+ modifiers,
180
+ });
181
+
182
+ try {
183
+ modifier(modifierQuery);
184
+ } catch (err) {
185
+ if (err instanceof modelClass.ModifierNotFoundError) {
186
+ throw modelClass.createValidationError({
187
+ type: ValidationErrorType.RelationExpression,
188
+ message: `could not find modifier "${modifierName}" for relation "${relation.name}"`,
189
+ });
190
+ } else {
191
+ throw err;
192
+ }
193
+ }
194
+ }
195
+
196
+ return modifierQuery;
197
+ }
198
+
199
+ function ensureIdAndRelationPropsAreSelected({ builder, tableNode }) {
200
+ const tableRef = builder.tableRef();
201
+
202
+ const cols = [
203
+ ...builder.modelClass().getIdColumnArray(),
204
+ ...tableNode.relation.relatedProp.cols,
205
+ ...tableNode.childNodes.reduce(
206
+ (cols, childNode) => [...cols, ...childNode.relation.ownerProp.cols],
207
+ [],
208
+ ),
209
+ ];
210
+
211
+ const selectStrings = uniqBy(cols)
212
+ .filter((col) => !builder.hasSelectionAs(col, col))
213
+ .map((col) => `${tableRef}.${col}`);
214
+
215
+ return builder.select(selectStrings);
216
+ }
217
+
218
+ function getSelectionsForNode({ builder, tableNode, userSelectQueries }) {
219
+ const userSelectQuery = userSelectQueries.get(tableNode);
220
+ const userSelections = userSelectQuery.findAllSelections();
221
+ const userSelectedAllColumns = isSelectAllSelectionSet(userSelections);
222
+
223
+ let selections = [];
224
+ let internalSelections = [];
225
+
226
+ if (tableNode.parentNode) {
227
+ selections = mapUserSelectionsFromSubqueryToMainQuery({ userSelections, tableNode });
228
+
229
+ if (userSelectedAllColumns && tableNode.relation.isObjectionManyToManyRelation) {
230
+ const extraSelections = getJoinTableExtraSelectionsForNode({ builder, tableNode });
231
+ selections = selections.concat(extraSelections);
232
+ }
233
+ }
234
+
235
+ if (userSelectedAllColumns) {
236
+ const allColumnSelections = getAllColumnSelectionsForNode({ builder, tableNode });
237
+ selections = allColumnSelections.concat(selections);
238
+ } else {
239
+ const idSelections = getIdSelectionsForNode({ tableNode });
240
+
241
+ for (const idSelection of idSelections) {
242
+ if (!userSelectQuery.hasSelectionAs(idSelection.column, idSelection.column)) {
243
+ selections.push(idSelection);
244
+ internalSelections.push(idSelection);
245
+ }
246
+ }
247
+ }
248
+
249
+ for (const childNode of tableNode.childNodes) {
250
+ const childResult = getSelectionsForNode({ builder, tableNode: childNode, userSelectQueries });
251
+
252
+ selections = selections.concat(childResult.selections);
253
+ internalSelections = internalSelections.concat(childResult.internalSelections);
254
+ }
255
+
256
+ return {
257
+ selections,
258
+ internalSelections,
259
+ };
260
+ }
261
+
262
+ function mapUserSelectionsFromSubqueryToMainQuery({ userSelections, tableNode }) {
263
+ return userSelections.filter(isNotSelectAll).map((selection) => {
264
+ return new Selection(
265
+ tableNode.alias,
266
+ selection.name,
267
+ tableNode.getColumnAliasForColumn(selection.name),
268
+ );
269
+ });
270
+ }
271
+
272
+ function getJoinTableExtraSelectionsForNode({ builder, tableNode }) {
273
+ return tableNode.relation.joinTableExtras.map((extra) => {
274
+ return new Selection(
275
+ tableNode.getJoinTableAlias(builder),
276
+ extra.joinTableCol,
277
+ tableNode.getColumnAliasForColumn(extra.aliasCol),
278
+ );
279
+ });
280
+ }
281
+
282
+ function getAllColumnSelectionsForNode({ builder, tableNode }) {
283
+ const table = builder.tableNameFor(tableNode.modelClass);
284
+ const tableMeta = tableNode.modelClass.tableMetadata({ table });
285
+
286
+ if (!tableMeta) {
287
+ throw new Error(
288
+ `table metadata has not been fetched for table '${table}'. Are you trying to call toKnexQuery() for a withGraphJoined query? To make sure the table metadata is fetched see the objection.initialize function.`,
289
+ );
290
+ }
291
+
292
+ return tableMeta.columns.map((columnName) => {
293
+ return new Selection(
294
+ tableNode.alias,
295
+ columnName,
296
+ tableNode.getColumnAliasForColumn(columnName),
297
+ );
298
+ });
299
+ }
300
+
301
+ function getIdSelectionsForNode({ tableNode }) {
302
+ return tableNode.modelClass.getIdColumnArray().map((columnName) => {
303
+ return new Selection(
304
+ tableNode.alias,
305
+ columnName,
306
+ tableNode.getColumnAliasForColumn(columnName),
307
+ );
308
+ });
309
+ }
310
+
311
+ function selectionsToStrings(selections) {
312
+ return selections.map((selection) => {
313
+ const selectStr = `${selection.table}.${selection.column}`;
314
+ return `${selectStr} as ${selection.alias}`;
315
+ });
316
+ }
317
+
318
+ function isSelectAll(selection) {
319
+ return selection.column === '*';
320
+ }
321
+
322
+ function isNotSelectAll(selection) {
323
+ return selection.column !== '*';
324
+ }
325
+
326
+ function isSelectAllSelectionSet(selections) {
327
+ return selections.length === 0 || selections.some(isSelectAll);
328
+ }
329
+
330
+ function checkAliasLength(modelClass, alias) {
331
+ if (alias.length > ID_LENGTH_LIMIT) {
332
+ throw modelClass.createValidationError({
333
+ type: ValidationErrorType.RelationExpression,
334
+ message: `identifier ${alias} is over ${ID_LENGTH_LIMIT} characters long and would be truncated by the database engine.`,
335
+ });
336
+ }
337
+ }
338
+
339
+ module.exports = {
340
+ RelationJoiner,
341
+ };