Skip to content

Document.parent() returns undefined for a populated sub document inside of a cursor #15494

@edorsey

Description

@edorsey

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

Node.js version

18.19

MongoDB server version

7

Typescript version (if applicable)

No response

Description

There appears to be a bug when calling Document.parent() on a document that is populated via a cursor. It works when there is no cursor, but fails when a cursor is used.

This appears to have been introduced in mongoose@8.3.5.

The following test passes with mongoose@8.3.4 and fails in mongoose@8.3.5 as well as mongoose@8.6.1.

Steps to Reproduce

import test, { after, before } from "node:test";
import assert from "node:assert";
import mongoose from "mongoose";

const childSchema = new mongoose.Schema({
  name: { type: String, required: true },
});

const Child = mongoose.model("Child", childSchema);

const parentSchema = new mongoose.Schema({
  name: { type: String, required: true },
  childId: { type: mongoose.Schema.Types.ObjectId },
});

parentSchema.virtual("child", {
  ref: "Child",
  localField: "childId",
  foreignField: "_id",
  justOne: true,
});

const Parent = mongoose.model("Parent", parentSchema);

let parent;
let child;
before(async () => {
  console.log("Connecting");
  await mongoose.connect(
    "mongodb://localhost:27018/mongoose-bug?directConnection=true"
  );

  console.log("Creating child");
  child = await Child.create({
    name: "child",
  });

  parent = await Parent.create({
    name: "parent",
    childId: child._id,
  });
});

after(async () => {
  await mongoose.disconnect();
});

test("should populate when document is already found and .parent() should work on populated child", async () => {
  await parent.populate(["child"]);

  assert.equal(parent.child.parent()._id, parent._id);
});

test("should populate via findOne and .parent() should work on populated child", async () => {
  const foundParent = await Parent.findOne({ _id: parent._id }).populate([
    "child",
  ]);
  assert(foundParent, "parent not found");

  assert.equal(foundParent.child.parent()._id, foundParent._id);
});

test("should populate via find with no cursor and .parent() should work on populated child", async () => {
  const foundParents = await Parent.find({
    _id: { $in: [parent._id] },
  }).populate(["child"]);
  assert(foundParents[0], "parent not found");

  for (const parent of foundParents) {
    assert.equal(parent.child.parent()._id, parent._id);
  }
});

test("should populate via find with a cursor and .parent() should work on populated child", async () => {
  const cursor = Parent.find({
    _id: { $in: [parent._id] },
  })
    .populate(["child"])
    .cursor();

  for await (const parent of cursor) {
    assert.equal(parent.child.parent()._id, parent._id);
  }
});

Output:

➜  mongoose-cursor-populate-parent-bug node --test                
Connecting
Creating child
✔ should populate when document is already found and .parent() should work on populated child (45.100167ms)
✔ should populate via findOne and .parent() should work on populated child (3.811459ms)
✔ should populate via find with no cursor and .parent() should work on populated child (4.311333ms)
✖ should populate via find with a cursor and .parent() should work on populated child (6.576625ms)
  TypeError [Error]: Cannot read properties of undefined (reading '_id')
      at TestContext.<anonymous> (file:///Users/edorsey/Development/mongoose-cursor-populate-parent-bug/bug.test.js:82:39)
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async Test.run (node:internal/test_runner/test:632:9)
      at async Test.processPendingSubtests (node:internal/test_runner/test:374:7)

ℹ tests 4
ℹ suites 0
ℹ pass 3
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 229.875792

✖ failing tests:

test at file:/Users/edorsey/Development/mongoose-cursor-populate-parent-bug/bug.test.js:74:1
✖ should populate via find with a cursor and .parent() should work on populated child (6.576625ms)
  TypeError [Error]: Cannot read properties of undefined (reading '_id')
      at TestContext.<anonymous> (file:///Users/edorsey/Development/mongoose-cursor-populate-parent-bug/bug.test.js:82:39)
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async Test.run (node:internal/test_runner/test:632:9)
      at async Test.processPendingSubtests (node:internal/test_runner/test:374:7)

Expected Behavior

I expect the last test to pass, just like the others and as it did in mongoose@8.3.4.

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

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions