Skip to content

Hydrate from JSON not hydrating populated virtual objects #14503

@ilovepixelart

Description

@ilovepixelart

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.2.4

Node.js version

20.12.1

MongoDB server version

7.0.7

Typescript version (if applicable)

5.4.3

Description

Consider such example

Story.ts

import { Schema, model } from 'mongoose'

import type IStory from '../interfaces/IStory'

const StorySchema = new Schema<IStory>({
  userId: {
    type: Schema.Types.ObjectId,
    ref: 'User',
    required: true,
    index: true,
  },
  title: {
    type: String,
    required: true,
  },
}, { timestamps: true })

export default model('Story', StorySchema)

User.ts

import { Schema, model } from 'mongoose'

import type IUser from '../interfaces/IUser'

const UserSchema = new Schema<IUser>({
  name: {
    type: String,
    required: true,
  },
  role: {
    type: String,
    required: true,
  },
  age: {
    type: Number,
  },
}, { timestamps: true })

UserSchema.virtual('stories', {
  ref: 'Story',
  localField: '_id',
  foreignField: 'userId',
})

export default model('User', UserSchema)

hydrate-populate-from-json.test.ts

const user = await User.create({ name: 'Alex', role: 'user' })
const story1 = await Story.create({ title: 'Ticket 1', userId: user._id })
const story2 = await Story.create({ title: 'Ticket 2', userId: user._id })

const populated = await User.findOne({ name: 'Alex' }).populate('stories').lean().exec()

expect(populated).not.toBeNull()
expect(populated?._id instanceof mongoose.Types.ObjectId).toBeTruthy()
expect(populated?.name).toBe('Alex')
expect(populated?.stories).not.toBeNull()
expect(populated?.stories?.length).toBe(2)

// This is what I expect also after hydration see failed comment bellow

expect(populated?.stories?.[0]._id).toEqual(story1._id)
expect(populated?.stories?.[0]._id instanceof mongoose.Types.ObjectId).toBeTruthy()
expect(typeof populated?.stories?.[0]._id).toBe('object')
expect(populated?.stories?.[0].createdAt instanceof Date).toBeTruthy()
expect(typeof populated?.stories?.[0].createdAt).toBe('object')

expect(populated?.stories?.[1]._id).toEqual(story2._id)
expect(populated?.stories?.[1]._id instanceof mongoose.Types.ObjectId).toBeTruthy()
expect(typeof populated?.stories?.[1]._id).toBe('object')
expect(populated?.stories?.[1].createdAt instanceof Date).toBeTruthy()
expect(typeof populated?.stories?.[1].createdAt).toBe('object')

const populatedJson = JSON.stringify(populated)
expect(populatedJson).not.toBeNull()

const hydrated = User.hydrate(JSON.parse(populatedJson))

// Root object was hydrated properly

expect(hydrated).not.toBeNull()
expect(hydrated?._id instanceof mongoose.Types.ObjectId).toBeTruthy()
expect(hydrated?.name).toBe('Alex')
expect(hydrated?.stories).not.toBeNull()
expect(hydrated?.stories?.length).toBe(2)

// Will fail here populated stories array is not hydrated _id and createdAt updatedAt are strings

expect(hydrated?.stories?.[0]._id).toEqual(story1._id)
expect(typeof hydrated?.stories?.[0]._id).toBe('object')
expect(hydrated?.stories?.[0]._id instanceof mongoose.Types.ObjectId).toBeTruthy()
expect(typeof hydrated?.stories?.[0].createdAt).toBe('object')
expect(hydrated?.stories?.[0].createdAt instanceof Date).toBeTruthy()

expect(hydrated?.stories?.[1]._id).toEqual(story2._id)
expect(typeof hydrated?.stories?.[1]._id).toBe('object')
expect(hydrated?.stories?.[1]._id instanceof mongoose.Types.ObjectId).toBeTruthy()
expect(typeof hydrated?.stories?.[1].createdAt).toBe('object')
expect(hydrated?.stories?.[1].createdAt instanceof Date).toBeTruthy()

Weird thing, when I use:

const hydrated = User.hydrate(JSON.parse(populatedJson), undefined, { hydratedPopulatedDocs: true })

Then even the root object now is not hydrated (_id and dates will be strings even on root level)

Steps to Reproduce

Check the description models and test provided

Expected Behavior

After hydrating json I expect to receive list of populated virtuals also in hydrated form

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