fix(db-postgres): sort by distance when the near operator is used (#12185)

Fixes https://github.com/payloadcms/payload/issues/12090, in MongoDB the
documents are sorted by distance automatically whenever the `near`
operation is used, which we have in the docs:
> When querying using the near operator, the returned documents will be
sorted by nearest first.
This fixes this incosistensty between Postgres and MongoDB.

⚠️ This change potentially can cause to produce different results, if
you used the `near` operator without `sort: 'pointFieldName'`.
This commit is contained in:
Sasha
2025-04-22 21:26:13 +03:00
committed by GitHub
parent 2c20051dbf
commit 9955818503
4 changed files with 26 additions and 14 deletions

View File

@@ -3,12 +3,14 @@ import type { FlattenedField, Where } from 'payload'
import type { DrizzleAdapter, GenericColumn } from '../types.js' import type { DrizzleAdapter, GenericColumn } from '../types.js'
import type { BuildQueryJoinAliases } from './buildQuery.js' import type { BuildQueryJoinAliases } from './buildQuery.js'
import type { QueryContext } from './parseParams.js'
import { parseParams } from './parseParams.js' import { parseParams } from './parseParams.js'
export function buildAndOrConditions({ export function buildAndOrConditions({
adapter, adapter,
aliasTable, aliasTable,
context,
fields, fields,
joins, joins,
locale, locale,
@@ -21,6 +23,7 @@ export function buildAndOrConditions({
adapter: DrizzleAdapter adapter: DrizzleAdapter
aliasTable?: Table aliasTable?: Table
collectionSlug?: string collectionSlug?: string
context: QueryContext
fields: FlattenedField[] fields: FlattenedField[]
globalSlug?: string globalSlug?: string
joins: BuildQueryJoinAliases joins: BuildQueryJoinAliases
@@ -41,6 +44,7 @@ export function buildAndOrConditions({
const result = parseParams({ const result = parseParams({
adapter, adapter,
aliasTable, aliasTable,
context,
fields, fields,
joins, joins,
locale, locale,

View File

@@ -3,6 +3,7 @@ import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
import type { FlattenedField, Sort, Where } from 'payload' import type { FlattenedField, Sort, Where } from 'payload'
import type { DrizzleAdapter, GenericColumn, GenericTable } from '../types.js' import type { DrizzleAdapter, GenericColumn, GenericTable } from '../types.js'
import type { QueryContext } from './parseParams.js'
import { buildOrderBy } from './buildOrderBy.js' import { buildOrderBy } from './buildOrderBy.js'
import { parseParams } from './parseParams.js' import { parseParams } from './parseParams.js'
@@ -52,24 +53,14 @@ const buildQuery = function buildQuery({
id: adapter.tables[tableName].id, id: adapter.tables[tableName].id,
} }
const orderBy = buildOrderBy({
adapter,
aliasTable,
fields,
joins,
locale,
parentIsLocalized,
selectFields,
sort,
tableName,
})
let where: SQL let where: SQL
const context: QueryContext = { sort }
if (incomingWhere && Object.keys(incomingWhere).length > 0) { if (incomingWhere && Object.keys(incomingWhere).length > 0) {
where = parseParams({ where = parseParams({
adapter, adapter,
aliasTable, aliasTable,
context,
fields, fields,
joins, joins,
locale, locale,
@@ -81,6 +72,18 @@ const buildQuery = function buildQuery({
}) })
} }
const orderBy = buildOrderBy({
adapter,
aliasTable,
fields,
joins,
locale,
parentIsLocalized,
selectFields,
sort: context.sort,
tableName,
})
return { return {
joins, joins,
orderBy, orderBy,

View File

@@ -1,5 +1,5 @@
import type { SQL, Table } from 'drizzle-orm' import type { SQL, Table } from 'drizzle-orm'
import type { FlattenedField, Operator, Where } from 'payload' import type { FlattenedField, Operator, Sort, Where } from 'payload'
import { and, isNotNull, isNull, ne, notInArray, or, sql } from 'drizzle-orm' import { and, isNotNull, isNull, ne, notInArray, or, sql } from 'drizzle-orm'
import { PgUUID } from 'drizzle-orm/pg-core' import { PgUUID } from 'drizzle-orm/pg-core'
@@ -14,9 +14,12 @@ import { buildAndOrConditions } from './buildAndOrConditions.js'
import { getTableColumnFromPath } from './getTableColumnFromPath.js' import { getTableColumnFromPath } from './getTableColumnFromPath.js'
import { sanitizeQueryValue } from './sanitizeQueryValue.js' import { sanitizeQueryValue } from './sanitizeQueryValue.js'
export type QueryContext = { sort: Sort }
type Args = { type Args = {
adapter: DrizzleAdapter adapter: DrizzleAdapter
aliasTable?: Table aliasTable?: Table
context: QueryContext
fields: FlattenedField[] fields: FlattenedField[]
joins: BuildQueryJoinAliases joins: BuildQueryJoinAliases
locale?: string locale?: string
@@ -30,6 +33,7 @@ type Args = {
export function parseParams({ export function parseParams({
adapter, adapter,
aliasTable, aliasTable,
context,
fields, fields,
joins, joins,
locale, locale,
@@ -57,6 +61,7 @@ export function parseParams({
const builtConditions = buildAndOrConditions({ const builtConditions = buildAndOrConditions({
adapter, adapter,
aliasTable, aliasTable,
context,
fields, fields,
joins, joins,
locale, locale,
@@ -342,6 +347,7 @@ export function parseParams({
) )
} }
if (geoConstraints.length) { if (geoConstraints.length) {
context.sort = relationOrPath
constraints.push(and(...geoConstraints)) constraints.push(and(...geoConstraints))
} }
break break

View File

@@ -1218,7 +1218,6 @@ describe('collections-rest', () => {
.GET(`/${pointSlug}`, { .GET(`/${pointSlug}`, {
query: { query: {
limit: 5, limit: 5,
sort: 'point',
where: { where: {
point: { point: {
// querying large enough range to include all docs // querying large enough range to include all docs