Mongodb $set not working - node.js

I'm trying to update the nested document. The query is returning the correct document, but the property isn't being updated.
Model:
{
_id: '560b072434b72aa4050fff9f',
trips: [
{
tripId: '561581ef9387780e76469e96',
startDate: "2015-11-17T06:00:00.000Z",
endDate: "2015-11-18T06:00:00.000Z"
},{
tripId: '5617d1bb1d42c4da90d3bdea',
startDate: "2015-10-17T06:00:00.000Z",
endDate: "2015-10-18T06:00:00.000Z"
}
],
}
Query:
UserData.update(
{ '_id': req.query._id, 'trips.tripId': req.query.tripId },
{ '$set': { 'trips.$.startDate' : req.query.newStartDate,
'trips.$.endDate' : req.query.newEndDate} },
{ 'multi': true },
function(e, doc){
console.log(doc);
}
);
Schema:
var userDataSchema = {
name: String,
trips: Array
};

#BlakesSeven led me to the answer. I added:
var ObjectId = require('mongodb').ObjectID;
before my app.route in server.js, installed mongodb using npm, then changed
{ '_id': req.query._id, 'trips.tripId': req.query.tripId },
to
{ '_id': ObjectId(req.query._id), 'trips.tripId': ObjectId(req.query.tripId) },.
Everything updates as it should now. Thanks Blakes Seven!

Related

mongoose sub document array change value

I got a mongoose query where I want to change a comment. I receive the commentid from a react app. But it doesn't work, what could be the problem?
An example comment array follows
"comments": [
{
"createdAt": "2018-11-22T08:28:36.881Z",
"_id": "5bf668b4001de72dc089c849", // commentid
"comment": "111111111111",
"username": "kohahn21"
},
....
]
What I have tried:
edit = await Post.update(
{ 'comments._id' : commentid },
{ '$set' : { 'comments.$.comment' : comment } },
{ new: true }
);
ctx.body = edit;
ctx.body
{
"n": 1,
"nModified": 1,
"ok": 1
}
Post Schema
const Comment = new Schema({
createdAt: { type: Date, default: Date.now },
username: String,
comment: String
});
const Post = new Schema({
username: String,
comments: {
type: [Comment],
default: []
},
});
module.exports = mongoose.model('Post',Post);
I would like to receive comments, which is a modified comment layout. What should I do?
Your syntax looks correct. But instead of 'items' it should be 'comments'.
Try Post.update( { 'comments._id' : commentid }, {'$set' : { 'comments.$.comment' : comment } });
btw the new flag is only available for find-and operators.

Decimal support Mongodb

Trying to use decimal128 datatype in my nodejs application.
mongo version "3.4.2",
os: { type: "Darwin", name: "Mac OS X", architecture: "x86_64", version: "16.7.0"
nodejs version 6.11.2
mongoose version 4.11.13
mongoose using native mongodb driver version 2.2.31
Mongodb config:
storage:
engine: wiredTiger
dbPath: "/Users/backend/Desktop/mongo/data"
What am i doing?
I've got mongoose subdocument schema
const Premises = mongoose.Schema({
floor : { type: mongoose.Schema.Types.Decimal128, required: true },
deleted : { type: Boolean, default: false },
created_at : { type: Date, default: Date.now },
updated_at : { type: Date, default: Date.now }
});
This schema is a subdocument of document with schema below:
...
premises : [ Premises ],
...
To add new subdocuments im using update method:
var queryFilter = {
'deleted' : false,
'buildings._id' : params.building_id
};
var premise = {
'_id': mongoose.Types.ObjectId(),
'floor': params['floor']
};
Block.update(queryFilter, { '$addToSet': { 'buildings.$.premises': premise } }, { safe: true }, function (error, result) {
result['_id'] = premise['_id'];
callback(error, result || null);
return;
});
Also i used code below:
var queryFilter = {
'deleted' : false,
'buildings._id' : params.building_id
};
var premise = {
'_id': mongoose.Types.ObjectId(),
'floor': mongoose.Types.Decimal128.fromString(params['floor'])
};
Block.update(queryFilter, { '$addToSet': { 'buildings.$.premises': premise } }, { safe: true }, function (error, result) {
result['_id'] = premise['_id'];
callback(error, result || null);
return;
});
But get the same error in both situations:
{"errors":{"name":"MongoError","message":"$numberDecimal is not valid
for
storage.","driver":true,"index":0,"code":52,"errmsg":"$numberDecimal
is not valid for
storage."},"data":{"ok":0,"n":0,"nModified":0,"_id":"59ce4e8cecba947a9a342f37"}}
I dont wanna use some workaround like
mongoose-double
to support negative numbers in my collections.
Much thanx for ur answers and solutions.
I faced with the same issue "$numberDecimal is not valid for storage" when I tried to add subdocument into array.
I tend to think this happens because of
Decimal128.prototype.toJSON = function() {
return { "$numberDecimal": this.toString() };
}
from http://mongodb.github.io/node-mongodb-native/2.2/api/node_modules_bson_lib_bson_decimal128.js.html
Maybe that is not the best solution but workaround below helped me:
mongoose.Types.Decimal128.prototype.toJSON = mongoose.Types.Decimal128.prototype.toString;
For adding item to an existing array I used:
entity.subentities.addToSet(subentity);
const updatedEntity = await entity.save();

Mongoose populate returning empty array

I am trying to use mongoose populate function but in response I am getting empty array, I have seen multiple posts regarding this
var MerchantSchema = new mongoose.Schema({
packages: [{ $type: mongoose.Schema.Types.ObjectId, ref: 'Package'}]
},
{
typeKey: '$type',
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at'}
});
module.exports = mongoose.model('Merchant', MerchantSchema);
This is my schema definition for the base model.
var mongoose = require('mongoose');
var PackageSchema = new mongoose.Schema({
merchant_id: { type: mongoose.Schema.Types.ObjectId, ref: 'Merchant' },
name: String,
original_price: Number,
discounted_price: Number
});
module.exports = mongoose.model('Package', PackageSchema);
And this is the model I am referring to. The data inside the Package model and Merchant model is being saved just fine.
Merchant document
Package document
But if I query using populate function I am being returned an empty string
Merchant
.findById( req.params.id, 'packages')
.populate('packages')
.exec(function (err, merchant) {
if (err)
next(err);
else
res.status(200).json(merchant);
});
Output:
{
"_id": "579b3b2dc2e8d61c0ecd2731",
"packages": []
}
Can anyone help me
Update:
Ok something really odd is happening. If I try to populate the Package document with merchant_id it is working but not the other way around.
Package
.find()
.populate('merchant_id')
.exec(function (err, packages) {
if(err)
next(err);
else
res.status(200).json(packages);
});
Output:
[
{
"_id": "579b3b51c2e8d61c0ecd2732",
"name": "Hair + Nails",
"original_price": 600,
"discounted_price": 400,
"merchant_id": {
"_id": "579b3b2dc2e8d61c0ecd2731",
"updated_at": "2016-07-29T11:17:37.474Z",
"created_at": "2016-07-29T11:17:01.216Z",
"name": "VLCC",
"logo_image": "http://vlccwellness.com/India/wp-content/uploads/2015/09/logo1.png?",
"cover_image": "http://image3.mouthshut.com/images/imagesp/925053993s.jpg?",
"__v": 1,
"tags": [],
"packages": [
"579b3b51c2e8d61c0ecd2732"
],
"work_hours": {
"opening_time": 1000,
"closing_time": 2100,
"holiday": "Sunday"
},
"information": {
"description": "Lorem Ipsum",
"gender": "Men",
"services": [
"Hair"
],
Use type insted of $type in MerchantSchema.
var MerchantSchema = new mongoose.Schema({
packages: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Package'}]
},
{
typeKey: '$type',
timestamps: { createdAt: 'created_at', updatedAt: 'updated_at'}
});
module.exports = mongoose.model('Merchant', MerchantSchema);
Verify there is an array of ObjectId against packages in your Merchant document.
Can you try by removing second parameter from the findBbyId:
Merchant
.findById( req.params.id)
.populate('packages')
.exec(function (err, merchant) {
if (err)
next(err);
else
res.status(200).json(merchant);
});
Well because your packages field on your Schema is an array you should populate it as an array as well.
Try
.populate([
{path:'packages', model:Package}
])
wher Package is the instance of your Package Model.
Make sure that packages array in Merchant schema contains ObjectIds of type string, (not number). You can ensure this with something like:
merchant.packages.map(r => { r._id = r._id + ''; });

Update $inc with Mongoose other behavior then MongoDB update

I try to update a document with mongoose and it fails. The query I can successful execute directly in Mongo is like:
db.orders.update(
{
orderId: 1014428,
'delivery.items.id': '5585d77c714a90fe0fc2fcb4'
},
{
$inc: {
"delivery.items.$.quantity" : 1
}
}
)
When I try to run the following update command with mongoose:
this.update(
{
orderId: this.orderId ,
"delivery.items.id": product.id
},
{
$inc: {
"delivery.items.$.quantity" : 1
}
}, function (err, raw) {
if (err) {
console.log(err);
}
console.log('The raw response from Mongo was ', raw);
}
);
I see the following error:
{ [MongoError: cannot use the part (items of delivery.items.id) to traverse the element ({items: [ { quantity: 1, price: 6.9, name: "Manipulationstechniken", brand: null, id: "5585d77c714a90fe0fc2fcb4" } ]})]
name: 'MongoError',
message: 'cannot use the part (items of delivery.items.id) to traverse the element ({items: [ { quantity: 1, price: 6.9, name: "Manipulationstechniken", brand: null, id: "5585d77c714a90fe0fc2fcb4" } ]})',
index: 0,
code: 16837,
errmsg: 'cannot use the part (items of delivery.items.id) to traverse the element ({items: [ { quantity: 1, price: 6.9, name: "Manipulationstechniken", brand: null, id: "5585d77c714a90fe0fc2fcb4" } ]})' }
The raw response from Mongo was { ok: 0, n: 0, nModified: 0 }
I tried so many things. Any advice on this?
As requested the schema:
var Order = new Schema({
orderId: Number,
orderDate: String,
customerName: String,
state: Number,
delivery: {
items: {type: Array, default: []},
state: { type: Number, default: 0 }
}
});
TL;DR: use your model Order instead of an instance this when doing more advanced queries:
Orders.update(
{
orderId: this.orderId ,
"delivery.items.id": product.id
},
{
$inc: {
"delivery.items.$.quantity" : 1
}
}, function (err, raw) {
if (err) {
console.log(err);
}
console.log('The raw response from Mongo was ', raw);
}
);
Explanation:
Mapping differences between Model.update() and Document.update().
The using the model, then Model.update() will be used and
Model.update(conditions, doc, options, callback)
will be mapped to:
db.collection.update(query = conditions, update = doc, options)
When using an instance instead your calling Document.update() and
Document.update(doc, options, callback)
will be mapped to the following:
db.collection.update(query = {_id: _id}, update = doc, options)
Don't know if this helps, but in this question the O.P. had a similar issue with mongoose 3.6.15, and claimed it was solved in 3.8.22
EDIT: In the linked question, the O.P. had the following working on mongodb
db.orchards.update(
({"orchardId": ObjectId("5391c137722b051908000000")},
{"trees" : { $elemMatch: {"name":"apple"}}}),
{ $push: { "trees.$.fruits": ObjectId("54c542c9d900000000001234") }})
But this not working in mongoose:
orchards.update(
({"orchardId": ObjectId.fromString(orchard.id)},
{"trees" : {$elemMatch: {"name": "apple"}}}),
{$push: {"trees.$.fruits": ObjectId("54c542c9d900000000001234") }},function(err, data){ ...
In a comment, he said the issue was solved switching to mongoose 3.8.22

Get result as an array instead of documents in mongodb for an attribute

I have a User collection with schema
{
name: String,
books: [
id: { type: Schema.Types.ObjectId, ref: 'Book' } ,
name: String
]
}
Is it possible to get an array of book ids instead of object?
something like:
["53eb797a63ff0e8229b4aca1", "53eb797a63ff0e8229b4aca2", "53eb797a63ff0e8229b4aca3"]
Or
{ids: ["53eb797a63ff0e8229b4aca1", "53eb797a63ff0e8229b4aca2", "53eb797a63ff0e8229b4aca3"]}
and not
{
_id: ObjectId("53eb79d863ff0e8229b97448"),
books:[
{"id" : ObjectId("53eb797a63ff0e8229b4aca1") },
{ "id" : ObjectId("53eb797a63ff0e8229b4acac") },
{ "id" : ObjectId("53eb797a63ff0e8229b4acad") }
]
}
Currently I am doing
User.findOne({}, {"books.id":1} ,function(err, result){
var bookIds = [];
result.books.forEach(function(book){
bookIds.push(book.id);
});
});
Is there any better way?
It could be easily done with Aggregation Pipeline, using $unwind and $group.
db.users.aggregate({
$unwind: '$books'
}, {
$group: {
_id: 'books',
ids: { $addToSet: '$books.id' }
}
})
the same operation using mongoose Model.aggregate() method:
User.aggregate().unwind('$books').group(
_id: 'books',
ids: { $addToSet: '$books.id' }
}).exec(function(err, res) {
// use res[0].ids
})
Note that books here is not a mongoose document, but a plain js object.
You can also add $match to select some part of users collection to run this aggregation query on.
For example, you may select only one particular user:
User.aggregate().match({
_id: uid
}).unwind('$books').group(
_id: 'books',
ids: { $addToSet: '$books.id' }
}).exec(function(err, res) {
// use res[0].ids
})
But if you're not interested in aggregating books from different users into single array, it's best to do it without using $group and $unwind:
User.aggregate().match({
_id: uid
}).project({
_id: 0,
ids: '$books.id'
}).exec(function(err, users) {
// use users[0].ids
})

Resources