Mongoose $push replacing whole array with a new array of one element - arrays

As per the title, I have an attribute books defined as
books: [{
ref: 'Book',
type: Schema.Types.ObjectId,
}]
and I'm trying to update it with
await MyModel.updateOne({ id: myId }, { $push: { books: book._id } })
but every time the above update is called, the whole array is replaced with an array of one element (with "element" being the last pushed one). Hence I end up having something like
"books": [
{
"$oid": "5b47aa8ae2d927da983733cb"
}
]
with always just one element.
This is happening running on
node 8.10
mongoose 5.14
What am I doing wrong?

You can try with update
MyModel.update({ id: myId }, { $push: { books: book._id } })

Related

mongo update : upsert an element in array

I would like to upsert an element in an array, based on doc _id and element _id. Currently it works only if the element is allready in the array (update works, insert not).
So, these collection:
[{
"_id": "5a65fcf363e2a32531ed9f9b",
"ressources": [
{
"_id": "5a65fd0363e2a32531ed9f9c"
}
]
}]
Receiving this request:
query = { _id: '5a65fcf363e2a32531ed9f9b', 'ressources._id': '5a65fd0363e2a32531ed9f9c' };
update = { '$set': { 'ressources.$': { '_id': '5a65fd0363e2a32531ed9f9c', qt: '153', unit: 'kg' } } };
options = {upsert:true};
collection.update(query,update,options);
Will give this ok result:
[{
"_id": "5a65fcf363e2a32531ed9f9b",
"ressources": [
{
"_id": "5a65fd0363e2a32531ed9f9c",
"qt": 153,
"unit": "kg"
}
]
}]
How to make the same request work with these initial collections:
[{
"_id": "5a65fcf363e2a32531ed9f9b"
}]
OR
[{
"_id": "5a65fcf363e2a32531ed9f9b",
"ressources": []
}]
How to make the upsert work?
Does upsert works with entire document only?
Currently, I face this error:
The positional operator did not find the match needed from the query.
Thanks
I also tried to figure out how to do it. I found only one way:
fetch model by id
update array manually (via javascript)
save the model
Sad to know that in 2018 you still have to do the stuff like it.
UPDATE:
This will update particular element in viewData array
db.collection.update({
"_id": args._id,
"viewData._id": widgetId
},
{
$set: {
"viewData.$.widgetData": widgetDoc.widgetData
}
})
$push command will add new items

MongoDB query into an array of arrays

I have the following schema into my DB:
{ name: 'AFG',
documents: [
{ name: 'doc1',
templates: [
{ name: 'templ1',
lastModified: <Date>},
...]
},
...]
}
I am trying to make a query to look for the template with name 'templ1'. If I find it, I have to compare the last modification date with another and update the value. If I don't find it, I have to push it into the array.
I have tried to do it with the following query:
Country.findOne({name : countrySelected, documents : {$elemMatch: {name: documentSelected}}, 'documents.templates' : {$elemMatch: {name: templateSelected}}}, function(err, templateFound){...}
But I get the following error:
MongoError: cannot use the part (documents of documents.templates) to traverse the element ({documents: [ { name: "Passport", templates: [] }, { name: "Visa", templates: [] }, { name: "Driver's Licence", templates: [] }, { name: "Id Card", templates: [] }, { name: "Other", templates: [] } ]})
Someone knows how can I do it? Thanks!!
You have to use the $in operator to search in arrays.
Country.findOne({
name : countrySelected,
documents: {
$elemMatch: {
$and: [{
name: {
$in: documentSelected //<-- must be array like ['doc1']
}
}, {
'templates.name': {
$in: documentSelected //<-- must be array like ['tmpl1']
}
}]
}
}
}, function(err, templateFound){
//do something with err and templateFound
});
To update the LastModified date you need to update it in the callback function and save the document.
See this answer for nested array updates

Filter array elements by nested array of objects

First, let me say that this question is slightly different to others I've seen regarding nested arrays in mongo.
Most of them ask how to get a specific element in a nested array. I want to get all elements of an array that has itself another array containing a given value.
I have documents, that look like this:
{
_id: something,
value: something,
items: [{
name: someItem,
price: somePrice,
stores:[
{name: storeName, url: someUrl },
{name: anotherStoreName, url: someOtherUrl}
]
},
{
name: someOtherItem,
price: someOtherPrice,
stores:[
{name: storeName, url: someUrl },
{name: yetAnotherStoreName, url: someOtherUrl}
]
}]
}
What I want is to get only the items elements that have a given store in the stores array.
This is, if I ask storeName, I should get both items in the example. But if I ask for anotherStoreName, I should only get the first element of the array items.
Searching for similar questions and trying the solutions I can only get the first matching element of items, but I want all the matching elements.
I'd appreciate any help.
You should use mongo aggregation to get result in following way.
First use $unwind to separate array of items and then add match condition.
db.collection.aggregate([{
$unwind: "$items"
}, {
$match: {
"items.stores.name": "storeName"
}
}, {
$project: {
_id: 0,
items: "$items" //you can add multiple fields here
}
}]);
You should use the aggregation framework to unwind elements on the items array and then treat them as individual documents.
db.data.aggregate([
{
$unwind: "$items"
},
{
$project: {
_id: 0,
items: "$items"
}
},
{
$match: {
"items.stores.name": "yetAnotherStoreName"
}
}
]);
$project just let you work with the important part of the document.
This works on the mongoshell be carefully when using your driver.
The result.

Update embedded mongoose document in array

Lets say that I have the following document in the books collection:
{
_id:0 ,
item: "TBD",
stock: 0,
info: { publisher: "1111", pages: 430 },
tags: [ "technology", "computer" ],
ratings: [ { _id:id1, by: "ijk", rating: 4 }, {_id:id2 by: "lmn", rating: 5 } ],
reorder: false
}
I would like to update the value of ratings[k].rating and all I know is the id of the collection and the _id of the objects existing in the array ratings.
The tutorial of mongoDB has the following example that uses the position of the object inside the array but I suppose that if the update can only be done by knowing the position, this means that I firstly have to find the position and then proceed with the update? Can I do the update with only one call and if so how I can do that?
db.books.update(
{ _id: 1 },
{
$inc: { stock: 5 },
$set: {
item: "ABC123",
"info.publisher": "2222",
tags: [ "software" ],
"ratings.1": { by: "xyz", rating: 3 }
}
}
)
Sorry for late answer; I think this is what you want to do with mongoose.
Books.findOneAndUpdate({
_id: 1,
'ratings._id': id1
},
{
$set: {
'ratings.$.rating' : 3
}
}, function(err, book){
// Response
});
Positional operator may help you:
db.books.update(
// find book by `book_id` with `rating_id` specified
{ "_id": book_id, "ratings._id": rating_id },
// set new `value` for that rating
{ $set: { 'ratings.$.rating': value }}
);
$ will save position of matched document.

Filtering an embedded array in MongoDB

I have a Mongodb document that contains an an array that is deeply imbedded inside the document. In one of my action, I would like to return the entire document but filter out the elements of that array that don't match that criteria.
Here is some simplified data:
{
id: 123 ,
vehicles : [
{name: 'Mercedes', listed: true},
{name: 'Nissan', listed: false},
...
]
}
So, in this example I want the entire document but I want the vehicles array to only have objects that have the listed property set to true.
Solutions
Ideally, I'm looking for a solution using mongo's queries (e.g. `$unwind, $elemMatch, etc...) but I'm also using mongoose so solution that uses Mongoose is OK.
You could use aggregation framework like this:
db.test312.aggregate(
{$unwind:"$vehicles"},
{$match:{"vehicles.name":"Nissan"}},
{$group:{_id:"$_id",vehicles:{$push:"$vehicles"}}}
)
You can use $addToSet on the group after unwinding and matching by listed equals true.
Sample shell query:
db.collection.aggregate([
{
$unwind: "$vehicles"
},
{
$match: {
"vehicles.listed": {
$eq: true
}
}
},
{
$group: {
_id: "$id",
vehicles: {
"$addToSet": {
name: "$vehicles.name",
listed: "$vehicles.listed"
}
}
}
},
{
$project: {
_id: 0,
id: "$_id",
vehicles: 1
}
}
]).pretty();

Resources