Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default functions and incorrect "this" document #12328

Closed
2 tasks done
raphael-papazikas opened this issue Aug 25, 2022 · 3 comments · Fixed by #12427
Closed
2 tasks done

Default functions and incorrect "this" document #12328

raphael-papazikas opened this issue Aug 25, 2022 · 3 comments · Fixed by #12427
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@raphael-papazikas
Copy link
Contributor

Prerequisites

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

Mongoose version

6.5.3

Node.js version

16.13.1

MongoDB server version

5.0.5

Description

If i have a Subdocument schema which has a default function that depends on an other property of this subdocument, the this context is not the correct Subdocument when querying from database. But when i use new testModel({...}) with the data that i expect in my database, the value is defaulted correctly (test1 compared to test3).

Steps to Reproduce

const mongoose = require("mongoose");
const connection = mongoose.createConnection("mongodb://localhost:27017/test");

const subSchema = new mongoose.Schema({
    propertyA: {type: String},
    propertyB: {type: String, default: function() {
        console.log("this", this);
        if (this.propertyA === "A") {
            return "B"
        }

        return "C"
    }}
})

const userSchema = new mongoose.Schema(
  {
    name: String,
    sub: {type: subSchema, default: () => ({})}
  },
);

const testModel = connection.model("User", userSchema);

async function populateCollection() {
    await new testModel({name: `test-0`}).save()
    
    const withPropertyA = await new testModel({name: `test-1`, sub: {propertyA: "A"}}).save()
    // remove sub.propertyB so that it should be defaulted on fetch time
    await testModel.findOneAndUpdate({_id: withPropertyA._id}, {$unset: {"sub.propertyB": ""}})

    await new testModel({name: `test-2`, sub: {propertyA: "B"}}).save()
}

async function cleanInsertTest() {
  await testModel.deleteMany({});

  await populateCollection();
}

async function findTest() {
  const test0 = await testModel.findOne({name: `test-0`});
  const test1 = await testModel.findOne({name: `test-1`});
  const test2 = await testModel.findOne({name: `test-2`});
  console.log("test0", test0.sub);
  console.log("test1", test1.sub);
  console.log("test2", test2.sub);

  const test3 = new testModel({sub: {...test1.sub.toObject(), propertyB: undefined}});
  console.log("test3", test3.sub)
  console.log("test1 vs test3", test1.sub.propertyB === test3.sub.propertyB)
}

async function run() {
  await cleanInsertTest();
  await findTest();
  connection.close();
}

run();

Expected Behavior

No response

@IslandRhythms IslandRhythms added the confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. label Aug 29, 2022
@IslandRhythms
Copy link
Collaborator

const mongoose = require("mongoose");

const subSchema = new mongoose.Schema({
    propertyA: {type: String},
    propertyB: {type: String, default: function() {
        console.log("this", this);
        if (this.propertyA === "A") {
            return "B"
        }

        return "C"
    }}
})

const userSchema = new mongoose.Schema(
  {
    name: String,
    sub: {type: subSchema, default: () => ({})}
  },
);

const testModel = mongoose.model("User", userSchema);

async function populateCollection() {
    await new testModel({name: `test-0`}).save()
    
    const withPropertyA = await new testModel({name: `test-1`, sub: {propertyA: "A"}}).save()
    // remove sub.propertyB so that it should be defaulted on fetch time
    await testModel.findOneAndUpdate({_id: withPropertyA._id}, {$unset: {"sub.propertyB": ""}})

    await new testModel({name: `test-2`, sub: {propertyA: "B"}}).save()
}

async function cleanInsertTest() {
  await testModel.deleteMany({});

  await populateCollection();
}

async function findTest() {
  const test0 = await testModel.findOne({name: `test-0`});
  const test1 = await testModel.findOne({name: `test-1`});
  const test2 = await testModel.findOne({name: `test-2`});
  console.log("test0", test0.sub);
  console.log("test1", test1.sub);
  console.log("test2", test2.sub);

  const test3 = new testModel({sub: {...test1.sub.toObject(), propertyB: undefined}});
  console.log('==================')
  console.log("test3", test3.sub)
  console.log("test1 vs test3", test1.sub.propertyB === test3.sub.propertyB)
}

async function run() {
  await mongoose.connect('mongodb://localhost:27017/test')
  await mongoose.connection.dropDatabase();
  await cleanInsertTest();
  await findTest();
  await mongoose.connection.close();
}

run();

@felipezarco
Copy link

Is the behaviour here expected to be the same with arrow functions? Will both keep this as the saving doc?

@vkarpov15
Copy link
Collaborator

@felipezarco no. Arrow functions use lexical this, so this in an arrow function is the same as this outside the function.

@Automattic Automattic locked and limited conversation to collaborators Dec 4, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants