diff --git a/packages/db-mongodb/src/count.ts b/packages/db-mongodb/src/count.ts index e88fb5e59c6..a82dabf562e 100644 --- a/packages/db-mongodb/src/count.ts +++ b/packages/db-mongodb/src/count.ts @@ -37,6 +37,17 @@ export const count: Count = async function count( session: await getSession(this, req), } + if (this.collation) { + const localizationConfig = this.payload.config.localization + const defaultLocale = + (typeof localizationConfig === 'object' && localizationConfig?.defaultLocale) || 'en' + + options.collation = { + locale: locale && locale !== 'all' && locale !== '*' ? locale : defaultLocale, + ...this.collation, + } + } + if (!useEstimatedCount && Object.keys(query).length === 0 && this.disableIndexHints !== true) { // Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding // a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents, diff --git a/packages/db-mongodb/src/countGlobalVersions.ts b/packages/db-mongodb/src/countGlobalVersions.ts index 0d4ff89a298..f9248a98280 100644 --- a/packages/db-mongodb/src/countGlobalVersions.ts +++ b/packages/db-mongodb/src/countGlobalVersions.ts @@ -36,6 +36,17 @@ export const countGlobalVersions: CountGlobalVersions = async function countGlob session: await getSession(this, req), } + if (this.collation) { + const localizationConfig = this.payload.config.localization + const defaultLocale = + (typeof localizationConfig === 'object' && localizationConfig?.defaultLocale) || 'en' + + options.collation = { + locale: locale && locale !== 'all' && locale !== '*' ? locale : defaultLocale, + ...this.collation, + } + } + if (!useEstimatedCount && Object.keys(query).length === 0 && this.disableIndexHints !== true) { // Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding // a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents, diff --git a/packages/db-mongodb/src/countVersions.ts b/packages/db-mongodb/src/countVersions.ts index d3ba5690160..d3bbb194022 100644 --- a/packages/db-mongodb/src/countVersions.ts +++ b/packages/db-mongodb/src/countVersions.ts @@ -40,6 +40,17 @@ export const countVersions: CountVersions = async function countVersions( session: await getSession(this, req), } + if (this.collation) { + const localizationConfig = this.payload.config.localization + const defaultLocale = + (typeof localizationConfig === 'object' && localizationConfig?.defaultLocale) || 'en' + + options.collation = { + locale: locale && locale !== 'all' && locale !== '*' ? locale : defaultLocale, + ...this.collation, + } + } + if (!useEstimatedCount && Object.keys(query).length === 0 && this.disableIndexHints !== true) { // Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding // a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents, diff --git a/packages/db-mongodb/src/find.ts b/packages/db-mongodb/src/find.ts index 2ec4af02f16..341e21b1703 100644 --- a/packages/db-mongodb/src/find.ts +++ b/packages/db-mongodb/src/find.ts @@ -90,7 +90,10 @@ export const find: Find = async function find( } if (this.collation) { - const defaultLocale = 'en' + const localizationConfig = this.payload.config.localization + const defaultLocale = + (typeof localizationConfig === 'object' && localizationConfig?.defaultLocale) || 'en' + paginationOptions.collation = { locale: locale && locale !== 'all' && locale !== '*' ? locale : defaultLocale, ...this.collation, @@ -105,11 +108,25 @@ export const find: Find = async function find( paginationOptions.useCustomCountFn = () => { return Promise.resolve( Model.countDocuments(query, { + collation: paginationOptions.collation, hint: { _id: 1 }, session, }), ) } + } else if (!useEstimatedCount && this.collation) { + // Workaround for mongoose-paginate-v2 bug: chaining .collation() on countDocuments breaks + // session context in transactions (mongoose 8.x). Provide custom count function that passes + // collation as an option instead. See: https://github.com/aravindnc/mongoose-paginate-v2/pull/240 + // TODO: Remove this workaround once mongoose-paginate-v2 is updated with the fix. + paginationOptions.useCustomCountFn = () => { + return Promise.resolve( + Model.countDocuments(query, { + collation: paginationOptions.collation, + session, + }), + ) + } } if (limit >= 0) { diff --git a/packages/db-mongodb/src/findGlobalVersions.ts b/packages/db-mongodb/src/findGlobalVersions.ts index 6170827e9e5..be58242e086 100644 --- a/packages/db-mongodb/src/findGlobalVersions.ts +++ b/packages/db-mongodb/src/findGlobalVersions.ts @@ -80,7 +80,10 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV } if (this.collation) { - const defaultLocale = 'en' + const localizationConfig = this.payload.config.localization + const defaultLocale = + (typeof localizationConfig === 'object' && localizationConfig?.defaultLocale) || 'en' + paginationOptions.collation = { locale: locale && locale !== 'all' && locale !== '*' ? locale : defaultLocale, ...this.collation, @@ -95,11 +98,25 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV paginationOptions.useCustomCountFn = () => { return Promise.resolve( Model.countDocuments(query, { + collation: paginationOptions.collation, hint: { _id: 1 }, session, }), ) } + } else if (!useEstimatedCount && this.collation) { + // Workaround for mongoose-paginate-v2 bug: chaining .collation() on countDocuments breaks + // session context in transactions (mongoose 8.x). Provide custom count function that passes + // collation as an option instead. See: https://github.com/aravindnc/mongoose-paginate-v2/pull/240 + // TODO: Remove this workaround once mongoose-paginate-v2 is updated with the fix. + paginationOptions.useCustomCountFn = () => { + return Promise.resolve( + Model.countDocuments(query, { + collation: paginationOptions.collation, + session, + }), + ) + } } if (limit >= 0) { diff --git a/packages/db-mongodb/src/findVersions.ts b/packages/db-mongodb/src/findVersions.ts index 2de9e5d4750..cc8b1dfc344 100644 --- a/packages/db-mongodb/src/findVersions.ts +++ b/packages/db-mongodb/src/findVersions.ts @@ -88,7 +88,10 @@ export const findVersions: FindVersions = async function findVersions( } if (this.collation) { - const defaultLocale = 'en' + const localizationConfig = this.payload.config.localization + const defaultLocale = + (typeof localizationConfig === 'object' && localizationConfig?.defaultLocale) || 'en' + paginationOptions.collation = { locale: locale && locale !== 'all' && locale !== '*' ? locale : defaultLocale, ...this.collation, @@ -103,11 +106,25 @@ export const findVersions: FindVersions = async function findVersions( paginationOptions.useCustomCountFn = () => { return Promise.resolve( Model.countDocuments(query, { + collation: paginationOptions.collation, hint: { _id: 1 }, session, }), ) } + } else if (!useEstimatedCount && this.collation) { + // Workaround for mongoose-paginate-v2 bug: chaining .collation() on countDocuments breaks + // session context in transactions (mongoose 8.x). Provide custom count function that passes + // collation as an option instead. See: https://github.com/aravindnc/mongoose-paginate-v2/pull/240 + // TODO: Remove this workaround once mongoose-paginate-v2 is updated with the fix. + paginationOptions.useCustomCountFn = () => { + return Promise.resolve( + Model.countDocuments(query, { + collation: paginationOptions.collation, + session, + }), + ) + } } if (limit >= 0) { diff --git a/packages/db-mongodb/src/queryDrafts.ts b/packages/db-mongodb/src/queryDrafts.ts index 7af5faaa78a..3aa2d3378bf 100644 --- a/packages/db-mongodb/src/queryDrafts.ts +++ b/packages/db-mongodb/src/queryDrafts.ts @@ -95,7 +95,10 @@ export const queryDrafts: QueryDrafts = async function queryDrafts( } if (this.collation) { - const defaultLocale = 'en' + const localizationConfig = this.payload.config.localization + const defaultLocale = + (typeof localizationConfig === 'object' && localizationConfig?.defaultLocale) || 'en' + paginationOptions.collation = { locale: locale && locale !== 'all' && locale !== '*' ? locale : defaultLocale, ...this.collation, @@ -114,7 +117,22 @@ export const queryDrafts: QueryDrafts = async function queryDrafts( paginationOptions.useCustomCountFn = () => { return Promise.resolve( Model.countDocuments(versionQuery, { + collation: paginationOptions.collation, hint: { _id: 1 }, + session, + }), + ) + } + } else if (!useEstimatedCount && this.collation) { + // Workaround for mongoose-paginate-v2 bug: chaining .collation() on countDocuments breaks + // session context in transactions (mongoose 8.x). Provide custom count function that passes + // collation as an option instead. See: https://github.com/aravindnc/mongoose-paginate-v2/pull/240 + // TODO: Remove this workaround once mongoose-paginate-v2 is updated with the fix. + paginationOptions.useCustomCountFn = () => { + return Promise.resolve( + Model.countDocuments(versionQuery, { + collation: paginationOptions.collation, + session, }), ) } diff --git a/test/database/int.spec.ts b/test/database/int.spec.ts index 53d3ea6bde1..c5a3ba4f37b 100644 --- a/test/database/int.spec.ts +++ b/test/database/int.spec.ts @@ -5617,4 +5617,56 @@ describe('database', () => { expect(collatedMappedResults).toEqual(expectedSortedItems) }, ) + + it( + 'ensure mongodb collation works with draft pagination without sort', + { db: 'mongo' }, + async () => { + // Clear any existing documents + await payload.delete({ collection: 'categories', where: {} }) + + // Create 15 draft documents + const createdIds: (number | string)[] = [] + for (let i = 0; i < 15; i++) { + const doc = await payload.create({ + collection: 'categories', + data: { name: `Category ${i}` }, + draft: true, + }) + createdIds.push(doc.id) + } + + // Enable collation + payload.db.collation = { strength: 2 } + + // Query drafts WITHOUT sort - this is the scenario that breaks + const resultsNoSort = await payload.find({ + collection: 'categories', + limit: 10, + draft: true, + // No sort parameter + }) + + console.log({ + totalDocs: resultsNoSort.totalDocs, + totalPages: resultsNoSort.totalPages, + docsLength: resultsNoSort.docs.length, + hasNextPage: resultsNoSort.hasNextPage, + }) + + // The bug: totalDocs returns 10 (same as limit) instead of 15 + expect(resultsNoSort.totalDocs).toBe(15) + expect(resultsNoSort.totalPages).toBe(2) + expect(resultsNoSort.hasNextPage).toBe(true) + expect(resultsNoSort.docs.length).toBe(10) + + // Clean up + for (const id of createdIds) { + await payload.delete({ collection: 'categories', id }) + } + + // Reset collation + payload.db.collation = undefined + }, + ) })