diff --git a/packages/db-mongodb/package.json b/packages/db-mongodb/package.json index a297aad66..95af5b604 100644 --- a/packages/db-mongodb/package.json +++ b/packages/db-mongodb/package.json @@ -47,14 +47,12 @@ }, "dependencies": { "mongoose": "8.9.5", - "mongoose-aggregate-paginate-v2": "1.1.2", "mongoose-paginate-v2": "1.8.5", "prompts": "2.4.2", "uuid": "10.0.0" }, "devDependencies": { "@payloadcms/eslint-config": "workspace:*", - "@types/mongoose-aggregate-paginate-v2": "1.0.12", "mongodb": "6.12.0", "mongodb-memory-server": "^10", "payload": "workspace:*" diff --git a/packages/db-mongodb/src/find.ts b/packages/db-mongodb/src/find.ts index 65f556ec3..e9886cc2b 100644 --- a/packages/db-mongodb/src/find.ts +++ b/packages/db-mongodb/src/find.ts @@ -7,6 +7,7 @@ import type { MongooseAdapter } from './index.js' import { buildQuery } from './queries/buildQuery.js' import { buildSortParam } from './queries/buildSortParam.js' +import { aggregatePaginate } from './utilities/aggregatePaginate.js' import { buildJoinAggregation } from './utilities/buildJoinAggregation.js' import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js' import { getSession } from './utilities/getSession.js' @@ -128,7 +129,20 @@ export const find: Find = async function find( }) // build join aggregation if (aggregate) { - result = await Model.aggregatePaginate(Model.aggregate(aggregate), paginationOptions) + result = await aggregatePaginate({ + adapter: this, + collation: paginationOptions.collation, + joinAggregation: aggregate, + limit: paginationOptions.limit, + Model, + page: paginationOptions.page, + pagination: paginationOptions.pagination, + projection: paginationOptions.projection, + query, + session: paginationOptions.options?.session, + sort: paginationOptions.sort as object, + useEstimatedCount: paginationOptions.useEstimatedCount, + }) } else { result = await Model.paginate(query, paginationOptions) } diff --git a/packages/db-mongodb/src/findOne.ts b/packages/db-mongodb/src/findOne.ts index 7fec58d94..ae69e7877 100644 --- a/packages/db-mongodb/src/findOne.ts +++ b/packages/db-mongodb/src/findOne.ts @@ -4,6 +4,7 @@ import type { FindOne } from 'payload' import type { MongooseAdapter } from './index.js' import { buildQuery } from './queries/buildQuery.js' +import { aggregatePaginate } from './utilities/aggregatePaginate.js' import { buildJoinAggregation } from './utilities/buildJoinAggregation.js' import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js' import { getSession } from './utilities/getSession.js' @@ -40,7 +41,6 @@ export const findOne: FindOne = async function findOne( collection, collectionConfig, joins, - limit: 1, locale, projection, query, @@ -48,7 +48,17 @@ export const findOne: FindOne = async function findOne( let doc if (aggregate) { - ;[doc] = await Model.aggregate(aggregate, { session }) + const { docs } = await aggregatePaginate({ + adapter: this, + joinAggregation: aggregate, + limit: 1, + Model, + pagination: false, + projection, + query, + session, + }) + doc = docs[0] } else { ;(options as Record).projection = projection doc = await Model.findOne(query, {}, options) diff --git a/packages/db-mongodb/src/index.ts b/packages/db-mongodb/src/index.ts index 0e9a4e278..93d87a653 100644 --- a/packages/db-mongodb/src/index.ts +++ b/packages/db-mongodb/src/index.ts @@ -92,7 +92,10 @@ export interface Args { /** Extra configuration options */ connectOptions?: { - /** Set false to disable $facet aggregation in non-supporting databases, Defaults to true */ + /** + * Set false to disable $facet aggregation in non-supporting databases, Defaults to true + * @deprecated Payload doesn't use `$facet` anymore anywhere. + */ useFacet?: boolean } & ConnectOptions /** 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 */ diff --git a/packages/db-mongodb/src/init.ts b/packages/db-mongodb/src/init.ts index de82610f2..d7b8e144f 100644 --- a/packages/db-mongodb/src/init.ts +++ b/packages/db-mongodb/src/init.ts @@ -2,7 +2,6 @@ import type { PaginateOptions } from 'mongoose' import type { Init, SanitizedCollectionConfig } from 'payload' import mongoose from 'mongoose' -import mongooseAggregatePaginate from 'mongoose-aggregate-paginate-v2' import paginate from 'mongoose-paginate-v2' import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload' @@ -48,10 +47,6 @@ export const init: Init = function init(this: MongooseAdapter) { }), ) - if (Object.keys(collection.joins).length > 0) { - versionSchema.plugin(mongooseAggregatePaginate) - } - const versionCollectionName = this.autoPluralization === true && !collection.dbName ? undefined : versionModelName @@ -59,14 +54,14 @@ export const init: Init = function init(this: MongooseAdapter) { versionModelName, versionSchema, versionCollectionName, - ) as CollectionModel + ) as unknown as CollectionModel } const modelName = getDBName({ config: collection }) const collectionName = this.autoPluralization === true && !collection.dbName ? undefined : modelName - this.collections[collection.slug] = mongoose.model( + this.collections[collection.slug] = mongoose.model( modelName, schema, collectionName, @@ -101,7 +96,7 @@ export const init: Init = function init(this: MongooseAdapter) { }), ) - this.versions[global.slug] = mongoose.model( + this.versions[global.slug] = mongoose.model( versionModelName, versionSchema, versionModelName, diff --git a/packages/db-mongodb/src/models/buildCollectionSchema.ts b/packages/db-mongodb/src/models/buildCollectionSchema.ts index da3c40df5..2e182afb5 100644 --- a/packages/db-mongodb/src/models/buildCollectionSchema.ts +++ b/packages/db-mongodb/src/models/buildCollectionSchema.ts @@ -1,7 +1,6 @@ import type { PaginateOptions, Schema } from 'mongoose' import type { Payload, SanitizedCollectionConfig } from 'payload' -import mongooseAggregatePaginate from 'mongoose-aggregate-paginate-v2' import paginate from 'mongoose-paginate-v2' import { getBuildQueryPlugin } from '../queries/getBuildQueryPlugin.js' @@ -44,12 +43,5 @@ export const buildCollectionSchema = ( .plugin(paginate, { useEstimatedCount: true }) .plugin(getBuildQueryPlugin({ collectionSlug: collection.slug })) - if ( - Object.keys(collection.joins).length > 0 || - Object.keys(collection.polymorphicJoins).length > 0 - ) { - schema.plugin(mongooseAggregatePaginate) - } - return schema } diff --git a/packages/db-mongodb/src/queryDrafts.ts b/packages/db-mongodb/src/queryDrafts.ts index 1a8ba6269..0b1e395e9 100644 --- a/packages/db-mongodb/src/queryDrafts.ts +++ b/packages/db-mongodb/src/queryDrafts.ts @@ -7,6 +7,7 @@ import type { MongooseAdapter } from './index.js' import { buildQuery } from './queries/buildQuery.js' import { buildSortParam } from './queries/buildSortParam.js' +import { aggregatePaginate } from './utilities/aggregatePaginate.js' import { buildJoinAggregation } from './utilities/buildJoinAggregation.js' import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js' import { getSession } from './utilities/getSession.js' @@ -116,10 +117,20 @@ export const queryDrafts: QueryDrafts = async function queryDrafts( // build join aggregation if (aggregate) { - result = await VersionModel.aggregatePaginate( - VersionModel.aggregate(aggregate), - paginationOptions, - ) + result = await aggregatePaginate({ + adapter: this, + collation: paginationOptions.collation, + joinAggregation: aggregate, + limit: paginationOptions.limit, + Model: VersionModel, + page: paginationOptions.page, + pagination: paginationOptions.pagination, + projection: paginationOptions.projection, + query: versionQuery, + session: paginationOptions.options?.session, + sort: paginationOptions.sort as object, + useEstimatedCount: paginationOptions.useEstimatedCount, + }) } else { result = await VersionModel.paginate(versionQuery, paginationOptions) } diff --git a/packages/db-mongodb/src/types.ts b/packages/db-mongodb/src/types.ts index b521efd8a..f1b130631 100644 --- a/packages/db-mongodb/src/types.ts +++ b/packages/db-mongodb/src/types.ts @@ -1,12 +1,5 @@ import type { ClientSession } from 'mongodb' -import type { - AggregatePaginateModel, - IndexDefinition, - IndexOptions, - Model, - PaginateModel, - SchemaOptions, -} from 'mongoose' +import type { IndexDefinition, IndexOptions, Model, PaginateModel, SchemaOptions } from 'mongoose' import type { ArrayField, BlocksField, @@ -37,10 +30,7 @@ import type { import type { BuildQueryArgs } from './queries/getBuildQueryPlugin.js' -export interface CollectionModel - extends Model, - PaginateModel, - AggregatePaginateModel { +export interface CollectionModel extends Model, PaginateModel { /** buildQuery is used to transform payload's where operator into what can be used by mongoose (e.g. id => _id) */ buildQuery: (args: BuildQueryArgs) => Promise> // TODO: Delete this } diff --git a/packages/db-mongodb/src/utilities/aggregatePaginate.ts b/packages/db-mongodb/src/utilities/aggregatePaginate.ts new file mode 100644 index 000000000..b2b6ae3c5 --- /dev/null +++ b/packages/db-mongodb/src/utilities/aggregatePaginate.ts @@ -0,0 +1,104 @@ +import type { CollationOptions } from 'mongodb' +import type { ClientSession, Model, PipelineStage } from 'mongoose' +import type { PaginatedDocs } from 'payload' + +import type { MongooseAdapter } from '../index.js' + +export const aggregatePaginate = async ({ + adapter, + collation, + joinAggregation, + limit, + Model, + page, + pagination, + projection, + query, + session, + sort, + useEstimatedCount, +}: { + adapter: MongooseAdapter + collation?: CollationOptions + joinAggregation?: PipelineStage[] + limit?: number + Model: Model + page?: number + pagination?: boolean + projection?: Record + query: Record + session?: ClientSession + sort?: object + useEstimatedCount?: boolean +}): Promise> => { + const aggregation: PipelineStage[] = [{ $match: query }] + + if (sort) { + const $sort: Record = {} + + Object.entries(sort).forEach(([key, value]) => { + $sort[key] = value === 'desc' ? -1 : 1 + }) + + aggregation.push({ $sort }) + } + + if (page) { + aggregation.push({ $skip: (page - 1) * (limit ?? 0) }) + } + + if (limit) { + aggregation.push({ $limit: limit }) + } + + if (joinAggregation) { + for (const stage of joinAggregation) { + aggregation.push(stage) + } + } + + if (projection) { + aggregation.push({ $project: projection }) + } + + let countPromise: Promise = Promise.resolve(null) + + if (pagination !== false && limit) { + if (useEstimatedCount) { + countPromise = Model.estimatedDocumentCount(query) + } else { + const hint = adapter.disableIndexHints !== true ? { _id: 1 } : undefined + countPromise = Model.countDocuments(query, { collation, hint, session }) + } + } + + const [docs, countResult] = await Promise.all([ + Model.aggregate(aggregation, { collation, session }), + countPromise, + ]) + + const count = countResult === null ? docs.length : countResult + + const totalPages = + pagination !== false && typeof limit === 'number' && limit !== 0 ? Math.ceil(count / limit) : 1 + + const hasPrevPage = pagination !== false && page > 1 + const hasNextPage = pagination !== false && totalPages > page + const pagingCounter = + pagination !== false && typeof limit === 'number' ? (page - 1) * limit + 1 : 1 + + const result: PaginatedDocs = { + docs, + hasNextPage, + hasPrevPage, + limit, + nextPage: hasNextPage ? page + 1 : null, + page, + pagingCounter, + prevPage: hasPrevPage ? page - 1 : null, + totalDocs: count, + totalPages, + } + + return result +} diff --git a/packages/db-mongodb/src/utilities/buildJoinAggregation.ts b/packages/db-mongodb/src/utilities/buildJoinAggregation.ts index 85f2355bd..4e60bd675 100644 --- a/packages/db-mongodb/src/utilities/buildJoinAggregation.ts +++ b/packages/db-mongodb/src/utilities/buildJoinAggregation.ts @@ -19,8 +19,6 @@ type BuildJoinAggregationArgs = { collection: CollectionSlug collectionConfig: SanitizedCollectionConfig joins: JoinQuery - // the number of docs to get at the top collection level - limit?: number locale: string projection?: Record // the where clause for the top collection @@ -34,10 +32,8 @@ export const buildJoinAggregation = async ({ collection, collectionConfig, joins, - limit, locale, projection, - query, versions, }: BuildJoinAggregationArgs): Promise => { if ( @@ -49,24 +45,8 @@ export const buildJoinAggregation = async ({ } const joinConfig = adapter.payload.collections[collection].config.joins + const aggregate: PipelineStage[] = [] const polymorphicJoinsConfig = adapter.payload.collections[collection].config.polymorphicJoins - const aggregate: PipelineStage[] = [ - { - $sort: { createdAt: -1 }, - }, - ] - - if (query) { - aggregate.push({ - $match: query, - }) - } - - if (limit) { - aggregate.push({ - $limit: limit, - }) - } for (const join of polymorphicJoinsConfig) { if (projection && !projection[join.joinPath]) { @@ -448,9 +428,5 @@ export const buildJoinAggregation = async ({ } } - if (projection) { - aggregate.push({ $project: projection }) - } - return aggregate } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 28536d68a..88445dc6b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -251,9 +251,6 @@ importers: mongoose: specifier: 8.9.5 version: 8.9.5(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3) - mongoose-aggregate-paginate-v2: - specifier: 1.1.2 - version: 1.1.2 mongoose-paginate-v2: specifier: 1.8.5 version: 1.8.5 @@ -267,9 +264,6 @@ importers: '@payloadcms/eslint-config': specifier: workspace:* version: link:../eslint-config - '@types/mongoose-aggregate-paginate-v2': - specifier: 1.0.12 - version: 1.0.12(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3) mongodb: specifier: 6.12.0 version: 6.12.0(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3) @@ -5355,9 +5349,6 @@ packages: '@types/minimist@1.2.5': resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - '@types/mongoose-aggregate-paginate-v2@1.0.12': - resolution: {integrity: sha512-wL8pgJQxqJagv5f5mR7aI8WgUu22nS6rVLoJm71W2Uu+iKfS8jgph2rRLfXrjo+dFt1s7ik5Zl+uGZ4f5GM6Vw==} - '@types/ms@0.7.34': resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} @@ -8353,10 +8344,6 @@ packages: socks: optional: true - mongoose-aggregate-paginate-v2@1.1.2: - resolution: {integrity: sha512-Ai478tHedZy3U2ITBEp2H4rQEviRan3TK4p/umlFqIzgPF1R0hNKvzzQGIb1l2h+Z32QLU3NqaoWKu4vOOUElQ==} - engines: {node: '>=4.0.0'} - mongoose-paginate-v2@1.8.5: resolution: {integrity: sha512-kFxhot+yw9KmpAGSSrF/o+f00aC2uawgNUbhyaM0USS9L7dln1NA77/pLg4lgOaRgXMtfgCENamjqZwIM1Zrig==} engines: {node: '>=4.0.0'} @@ -14808,20 +14795,6 @@ snapshots: '@types/minimist@1.2.5': {} - '@types/mongoose-aggregate-paginate-v2@1.0.12(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3)': - dependencies: - '@types/node': 22.5.4 - mongoose: 8.9.5(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3) - transitivePeerDependencies: - - '@aws-sdk/credential-providers' - - '@mongodb-js/zstd' - - gcp-metadata - - kerberos - - mongodb-client-encryption - - snappy - - socks - - supports-color - '@types/ms@0.7.34': {} '@types/mysql@2.15.26': @@ -18496,8 +18469,6 @@ snapshots: '@aws-sdk/credential-providers': 3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)) socks: 2.8.3 - mongoose-aggregate-paginate-v2@1.1.2: {} - mongoose-paginate-v2@1.8.5: {} mongoose@8.9.5(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3):