fix(db-mongodb): ensures same level operators are respected (#11087)

### What?
If you had multiple operator constraints on a single field, the last one
defined would be the only one used.

Example:
```ts
where: {
  id: {
    in: [doc2.id],
    not_in: [], // <-- only respected this operator constraint
  },
}
```

and
```ts
where: {
  id: {
    not_in: [],
    in: [doc2.id], // <-- only respected this operator constraint
  },
}
```

They would yield different results.

### Why?
The results were not merged into an `$and` query inside parseParams.

### How?
Merges the results within an `$and` constraint.

Fixes https://github.com/payloadcms/payload/issues/10944

Supersedes https://github.com/payloadcms/payload/pull/11011
This commit is contained in:
Jarrod Flesch
2025-02-10 16:29:08 -05:00
committed by GitHub
parent 95ec57575d
commit d2fe9b0807
4 changed files with 107 additions and 35 deletions

View File

@@ -52,30 +52,37 @@ export async function parseParams({
// So we need to loop on keys again here to handle each operator independently
const pathOperators = where[relationOrPath]
if (typeof pathOperators === 'object') {
for (const operator of Object.keys(pathOperators)) {
if (validOperatorSet.has(operator as Operator)) {
const searchParam = await buildSearchParam({
collectionSlug,
fields,
globalSlug,
incomingPath: relationOrPath,
locale,
operator,
payload,
val: pathOperators[operator],
})
const validOperators = Object.keys(pathOperators).filter((operator) =>
validOperatorSet.has(operator as Operator),
)
for (const operator of validOperators) {
const searchParam = await buildSearchParam({
collectionSlug,
fields,
globalSlug,
incomingPath: relationOrPath,
locale,
operator,
payload,
val: pathOperators[operator],
})
if (searchParam?.value && searchParam?.path) {
result = {
...result,
[searchParam.path]: searchParam.value,
if (searchParam?.value && searchParam?.path) {
if (validOperators.length > 1) {
if (!result.$and) {
result.$and = []
}
} else if (typeof searchParam?.value === 'object') {
result = deepMergeWithCombinedArrays(result, searchParam.value, {
// dont clone Types.ObjectIDs
clone: false,
result.$and.push({
[searchParam.path]: searchParam.value,
})
} else {
result[searchParam.path] = searchParam.value
}
} else if (typeof searchParam?.value === 'object') {
result = deepMergeWithCombinedArrays(result, searchParam.value, {
// dont clone Types.ObjectIDs
clone: false,
})
}
}
}

View File

@@ -87,9 +87,11 @@ export const sanitizeQueryValue = ({
if (field.type === 'number') {
formattedValue = formattedValue.map((arrayVal) => parseFloat(arrayVal))
}
} else if (typeof formattedValue === 'number') {
formattedValue = [formattedValue]
}
if (!Array.isArray(formattedValue) || formattedValue.length === 0) {
if (!Array.isArray(formattedValue)) {
return null
}
}