mirror of
https://github.com/directus/directus.git
synced 2026-01-30 15:58:22 -05:00
Allow functions in sort/filter
This commit is contained in:
@@ -171,7 +171,7 @@ function getDBQuery(
|
||||
delete queryCopy.limit;
|
||||
}
|
||||
|
||||
applyQuery(table, dbQuery, queryCopy, schema);
|
||||
applyQuery(knex, table, dbQuery, queryCopy, schema);
|
||||
|
||||
return dbQuery;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { customAlphabet } from 'nanoid';
|
||||
import validate from 'uuid-validate';
|
||||
import { InvalidQueryException } from '../exceptions';
|
||||
import { Aggregate, Filter, Query, Relation, SchemaOverview } from '../types';
|
||||
import { applyFunctionToColumnName } from './apply-function-to-column-name';
|
||||
import { getColumn } from './get-column';
|
||||
import { getRelationType } from './get-relation-type';
|
||||
|
||||
const generateAlias = customAlphabet('abcdefghijklmnopqrstuvwxyz', 5);
|
||||
@@ -12,6 +14,7 @@ const generateAlias = customAlphabet('abcdefghijklmnopqrstuvwxyz', 5);
|
||||
* Apply the Query to a given Knex query builder instance
|
||||
*/
|
||||
export default function applyQuery(
|
||||
knex: Knex,
|
||||
collection: string,
|
||||
dbQuery: Knex.QueryBuilder,
|
||||
query: Query,
|
||||
@@ -22,7 +25,7 @@ export default function applyQuery(
|
||||
dbQuery.orderBy(
|
||||
query.sort.map((sort) => ({
|
||||
...sort,
|
||||
column: `${collection}.${sort.column}`,
|
||||
column: getColumn(knex, collection, sort.column, false) as any,
|
||||
}))
|
||||
);
|
||||
}
|
||||
@@ -40,7 +43,7 @@ export default function applyQuery(
|
||||
}
|
||||
|
||||
if (query.filter) {
|
||||
applyFilter(schema, dbQuery, query.filter, collection, subQuery);
|
||||
applyFilter(knex, schema, dbQuery, query.filter, collection, subQuery);
|
||||
}
|
||||
|
||||
if (query.search) {
|
||||
@@ -48,7 +51,7 @@ export default function applyQuery(
|
||||
}
|
||||
|
||||
if (query.group) {
|
||||
dbQuery.groupBy(query.group);
|
||||
dbQuery.groupBy(query.group.map(applyFunctionToColumnName));
|
||||
}
|
||||
|
||||
if (query.aggregate) {
|
||||
@@ -95,6 +98,7 @@ export default function applyQuery(
|
||||
* ```
|
||||
*/
|
||||
export function applyFilter(
|
||||
knex: Knex,
|
||||
schema: SchemaOverview,
|
||||
rootQuery: Knex.QueryBuilder,
|
||||
rootFilter: Filter,
|
||||
@@ -106,7 +110,7 @@ export function applyFilter(
|
||||
const aliasMap: Record<string, string> = {};
|
||||
|
||||
addJoins(rootQuery, rootFilter, collection);
|
||||
addWhereClauses(rootQuery, rootFilter, collection);
|
||||
addWhereClauses(knex, rootQuery, rootFilter, collection);
|
||||
|
||||
function addJoins(dbQuery: Knex.QueryBuilder, filter: Filter, collection: string) {
|
||||
for (const [key, value] of Object.entries(filter)) {
|
||||
@@ -223,6 +227,7 @@ export function applyFilter(
|
||||
}
|
||||
|
||||
function addWhereClauses(
|
||||
knex: Knex,
|
||||
dbQuery: Knex.QueryBuilder,
|
||||
filter: Filter,
|
||||
collection: string,
|
||||
@@ -239,7 +244,7 @@ export function applyFilter(
|
||||
/** @NOTE this callback function isn't called until Knex runs the query */
|
||||
dbQuery[logical].where((subQuery) => {
|
||||
value.forEach((subFilter: Record<string, any>) => {
|
||||
addWhereClauses(subQuery, subFilter, collection, key === '_and' ? 'and' : 'or');
|
||||
addWhereClauses(knex, subQuery, subFilter, collection, key === '_and' ? 'and' : 'or');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -282,6 +287,7 @@ export function applyFilter(
|
||||
subQueryKnex.select({ [field]: column }).from(collection);
|
||||
|
||||
applyQuery(
|
||||
knex,
|
||||
relation!.collection,
|
||||
subQueryKnex,
|
||||
{
|
||||
@@ -295,26 +301,33 @@ export function applyFilter(
|
||||
}
|
||||
|
||||
function applyFilterToQuery(key: string, operator: string, compareValue: any, logical: 'and' | 'or' = 'and') {
|
||||
const [table, column] = key.split('.');
|
||||
|
||||
// Is processed through Knex.Raw, so should be safe to string-inject into these where queries
|
||||
const selectionRaw = getColumn(knex, table, column, false) as any;
|
||||
// Knex supports "raw" in the columnName parameter, but isn't typed as such. Too bad..
|
||||
// See https://github.com/knex/knex/issues/4518 @TODO remove as any once knex is updated
|
||||
|
||||
// These operators don't rely on a value, and can thus be used without one (eg `?filter[field][_null]`)
|
||||
if (operator === '_null' || (operator === '_nnull' && compareValue === false)) {
|
||||
dbQuery[logical].whereNull(key);
|
||||
dbQuery[logical].whereNull(selectionRaw);
|
||||
}
|
||||
|
||||
if (operator === '_nnull' || (operator === '_null' && compareValue === false)) {
|
||||
dbQuery[logical].whereNotNull(key);
|
||||
dbQuery[logical].whereNotNull(selectionRaw);
|
||||
}
|
||||
|
||||
if (operator === '_empty' || (operator === '_nempty' && compareValue === false)) {
|
||||
dbQuery[logical].andWhere((query) => {
|
||||
query.whereNull(key);
|
||||
query.orWhere(key, '=', '');
|
||||
query.whereNull(selectionRaw);
|
||||
query.orWhere(selectionRaw, '=', '');
|
||||
});
|
||||
}
|
||||
|
||||
if (operator === '_nempty' || (operator === '_empty' && compareValue === false)) {
|
||||
dbQuery[logical].andWhere((query) => {
|
||||
query.whereNotNull(key);
|
||||
query.orWhere(key, '!=', '');
|
||||
query.whereNotNull(selectionRaw);
|
||||
query.orWhere(selectionRaw, '!=', '');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -333,49 +346,49 @@ export function applyFilter(
|
||||
}
|
||||
|
||||
if (operator === '_eq') {
|
||||
dbQuery[logical].where({ [key]: compareValue });
|
||||
dbQuery[logical].where(selectionRaw, '=', compareValue);
|
||||
}
|
||||
|
||||
if (operator === '_neq') {
|
||||
dbQuery[logical].whereNot({ [key]: compareValue });
|
||||
dbQuery[logical].whereNot(selectionRaw, compareValue);
|
||||
}
|
||||
|
||||
if (operator === '_contains') {
|
||||
dbQuery[logical].where(key, 'like', `%${compareValue}%`);
|
||||
dbQuery[logical].where(selectionRaw, 'like', `%${compareValue}%`);
|
||||
}
|
||||
|
||||
if (operator === '_ncontains') {
|
||||
dbQuery[logical].whereNot(key, 'like', `%${compareValue}%`);
|
||||
dbQuery[logical].whereNot(selectionRaw, 'like', `%${compareValue}%`);
|
||||
}
|
||||
|
||||
if (operator === '_gt') {
|
||||
dbQuery[logical].where(key, '>', compareValue);
|
||||
dbQuery[logical].where(selectionRaw, '>', compareValue);
|
||||
}
|
||||
|
||||
if (operator === '_gte') {
|
||||
dbQuery[logical].where(key, '>=', compareValue);
|
||||
dbQuery[logical].where(selectionRaw, '>=', compareValue);
|
||||
}
|
||||
|
||||
if (operator === '_lt') {
|
||||
dbQuery[logical].where(key, '<', compareValue);
|
||||
dbQuery[logical].where(selectionRaw, '<', compareValue);
|
||||
}
|
||||
|
||||
if (operator === '_lte') {
|
||||
dbQuery[logical].where(key, '<=', compareValue);
|
||||
dbQuery[logical].where(selectionRaw, '<=', compareValue);
|
||||
}
|
||||
|
||||
if (operator === '_in') {
|
||||
let value = compareValue;
|
||||
if (typeof value === 'string') value = value.split(',');
|
||||
|
||||
dbQuery[logical].whereIn(key, value as string[]);
|
||||
dbQuery[logical].whereIn(selectionRaw, value as string[]);
|
||||
}
|
||||
|
||||
if (operator === '_nin') {
|
||||
let value = compareValue;
|
||||
if (typeof value === 'string') value = value.split(',');
|
||||
|
||||
dbQuery[logical].whereNotIn(key, value as string[]);
|
||||
dbQuery[logical].whereNotIn(selectionRaw, value as string[]);
|
||||
}
|
||||
|
||||
if (operator === '_between') {
|
||||
@@ -384,7 +397,7 @@ export function applyFilter(
|
||||
let value = compareValue;
|
||||
if (typeof value === 'string') value = value.split(',');
|
||||
|
||||
dbQuery[logical].whereBetween(key, value);
|
||||
dbQuery[logical].whereBetween(selectionRaw, value);
|
||||
}
|
||||
|
||||
if (operator === '_nbetween') {
|
||||
@@ -393,7 +406,7 @@ export function applyFilter(
|
||||
let value = compareValue;
|
||||
if (typeof value === 'string') value = value.split(',');
|
||||
|
||||
dbQuery[logical].whereNotBetween(key, value);
|
||||
dbQuery[logical].whereNotBetween(selectionRaw, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user