Files
klz-cables.com/.pnpm-store/v10/files/a0/e3d574d678c2b8cff946ebefeb27b1a38de684d735587ef819d4f8e6c7a4e1abbec7f19f46ece928382a319485a7465c6177953e87717205dca273994245d5
Marc Mintel 5397309103
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
fix(products): fix breadcrumbs and product filtering (backport from main)
2026-02-24 16:04:21 +01:00

356 lines
22 KiB
Plaintext

import { and, getTableName, isNotNull, isNull, ne, notInArray, or, sql } from 'drizzle-orm';
import { PgUUID } from 'drizzle-orm/pg-core';
import { APIError, QueryError } from 'payload';
import { validOperatorSet } from 'payload/shared';
import { escapeSQLValue } from '../utilities/escapeSQLValue.js';
import { getNameFromDrizzleTable } from '../utilities/getNameFromDrizzleTable.js';
import { isValidStringID } from '../utilities/isValidStringID.js';
import { DistinctSymbol } from '../utilities/rawConstraint.js';
import { buildAndOrConditions } from './buildAndOrConditions.js';
import { getTableColumnFromPath } from './getTableColumnFromPath.js';
import { sanitizeQueryValue } from './sanitizeQueryValue.js';
export function parseParams({ adapter, aliasTable, context, fields, joins, locale, parentIsLocalized, selectFields, selectLocale, tableName, where }) {
let result;
const constraints = [];
if (typeof where === 'object' && Object.keys(where).length > 0) {
// We need to determine if the whereKey is an AND, OR, or a schema path
for (const relationOrPath of Object.keys(where)){
if (relationOrPath) {
const condition = where[relationOrPath];
let conditionOperator;
if (relationOrPath.toLowerCase() === 'and') {
conditionOperator = and;
} else if (relationOrPath.toLowerCase() === 'or') {
conditionOperator = or;
}
if (Array.isArray(condition)) {
const builtConditions = buildAndOrConditions({
adapter,
aliasTable,
context,
fields,
joins,
locale,
parentIsLocalized,
selectFields,
selectLocale,
tableName,
where: condition
});
if (builtConditions.length > 0) {
result = conditionOperator(...builtConditions);
}
} else {
// It's a path - and there can be multiple comparisons on a single path.
// For example - title like 'test' and title not equal to 'tester'
// So we need to loop on keys again here to handle each operator independently
const pathOperators = where[relationOrPath];
if (typeof pathOperators === 'object') {
for (let operator of Object.keys(pathOperators)){
if (validOperatorSet.has(operator)) {
const val = where[relationOrPath][operator];
const { columnName, columns, constraints: queryConstraints, field, getNotNullColumnByValue, pathSegments, rawColumn, table } = getTableColumnFromPath({
adapter,
aliasTable,
collectionPath: relationOrPath,
fields,
joins,
locale,
parentIsLocalized,
pathSegments: relationOrPath.replace(/__/g, '.').split('.'),
selectFields,
selectLocale,
tableName,
value: val
});
const resolvedColumn = rawColumn || (aliasTable && tableName === getNameFromDrizzleTable(table) ? aliasTable[columnName] : table[columnName]);
if (val === DistinctSymbol) {
selectFields['_selected'] = resolvedColumn;
break;
}
queryConstraints.forEach(({ columnName: col, table: constraintTable, value })=>{
if (typeof value === 'string' && value.indexOf('%') > -1) {
constraints.push(adapter.operators.like(constraintTable[col], value));
} else {
constraints.push(adapter.operators.equals(constraintTable[col], value));
}
});
if (([
'json',
'richText'
].includes(field.type) || field.type === 'blocks' && adapter.blocksAsJSON) && Array.isArray(pathSegments) && pathSegments.length > 1) {
if (adapter.name === 'postgres') {
const constraint = adapter.createJSONQuery({
column: rawColumn || table[columnName],
operator,
pathSegments,
value: val
});
constraints.push(sql.raw(constraint));
break;
}
const segments = pathSegments.slice(1);
segments.unshift(table[columnName].name);
if (field.type === 'richText') {
// use the table name from the nearest join to handle blocks, arrays, etc. or use the tableName arg
const jsonTable = joins.length === 0 ? tableName : joins[joins.length - 1].table[Object.getOwnPropertySymbols(joins[joins.length - 1].table)[0]];
const jsonQuery = adapter.createJSONQuery({
operator,
pathSegments: segments,
table: jsonTable,
treatAsArray: [
'children'
],
treatRootAsArray: true,
value: val
});
constraints.push(sql.raw(jsonQuery));
break;
}
const jsonQuery = adapter.convertPathToJSONTraversal(pathSegments);
const operatorKeys = {
contains: {
operator: 'like',
wildcard: '%'
},
equals: {
operator: '=',
wildcard: ''
},
exists: {
operator: val === true ? 'is not null' : 'is null',
wildcard: ''
},
in: {
operator: 'in',
wildcard: ''
},
like: {
operator: 'like',
wildcard: '%'
},
not_equals: {
operator: '<>',
wildcard: ''
},
not_in: {
operator: 'not in',
wildcard: ''
},
not_like: {
operator: 'not like',
wildcard: '%'
}
};
let formattedValue = val;
if (adapter.name === 'sqlite' && operator === 'equals' && !isNaN(val)) {
formattedValue = val;
} else if ([
'in',
'not_in'
].includes(operator) && Array.isArray(val)) {
formattedValue = `(${val.map((v)=>`${escapeSQLValue(v)}`).join(',')})`;
} else {
formattedValue = `'${operatorKeys[operator].wildcard}${escapeSQLValue(val)}${operatorKeys[operator].wildcard}'`;
}
if (operator === 'exists') {
formattedValue = '';
}
let jsonQuerySelector = `${table[columnName].name}${jsonQuery}`;
if (adapter.name === 'sqlite' && operator === 'not_like') {
jsonQuerySelector = `COALESCE(${table[columnName].name}${jsonQuery}, '')`;
}
const rawSQLQuery = `${jsonQuerySelector} ${operatorKeys[operator].operator} ${formattedValue}`;
constraints.push(sql.raw(rawSQLQuery));
break;
}
if (getNotNullColumnByValue) {
const columnName = getNotNullColumnByValue(val);
if (columnName) {
constraints.push(isNotNull(table[columnName]));
} else {
throw new QueryError([
{
path: relationOrPath
}
]);
}
break;
}
if (operator === 'like' && (field.type === 'number' || field.type === 'relationship' || field.type === 'upload' || table[columnName].columnType === 'PgUUID')) {
operator = 'equals';
}
if (operator === 'like') {
constraints.push(and(...val.split(' ').map((word)=>adapter.operators.like(table[columnName], `%${word}%`))));
break;
}
const sanitizedQueryValue = sanitizeQueryValue({
adapter,
columns,
field,
isUUID: table?.[columnName] instanceof PgUUID,
operator,
relationOrPath,
val
});
if (sanitizedQueryValue === null) {
break;
}
const { columns: queryColumns, operator: queryOperator, value: queryValue } = sanitizedQueryValue;
// Handle polymorphic relationships by value
if (queryColumns) {
if (!queryColumns.length) {
break;
}
let wrapOperator = or;
if (queryValue === null && [
'equals',
'not_equals'
].includes(operator)) {
if (operator === 'equals') {
wrapOperator = and;
}
constraints.push(wrapOperator(...queryColumns.map(({ rawColumn })=>operator === 'equals' ? isNull(rawColumn) : isNotNull(rawColumn))));
break;
}
if ([
'not_equals',
'not_in'
].includes(operator)) {
wrapOperator = and;
}
constraints.push(wrapOperator(...queryColumns.map(({ rawColumn, value })=>adapter.operators[queryOperator](rawColumn, value))));
break;
}
if (queryOperator === 'not_equals' && queryValue !== null) {
constraints.push(or(isNull(resolvedColumn), /* eslint-disable @typescript-eslint/no-explicit-any */ ne(resolvedColumn, queryValue)));
break;
}
if ((field.type === 'relationship' || field.type === 'upload') && Array.isArray(queryValue) && operator === 'not_in') {
constraints.push(sql`(${notInArray(table[columnName], queryValue)} OR
${table[columnName]}
IS
NULL)`);
break;
}
if (operator === 'equals' && queryValue === null) {
constraints.push(isNull(resolvedColumn));
break;
}
if (operator === 'not_equals' && queryValue === null) {
constraints.push(isNotNull(resolvedColumn));
break;
}
if (field.type === 'point' && adapter.name === 'postgres') {
switch(operator){
case 'intersects':
{
constraints.push(sql`ST_Intersects(${table[columnName]}, ST_GeomFromGeoJSON(${JSON.stringify(queryValue)}))`);
break;
}
case 'near':
{
const [lng, lat, maxDistance, minDistance] = queryValue;
const geoConstraints = [];
if (typeof maxDistance === 'number' && !Number.isNaN(maxDistance)) {
geoConstraints.push(sql`ST_DWithin(ST_Transform(${table[columnName]}, 3857), ST_Transform(ST_SetSRID(ST_MakePoint(${lng}, ${lat}), 4326), 3857), ${maxDistance})`);
}
if (typeof minDistance === 'number' && !Number.isNaN(minDistance)) {
geoConstraints.push(sql`ST_Distance(ST_Transform(${table[columnName]}, 3857), ST_Transform(ST_SetSRID(ST_MakePoint(${lng}, ${lat}), 4326), 3857)) >= ${minDistance}`);
}
if (geoConstraints.length) {
context.sort = relationOrPath;
context.rawSort = sql`${table[columnName]} <-> ST_SetSRID(ST_MakePoint(${lng}, ${lat}), 4326)`;
constraints.push(and(...geoConstraints));
}
break;
}
case 'within':
{
constraints.push(sql`ST_Within(${table[columnName]}, ST_GeomFromGeoJSON(${JSON.stringify(queryValue)}))`);
break;
}
default:
break;
}
break;
}
const orConditions = [];
let resolvedQueryValue = queryValue;
if (operator === 'in' && Array.isArray(queryValue) && queryValue.some((v)=>v === null)) {
orConditions.push(isNull(resolvedColumn));
resolvedQueryValue = queryValue.filter((v)=>v !== null);
}
if (operator === 'contains' && Array.isArray(queryValue) && 'hasMany' in field && field.hasMany && [
'number',
'select',
'text'
].includes(field.type)) {
// Create OR conditions for each value in the array
orConditions.push(...queryValue.map((val)=>adapter.operators[queryOperator](resolvedColumn, val)));
// Set constraint to combine all OR conditions
const constraint = orConditions.length > 0 ? or(...orConditions) : undefined;
if (constraint) {
constraints.push(constraint);
}
break;
}
let constraint = adapter.operators[queryOperator](resolvedColumn, resolvedQueryValue);
if (adapter.limitedBoundParameters && (operator === 'in' || operator === 'not_in') && relationOrPath === 'id' && Array.isArray(queryValue)) {
let isInvalid = false;
for (const val of queryValue){
if (typeof val === 'number' || val === null) {
continue;
}
if (typeof val === 'string') {
if (!isValidStringID(val)) {
isInvalid = true;
break;
} else {
continue;
}
}
isInvalid = true;
break;
}
if (isInvalid) {
throw new APIError(`Invalid ID value in ${JSON.stringify(queryValue)}`);
}
constraints.push(sql.raw(`"${getTableName(resolvedColumn.table)}"."${resolvedColumn.name}" ${operator === 'in' ? 'IN' : 'NOT IN'} (${queryValue.map((e)=>{
if (e === null) {
return `NULL`;
}
if (typeof e === 'number') {
return e;
}
return `'${e}'`;
}).join(',')})`));
break;
}
if (orConditions.length) {
orConditions.push(constraint);
constraint = or(...orConditions);
}
constraints.push(constraint);
}
}
}
}
}
}
}
if (constraints.length > 0) {
if (result) {
result = and(result, ...constraints);
} else {
result = and(...constraints);
}
}
if (constraints.length === 1 && !result) {
;
[result] = constraints;
}
return result;
}
//# sourceMappingURL=parseParams.js.map