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

Remove direction to include virutals in toObject #13273

Merged
merged 2 commits into from May 16, 2023

Conversation

iatenine
Copy link
Contributor

Passing {virtuals: true} to toObject does not have the same effect as toJSON{ vrituals: true} Tested on Ubuntu 22.04 and Windows 10 using version "^6.9.2"

Can be replicated with the following model file:

const postSchema = new Schema(
  {
    text: String,
    username: String,
    comments: [{ type: Schema.Types.ObjectId, ref: 'comment' }],
  },
  {
    // replace with toJSON to revert to correct behavior
    toObject: {
      virtuals: true,
    },
  }
);

postSchema.virtual('commentCount').get(function () {
  return this.comments.length;
});

// Initialize our Post model
const Post = model('post', postSchema);

Please correct if I'm using this code erroneously

Summary

Documentation directly states including virtuals can be achieved via the same process for both toJSON and toObject but { virtuals: true } has no impact on toObject

Examples

toJSON response:

{
		"_id": "643631713ac0c3db07a42df3",
		"text": " curabitur dolor purus ut ornare amet imsum elit lacinia blandit",
		"username": "Abaan",
		"comments": [
			"643631713ac0c3db07a42dee"
		],
		"commentCount": 1,
		"id": "643631713ac0c3db07a42df3"
},

toObject response:

{
		"_id": "643631713ac0c3db07a42df3",
		"text": " curabitur dolor purus ut ornare amet imsum elit lacinia blandit",
		"username": "Abaan",
		"comments": [
			"643631713ac0c3db07a42dee"
		]
},

Passing {virtuals: true} to toObject does not have the same effect as toJSON{ vrituals: true}
Tested on Ubuntu 22.04 and Windows 10 using version "^6.9.2"


Can be replicated with the following model file:

const postSchema = new Schema(
  {
    text: String,
    username: String,
    comments: [{ type: Schema.Types.ObjectId, ref: 'comment' }],
  },
  {
    // replace with toJSON to revert to correct behavior
    toObject: {
      virtuals: true,
    },
  }
);

// Create a virtual property `commentCount` that gets the amount of comments per post
postSchema.virtual('commentCount').get(function () {
  return this.comments.length;
});

// Initialize our Post model
const Post = model('post', postSchema);

Please correct if I'm using this code erroneously
@vkarpov15
Copy link
Collaborator

"Pass { virtuals: true } to either
toObject() or toJSON()." means calling JSON.stringify(doc.toObject({ virtuals: true })) or JSON.stringify(doc.toJSON({ virtuals: true })) . Not for setting the schema-level toJSON option.

I think we just need an example of the two different approaches for getting virtuals into JSON.stringify() output: using schema toJSON option, or calling JSON.stringify(doc.toObject({ virtuals: true }))

@iatenine
Copy link
Contributor Author

@vkarpov15 Could you provide such an example?

All sources I've found including the Mongoose docs and StackOverflow seem to imply the behavior will be the same for both

@vkarpov15
Copy link
Collaborator

@iatenine "Could you provide such an example?" <-- I don't understand, can you please clarify?

@iatenine
Copy link
Contributor Author

@vkarpov15 well if I understand correctly, there's a different way to include virtual for toObject

I get they use a different approach under the hood causing similar, but different, behavior but it's not obvious how virtuals can be achieved without switching to toJson

@hasezoey
Copy link
Collaborator

i dont understand what the problem here is, given the following code:

// NodeJS: 18.10.0
// MongoDB: 5.0 (Docker)
// Typescript 4.9.5
import * as mongoose from 'mongoose'; // mongoose@7.0.3

const schema1 = new mongoose.Schema({
  normalProp: String,
});

schema1.virtual('virtProp').get(() => 'hello1');

const model1 = mongoose.model('Test1', schema1);

const doc1 = new model1({ normalProp: 'normal1' });

console.log('1normal', doc1);
console.log('1toJSON virtuals', doc1.toJSON({ virtuals: true }));
console.log('1toObject virtuals', doc1.toObject({ virtuals: true }));

const schema2 = new mongoose.Schema(
  {
    normalProp: String,
  },
  {
    toObject: {
      virtuals: true,
    },
    toJSON: {
      virtuals: true,
    },
  }
);

schema2.virtual('virtProp').get(() => 'hello2');

const model2 = mongoose.model('Test2', schema2);

const doc2 = new model2({ normalProp: 'normal2' });

console.log('2normal', doc2);
console.log('2toJSON', doc2.toJSON());
console.log('2toObject', doc2.toObject());

the output is

1normal { <-- no virtuals expected, because of no options
  normalProp: 'normal1',
  _id: new ObjectId("64479cb47b50cd461a7b3022")
}
1toJSON virtuals {
  normalProp: 'normal1',
  _id: new ObjectId("64479cb47b50cd461a7b3022"),
  virtProp: 'hello1', <-- virtual present because of direct function option
  id: '64479cb47b50cd461a7b3022'
}
1toObject virtuals {
  normalProp: 'normal1',
  _id: new ObjectId("64479cb47b50cd461a7b3022"),
  virtProp: 'hello1', <-- virtual present because of direct function option
  id: '64479cb47b50cd461a7b3022'
}
2normal {
  normalProp: 'normal2',
  _id: new ObjectId("64479cb47b50cd461a7b3023"),
  virtProp: 'hello2', <-- virtual present here because of schema-option
  id: '64479cb47b50cd461a7b3023'
}
2toJSON {
  normalProp: 'normal2',
  _id: new ObjectId("64479cb47b50cd461a7b3023"),
  virtProp: 'hello2', <-- virtual present here because of schema-option
  id: '64479cb47b50cd461a7b3023'
}
2toObject {
  normalProp: 'normal2',
  _id: new ObjectId("64479cb47b50cd461a7b3023"),
  virtProp: 'hello2', <-- virtual present here because of schema-option
  id: '64479cb47b50cd461a7b3023'
}

@elias-soykat
Copy link

elias-soykat commented May 9, 2023

the virtual feture is something complex compare to other features. the docs also not clear about schema level virtuals.

@hasezoey
Copy link
Collaborator

hasezoey commented May 9, 2023

the docs also not clear about schema level virtuals

could you explain what you mean with that? because from what i know there is only one kind of virtual, only different ways to define them

@elias-soykat
Copy link

the docs also not clear about schema level virtuals

could you explain what you mean with that? because from what i know there is only one kind of virtual, only different ways to define them

I don't know when i define in modal below code.

{ timestamps: true, versionKey: false, toJSON: { virtuals: true }, toObject: { virtuals: true } }

i get a id property in every documents.

but i don't define it like this.

userSchema.virtual('id')
    .get(function () {
        return this._id
    })

btw i love mongoose <3 i want contribute in this repo. suggest me some issues. thank you.

@hasezoey
Copy link
Collaborator

i get a id property in every documents.

the id virtual is added and enabled by default in mongoose, it can be disabled with the schema option id: false, and since you enable virtuals to be output in toJSON / toObject it will show up unless you disable it or manually remove it

@elias-soykat
Copy link

okay, thank you

@vkarpov15 vkarpov15 merged commit c1c3dfe into Automattic:master May 16, 2023
1 check passed
@vkarpov15 vkarpov15 added this to the 7.1.2 milestone May 16, 2023
@iatenine
Copy link
Contributor Author

Thank you @vkarpov15
Knew I needed to understand it better to clarify the wording but didn't have time so glad you were able to correct and merge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants