directus 9.9.0 → 9.11.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 (132) hide show
  1. package/README.md +1 -1
  2. package/dist/app.js +3 -0
  3. package/dist/auth/drivers/oauth2.d.ts +1 -1
  4. package/dist/auth/drivers/oauth2.js +14 -11
  5. package/dist/auth/drivers/openid.d.ts +1 -1
  6. package/dist/auth/drivers/openid.js +14 -11
  7. package/dist/cli/commands/schema/apply.js +4 -3
  8. package/dist/controllers/assets.js +8 -9
  9. package/dist/controllers/files.js +3 -0
  10. package/dist/database/helpers/date/dialects/sqlite.js +6 -2
  11. package/dist/database/helpers/fn/dialects/postgres.js +5 -1
  12. package/dist/database/migrations/20210225A-add-relations-sort-field.js +2 -1
  13. package/dist/database/migrations/20210506A-rename-interfaces.js +2 -1
  14. package/dist/database/migrations/20210802A-replace-groups.js +2 -1
  15. package/dist/database/migrations/20210805A-update-groups.js +2 -1
  16. package/dist/database/migrations/20210805B-change-image-metadata-structure.js +3 -2
  17. package/dist/database/migrations/20211007A-update-presets.js +5 -4
  18. package/dist/database/run-ast.js +11 -15
  19. package/dist/database/system-data/fields/collections.yaml +1 -1
  20. package/dist/env.js +8 -3
  21. package/dist/exceptions/index.d.ts +1 -0
  22. package/dist/exceptions/index.js +1 -0
  23. package/dist/exceptions/invalid-provider.d.ts +4 -0
  24. package/dist/exceptions/invalid-provider.js +10 -0
  25. package/dist/exceptions/range-not-satisfiable.d.ts +2 -2
  26. package/dist/exceptions/range-not-satisfiable.js +5 -1
  27. package/dist/middleware/graphql.js +2 -1
  28. package/dist/services/assets.js +27 -1
  29. package/dist/services/authentication.js +4 -1
  30. package/dist/services/collections.js +2 -0
  31. package/dist/services/fields.js +17 -8
  32. package/dist/services/graphql.js +75 -34
  33. package/dist/services/import-export.d.ts +1 -1
  34. package/dist/services/import-export.js +14 -11
  35. package/dist/services/items.d.ts +3 -3
  36. package/dist/services/items.js +20 -6
  37. package/dist/services/payload.d.ts +3 -3
  38. package/dist/services/payload.js +11 -8
  39. package/dist/services/specifications.js +1 -3
  40. package/dist/services/users.d.ts +4 -0
  41. package/dist/services/users.js +24 -1
  42. package/dist/utils/{apply-query.d.ts → apply-query/index.d.ts} +2 -1
  43. package/dist/utils/apply-query/index.js +394 -0
  44. package/dist/utils/apply-query/operators/between.operator.d.ts +2 -0
  45. package/dist/utils/apply-query/operators/between.operator.js +16 -0
  46. package/dist/utils/apply-query/operators/contains.operator.d.ts +2 -0
  47. package/dist/utils/apply-query/operators/contains.operator.js +9 -0
  48. package/dist/utils/apply-query/operators/ends-with.operator.d.ts +2 -0
  49. package/dist/utils/apply-query/operators/ends-with.operator.js +9 -0
  50. package/dist/utils/apply-query/operators/equals.operator.d.ts +2 -0
  51. package/dist/utils/apply-query/operators/equals.operator.js +9 -0
  52. package/dist/utils/apply-query/operators/greather-than-equals.operator.d.ts +2 -0
  53. package/dist/utils/apply-query/operators/greather-than-equals.operator.js +9 -0
  54. package/dist/utils/apply-query/operators/greather-than.operator.d.ts +2 -0
  55. package/dist/utils/apply-query/operators/greather-than.operator.js +9 -0
  56. package/dist/utils/apply-query/operators/in.operator.d.ts +2 -0
  57. package/dist/utils/apply-query/operators/in.operator.js +14 -0
  58. package/dist/utils/apply-query/operators/index.d.ts +3 -0
  59. package/dist/utils/apply-query/operators/index.js +72 -0
  60. package/dist/utils/apply-query/operators/insensitive-contains.operator.d.ts +2 -0
  61. package/dist/utils/apply-query/operators/insensitive-contains.operator.js +9 -0
  62. package/dist/utils/apply-query/operators/insensitive-ends-with.operator.d.ts +2 -0
  63. package/dist/utils/apply-query/operators/insensitive-ends-with.operator.js +9 -0
  64. package/dist/utils/apply-query/operators/insensitive-equals.operator.d.ts +2 -0
  65. package/dist/utils/apply-query/operators/insensitive-equals.operator.js +9 -0
  66. package/dist/utils/apply-query/operators/insensitive-not-contains.operator.d.ts +2 -0
  67. package/dist/utils/apply-query/operators/insensitive-not-contains.operator.js +9 -0
  68. package/dist/utils/apply-query/operators/insensitive-not-ends-with.operator.d.ts +2 -0
  69. package/dist/utils/apply-query/operators/insensitive-not-ends-with.operator.js +9 -0
  70. package/dist/utils/apply-query/operators/insensitive-not-equals.operator.d.ts +2 -0
  71. package/dist/utils/apply-query/operators/insensitive-not-equals.operator.js +9 -0
  72. package/dist/utils/apply-query/operators/insensitive-not-starts-with.operator.d.ts +2 -0
  73. package/dist/utils/apply-query/operators/insensitive-not-starts-with.operator.js +9 -0
  74. package/dist/utils/apply-query/operators/insensitive-starts-with.operator.d.ts +2 -0
  75. package/dist/utils/apply-query/operators/insensitive-starts-with.operator.js +9 -0
  76. package/dist/utils/apply-query/operators/intersects-bbox.operator.d.ts +2 -0
  77. package/dist/utils/apply-query/operators/intersects-bbox.operator.js +9 -0
  78. package/dist/utils/apply-query/operators/intersects.operator.d.ts +2 -0
  79. package/dist/utils/apply-query/operators/intersects.operator.js +9 -0
  80. package/dist/utils/apply-query/operators/is-empty.operator.d.ts +2 -0
  81. package/dist/utils/apply-query/operators/is-empty.operator.js +14 -0
  82. package/dist/utils/apply-query/operators/is-not-empty.operator.d.ts +2 -0
  83. package/dist/utils/apply-query/operators/is-not-empty.operator.js +14 -0
  84. package/dist/utils/apply-query/operators/is-not-null.operator.d.ts +2 -0
  85. package/dist/utils/apply-query/operators/is-not-null.operator.js +14 -0
  86. package/dist/utils/apply-query/operators/is-null.operator.d.ts +2 -0
  87. package/dist/utils/apply-query/operators/is-null.operator.js +14 -0
  88. package/dist/utils/apply-query/operators/less-than-equals.operator.d.ts +2 -0
  89. package/dist/utils/apply-query/operators/less-than-equals.operator.js +9 -0
  90. package/dist/utils/apply-query/operators/less-than.operator.d.ts +2 -0
  91. package/dist/utils/apply-query/operators/less-than.operator.js +9 -0
  92. package/dist/utils/apply-query/operators/not-between.operator.d.ts +2 -0
  93. package/dist/utils/apply-query/operators/not-between.operator.js +16 -0
  94. package/dist/utils/apply-query/operators/not-contains.operator.d.ts +2 -0
  95. package/dist/utils/apply-query/operators/not-contains.operator.js +9 -0
  96. package/dist/utils/apply-query/operators/not-ends-with.operator.d.ts +2 -0
  97. package/dist/utils/apply-query/operators/not-ends-with.operator.js +9 -0
  98. package/dist/utils/apply-query/operators/not-equals.operator.d.ts +2 -0
  99. package/dist/utils/apply-query/operators/not-equals.operator.js +9 -0
  100. package/dist/utils/apply-query/operators/not-in.operator.d.ts +2 -0
  101. package/dist/utils/apply-query/operators/not-in.operator.js +14 -0
  102. package/dist/utils/apply-query/operators/not-intersects-bbox.operator.d.ts +2 -0
  103. package/dist/utils/apply-query/operators/not-intersects-bbox.operator.js +9 -0
  104. package/dist/utils/apply-query/operators/not-intersects.operator.d.ts +2 -0
  105. package/dist/utils/apply-query/operators/not-intersects.operator.js +9 -0
  106. package/dist/utils/apply-query/operators/not-starts-with.operator.d.ts +2 -0
  107. package/dist/utils/apply-query/operators/not-starts-with.operator.js +9 -0
  108. package/dist/utils/apply-query/operators/operator-register.d.ts +13 -0
  109. package/dist/utils/apply-query/operators/operator-register.js +7 -0
  110. package/dist/utils/apply-query/operators/starts-with.operator.d.ts +2 -0
  111. package/dist/utils/apply-query/operators/starts-with.operator.js +9 -0
  112. package/dist/utils/apply-snapshot.d.ts +3 -3
  113. package/dist/utils/apply-snapshot.js +64 -49
  114. package/dist/utils/get-ast-from-query.js +10 -4
  115. package/dist/utils/get-column-path.d.ts +16 -0
  116. package/dist/utils/get-column-path.js +46 -0
  117. package/dist/utils/get-default-value.js +4 -3
  118. package/dist/utils/get-permissions.d.ts +1 -1
  119. package/dist/utils/get-permissions.js +9 -8
  120. package/dist/utils/get-relation-info.d.ts +7 -0
  121. package/dist/utils/get-relation-info.js +45 -0
  122. package/dist/utils/get-relation-type.d.ts +1 -1
  123. package/dist/utils/get-schema.js +5 -1
  124. package/dist/utils/get-snapshot.js +22 -4
  125. package/dist/utils/merge-permissions-for-share.js +1 -1
  126. package/dist/utils/parse-json.d.ts +5 -0
  127. package/dist/utils/parse-json.js +19 -0
  128. package/dist/utils/reduce-schema.js +18 -11
  129. package/dist/utils/sanitize-query.d.ts +1 -2
  130. package/dist/utils/sanitize-query.js +6 -5
  131. package/package.json +16 -18
  132. package/dist/utils/apply-query.js +0 -498
@@ -1,498 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.applyAggregate = exports.applySearch = exports.applyFilter = void 0;
7
- const lodash_1 = require("lodash");
8
- const nanoid_1 = require("nanoid");
9
- const uuid_validate_1 = __importDefault(require("uuid-validate"));
10
- const exceptions_1 = require("../exceptions");
11
- const get_column_1 = require("./get-column");
12
- const get_relation_type_1 = require("./get-relation-type");
13
- const helpers_1 = require("../database/helpers");
14
- const utils_1 = require("@directus/shared/utils");
15
- const generateAlias = (0, nanoid_1.customAlphabet)('abcdefghijklmnopqrstuvwxyz', 5);
16
- /**
17
- * Apply the Query to a given Knex query builder instance
18
- */
19
- function applyQuery(knex, collection, dbQuery, query, schema, subQuery = false) {
20
- if (query.sort) {
21
- dbQuery.orderBy(query.sort.map((sortField) => {
22
- let column = sortField;
23
- let order = 'asc';
24
- if (sortField.startsWith('-')) {
25
- column = column.substring(1);
26
- order = 'desc';
27
- }
28
- return {
29
- order,
30
- column: (0, get_column_1.getColumn)(knex, collection, column, false, schema),
31
- };
32
- }));
33
- }
34
- if (typeof query.limit === 'number' && query.limit !== -1) {
35
- dbQuery.limit(query.limit);
36
- }
37
- if (query.offset) {
38
- dbQuery.offset(query.offset);
39
- }
40
- if (query.page && query.limit && query.limit !== -1) {
41
- dbQuery.offset(query.limit * (query.page - 1));
42
- }
43
- if (query.search) {
44
- applySearch(schema, dbQuery, query.search, collection);
45
- }
46
- if (query.group) {
47
- dbQuery.groupBy(query.group.map((column) => (0, get_column_1.getColumn)(knex, collection, column, false, schema)));
48
- }
49
- if (query.aggregate) {
50
- applyAggregate(dbQuery, query.aggregate, collection);
51
- }
52
- if (query.filter) {
53
- applyFilter(knex, schema, dbQuery, query.filter, collection, subQuery);
54
- }
55
- return dbQuery;
56
- }
57
- exports.default = applyQuery;
58
- function getRelationInfo(relations, collection, field) {
59
- var _a, _b;
60
- const implicitRelation = (_a = field.match(/^\$FOLLOW\((.*?),(.*?)(?:,(.*?))?\)$/)) === null || _a === void 0 ? void 0 : _a.slice(1);
61
- if (implicitRelation) {
62
- if (implicitRelation[2] === undefined) {
63
- const [m2oCollection, m2oField] = implicitRelation;
64
- const relation = {
65
- collection: m2oCollection,
66
- field: m2oField,
67
- related_collection: collection,
68
- schema: null,
69
- meta: null,
70
- };
71
- return { relation, relationType: 'o2m' };
72
- }
73
- else {
74
- const [a2oCollection, a2oItemField, a2oCollectionField] = implicitRelation;
75
- const relation = {
76
- collection: a2oCollection,
77
- field: a2oItemField,
78
- related_collection: collection,
79
- schema: null,
80
- meta: {
81
- one_collection_field: a2oCollectionField,
82
- one_field: field,
83
- },
84
- };
85
- return { relation, relationType: 'o2a' };
86
- }
87
- }
88
- const relation = (_b = relations.find((relation) => {
89
- var _a;
90
- return ((relation.collection === collection && relation.field === field) ||
91
- (relation.related_collection === collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === field));
92
- })) !== null && _b !== void 0 ? _b : null;
93
- const relationType = relation ? (0, get_relation_type_1.getRelationType)({ relation, collection, field }) : null;
94
- return { relation, relationType };
95
- }
96
- function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery = false) {
97
- const helpers = (0, helpers_1.getHelpers)(knex);
98
- const relations = schema.relations;
99
- const aliasMap = {};
100
- addJoins(rootQuery, rootFilter, collection);
101
- addWhereClauses(knex, rootQuery, rootFilter, collection);
102
- return rootQuery;
103
- function addJoins(dbQuery, filter, collection) {
104
- for (const [key, value] of Object.entries(filter)) {
105
- if (key === '_or' || key === '_and') {
106
- // If the _or array contains an empty object (full permissions), we should short-circuit and ignore all other
107
- // permission checks, as {} already matches full permissions.
108
- if (key === '_or' && value.some((subFilter) => Object.keys(subFilter).length === 0))
109
- continue;
110
- value.forEach((subFilter) => {
111
- addJoins(dbQuery, subFilter, collection);
112
- });
113
- continue;
114
- }
115
- const filterPath = getFilterPath(key, value);
116
- if (filterPath.length > 1) {
117
- addJoin(filterPath, collection);
118
- }
119
- }
120
- function addJoin(path, collection) {
121
- path = (0, lodash_1.clone)(path);
122
- followRelation(path);
123
- function followRelation(pathParts, parentCollection = collection, parentAlias) {
124
- /**
125
- * For A2M fields, the path can contain an optional collection scope <field>:<scope>
126
- */
127
- const pathRoot = pathParts[0].split(':')[0];
128
- const { relation, relationType } = getRelationInfo(relations, parentCollection, pathRoot);
129
- if (!relation) {
130
- return;
131
- }
132
- const alias = generateAlias();
133
- (0, lodash_1.set)(aliasMap, parentAlias ? [parentAlias, ...pathParts] : pathParts, alias);
134
- if (relationType === 'm2o') {
135
- dbQuery.leftJoin({ [alias]: relation.related_collection }, `${parentAlias || parentCollection}.${relation.field}`, `${alias}.${schema.collections[relation.related_collection].primary}`);
136
- }
137
- if (relationType === 'a2o') {
138
- const pathScope = pathParts[0].split(':')[1];
139
- if (!pathScope) {
140
- throw new exceptions_1.InvalidQueryException(`You have to provide a collection scope when filtering on a many-to-any item`);
141
- }
142
- dbQuery.leftJoin({ [alias]: pathScope }, (joinClause) => {
143
- joinClause
144
- .onVal(relation.meta.one_collection_field, '=', pathScope)
145
- .andOn(`${parentAlias || parentCollection}.${relation.field}`, '=', knex.raw(`CAST(?? AS CHAR(255))`, `${alias}.${schema.collections[pathScope].primary}`));
146
- });
147
- }
148
- if (relationType === 'o2a') {
149
- dbQuery.leftJoin({ [alias]: relation.collection }, (joinClause) => {
150
- joinClause
151
- .onVal(relation.meta.one_collection_field, '=', parentCollection)
152
- .andOn(`${alias}.${relation.field}`, '=', knex.raw(`CAST(?? AS CHAR(255))`, `${parentAlias || parentCollection}.${schema.collections[parentCollection].primary}`));
153
- });
154
- }
155
- // Still join o2m relations when in subquery OR when the o2m relation is not at the root level
156
- if (relationType === 'o2m' && (subQuery === true || parentAlias !== undefined)) {
157
- dbQuery.leftJoin({ [alias]: relation.collection }, `${parentAlias || parentCollection}.${schema.collections[relation.related_collection].primary}`, `${alias}.${relation.field}`);
158
- }
159
- if (relationType === 'm2o' || subQuery === true || (relationType === 'o2m' && parentAlias !== undefined)) {
160
- let parent;
161
- if (relationType === 'm2o') {
162
- parent = relation.related_collection;
163
- }
164
- else if (relationType === 'a2o') {
165
- const pathScope = pathParts[0].split(':')[1];
166
- if (!pathScope) {
167
- throw new exceptions_1.InvalidQueryException(`You have to provide a collection scope when filtering on a many-to-any item`);
168
- }
169
- parent = pathScope;
170
- }
171
- else {
172
- parent = relation.collection;
173
- }
174
- pathParts.shift();
175
- if (pathParts.length) {
176
- followRelation(pathParts, parent, alias);
177
- }
178
- }
179
- }
180
- }
181
- }
182
- function addWhereClauses(knex, dbQuery, filter, collection, logical = 'and') {
183
- var _a, _b;
184
- for (const [key, value] of Object.entries(filter)) {
185
- if (key === '_or' || key === '_and') {
186
- // If the _or array contains an empty object (full permissions), we should short-circuit and ignore all other
187
- // permission checks, as {} already matches full permissions.
188
- if (key === '_or' && value.some((subFilter) => Object.keys(subFilter).length === 0)) {
189
- continue;
190
- }
191
- /** @NOTE this callback function isn't called until Knex runs the query */
192
- dbQuery[logical].where((subQuery) => {
193
- value.forEach((subFilter) => {
194
- addWhereClauses(knex, subQuery, subFilter, collection, key === '_and' ? 'and' : 'or');
195
- });
196
- });
197
- continue;
198
- }
199
- const filterPath = getFilterPath(key, value);
200
- /**
201
- * For A2M fields, the path can contain an optional collection scope <field>:<scope>
202
- */
203
- const pathRoot = filterPath[0].split(':')[0];
204
- const { relation, relationType } = getRelationInfo(relations, collection, pathRoot);
205
- const { operator: filterOperator, value: filterValue } = getOperation(key, value);
206
- if (relationType === 'm2o' || relationType === 'a2o' || relationType === null) {
207
- if (filterPath.length > 1) {
208
- const columnName = getWhereColumn(filterPath, collection);
209
- if (!columnName)
210
- continue;
211
- applyFilterToQuery(columnName, filterOperator, filterValue, logical);
212
- }
213
- else {
214
- applyFilterToQuery(`${collection}.${filterPath[0]}`, filterOperator, filterValue, logical);
215
- }
216
- }
217
- else if (subQuery === false || filterPath.length > 1) {
218
- if (!relation)
219
- continue;
220
- let pkField = `${collection}.${schema.collections[relation.related_collection].primary}`;
221
- if (relationType === 'o2a') {
222
- pkField = knex.raw(`CAST(?? AS CHAR(255))`, [pkField]);
223
- }
224
- const subQueryBuilder = (filter) => (subQueryKnex) => {
225
- const field = relation.field;
226
- const collection = relation.collection;
227
- const column = `${collection}.${field}`;
228
- subQueryKnex
229
- .select({ [field]: column })
230
- .from(collection)
231
- .whereNotNull(field);
232
- applyQuery(knex, relation.collection, subQueryKnex, { filter }, schema, true);
233
- };
234
- if (((_a = Object.keys(value)) === null || _a === void 0 ? void 0 : _a[0]) === '_none') {
235
- dbQuery[logical].whereNotIn(pkField, subQueryBuilder(Object.values(value)[0]));
236
- }
237
- else if (((_b = Object.keys(value)) === null || _b === void 0 ? void 0 : _b[0]) === '_some') {
238
- dbQuery[logical].whereIn(pkField, subQueryBuilder(Object.values(value)[0]));
239
- }
240
- else {
241
- dbQuery[logical].whereIn(pkField, subQueryBuilder(value));
242
- }
243
- }
244
- }
245
- function applyFilterToQuery(key, operator, compareValue, logical = 'and') {
246
- const [table, column] = key.split('.');
247
- // Is processed through Knex.Raw, so should be safe to string-inject into these where queries
248
- const selectionRaw = (0, get_column_1.getColumn)(knex, table, column, false, schema);
249
- // Knex supports "raw" in the columnName parameter, but isn't typed as such. Too bad..
250
- // See https://github.com/knex/knex/issues/4518 @TODO remove as any once knex is updated
251
- // These operators don't rely on a value, and can thus be used without one (eg `?filter[field][_null]`)
252
- if (operator === '_null' || (operator === '_nnull' && compareValue === false)) {
253
- dbQuery[logical].whereNull(selectionRaw);
254
- }
255
- if (operator === '_nnull' || (operator === '_null' && compareValue === false)) {
256
- dbQuery[logical].whereNotNull(selectionRaw);
257
- }
258
- if (operator === '_empty' || (operator === '_nempty' && compareValue === false)) {
259
- dbQuery[logical].andWhere((query) => {
260
- query.where(key, '=', '');
261
- });
262
- }
263
- if (operator === '_nempty' || (operator === '_empty' && compareValue === false)) {
264
- dbQuery[logical].andWhere((query) => {
265
- query.where(key, '!=', '');
266
- });
267
- }
268
- // Cast filter value (compareValue) based on function used
269
- if (column.includes('(') && column.includes(')')) {
270
- const functionName = column.split('(')[0];
271
- const type = (0, utils_1.getOutputTypeForFunction)(functionName);
272
- if (['bigInteger', 'integer', 'float', 'decimal'].includes(type)) {
273
- compareValue = Number(compareValue);
274
- }
275
- }
276
- // Cast filter value (compareValue) based on type of field being filtered against
277
- const [collection, field] = key.split('.');
278
- if (collection in schema.collections && field in schema.collections[collection].fields) {
279
- const type = schema.collections[collection].fields[field].type;
280
- if (['date', 'dateTime', 'time', 'timestamp'].includes(type)) {
281
- if (Array.isArray(compareValue)) {
282
- compareValue = compareValue.map((val) => helpers.date.parse(val));
283
- }
284
- else {
285
- compareValue = helpers.date.parse(compareValue);
286
- }
287
- }
288
- if (['bigInteger', 'integer', 'float', 'decimal'].includes(type)) {
289
- if (Array.isArray(compareValue)) {
290
- compareValue = compareValue.map((val) => Number(val));
291
- }
292
- else {
293
- compareValue = Number(compareValue);
294
- }
295
- }
296
- }
297
- // The following fields however, require a value to be run. If no value is passed, we
298
- // ignore them. This allows easier use in GraphQL, where you wouldn't be able to
299
- // conditionally build out your filter structure (#4471)
300
- if (compareValue === undefined)
301
- return;
302
- if (Array.isArray(compareValue)) {
303
- // Tip: when using a `[Type]` type in GraphQL, but don't provide the variable, it'll be
304
- // reported as [undefined].
305
- // We need to remove any undefined values, as they are useless
306
- compareValue = compareValue.filter((val) => val !== undefined);
307
- }
308
- if (operator === '_eq') {
309
- dbQuery[logical].where(selectionRaw, '=', compareValue);
310
- }
311
- if (operator === '_neq') {
312
- dbQuery[logical].whereNot(selectionRaw, compareValue);
313
- }
314
- if (operator === '_contains') {
315
- dbQuery[logical].where(selectionRaw, 'like', `%${compareValue}%`);
316
- }
317
- if (operator === '_ncontains') {
318
- dbQuery[logical].whereNot(selectionRaw, 'like', `%${compareValue}%`);
319
- }
320
- if (operator === '_starts_with') {
321
- dbQuery[logical].where(key, 'like', `${compareValue}%`);
322
- }
323
- if (operator === '_nstarts_with') {
324
- dbQuery[logical].whereNot(key, 'like', `${compareValue}%`);
325
- }
326
- if (operator === '_ends_with') {
327
- dbQuery[logical].where(key, 'like', `%${compareValue}`);
328
- }
329
- if (operator === '_nends_with') {
330
- dbQuery[logical].whereNot(key, 'like', `%${compareValue}`);
331
- }
332
- if (operator === '_gt') {
333
- dbQuery[logical].where(selectionRaw, '>', compareValue);
334
- }
335
- if (operator === '_gte') {
336
- dbQuery[logical].where(selectionRaw, '>=', compareValue);
337
- }
338
- if (operator === '_lt') {
339
- dbQuery[logical].where(selectionRaw, '<', compareValue);
340
- }
341
- if (operator === '_lte') {
342
- dbQuery[logical].where(selectionRaw, '<=', compareValue);
343
- }
344
- if (operator === '_in') {
345
- let value = compareValue;
346
- if (typeof value === 'string')
347
- value = value.split(',');
348
- dbQuery[logical].whereIn(selectionRaw, value);
349
- }
350
- if (operator === '_nin') {
351
- let value = compareValue;
352
- if (typeof value === 'string')
353
- value = value.split(',');
354
- dbQuery[logical].whereNotIn(selectionRaw, value);
355
- }
356
- if (operator === '_between') {
357
- if (compareValue.length !== 2)
358
- return;
359
- let value = compareValue;
360
- if (typeof value === 'string')
361
- value = value.split(',');
362
- dbQuery[logical].whereBetween(selectionRaw, value);
363
- }
364
- if (operator === '_nbetween') {
365
- if (compareValue.length !== 2)
366
- return;
367
- let value = compareValue;
368
- if (typeof value === 'string')
369
- value = value.split(',');
370
- dbQuery[logical].whereNotBetween(selectionRaw, value);
371
- }
372
- if (operator == '_intersects') {
373
- dbQuery[logical].whereRaw(helpers.st.intersects(key, compareValue));
374
- }
375
- if (operator == '_nintersects') {
376
- dbQuery[logical].whereRaw(helpers.st.nintersects(key, compareValue));
377
- }
378
- if (operator == '_intersects_bbox') {
379
- dbQuery[logical].whereRaw(helpers.st.intersects_bbox(key, compareValue));
380
- }
381
- if (operator == '_nintersects_bbox') {
382
- dbQuery[logical].whereRaw(helpers.st.nintersects_bbox(key, compareValue));
383
- }
384
- }
385
- function getWhereColumn(path, collection) {
386
- return followRelation(path);
387
- function followRelation(pathParts, parentCollection = collection, parentAlias) {
388
- /**
389
- * For A2M fields, the path can contain an optional collection scope <field>:<scope>
390
- */
391
- const pathRoot = pathParts[0].split(':')[0];
392
- const { relation, relationType } = getRelationInfo(relations, parentCollection, pathRoot);
393
- if (!relation) {
394
- throw new exceptions_1.InvalidQueryException(`"${parentCollection}.${pathRoot}" is not a relational field`);
395
- }
396
- const alias = (0, lodash_1.get)(aliasMap, parentAlias ? [parentAlias, ...pathParts] : pathParts);
397
- const remainingParts = pathParts.slice(1);
398
- let parent;
399
- if (relationType === 'a2o') {
400
- const pathScope = pathParts[0].split(':')[1];
401
- if (!pathScope) {
402
- throw new exceptions_1.InvalidQueryException(`You have to provide a collection scope when filtering on a many-to-any item`);
403
- }
404
- parent = pathScope;
405
- }
406
- else if (relationType === 'm2o') {
407
- parent = relation.related_collection;
408
- }
409
- else {
410
- parent = relation.collection;
411
- }
412
- if (remainingParts.length === 1) {
413
- return `${alias || parent}.${remainingParts[0]}`;
414
- }
415
- if (remainingParts.length) {
416
- return followRelation(remainingParts, parent, alias);
417
- }
418
- }
419
- }
420
- }
421
- }
422
- exports.applyFilter = applyFilter;
423
- async function applySearch(schema, dbQuery, searchQuery, collection) {
424
- const fields = Object.entries(schema.collections[collection].fields);
425
- dbQuery.andWhere(function () {
426
- fields.forEach(([name, field]) => {
427
- if (['text', 'string'].includes(field.type)) {
428
- this.orWhereRaw(`LOWER(??) LIKE ?`, [`${collection}.${name}`, `%${searchQuery.toLowerCase()}%`]);
429
- }
430
- else if (['bigInteger', 'integer', 'decimal', 'float'].includes(field.type)) {
431
- const number = Number(searchQuery);
432
- if (!isNaN(number))
433
- this.orWhere({ [`${collection}.${name}`]: number });
434
- }
435
- else if (field.type === 'uuid' && (0, uuid_validate_1.default)(searchQuery)) {
436
- this.orWhere({ [`${collection}.${name}`]: searchQuery });
437
- }
438
- });
439
- });
440
- }
441
- exports.applySearch = applySearch;
442
- function applyAggregate(dbQuery, aggregate, collection) {
443
- for (const [operation, fields] of Object.entries(aggregate)) {
444
- if (!fields)
445
- continue;
446
- for (const field of fields) {
447
- if (operation === 'avg') {
448
- dbQuery.avg(`${collection}.${field}`, { as: `avg->${field}` });
449
- }
450
- if (operation === 'avgDistinct') {
451
- dbQuery.avgDistinct(`${collection}.${field}`, { as: `avgDistinct->${field}` });
452
- }
453
- if (operation === 'count') {
454
- if (field === '*') {
455
- dbQuery.count('*', { as: 'count' });
456
- }
457
- else {
458
- dbQuery.count(`${collection}.${field}`, { as: `count->${field}` });
459
- }
460
- }
461
- if (operation === 'countDistinct') {
462
- dbQuery.countDistinct(`${collection}.${field}`, { as: `countDistinct->${field}` });
463
- }
464
- if (operation === 'sum') {
465
- dbQuery.sum(`${collection}.${field}`, { as: `sum->${field}` });
466
- }
467
- if (operation === 'sumDistinct') {
468
- dbQuery.sumDistinct(`${collection}.${field}`, { as: `sumDistinct->${field}` });
469
- }
470
- if (operation === 'min') {
471
- dbQuery.min(`${collection}.${field}`, { as: `min->${field}` });
472
- }
473
- if (operation === 'max') {
474
- dbQuery.max(`${collection}.${field}`, { as: `max->${field}` });
475
- }
476
- }
477
- }
478
- }
479
- exports.applyAggregate = applyAggregate;
480
- function getFilterPath(key, value) {
481
- const path = [key];
482
- if (typeof Object.keys(value)[0] === 'string' && Object.keys(value)[0].startsWith('_') === true) {
483
- return path;
484
- }
485
- if ((0, lodash_1.isPlainObject)(value)) {
486
- path.push(...getFilterPath(Object.keys(value)[0], Object.values(value)[0]));
487
- }
488
- return path;
489
- }
490
- function getOperation(key, value) {
491
- if (key.startsWith('_') && key !== '_and' && key !== '_or') {
492
- return { operator: key, value };
493
- }
494
- else if ((0, lodash_1.isPlainObject)(value) === false) {
495
- return { operator: '_eq', value };
496
- }
497
- return getOperation(Object.keys(value)[0], Object.values(value)[0]);
498
- }