diff --git a/packages/db-mongodb/src/index.ts b/packages/db-mongodb/src/index.ts index e1538a45fc8..f1595cddae8 100644 --- a/packages/db-mongodb/src/index.ts +++ b/packages/db-mongodb/src/index.ts @@ -23,6 +23,7 @@ import type { import mongoose from 'mongoose' import { createDatabaseAdapter, defaultBeginTransaction, findMigrationDir } from 'payload' +import type { MongooseIDType } from './models/buildSchema.js' import type { CollectionModel, GlobalModel, @@ -146,6 +147,10 @@ export interface Args { * NOTE: not recommended for production. This can slow down the initialization of Payload. */ ensureIndexes?: boolean + /** + * The type to use for IDs in MongoDB. Can be `String`, `Number`, `mongoose.Schema.Types.ObjectId`, or `mongoose.Schema.Types.BigInt`. Defaults to `String`. + */ + idType?: MongooseIDType migrationDir?: string /** * typed as any to avoid dependency @@ -262,6 +267,7 @@ export function mongooseAdapter({ disableFallbackSort = false, disableIndexHints = false, ensureIndexes = false, + idType: mongooseIDType, migrationDir: migrationDirArg, mongoMemoryServer, prodMigrations, @@ -328,6 +334,7 @@ export function mongooseAdapter({ findGlobalVersions, findOne, findVersions, + idType: mongooseIDType, init, migrateFresh, migrationDir, diff --git a/packages/db-mongodb/src/init.ts b/packages/db-mongodb/src/init.ts index e60dcdb6969..6b7f2377692 100644 --- a/packages/db-mongodb/src/init.ts +++ b/packages/db-mongodb/src/init.ts @@ -30,7 +30,7 @@ export const init: Init = async function init(this: MongooseAdapter) { this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => { const schemaOptions = this.collectionsSchemaOptions?.[collection.slug] - const schema = buildCollectionSchema(collection, this.payload, schemaOptions) + const schema = buildCollectionSchema(collection, this.payload, schemaOptions, this.idType) if (collection.versions) { const versionModelName = getDBName({ config: collection, versions: true }) @@ -40,6 +40,7 @@ export const init: Init = async function init(this: MongooseAdapter) { buildSchemaOptions: { disableUnique: true, draftsEnabled: true, + idType: this.idType, indexSortableFields: this.payload.config.indexSortableFields, options: { minimize: false, @@ -92,6 +93,7 @@ export const init: Init = async function init(this: MongooseAdapter) { buildSchemaOptions: { disableUnique: true, draftsEnabled: true, + idType: this.idType, indexSortableFields: this.payload.config.indexSortableFields, options: { minimize: false, diff --git a/packages/db-mongodb/src/models/buildCollectionSchema.ts b/packages/db-mongodb/src/models/buildCollectionSchema.ts index 8c7a3d2da05..47a48e4a404 100644 --- a/packages/db-mongodb/src/models/buildCollectionSchema.ts +++ b/packages/db-mongodb/src/models/buildCollectionSchema.ts @@ -3,6 +3,8 @@ import type { Payload, SanitizedCollectionConfig } from 'payload' import paginate from 'mongoose-paginate-v2' +import type { MongooseIDType } from './buildSchema.js' + import { getBuildQueryPlugin } from '../queries/getBuildQueryPlugin.js' import { buildSchema } from './buildSchema.js' @@ -10,12 +12,14 @@ export const buildCollectionSchema = ( collection: SanitizedCollectionConfig, payload: Payload, schemaOptions = {}, + idType?: MongooseIDType, ): Schema => { const schema = buildSchema({ buildSchemaOptions: { draftsEnabled: Boolean( typeof collection?.versions === 'object' && collection.versions.drafts, ), + idType, indexSortableFields: payload.config.indexSortableFields, options: { minimize: false, diff --git a/packages/db-mongodb/src/models/buildSchema.ts b/packages/db-mongodb/src/models/buildSchema.ts index 70648be1fba..fd7a32305c5 100644 --- a/packages/db-mongodb/src/models/buildSchema.ts +++ b/packages/db-mongodb/src/models/buildSchema.ts @@ -39,10 +39,18 @@ import { tabHasName, } from 'payload/shared' +export type MongooseIDType = + | typeof mongoose.Schema.Types.BigInt + | typeof mongoose.Schema.Types.ObjectId + | typeof mongoose.Schema.Types.UUID + | typeof Number + | typeof String + export type BuildSchemaOptions = { allowIDField?: boolean disableUnique?: boolean draftsEnabled?: boolean + idType?: MongooseIDType indexSortableFields?: boolean options?: SchemaOptions } @@ -64,6 +72,20 @@ const formatDefaultValue = (field: FieldAffectingData) => ? field.defaultValue : undefined +const getIdFieldSchema = (idType: MongooseIDType): SchemaTypeOptions => { + const baseSchema: SchemaTypeOptions = { + type: idType, + required: true, + } + + // UUID types need a default generator + if (idType === mongoose.Schema.Types.UUID) { + baseSchema.default = () => new mongoose.Types.UUID() + } + + return baseSchema +} + const formatBaseSchema = ({ buildSchemaOptions, field, @@ -152,17 +174,24 @@ export const buildSchema = (args: { const fieldsToSearch = flattenedFields || schemaFields const idField = fieldsToSearch.find((field) => fieldAffectsData(field) && field.name === 'id') if (idField) { + const idType = + idField.type === 'number' + ? payload.db.useBigIntForNumberIDs + ? mongoose.Schema.Types.BigInt + : Number + : buildSchemaOptions.idType + ? buildSchemaOptions.idType + : String fields = { - _id: - idField.type === 'number' - ? payload.db.useBigIntForNumberIDs - ? mongoose.Schema.Types.BigInt - : Number - : String, + _id: getIdFieldSchema(idType), } schemaFields = schemaFields.filter( (field) => !(fieldAffectsData(field) && field.name === 'id'), ) + } else if (buildSchemaOptions.idType) { + fields = { + _id: getIdFieldSchema(buildSchemaOptions.idType), + } } }