joist-core 2.2.0-next.9 → 2.3.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.
- package/build/Aliases.d.ts.map +1 -1
- package/build/Aliases.js.map +1 -1
- package/build/BaseEntity.d.ts +2 -0
- package/build/BaseEntity.d.ts.map +1 -1
- package/build/BaseEntity.js.map +1 -1
- package/build/Entity.d.ts +1 -0
- package/build/Entity.d.ts.map +1 -1
- package/build/EntityFilter.d.ts +27 -8
- package/build/EntityFilter.d.ts.map +1 -1
- package/build/EntityFilter.js +18 -0
- package/build/EntityFilter.js.map +1 -1
- package/build/EntityGraphQLFilter.d.ts +3 -0
- package/build/EntityGraphQLFilter.d.ts.map +1 -1
- package/build/EntityGraphQLFilter.js.map +1 -1
- package/build/EntityManager.d.ts +14 -11
- package/build/EntityManager.d.ts.map +1 -1
- package/build/EntityManager.js +61 -28
- package/build/EntityManager.js.map +1 -1
- package/build/EntityMetadata.d.ts +31 -1
- package/build/EntityMetadata.d.ts.map +1 -1
- package/build/EntityMetadata.js +17 -0
- package/build/EntityMetadata.js.map +1 -1
- package/build/IndexManager.d.ts +3 -3
- package/build/IndexManager.d.ts.map +1 -1
- package/build/IndexManager.js +30 -20
- package/build/IndexManager.js.map +1 -1
- package/build/InstanceData.d.ts +2 -0
- package/build/InstanceData.d.ts.map +1 -1
- package/build/InstanceData.js +3 -1
- package/build/InstanceData.js.map +1 -1
- package/build/JoinRows.d.ts +54 -12
- package/build/JoinRows.d.ts.map +1 -1
- package/build/JoinRows.js +114 -44
- package/build/JoinRows.js.map +1 -1
- package/build/QueryParser.d.ts +6 -1
- package/build/QueryParser.d.ts.map +1 -1
- package/build/QueryParser.js +293 -190
- package/build/QueryParser.js.map +1 -1
- package/build/QueryParser.test.d.ts +2 -0
- package/build/QueryParser.test.d.ts.map +1 -0
- package/build/QueryParser.test.js +27 -0
- package/build/QueryParser.test.js.map +1 -0
- package/build/Todo.d.ts +4 -0
- package/build/Todo.d.ts.map +1 -1
- package/build/Todo.js.map +1 -1
- package/build/batchloaders/enumCollectionBatchLoader.d.ts +7 -0
- package/build/batchloaders/enumCollectionBatchLoader.d.ts.map +1 -0
- package/build/batchloaders/enumCollectionBatchLoader.js +54 -0
- package/build/batchloaders/enumCollectionBatchLoader.js.map +1 -0
- package/build/batchloaders/manyToManyBatchLoader.d.ts.map +1 -1
- package/build/batchloaders/manyToManyBatchLoader.js +10 -2
- package/build/batchloaders/manyToManyBatchLoader.js.map +1 -1
- package/build/batchloaders/populateBatchLoader.d.ts.map +1 -1
- package/build/batchloaders/populateBatchLoader.js +26 -8
- package/build/batchloaders/populateBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveChildrenBatchLoader.d.ts.map +1 -1
- package/build/batchloaders/recursiveChildrenBatchLoader.js +2 -0
- package/build/batchloaders/recursiveChildrenBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveM2mBatchLoader.d.ts.map +1 -1
- package/build/batchloaders/recursiveM2mBatchLoader.js +1 -3
- package/build/batchloaders/recursiveM2mBatchLoader.js.map +1 -1
- package/build/batchloaders/recursiveParentsBatchLoader.d.ts.map +1 -1
- package/build/batchloaders/recursiveParentsBatchLoader.js +2 -0
- package/build/batchloaders/recursiveParentsBatchLoader.js.map +1 -1
- package/build/changes.d.ts +13 -0
- package/build/changes.d.ts.map +1 -1
- package/build/changes.js +49 -0
- package/build/changes.js.map +1 -1
- package/build/configure.d.ts +2 -0
- package/build/configure.d.ts.map +1 -1
- package/build/configure.js +6 -0
- package/build/configure.js.map +1 -1
- package/build/dataloaders/findCountDataLoader.d.ts +12 -2
- package/build/dataloaders/findCountDataLoader.d.ts.map +1 -1
- package/build/dataloaders/findCountDataLoader.js +45 -5
- package/build/dataloaders/findCountDataLoader.js.map +1 -1
- package/build/dataloaders/findDataLoader.d.ts +44 -9
- package/build/dataloaders/findDataLoader.d.ts.map +1 -1
- package/build/dataloaders/findDataLoader.js +123 -34
- package/build/dataloaders/findDataLoader.js.map +1 -1
- package/build/dataloaders/findIdsDataLoader.js +4 -4
- package/build/dataloaders/findIdsDataLoader.js.map +1 -1
- package/build/dataloaders/findOrCreateDataLoader.d.ts.map +1 -1
- package/build/dataloaders/findOrCreateDataLoader.js +2 -2
- package/build/dataloaders/findOrCreateDataLoader.js.map +1 -1
- package/build/dataloaders/findPaginatedDataLoader.d.ts.map +1 -1
- package/build/dataloaders/findPaginatedDataLoader.js +14 -8
- package/build/dataloaders/findPaginatedDataLoader.js.map +1 -1
- package/build/dataloaders/manyToManyFindDataLoader.js +7 -1
- package/build/dataloaders/manyToManyFindDataLoader.js.map +1 -1
- package/build/fields.d.ts +2 -0
- package/build/fields.d.ts.map +1 -1
- package/build/fields.js +2 -0
- package/build/fields.js.map +1 -1
- package/build/index.d.ts +3 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -1
- package/build/index.js.map +1 -1
- package/build/loadHints.d.ts +4 -4
- package/build/loadHints.d.ts.map +1 -1
- package/build/loadHints.js.map +1 -1
- package/build/loadLens.d.ts.map +1 -1
- package/build/loadLens.js +20 -4
- package/build/loadLens.js.map +1 -1
- package/build/newEntity.d.ts.map +1 -1
- package/build/newEntity.js +16 -14
- package/build/newEntity.js.map +1 -1
- package/build/newTestInstance.d.ts.map +1 -1
- package/build/newTestInstance.js +3 -0
- package/build/newTestInstance.js.map +1 -1
- package/build/normalizeHints.d.ts +7 -0
- package/build/normalizeHints.d.ts.map +1 -1
- package/build/normalizeHints.js +40 -2
- package/build/normalizeHints.js.map +1 -1
- package/build/preloading/JsonAggregatePreloader.d.ts.map +1 -1
- package/build/preloading/JsonAggregatePreloader.js +91 -9
- package/build/preloading/JsonAggregatePreloader.js.map +1 -1
- package/build/preloading/canPreload.d.ts +2 -2
- package/build/preloading/canPreload.d.ts.map +1 -1
- package/build/preloading/canPreload.js +3 -0
- package/build/preloading/canPreload.js.map +1 -1
- package/build/preloading/partitionHint.d.ts.map +1 -1
- package/build/preloading/partitionHint.js +10 -4
- package/build/preloading/partitionHint.js.map +1 -1
- package/build/reactiveHints.d.ts +2 -2
- package/build/reactiveHints.d.ts.map +1 -1
- package/build/reactiveHints.js +9 -0
- package/build/reactiveHints.js.map +1 -1
- package/build/relations/EnumCollection.d.ts +81 -0
- package/build/relations/EnumCollection.d.ts.map +1 -0
- package/build/relations/EnumCollection.js +211 -0
- package/build/relations/EnumCollection.js.map +1 -0
- package/build/relations/ManyToManyCollection.d.ts +1 -0
- package/build/relations/ManyToManyCollection.d.ts.map +1 -1
- package/build/relations/ManyToManyCollection.js +5 -2
- package/build/relations/ManyToManyCollection.js.map +1 -1
- package/build/relations/ManyToManyLargeCollection.d.ts +3 -2
- package/build/relations/ManyToManyLargeCollection.d.ts.map +1 -1
- package/build/relations/ManyToManyLargeCollection.js +7 -4
- package/build/relations/ManyToManyLargeCollection.js.map +1 -1
- package/build/relations/OneToManyCollection.d.ts.map +1 -1
- package/build/relations/OneToManyCollection.js +2 -2
- package/build/relations/OneToManyCollection.js.map +1 -1
- package/build/relations/OneToManyLargeCollection.d.ts.map +1 -1
- package/build/relations/OneToManyLargeCollection.js +2 -1
- package/build/relations/OneToManyLargeCollection.js.map +1 -1
- package/build/relations/ReactiveManyToMany.d.ts +1 -0
- package/build/relations/ReactiveManyToMany.d.ts.map +1 -1
- package/build/relations/ReactiveManyToMany.js +4 -1
- package/build/relations/ReactiveManyToMany.js.map +1 -1
- package/build/relations/ReactiveManyToManyOtherSide.d.ts +1 -0
- package/build/relations/ReactiveManyToManyOtherSide.d.ts.map +1 -1
- package/build/relations/ReactiveManyToManyOtherSide.js +4 -1
- package/build/relations/ReactiveManyToManyOtherSide.js.map +1 -1
- package/build/relations/RecursiveCollection.d.ts +2 -6
- package/build/relations/RecursiveCollection.d.ts.map +1 -1
- package/build/relations/RecursiveCollection.js +9 -17
- package/build/relations/RecursiveCollection.js.map +1 -1
- package/build/relations/RecursiveCycleError.d.ts +13 -0
- package/build/relations/RecursiveCycleError.d.ts.map +1 -0
- package/build/relations/RecursiveCycleError.js +15 -0
- package/build/relations/RecursiveCycleError.js.map +1 -0
- package/build/relations/hasProperty.d.ts +2 -1
- package/build/relations/hasProperty.d.ts.map +1 -1
- package/build/relations/hasProperty.js +15 -5
- package/build/relations/hasProperty.js.map +1 -1
- package/build/relations/index.d.ts +1 -0
- package/build/relations/index.d.ts.map +1 -1
- package/build/relations/index.js +5 -2
- package/build/relations/index.js.map +1 -1
- package/build/rules.js +1 -1
- package/build/rules.js.map +1 -1
- package/build/scopes.d.ts +185 -0
- package/build/scopes.d.ts.map +1 -0
- package/build/scopes.js +302 -0
- package/build/scopes.js.map +1 -0
- package/build/temporal.d.ts +32 -2
- package/build/temporal.d.ts.map +1 -1
- package/build/temporal.js +18 -0
- package/build/temporal.js.map +1 -1
- package/build/upsert.d.ts.map +1 -1
- package/build/upsert.js +9 -2
- package/build/upsert.js.map +1 -1
- package/package.json +7 -7
package/build/QueryParser.js
CHANGED
|
@@ -5,11 +5,13 @@ exports.parseFindQuery = parseFindQuery;
|
|
|
5
5
|
exports.maybeAddIdNotNulls = maybeAddIdNotNulls;
|
|
6
6
|
exports.parseAlias = parseAlias;
|
|
7
7
|
exports.parseEntityFilter = parseEntityFilter;
|
|
8
|
+
exports.findFilterField = findFilterField;
|
|
8
9
|
exports.parseValueFilter = parseValueFilter;
|
|
9
10
|
exports.mapToDb = mapToDb;
|
|
10
11
|
exports.maybeAddOrderBy = maybeAddOrderBy;
|
|
11
12
|
exports.addTablePerClassJoinsAndClassTag = addTablePerClassJoinsAndClassTag;
|
|
12
13
|
exports.maybeAddNotSoftDeleted = maybeAddNotSoftDeleted;
|
|
14
|
+
exports.filterSoftDeletes = filterSoftDeletes;
|
|
13
15
|
exports.getTables = getTables;
|
|
14
16
|
exports.makeLike = makeLike;
|
|
15
17
|
const joist_utils_1 = require("joist-utils");
|
|
@@ -21,6 +23,7 @@ const QueryVisitor_1 = require("./QueryVisitor");
|
|
|
21
23
|
const configure_1 = require("./configure");
|
|
22
24
|
const index_1 = require("./index");
|
|
23
25
|
const keywords_1 = require("./keywords");
|
|
26
|
+
const scopes_1 = require("./scopes");
|
|
24
27
|
const utils_1 = require("./utils");
|
|
25
28
|
/** A marker condition for alias methods to indicate they should be skipped/pruned. */
|
|
26
29
|
exports.skipCondition = {
|
|
@@ -50,10 +53,10 @@ function parseFindQuery(meta, filter, opts = {}) {
|
|
|
50
53
|
aliases[abbrev] = i + 1;
|
|
51
54
|
return i === 0 ? abbrev : `${abbrev}${i}`;
|
|
52
55
|
}
|
|
53
|
-
function addSoftDeleteCondition(meta, alias) {
|
|
56
|
+
function addSoftDeleteCondition(meta, alias, targetCb) {
|
|
54
57
|
if (filterSoftDeletes(meta, softDeletes)) {
|
|
55
58
|
const column = meta.allFields[(0, EntityMetadata_1.getBaseMeta)(meta).timestampFields.deletedAt].serde?.columns[0];
|
|
56
|
-
|
|
59
|
+
targetCb.addSimpleCondition({
|
|
57
60
|
kind: "column",
|
|
58
61
|
alias,
|
|
59
62
|
column: column.columnName,
|
|
@@ -63,10 +66,11 @@ function parseFindQuery(meta, filter, opts = {}) {
|
|
|
63
66
|
});
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
|
-
function addTable(meta, alias, join, col1, col2, filter, fieldName) {
|
|
69
|
+
function addTable(meta, alias, join, col1, col2, filter, fieldName, targetCb = cb) {
|
|
67
70
|
// look at filter, is it `{ book: "b2" }` or `{ book: { ... } }`
|
|
68
|
-
const
|
|
69
|
-
|
|
71
|
+
const scopeFilter = (0, scopes_1.isScope)(filter);
|
|
72
|
+
const ef = scopeFilter ? undefined : parseEntityFilter(meta, filter);
|
|
73
|
+
if (!scopeFilter && !ef && join !== "primary" && !(0, Aliases_1.isAlias)(filter)) {
|
|
70
74
|
return;
|
|
71
75
|
}
|
|
72
76
|
if (join === "primary") {
|
|
@@ -106,9 +110,9 @@ function parseFindQuery(meta, filter, opts = {}) {
|
|
|
106
110
|
addTablePerClassJoinsAndClassTag({ selects, tables, orderBys }, meta, alias, false);
|
|
107
111
|
}
|
|
108
112
|
if (needsStiDiscriminator(meta)) {
|
|
109
|
-
addStiSubtypeFilter(
|
|
113
|
+
addStiSubtypeFilter(targetCb, meta, alias);
|
|
110
114
|
}
|
|
111
|
-
addSoftDeleteCondition(meta, alias);
|
|
115
|
+
addSoftDeleteCondition(meta, alias, targetCb);
|
|
112
116
|
// The user's locally declared aliases, i.e. `const [a, b] = aliases(Author, Book)`,
|
|
113
117
|
// aren't guaranteed to line up with the aliases we've assigned internally, like `a`
|
|
114
118
|
// might actually be `a1` if there are two `authors` tables in the query, so push the
|
|
@@ -119,202 +123,276 @@ function parseFindQuery(meta, filter, opts = {}) {
|
|
|
119
123
|
else if ((0, Aliases_1.isAlias)(filter)) {
|
|
120
124
|
(0, Aliases_1.getAliasMgmt)(filter).setAlias(meta, alias);
|
|
121
125
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
126
|
+
addFilterAt(meta, alias, filter, targetCb, ef, join);
|
|
127
|
+
}
|
|
128
|
+
function addFilterAt(meta, tableAlias, filter, targetCb, parsed = parseEntityFilter(meta, filter), parentJoin = "primary") {
|
|
129
|
+
if ((0, scopes_1.isScope)(filter)) {
|
|
130
|
+
addScopeFilterAt(meta, tableAlias, filter, targetCb, parentJoin);
|
|
131
|
+
}
|
|
132
|
+
else if (parsed && parsed.kind === "join") {
|
|
133
|
+
addSubFilter(meta, tableAlias, parsed.subFilter, targetCb, parentJoin);
|
|
134
|
+
}
|
|
135
|
+
else if (parsed) {
|
|
136
|
+
const column = meta.fields["id"].serde.columns[0];
|
|
137
|
+
targetCb.addValueFilter(tableAlias, column, parsed);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function addScopeFilterAt(meta, tableAlias, scope, targetCb, parentJoin) {
|
|
141
|
+
for (const fragment of (0, scopes_1.resolveScope)(scope).fragments) {
|
|
142
|
+
if (fragment.kind === "filter") {
|
|
143
|
+
addFilterAt(meta, tableAlias, fragment.filter, targetCb, undefined, parentJoin);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
const a = (0, Aliases_1.alias)(meta.cstr);
|
|
147
|
+
const result = fragment.fn(a);
|
|
148
|
+
(0, Aliases_1.getAliasMgmt)(a).setAlias(meta, tableAlias);
|
|
149
|
+
if ((0, scopes_1.isScopeJoinFilter)(result)) {
|
|
150
|
+
// Parse the join tree first so its `as:` bindings re-root the aliases that
|
|
151
|
+
// `conditions` reference, then add the conditions against the now-bound aliases.
|
|
152
|
+
addFilterAt(meta, tableAlias, result.where, targetCb, undefined, parentJoin);
|
|
153
|
+
targetCb.maybeAddExpression(result.conditions);
|
|
137
154
|
}
|
|
138
|
-
else
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if ((0, Aliases_1.isAlias)(sub)) {
|
|
143
|
-
const a = getAlias(field.otherMetadata().tableName);
|
|
144
|
-
addTable(field.otherMetadata(), a, joinKind, (0, keywords_1.kqDot)(fa, column.columnName), (0, keywords_1.kqDot)(a, "id"), sub);
|
|
145
|
-
}
|
|
146
|
-
const f = parseEntityFilter(field.otherMetadata(), sub);
|
|
147
|
-
// Probe the filter and see if it's just an id (...and not soft deleted), if so we can avoid the join
|
|
148
|
-
if (!f) {
|
|
149
|
-
// skip
|
|
150
|
-
}
|
|
151
|
-
else if (f.kind === "join" || filterSoftDeletes(field.otherMetadata(), softDeletes)) {
|
|
152
|
-
const a = getAlias(field.otherMetadata().tableName);
|
|
153
|
-
addTable(field.otherMetadata(), a, joinKind, (0, keywords_1.kqDot)(fa, column.columnName), (0, keywords_1.kqDot)(a, "id"), sub);
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
cb.addValueFilter(fa, column, f);
|
|
157
|
-
}
|
|
155
|
+
else {
|
|
156
|
+
const conditions = Array.isArray(result) ? result : [result];
|
|
157
|
+
if (conditions.length > 0)
|
|
158
|
+
targetCb.maybeAddExpression({ and: conditions });
|
|
158
159
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function addSubFilter(meta, tableAlias, subFilter, targetCb, parentJoin) {
|
|
164
|
+
// subFilter really means we're matching against the entity columns/further joins
|
|
165
|
+
Object.keys(subFilter).forEach((key) => {
|
|
166
|
+
// Skip the `{ as: ... }` alias binding
|
|
167
|
+
if (key === "as")
|
|
168
|
+
return;
|
|
169
|
+
if (key === "and" || key === "or") {
|
|
170
|
+
addLogicalFilter(meta, tableAlias, key, subFilter[key], targetCb, parentJoin);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const field = findFilterField(meta, key) ?? (0, utils_1.fail)(`Field '${key}' not found on ${meta.tableName}`);
|
|
174
|
+
const fa = `${tableAlias}${field.aliasSuffix}`;
|
|
175
|
+
if (field.kind === "primitive" || field.kind === "primaryKey" || field.kind === "enum") {
|
|
176
|
+
const column = field.serde.columns[0];
|
|
177
|
+
parseValueFilter(subFilter[key]).forEach((filter) => {
|
|
178
|
+
targetCb.addValueFilter(fa, column, filter);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
else if (field.kind === "m2o") {
|
|
182
|
+
const column = field.serde.columns[0];
|
|
183
|
+
const sub = subFilter[key];
|
|
184
|
+
const joinKind = field.required && parentJoin !== "outer" ? "inner" : "outer";
|
|
185
|
+
if ((0, Aliases_1.isAlias)(sub)) {
|
|
186
|
+
const a = getAlias(field.otherMetadata().tableName);
|
|
187
|
+
addTable(field.otherMetadata(), a, joinKind, (0, keywords_1.kqDot)(fa, column.columnName), (0, keywords_1.kqDot)(a, "id"), sub, undefined, targetCb);
|
|
188
|
+
}
|
|
189
|
+
const f = parseEntityFilter(field.otherMetadata(), sub);
|
|
190
|
+
// Probe the filter and see if it's just an id (...and not soft deleted), if so we can avoid the join
|
|
191
|
+
if (!f) {
|
|
192
|
+
// skip
|
|
193
|
+
}
|
|
194
|
+
else if (f.kind === "join" || filterSoftDeletes(field.otherMetadata(), softDeletes)) {
|
|
195
|
+
const a = getAlias(field.otherMetadata().tableName);
|
|
196
|
+
addTable(field.otherMetadata(), a, joinKind, (0, keywords_1.kqDot)(fa, column.columnName), (0, keywords_1.kqDot)(a, "id"), sub, undefined, targetCb);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
targetCb.addValueFilter(fa, column, f);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (field.kind === "poly") {
|
|
203
|
+
const f = parseEntityFilter(meta, subFilter[key]);
|
|
204
|
+
if (!f) {
|
|
205
|
+
// skip
|
|
206
|
+
}
|
|
207
|
+
else if (f.kind === "join") {
|
|
208
|
+
throw new Error("Joins through polys are not supported");
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
// We're left with basically a ValueFilter against the ids
|
|
212
|
+
// For now only support eq/ne/in/is-null
|
|
213
|
+
if (f.kind === "eq" || f.kind === "ne") {
|
|
214
|
+
if (isNilIdValue(f.value))
|
|
215
|
+
return;
|
|
216
|
+
const comp = field.components.find((p) => {
|
|
217
|
+
const otherMeta = p.otherMetadata();
|
|
218
|
+
const cstr = (0, index_1.getConstructorFromTaggedId)(f.value);
|
|
219
|
+
// tagged ids from subclasses always map to the base class, so we should compare to the base class if we don't directly match
|
|
220
|
+
return otherMeta.cstr === cstr || otherMeta.baseType === cstr.name;
|
|
221
|
+
});
|
|
222
|
+
if (!comp)
|
|
223
|
+
(0, utils_1.fail)(`Invalid tagged id passed to ${meta.type}.${key}: ${f.value}`);
|
|
224
|
+
const column = field.serde.columns.find((c) => c.columnName === comp.columnName);
|
|
225
|
+
targetCb.addValueFilter(fa, column, f);
|
|
166
226
|
}
|
|
167
|
-
else {
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
if (f.kind === "eq" || f.kind === "ne") {
|
|
171
|
-
if (isNilIdValue(f.value))
|
|
172
|
-
return;
|
|
173
|
-
const comp = field.components.find((p) => {
|
|
174
|
-
const otherMeta = p.otherMetadata();
|
|
175
|
-
const cstr = (0, index_1.getConstructorFromTaggedId)(f.value);
|
|
176
|
-
// tagged ids from subclasses always map to the base class, so we should compare to the base class if we don't directly match
|
|
177
|
-
return otherMeta.cstr === cstr || otherMeta.baseType === cstr.name;
|
|
178
|
-
});
|
|
179
|
-
if (!comp)
|
|
180
|
-
(0, utils_1.fail)(`Invalid tagged id passed to ${meta.type}.${key}: ${f.value}`);
|
|
227
|
+
else if (f.kind === "is-null") {
|
|
228
|
+
// Add a condition for every component--these can be AND-d with the rest of the simple/inline conditions
|
|
229
|
+
field.components.forEach((comp) => {
|
|
181
230
|
const column = field.serde.columns.find((c) => c.columnName === comp.columnName);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
cb.addSimpleCondition({
|
|
189
|
-
kind: "column",
|
|
190
|
-
alias: fa,
|
|
191
|
-
column: comp.columnName,
|
|
192
|
-
dbType: column.dbType,
|
|
193
|
-
cond: f,
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
else if (f.kind === "not-null") {
|
|
198
|
-
const conditions = field.components.map((comp) => {
|
|
199
|
-
const column = field.serde.columns.find((c) => c.columnName === comp.columnName);
|
|
200
|
-
return {
|
|
201
|
-
kind: "column",
|
|
202
|
-
alias: fa,
|
|
203
|
-
column: comp.columnName,
|
|
204
|
-
dbType: column.dbType,
|
|
205
|
-
cond: { kind: "not-null" },
|
|
206
|
-
};
|
|
207
|
-
});
|
|
208
|
-
cb.addParsedExpression({ kind: "exp", op: "or", conditions });
|
|
209
|
-
}
|
|
210
|
-
else if (f.kind === "in") {
|
|
211
|
-
// Split up the ids by constructor
|
|
212
|
-
const idsByConstructor = (0, joist_utils_1.groupBy)(f.value, (id) => (0, index_1.getConstructorFromTaggedId)(id).name);
|
|
213
|
-
// Or together `parent_book_id in (1,2,3) OR parent_author_id IN (4,5,6)`
|
|
214
|
-
// ...if there is a `parent IN [b:1, b:2, a:1, null]` we'd need to pull the `null` out and do an `OR (all columns are null)`...
|
|
215
|
-
const conditions = Object.entries(idsByConstructor).map(([cstrName, ids]) => {
|
|
216
|
-
const column = field.serde.columns.find(
|
|
217
|
-
// tagged ids from subclasses always map to the base class, so we should compare to the base class if we don't directly match
|
|
218
|
-
(c) => c.otherMetadata().cstr.name === cstrName || c.otherMetadata().baseType === cstrName) ?? (0, utils_1.fail)(`Invalid tagged ids passed to ${meta.type}.${key}: ${ids}`);
|
|
219
|
-
return {
|
|
220
|
-
kind: "column",
|
|
221
|
-
alias: fa,
|
|
222
|
-
column: column.columnName,
|
|
223
|
-
dbType: column.dbType,
|
|
224
|
-
cond: mapToDb(column, { kind: "in", value: ids }),
|
|
225
|
-
};
|
|
231
|
+
targetCb.addSimpleCondition({
|
|
232
|
+
kind: "column",
|
|
233
|
+
alias: fa,
|
|
234
|
+
column: comp.columnName,
|
|
235
|
+
dbType: column.dbType,
|
|
236
|
+
cond: f,
|
|
226
237
|
});
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
else if (f.kind === "not-null") {
|
|
241
|
+
const conditions = field.components.map((comp) => {
|
|
242
|
+
const column = field.serde.columns.find((c) => c.columnName === comp.columnName);
|
|
243
|
+
return {
|
|
244
|
+
kind: "column",
|
|
245
|
+
alias: fa,
|
|
246
|
+
column: comp.columnName,
|
|
247
|
+
dbType: column.dbType,
|
|
248
|
+
cond: { kind: "not-null" },
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
targetCb.addParsedExpression({ kind: "exp", op: "or", conditions });
|
|
252
|
+
}
|
|
253
|
+
else if (f.kind === "in") {
|
|
254
|
+
// An empty `in` matches nothing, so emit a single always-false condition (consistent
|
|
255
|
+
// with a non-poly m2o `{ relation: [] }`); otherwise grouping by constructor would yield
|
|
256
|
+
// zero conditions and prune the filter entirely, incorrectly matching every row.
|
|
257
|
+
if (f.value.length === 0) {
|
|
258
|
+
targetCb.addValueFilter(fa, field.serde.columns[0], { kind: "in", value: [] });
|
|
259
|
+
return;
|
|
233
260
|
}
|
|
261
|
+
// Split up the ids by constructor
|
|
262
|
+
const idsByConstructor = (0, joist_utils_1.groupBy)(f.value, (id) => (0, index_1.getConstructorFromTaggedId)(id).name);
|
|
263
|
+
// Or together `parent_book_id in (1,2,3) OR parent_author_id IN (4,5,6)`
|
|
264
|
+
// ...if there is a `parent IN [b:1, b:2, a:1, null]` we'd need to pull the `null` out and do an `OR (all columns are null)`...
|
|
265
|
+
const conditions = Object.entries(idsByConstructor).map(([cstrName, ids]) => {
|
|
266
|
+
const column = field.serde.columns.find(
|
|
267
|
+
// tagged ids from subclasses always map to the base class, so we should compare to the base class if we don't directly match
|
|
268
|
+
(c) => c.otherMetadata().cstr.name === cstrName || c.otherMetadata().baseType === cstrName) ?? (0, utils_1.fail)(`Invalid tagged ids passed to ${meta.type}.${key}: ${ids}`);
|
|
269
|
+
return {
|
|
270
|
+
kind: "column",
|
|
271
|
+
alias: fa,
|
|
272
|
+
column: column.columnName,
|
|
273
|
+
dbType: column.dbType,
|
|
274
|
+
cond: mapToDb(column, { kind: "in", value: ids }),
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
targetCb.addParsedExpression({ kind: "exp", op: "or", conditions });
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
throw new Error(`Filters on polys for ${f.kind} are not supported`);
|
|
234
281
|
}
|
|
235
282
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
283
|
+
}
|
|
284
|
+
else if (field.kind === "o2o") {
|
|
285
|
+
// We have to always join into o2os, i.e. we can't probe the filter like we do for m2os
|
|
286
|
+
const otherMeta = field.otherMetadata();
|
|
287
|
+
const a = getAlias(otherMeta.tableName);
|
|
288
|
+
const otherField = otherMeta.allFields[field.otherFieldName];
|
|
289
|
+
const otherColumn =
|
|
290
|
+
// if our other is a poly, we need to find a matching column rather than just picking the first
|
|
291
|
+
otherField.kind === "poly"
|
|
292
|
+
? otherField.components.find((c) => c.otherMetadata() === meta || c.otherMetadata() === (0, EntityMetadata_1.getBaseMeta)(meta))
|
|
293
|
+
.columnName
|
|
294
|
+
: otherField.serde.columns[0].columnName;
|
|
295
|
+
addTable(field.otherMetadata(), a, "outer", (0, keywords_1.kqDot)(tableAlias, "id"), (0, keywords_1.kqDot)(a, otherColumn), subFilter[key], field.otherFieldName, targetCb);
|
|
296
|
+
}
|
|
297
|
+
else if (field.kind === "o2m") {
|
|
298
|
+
const otherMeta = field.otherMetadata();
|
|
299
|
+
const otherField = otherMeta.allFields[field.otherFieldName];
|
|
300
|
+
let otherColumn = otherField.serde.columns[0].columnName;
|
|
301
|
+
if (otherField.kind === "poly") {
|
|
302
|
+
const otherComponent = otherField.components.find((c) => c.otherMetadata() === meta) ??
|
|
303
|
+
(0, utils_1.fail)(`No poly component found for ${otherField.fieldName}`);
|
|
304
|
+
otherColumn = otherComponent.columnName;
|
|
247
305
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
otherColumn = otherComponent.columnName;
|
|
306
|
+
const isCtiBaseFk = otherMeta.inheritanceType === "cti" && field.otherFieldName && !(field.otherFieldName in otherMeta.fields);
|
|
307
|
+
const a = getAlias(otherMeta.tableName);
|
|
308
|
+
addTable(otherMeta, a, "outer", (0, keywords_1.kqDot)(tableAlias, "id"), (0, keywords_1.kqDot)(a, otherColumn), subFilter[key], field.otherFieldName, targetCb);
|
|
309
|
+
if (!isCtiBaseFk) {
|
|
310
|
+
const table = tables.find((t) => t.alias === a);
|
|
311
|
+
if (table && (table.join === "inner" || table.join === "outer")) {
|
|
312
|
+
table.collection = { parentAlias: tableAlias, rootAlias: a, kind: "o2m" };
|
|
256
313
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else if (field.kind === "m2m") {
|
|
317
|
+
const sub = subFilter[key];
|
|
318
|
+
const f = parseEntityFilter(field.otherMetadata(), sub);
|
|
319
|
+
if (!f && !(0, Aliases_1.isAlias)(sub))
|
|
320
|
+
return;
|
|
321
|
+
const ja = getAlias(field.joinTableName);
|
|
322
|
+
tables.push({
|
|
323
|
+
join: "outer",
|
|
324
|
+
alias: ja,
|
|
325
|
+
table: field.joinTableName,
|
|
326
|
+
col1: (0, keywords_1.kqDot)(tableAlias, "id"),
|
|
327
|
+
col2: (0, keywords_1.kqDot)(ja, field.columnNames[0]),
|
|
328
|
+
collection: { parentAlias: tableAlias, rootAlias: ja, kind: "m2m" },
|
|
329
|
+
});
|
|
330
|
+
if ((0, Aliases_1.isAlias)(sub) || f?.kind === "join" || filterSoftDeletes(field.otherMetadata(), softDeletes)) {
|
|
331
|
+
const a = getAlias(field.otherMetadata().tableName);
|
|
332
|
+
addTable(field.otherMetadata(), a, "outer", (0, keywords_1.kqDot)(ja, field.columnNames[1]), (0, keywords_1.kqDot)(a, "id"), sub, undefined, targetCb);
|
|
333
|
+
const table = tables.find((t) => t.alias === a);
|
|
334
|
+
if (table && (table.join === "inner" || table.join === "outer")) {
|
|
335
|
+
table.collection = { parentAlias: ja, rootAlias: ja, kind: "m2m" };
|
|
265
336
|
}
|
|
266
337
|
}
|
|
267
|
-
else if (
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
338
|
+
else if (f) {
|
|
339
|
+
const otherMeta = field.otherMetadata();
|
|
340
|
+
const column = {
|
|
341
|
+
columnName: field.columnNames[1],
|
|
342
|
+
dbType: otherMeta.idDbType,
|
|
343
|
+
mapToDb(value) {
|
|
344
|
+
return value === null || isNilIdValue(value)
|
|
345
|
+
? value
|
|
346
|
+
: (0, index_1.keyToNumber)(otherMeta, (0, index_1.maybeResolveReferenceToId)(value));
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
targetCb.addSimpleCondition({
|
|
350
|
+
kind: "column",
|
|
275
351
|
alias: ja,
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
collection: { parentAlias: alias, rootAlias: ja, kind: "m2m" },
|
|
352
|
+
column: field.columnNames[1],
|
|
353
|
+
dbType: otherMeta.idDbType,
|
|
354
|
+
cond: mapToDb(column, f),
|
|
280
355
|
});
|
|
281
|
-
if ((0, Aliases_1.isAlias)(sub) || f?.kind === "join" || filterSoftDeletes(field.otherMetadata(), softDeletes)) {
|
|
282
|
-
const a = getAlias(field.otherMetadata().tableName);
|
|
283
|
-
addTable(field.otherMetadata(), a, "outer", (0, keywords_1.kqDot)(ja, field.columnNames[1]), (0, keywords_1.kqDot)(a, "id"), sub);
|
|
284
|
-
const table = tables.find((t) => t.alias === a);
|
|
285
|
-
if (table && (table.join === "inner" || table.join === "outer")) {
|
|
286
|
-
table.collection = { parentAlias: ja, rootAlias: ja, kind: "m2m" };
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
else if (f) {
|
|
290
|
-
const otherMeta = field.otherMetadata();
|
|
291
|
-
const column = {
|
|
292
|
-
columnName: field.columnNames[1],
|
|
293
|
-
dbType: otherMeta.idDbType,
|
|
294
|
-
mapToDb(value) {
|
|
295
|
-
return value === null || isNilIdValue(value)
|
|
296
|
-
? value
|
|
297
|
-
: (0, index_1.keyToNumber)(otherMeta, (0, index_1.maybeResolveReferenceToId)(value));
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
cb.addSimpleCondition({
|
|
301
|
-
kind: "column",
|
|
302
|
-
alias: ja,
|
|
303
|
-
column: field.columnNames[1],
|
|
304
|
-
dbType: otherMeta.idDbType,
|
|
305
|
-
cond: mapToDb(column, f),
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
356
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
357
|
+
}
|
|
358
|
+
else if (field.kind === "m2mEnum") {
|
|
359
|
+
// Membership filter, e.g. `{ logoColors: Color.Red }` / `{ logoColors: [Red, Green] }`: join the
|
|
360
|
+
// join table and filter its enum-id column, mapping enum codes -> their numeric ids.
|
|
361
|
+
const ja = getAlias(field.joinTableName);
|
|
362
|
+
tables.push({
|
|
363
|
+
join: "outer",
|
|
364
|
+
alias: ja,
|
|
365
|
+
table: field.joinTableName,
|
|
366
|
+
col1: (0, keywords_1.kqDot)(tableAlias, "id"),
|
|
367
|
+
col2: (0, keywords_1.kqDot)(ja, field.columnNames[0]),
|
|
368
|
+
collection: { parentAlias: tableAlias, rootAlias: ja, kind: "m2m" },
|
|
369
|
+
});
|
|
370
|
+
const column = {
|
|
371
|
+
columnName: field.columnNames[1],
|
|
372
|
+
dbType: "int",
|
|
373
|
+
mapToDb(value) {
|
|
374
|
+
return value === null || value === undefined ? value : field.enumDetailType.getByCode(value).id;
|
|
375
|
+
},
|
|
376
|
+
};
|
|
377
|
+
parseValueFilter(subFilter[key]).forEach((filter) => {
|
|
378
|
+
targetCb.addValueFilter(ja, column, filter);
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
throw new Error(`Unsupported field ${key}`);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
function addLogicalFilter(meta, tableAlias, op, value, targetCb, parentJoin) {
|
|
387
|
+
const filters = Array.isArray(value) ? value : [value];
|
|
388
|
+
const conditions = filters.flatMap((filter) => {
|
|
389
|
+
const branchCb = new index_1.ConditionBuilder();
|
|
390
|
+
addFilterAt(meta, tableAlias, filter, branchCb, undefined, parentJoin);
|
|
391
|
+
const condition = branchCb.toExpressionFilter();
|
|
392
|
+
return condition ? [condition] : [];
|
|
393
|
+
});
|
|
394
|
+
if (conditions.length > 0)
|
|
395
|
+
targetCb.addParsedExpression({ kind: "exp", op, conditions });
|
|
318
396
|
}
|
|
319
397
|
function addOrderBy(meta, alias, orderBy) {
|
|
320
398
|
const entries = Object.entries(orderBy);
|
|
@@ -446,6 +524,15 @@ function parseEntityFilter(meta, filter) {
|
|
|
446
524
|
// We're just binding an alias to this position in the join tree
|
|
447
525
|
return undefined;
|
|
448
526
|
}
|
|
527
|
+
else if ((0, scopes_1.isScope)(filter)) {
|
|
528
|
+
// A scope on a relation (e.g. `{ author: Author.adult }`) always implies conditions on the
|
|
529
|
+
// related entity, so signal "needs a join" via the existing `join` discriminant rather than
|
|
530
|
+
// adding a dedicated `kind: "scope"` that every relation branch would have to learn. The empty
|
|
531
|
+
// `subFilter` is intentionally never read: the m2o/m2m branches only check `kind === "join"` to
|
|
532
|
+
// decide whether to join, then re-dispatch the original scope value back through `addTable`,
|
|
533
|
+
// where `isScope(...)` routes it to `addScopeFilterAt` to apply the scope's actual fragments.
|
|
534
|
+
return { kind: "join", subFilter: {} };
|
|
535
|
+
}
|
|
449
536
|
else if (filter === null) {
|
|
450
537
|
return { kind: "is-null" };
|
|
451
538
|
}
|
|
@@ -553,6 +640,13 @@ function nilIdValue(meta) {
|
|
|
553
640
|
function isNilIdValue(value) {
|
|
554
641
|
return value === -1 || value === "00000000-0000-0000-0000-000000000000";
|
|
555
642
|
}
|
|
643
|
+
/** Finds a filter field by fieldName or generated fieldIdName. */
|
|
644
|
+
function findFilterField(meta, key) {
|
|
645
|
+
return (meta.allFields[key] ??
|
|
646
|
+
meta.polyComponentFields?.[key] ??
|
|
647
|
+
Object.values(meta.allFields).find((field) => field.fieldIdName === key) ??
|
|
648
|
+
Object.values(meta.polyComponentFields ?? {}).find((field) => field.fieldIdName === key));
|
|
649
|
+
}
|
|
556
650
|
/**
|
|
557
651
|
* Parses the many/hodgepodge (ergonomic!) patterns of value filters into a `ParsedValueFilter[]`.
|
|
558
652
|
*
|
|
@@ -579,7 +673,10 @@ function parseValueFilter(filter) {
|
|
|
579
673
|
else if (keys.length === 2 && "op" in filter && "value" in filter) {
|
|
580
674
|
// Probe for `findGql` op & value
|
|
581
675
|
const { op, value } = filter;
|
|
582
|
-
if (value
|
|
676
|
+
if (shouldPruneValueFilter(op, value)) {
|
|
677
|
+
return [];
|
|
678
|
+
}
|
|
679
|
+
else if (value === null) {
|
|
583
680
|
return [{ kind: "is-null" }];
|
|
584
681
|
}
|
|
585
682
|
else {
|
|
@@ -594,7 +691,7 @@ function parseValueFilter(filter) {
|
|
|
594
691
|
return Object.entries(filter)
|
|
595
692
|
.map(([key, value]) => {
|
|
596
693
|
// Always do condition pruning on the value
|
|
597
|
-
if (value
|
|
694
|
+
if (shouldPruneValueFilter(key, value)) {
|
|
598
695
|
return undefined;
|
|
599
696
|
}
|
|
600
697
|
switch (key) {
|
|
@@ -859,4 +956,10 @@ function addStiSubtypeFilter(cb, subtypeMeta, alias) {
|
|
|
859
956
|
function makeLike(search) {
|
|
860
957
|
return search ? `%${search.replace(/\s+/g, "%")}%` : undefined;
|
|
861
958
|
}
|
|
959
|
+
/** Returns true for values that should be treated like an omitted filter. */
|
|
960
|
+
function shouldPruneValueFilter(key, value) {
|
|
961
|
+
if (value === undefined)
|
|
962
|
+
return true;
|
|
963
|
+
return value === false && key !== "eq" && key !== "ne";
|
|
964
|
+
}
|
|
862
965
|
//# sourceMappingURL=QueryParser.js.map
|