Skip to content

Manually populated documents get saved as strings instead of ObjectIds in combination with SchemaType getter #14759

Closed
@fardolieri

Description

@fardolieri

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

8.5.1

Node.js version

20.15.1

MongoDB server version

7.0.12

Typescript version (if applicable)

No response

Description

I use a SchemaType getter on ObjectId because I think strings are more convenient to work with than ObjectIds.
Their use is described and mentioned here:

Have a look at the code snippet below. I cannot query for a pet anymore, given its owner.

I think that is because pet.save() unexpectedly saved the owner id as a string into the mongoDB instead of an ObjectId.

image

Steps to Reproduce

/**
 * I want to globally work with strings instead of ObjectIds
 * because that allows me to reuse my underlying types
 * across server and client.
 *
 * Also see:
 * https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.get()
 * https://thecodebarbarian.com/whats-new-in-mongoose-54-global-schematype-configuration.html#schematype-getters
 * https://github.com/Automattic/mongoose/issues/6996#issuecomment-434063799
 */
mongoose.Schema.ObjectId.get(
  value => {
    if (value?.constructor?.name?.toLowerCase() === 'objectid') return String(value)
    return value
  }
)

const ownerSchema = new mongoose.Schema({ name: 'String' })
const petSchema = new mongoose.Schema({
  name: 'String',
  owner: { type: 'ObjectId', ref: 'owner' }
})

const Owner = mongoose.model('owner', ownerSchema)
const Pet = mongoose.model('pet', petSchema)

const owner = new Owner({ name: 'Alice' })
const pet = new Pet({ name: 'Kitty', owner: owner })

await owner.save()
// pet.owner = pet.owner._id // Manually depopulating it like this makes the whole thing work
await pet.save()

/**
 * The property 'owner' of 'pet' has unexpectedly been saved
 * as a string into the mongoDB instead of ObjectId.
 * 
 * No matter how I query for it, mongoose is not
 * able to find it...
 */

const reloadAttempts = await Promise.all([
  Pet.findOne({ owner: owner }),
  Pet.findOne({ owner: owner._id }),
  Pet.findOne({ owner: String(owner._id) }),
  Pet.findOne({ owner: new mongoose.Types.ObjectId(owner._id) }),
])

console.log(reloadAttempts) // Logs [ null, null, null, null ] but shouldn't

Expected Behavior

I expect my reloadAttempts to not be all null.

Also the ObjectId getter should not change the mongoDB data structure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugWe've confirmed this is a bug in Mongoose and will fix it.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions