-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
Mongoose >= 7.0.0 inserts default values after querying with a single field projection and $elemMatch #14893
Comments
I'm unable to repro, the following script shows that Mongoose isn't saving defaults: const mongoose = require('mongoose');
mongoose.set('debug', true);
console.log(mongoose.version);
const { Schema } = mongoose;
const MySchema = new Schema({
details: [{
name: String,
id: String,
age: Number
}],
addresses: [{
streetName: String,
streetNumber: String
}],
permissions: {
add: {type: Boolean, default: false},
other: {type: Number, default: 3}
}
});
run().catch(err => console.log(err));
async function run() {
await mongoose.connect('mongodb://127.0.0.1:27017/mongoose_test');
const Test = mongoose.model('Test', MySchema);
await Test.deleteMany({});
await Test.collection.insertOne({
details: [{
name: 'Gary',
age: 23,
id: '12345'
}]
});
const doc = await Test.findOne({}, 'details').select({details: {$elemMatch: {id: '12345'}}}).exec();
console.log(doc);
await doc.save();
} The output is as follows.
Please modify the script in this post to demonstrate the issue you're seeing. |
Thanks for the reply.
Please note the following: In the projection above The outcome in mongoose7
This, to me, seems like an ongoing bug since I believe in any scenario the query shouldn't result with the default values when using select and $elemMatch and in any way shouldn't overwrite the doc with default values. Thanks again |
Hi @vkarpov15 , the details below were obtained by running the above script without a projection. I've managed to narrow down this behavior to a change in version
|
When providing a projection of Output using Mongoose $ node 01377321.js
7.6.6
(node:10874) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Mongoose: tests.deleteMany({}, {})
Mongoose: tests.insertMany([ { field1: true, field2: [ { subField1: new ObjectId("66eac042ea4c2dd344a670ef"), subField2: false, subField3: false, _id: new ObjectId("66eac042ea4c2dd344a670f3") }, { subField1: new ObjectId("66eac042ea4c2dd344a670f0"), subField2: true, subField3: true, _id: new ObjectId("66eac042ea4c2dd344a670f4") } ], field3: 1, field4: [ { name: 'Some name', amount: 100, price: 50, _id: new ObjectId("66eac042ea4c2dd344a670f5"), amounts: [], stockItems: [] } ], _id: new ObjectId("66eac042ea4c2dd344a670f2"), __v: 0 }], {})
Mongoose: tests.findOne({}, {})
fullDocPreSave {
_id: new ObjectId("66eac042ea4c2dd344a670f2"),
field1: true,
field2: [
{
subField1: new ObjectId("66eac042ea4c2dd344a670ef"),
subField2: false,
subField3: false,
_id: new ObjectId("66eac042ea4c2dd344a670f3")
},
{
subField1: new ObjectId("66eac042ea4c2dd344a670f0"),
subField2: true,
subField3: true,
_id: new ObjectId("66eac042ea4c2dd344a670f4")
}
],
field3: 1,
field4: [
{
name: 'Some name',
amount: 100,
price: 50,
_id: new ObjectId("66eac042ea4c2dd344a670f5"),
amounts: [],
stockItems: []
}
],
__v: 0
}
Mongoose: tests.findOne({ _id: ObjectId("66eac042ea4c2dd344a670f2") }, { projection: { field2: { '$elemMatch': { subField1: ObjectId("66eac042ea4c2dd344a670ef") } }, undefined: 1 }})
doc {
_id: new ObjectId("66eac042ea4c2dd344a670f2"),
field2: [
{
subField1: new ObjectId("66eac042ea4c2dd344a670ef"),
subField2: false,
subField3: false,
_id: new ObjectId("66eac042ea4c2dd344a670f3")
}
]
}
Mongoose: tests.updateOne({ _id: ObjectId("66eac042ea4c2dd344a670f2"), field2: { '$elemMatch': { subField1: ObjectId("66eac042ea4c2dd344a670ef") } }}, { '$set': { 'field2.$.subField2': true } }, {})
Mongoose: tests.findOne({}, {})
fullDocPostSave {
_id: new ObjectId("66eac042ea4c2dd344a670f2"),
field1: true,
field2: [
{
subField1: new ObjectId("66eac042ea4c2dd344a670ef"),
subField2: true,
subField3: false,
_id: new ObjectId("66eac042ea4c2dd344a670f3")
},
{
subField1: new ObjectId("66eac042ea4c2dd344a670f0"),
subField2: true,
subField3: true,
_id: new ObjectId("66eac042ea4c2dd344a670f4")
}
],
field3: 1,
field4: [
{
name: 'Some name',
amount: 100,
price: 50,
_id: new ObjectId("66eac042ea4c2dd344a670f5"),
amounts: [],
stockItems: []
}
],
__v: 0
} Output using Mongoose $ node 01377321.js
7.6.7
(node:10903) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Mongoose: tests.deleteMany({}, {})
Mongoose: tests.insertMany([ { field1: true, field2: [ { subField1: new ObjectId("66eac048f48feac1f08838f6"), subField2: false, subField3: false, _id: new ObjectId("66eac048f48feac1f08838fa") }, { subField1: new ObjectId("66eac048f48feac1f08838f7"), subField2: true, subField3: true, _id: new ObjectId("66eac048f48feac1f08838fb") } ], field3: 1, field4: [ { name: 'Some name', amount: 100, price: 50, _id: new ObjectId("66eac048f48feac1f08838fc"), amounts: [], stockItems: [] } ], _id: new ObjectId("66eac048f48feac1f08838f9"), __v: 0 }], {})
Mongoose: tests.findOne({}, {})
fullDocPreSave {
_id: new ObjectId("66eac048f48feac1f08838f9"),
field1: true,
field2: [
{
subField1: new ObjectId("66eac048f48feac1f08838f6"),
subField2: false,
subField3: false,
_id: new ObjectId("66eac048f48feac1f08838fa")
},
{
subField1: new ObjectId("66eac048f48feac1f08838f7"),
subField2: true,
subField3: true,
_id: new ObjectId("66eac048f48feac1f08838fb")
}
],
field3: 1,
field4: [
{
name: 'Some name',
amount: 100,
price: 50,
_id: new ObjectId("66eac048f48feac1f08838fc"),
amounts: [],
stockItems: []
}
],
__v: 0
}
Mongoose: tests.findOne({ _id: ObjectId("66eac048f48feac1f08838f9") }, { projection: { field2: { '$elemMatch': { subField1: ObjectId("66eac048f48feac1f08838f6") } }, undefined: 1 }})
doc {
field1: false,
field3: 5,
_id: new ObjectId("66eac048f48feac1f08838f9"),
field2: [
{
subField1: new ObjectId("66eac048f48feac1f08838f6"),
subField2: false,
subField3: false,
_id: new ObjectId("66eac048f48feac1f08838fa")
}
],
field4: []
}
Mongoose: tests.updateOne({ _id: ObjectId("66eac048f48feac1f08838f9"), field2: { '$elemMatch': { subField1: ObjectId("66eac048f48feac1f08838f6") } }}, { '$set': { 'field2.$.subField2': true, field1: false, field3: 5, field4: [] }}, {})
Mongoose: tests.findOne({}, {})
fullDocPostSave {
_id: new ObjectId("66eac048f48feac1f08838f9"),
field1: false,
field2: [
{
subField1: new ObjectId("66eac048f48feac1f08838f6"),
subField2: true,
subField3: false,
_id: new ObjectId("66eac048f48feac1f08838fa")
},
{
subField1: new ObjectId("66eac048f48feac1f08838f7"),
subField2: true,
subField3: true,
_id: new ObjectId("66eac048f48feac1f08838fb")
}
],
field3: 5,
field4: [],
__v: 0
} |
…ive if elemMatch on a Date, ObjectId, etc. Fix #14893
fix(projection): avoid setting projection to unknown exclusive/inclusive if elemMatch on a Date, ObjectId, etc.
Prerequisites
Mongoose version
7.8.0
Node.js version
20.16.0
MongoDB version
5.0.28
MongoDB driver version
5.9.2
Operating system
None
Operating system version (i.e. 20.04, 11.3, 10)
No response
Issue
Before mongoose 7, If we made a query with an $elemMatch projection (on a single subdocument array - no other fields are mentioned in the projection), the document will return with the requested subdocument array item only (as requested) and without any other document fields.
Example Schema:
Example:
The returned doc will look as follows:
If I try to call doc.permissions it will be undefined
After v7, when using an $elemMatch projection it seems as if the document returns with all the default fields so that when eventually we call a save() action on the document, it saves the document and overrides any field that has a default value
Example:
The returned doc will look as follows:
The addresses subDocument array returns with the default schema values even though it may have multiple items in the array. The same goes for the permissions object which may have an "add" value of true.
If I modify the doc (the specific subdocument array item requested in the projection) and then call save(), the document saves all fields with their default values. It overwrites the current values with the defaults event though the fields were not requested in the projection.
Is this the proper behavior? is there any way to overcome this issue and not get the default values if they are not requested in the projection?
Thanks
The text was updated successfully, but these errors were encountered: