@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
81 lines (80 loc) • 3.59 kB
JavaScript
import { NUMERIC_TYPES } from '@directus/constants';
import { isIn } from '@directus/utils';
import { getCases } from '../../../../permissions/modules/process-ast/lib/get-cases.js';
import { isValidUuid } from '../../../../utils/is-valid-uuid.js';
import { parseNumericString } from '../../../../utils/parse-numeric-string.js';
import { getHelpers } from '../../../helpers/index.js';
import { applyFilter } from './filter/index.js';
export function applySearch(knex, schema, dbQuery, searchQuery, collection, aliasMap, permissions) {
const { number: numberHelper } = getHelpers(knex);
const allowedFields = new Set(permissions.filter((p) => p.collection === collection).flatMap((p) => p.fields ?? []));
let fields = Object.entries(schema.collections[collection].fields);
// filter out fields that are not searchable
fields = fields.filter(([_name, field]) => field.searchable !== false && field.special.includes('conceal') !== true);
const { cases, caseMap } = getCases(collection, permissions, []);
// Add field restrictions if non-admin and "everything" is not allowed
if (cases.length !== 0 && !allowedFields.has('*')) {
fields = fields.filter((field) => allowedFields.has(field[0]));
}
dbQuery.andWhere(function (queryBuilder) {
let needsFallbackCondition = true;
fields.forEach(([name, field]) => {
// only account for when cases when full access is not given
const whenCases = allowedFields.has('*') ? [] : (caseMap[name] ?? []).map((caseIndex) => cases[caseIndex]);
const fieldType = getFieldType(field);
if (fieldType !== null) {
needsFallbackCondition = false;
}
else {
return;
}
if (cases.length !== 0 && whenCases?.length !== 0) {
queryBuilder.orWhere((subQuery) => {
addSearchCondition(subQuery, name, fieldType, 'and');
applyFilter(knex, schema, subQuery, { _or: whenCases }, collection, aliasMap, cases, permissions);
});
}
else {
addSearchCondition(queryBuilder, name, fieldType, 'or');
}
});
if (needsFallbackCondition) {
queryBuilder.orWhereRaw('1 = 0');
}
});
function addSearchCondition(queryBuilder, name, fieldType, logical) {
if (fieldType === null) {
return;
}
if (fieldType === 'string') {
queryBuilder[logical].whereRaw(`LOWER(??) LIKE ?`, [`${collection}.${name}`, `%${searchQuery.toLowerCase()}%`]);
}
else if (fieldType === 'numeric') {
numberHelper.addSearchCondition(queryBuilder, collection, name, parseNumericString(searchQuery), logical);
}
else if (fieldType === 'uuid') {
queryBuilder[logical].where({ [`${collection}.${name}`]: searchQuery });
}
}
function getFieldType(field) {
if (['text', 'string'].includes(field.type)) {
return 'string';
}
if (isNumericField(field)) {
const number = parseNumericString(searchQuery);
if (number === null) {
return null;
}
if (numberHelper.isNumberValid(number, field)) {
return 'numeric';
}
}
if (field.type === 'uuid' && isValidUuid(searchQuery)) {
return 'uuid';
}
return null;
}
}
function isNumericField(field) {
return isIn(field.type, NUMERIC_TYPES);
}