fix(db-mongodb): add ability to disable fallback sort and no longer adds a fallback for unique fields (#12961)
You can now disable fallback sort in the mongodb adapter by passing `disableFallbackSort: true` in the options. We also no longer add fallback sort to sorts on unique fields by default now. This came out of a discussion in this issue https://github.com/payloadcms/payload/issues/12690 and the linked PR https://github.com/payloadcms/payload/pull/12888 Closes https://github.com/payloadcms/payload/issues/12690
This commit is contained in:
@@ -118,6 +118,13 @@ export interface Args {
|
||||
*/
|
||||
useFacet?: boolean
|
||||
} & ConnectOptions
|
||||
/**
|
||||
* We add a secondary sort based on `createdAt` to ensure that results are always returned in the same order when sorting by a non-unique field.
|
||||
* This is because MongoDB does not guarantee the order of results, however in very large datasets this could affect performance.
|
||||
*
|
||||
* Set to `true` to disable this behaviour.
|
||||
*/
|
||||
disableFallbackSort?: boolean
|
||||
/** Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false */
|
||||
disableIndexHints?: boolean
|
||||
/**
|
||||
@@ -131,6 +138,7 @@ export interface Args {
|
||||
*/
|
||||
mongoMemoryServer?: MongoMemoryReplSet
|
||||
prodMigrations?: Migration[]
|
||||
|
||||
transactionOptions?: false | TransactionOptions
|
||||
|
||||
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
|
||||
@@ -198,6 +206,7 @@ export function mongooseAdapter({
|
||||
autoPluralization = true,
|
||||
collectionsSchemaOptions = {},
|
||||
connectOptions,
|
||||
disableFallbackSort = false,
|
||||
disableIndexHints = false,
|
||||
ensureIndexes = false,
|
||||
migrationDir: migrationDirArg,
|
||||
@@ -251,6 +260,7 @@ export function mongooseAdapter({
|
||||
deleteOne,
|
||||
deleteVersions,
|
||||
destroy,
|
||||
disableFallbackSort,
|
||||
find,
|
||||
findGlobal,
|
||||
findGlobalVersions,
|
||||
|
||||
121
packages/db-mongodb/src/queries/buildSortParam.spec.ts
Normal file
121
packages/db-mongodb/src/queries/buildSortParam.spec.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import type { Config, SanitizedConfig } from 'payload'
|
||||
|
||||
import { sanitizeConfig } from 'payload'
|
||||
|
||||
import { buildSortParam } from './buildSortParam.js'
|
||||
import { MongooseAdapter } from '../index.js'
|
||||
|
||||
let config: SanitizedConfig
|
||||
|
||||
describe('builds sort params', () => {
|
||||
beforeAll(async () => {
|
||||
config = await sanitizeConfig({
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
fallback: true,
|
||||
locales: ['en', 'es'],
|
||||
},
|
||||
} as Config)
|
||||
})
|
||||
it('adds a fallback on non-unique field', () => {
|
||||
const result = buildSortParam({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'order',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
locale: 'en',
|
||||
sort: 'order',
|
||||
timestamps: true,
|
||||
adapter: {
|
||||
disableFallbackSort: false,
|
||||
} as MongooseAdapter,
|
||||
})
|
||||
|
||||
expect(result).toStrictEqual({ order: 'asc', createdAt: 'desc' })
|
||||
})
|
||||
|
||||
it('adds a fallback when sort isnt provided', () => {
|
||||
const result = buildSortParam({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'order',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
locale: 'en',
|
||||
sort: undefined,
|
||||
timestamps: true,
|
||||
adapter: {
|
||||
disableFallbackSort: false,
|
||||
} as MongooseAdapter,
|
||||
})
|
||||
|
||||
expect(result).toStrictEqual({ createdAt: 'desc' })
|
||||
})
|
||||
|
||||
it('does not add a fallback on non-unique field when disableFallbackSort is true', () => {
|
||||
const result = buildSortParam({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'order',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
locale: 'en',
|
||||
sort: 'order',
|
||||
timestamps: true,
|
||||
adapter: {
|
||||
disableFallbackSort: true,
|
||||
} as MongooseAdapter,
|
||||
})
|
||||
|
||||
expect(result).toStrictEqual({ order: 'asc' })
|
||||
})
|
||||
|
||||
// This test should be true even when disableFallbackSort is false
|
||||
it('does not add a fallback on unique field', () => {
|
||||
const result = buildSortParam({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'order',
|
||||
type: 'number',
|
||||
unique: true, // Marking this field as unique
|
||||
},
|
||||
],
|
||||
locale: 'en',
|
||||
sort: 'order',
|
||||
timestamps: true,
|
||||
adapter: {
|
||||
disableFallbackSort: false,
|
||||
} as MongooseAdapter,
|
||||
})
|
||||
|
||||
expect(result).toStrictEqual({ order: 'asc' })
|
||||
})
|
||||
})
|
||||
@@ -19,7 +19,7 @@ type Args = {
|
||||
fields: FlattenedField[]
|
||||
locale?: string
|
||||
parentIsLocalized?: boolean
|
||||
sort: Sort
|
||||
sort?: Sort
|
||||
sortAggregation?: PipelineStage[]
|
||||
timestamps: boolean
|
||||
versions?: boolean
|
||||
@@ -150,6 +150,12 @@ export const buildSortParam = ({
|
||||
sort = [sort]
|
||||
}
|
||||
|
||||
// We use this flag to determine if the sort is unique or not to decide whether to add a fallback sort.
|
||||
const isUniqueSort = sort.some((item) => {
|
||||
const field = getFieldByPath({ fields, path: item })
|
||||
return field?.field?.unique
|
||||
})
|
||||
|
||||
// In the case of Mongo, when sorting by a field that is not unique, the results are not guaranteed to be in the same order each time.
|
||||
// So we add a fallback sort to ensure that the results are always in the same order.
|
||||
let fallbackSort = '-id'
|
||||
@@ -158,7 +164,12 @@ export const buildSortParam = ({
|
||||
fallbackSort = '-createdAt'
|
||||
}
|
||||
|
||||
if (!(sort.includes(fallbackSort) || sort.includes(fallbackSort.replace('-', '')))) {
|
||||
const includeFallbackSort =
|
||||
!adapter.disableFallbackSort &&
|
||||
!isUniqueSort &&
|
||||
!(sort.includes(fallbackSort) || sort.includes(fallbackSort.replace('-', '')))
|
||||
|
||||
if (includeFallbackSort) {
|
||||
sort.push(fallbackSort)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user