diff --git a/src/config/defaults.ts b/src/config/defaults.ts index da7d231715..a5f558c9c5 100644 --- a/src/config/defaults.ts +++ b/src/config/defaults.ts @@ -56,4 +56,7 @@ export const defaults: Config = { localization: false, telemetry: true, custom: {}, + database: { + queryDrafts_2_0: false, + }, }; diff --git a/src/config/schema.ts b/src/config/schema.ts index 91a75275f9..ef7b715235 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -178,4 +178,7 @@ export default joi.object({ onInit: joi.func(), debug: joi.boolean(), custom: joi.object().pattern(joi.string(), joi.any()), + database: joi.object().keys({ + queryDrafts_2_0: joi.boolean(), + }), }); diff --git a/src/config/types.ts b/src/config/types.ts index bf5057585e..7609458061 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -555,6 +555,15 @@ export type Config = { onInit?: (payload: Payload) => Promise | void; /** Extension point to add your custom data. */ custom?: Record; + /** database specific configurations */ + database?: { + /** + * Enable the v2.0 drafts query improvement, before 2.0. + * + * You must run the migration script for this feature to function properly. + */ + queryDrafts_2_0?: boolean; + }; }; export type SanitizedConfig = Omit< diff --git a/src/versions/buildCollectionFields.ts b/src/versions/buildCollectionFields.ts index 9a37453d5a..933e2bc6a2 100644 --- a/src/versions/buildCollectionFields.ts +++ b/src/versions/buildCollectionFields.ts @@ -40,7 +40,6 @@ export const buildVersionCollectionFields = (collection: SanitizedCollectionConf }); } - // TODO: behind feature flag if (collection?.versions?.drafts) { fields.push({ name: 'latest', diff --git a/src/versions/buildGlobalFields.ts b/src/versions/buildGlobalFields.ts index 3a31aeac1a..cad4a72362 100644 --- a/src/versions/buildGlobalFields.ts +++ b/src/versions/buildGlobalFields.ts @@ -32,7 +32,6 @@ export const buildVersionGlobalFields = (global: SanitizedGlobalConfig): Field[] }); } - // TODO: behind feature flag if (global?.versions?.drafts) { fields.push({ name: 'latest', diff --git a/src/versions/drafts/queryDrafts.ts b/src/versions/drafts/queryDrafts.ts index 226a8943ef..fedf66378f 100644 --- a/src/versions/drafts/queryDrafts.ts +++ b/src/versions/drafts/queryDrafts.ts @@ -5,6 +5,15 @@ import { Payload } from '../../payload'; import { PaginatedDocs } from '../../mongoose/types'; import { Collection, CollectionModel, TypeWithID } from '../../collections/config/types'; import { combineQueries } from '../../database/combineQueries'; +import { hasWhereAccessResult } from '../../auth'; +import { appendVersionToQueryKey } from './appendVersionToQueryKey'; + +type AggregateVersion = { + _id: string + version: T + updatedAt: string + createdAt: string +} type Args = { accessResult: AccessResult @@ -16,7 +25,96 @@ type Args = { where: Where } -export const queryDrafts = async ({ +export const queryDrafts = async (args: Args): Promise> => { + if (args.payload.config?.database?.queryDrafts_2_0) { + return queryDraftsV2(args); + } + + return queryDraftsV1(args); +}; + +const queryDraftsV1 = async ({ + accessResult, + collection, + req, + overrideAccess, + payload, + paginationOptions, + where: incomingWhere, +}: Args): Promise> => { + const VersionModel = payload.versions[collection.config.slug] as CollectionModel; + + const where = appendVersionToQueryKey(incomingWhere || {}); + + let versionAccessResult; + + if (hasWhereAccessResult(accessResult)) { + versionAccessResult = appendVersionToQueryKey(accessResult); + } + + const versionQuery = await VersionModel.buildQuery({ + where, + access: versionAccessResult, + req, + overrideAccess, + }); + + const aggregate = VersionModel.aggregate>([ + // Sort so that newest are first + { $sort: { updatedAt: -1 } }, + // Group by parent ID, and take the first of each + { + $group: { + _id: '$parent', + version: { $first: '$version' }, + updatedAt: { $first: '$updatedAt' }, + createdAt: { $first: '$createdAt' }, + }, + }, + // Filter based on incoming query + { $match: versionQuery }, + ], { + allowDiskUse: true, + }); + + let result; + + if (paginationOptions) { + const aggregatePaginateOptions = { + ...paginationOptions, + useFacet: payload.mongoOptions?.useFacet, + sort: Object.entries(paginationOptions.sort) + .reduce((sort, [incomingSortKey, order]) => { + let key = incomingSortKey; + + if (!['createdAt', 'updatedAt', '_id'].includes(incomingSortKey)) { + key = `version.${incomingSortKey}`; + } + + return { + ...sort, + [key]: order === 'asc' ? 1 : -1, + }; + }, {}), + }; + + result = await VersionModel.aggregatePaginate(aggregate, aggregatePaginateOptions); + } else { + result = aggregate.exec(); + } + + return { + ...result, + docs: result.docs.map((doc) => ({ + _id: doc._id, + ...doc.version, + updatedAt: doc.updatedAt, + createdAt: doc.createdAt, + })), + }; +}; + +const queryDraftsV2 = async ({ accessResult, collection, req,