Skip to content

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

Closed
@fardolieri

Description

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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