Push object into array if exists otherwise set object in MongoDB - arrays

This is the document I currently have:
{
"_id": "",
"title": "My Watchlist",
"series": [{
"seriesId": 1,
"following": true,
"seasons": []
}, {
"seriesId": 1,
"following": false,
"seasons": []
}]
}
As you can see there are currently 2 objects with the seriesId 1, but with a different following boolean.
If the query matches with _id it should push the new object into series, if within the "series"-array an object with the same "seriesId" already exists it should change the fields within that object, instead of adding a new object.
I currently have the following query:
users.update(
{"_id": req.body.userId},
{
"$push": {
"series": {"seriesId": req.body.seriesId, "following": req.body.following}
}
}, (err, data) => {
if (err)
next(err);
});
If I use $set it does not add the object if it didn't originaly exist yet, and as far as I know you cannot both use $push and $set?
Can this be fixed in any way or do I have to rethink my Schema?

You can use two update query :
if _id is found and seriesId is not in the array, add the new item to the array :
db.series.update({
"_id": req.body.userId,
"series": {
"$not": {
"$elemMatch": {
"seriesId": req.body.seriesId
}
}
}
}, {
$addToSet: {
series: {
"seriesId": req.body.seriesId,
"following": req.body.following,
"seasons": []
}
}
}, { multi: true });
if _id is found and seriesId is found in the array, update the array item :
db.series.update({
"_id": req.body.userId,
"series.seriesId": req.body.seriesId
}, {
$set: {
"series.$.following": req.body.following
}
}, { multi: true });

Related

Add new column to multi nested array in mongo db

"ParentType": {
"Food": [
{
"Name": "Burger",
"FoodId": "5e3abe145c1bfb31b4e335de",
"Price": 0,
"Quantity": 1,
"SubCategory": 0
}
],
"Inventory": [
{
"Name": "Small Popcorn",
"InventoryId": "5e3a64245c1bfb31b4e335b7",
"Price": 0,
"Quantity": 1,
"SubCategory": 0
}
]
}
I need to add UOM as new column only for Inventory array.I have used aggregate as below but collection is not getting updated.Pls help me with adding this new Column in mongodb
db.Concession.aggregate([
{
$addFields: { ParentType.Inventory.UOM: "null" }
}
])
add UOM to all eliment in Inventory
db.collection.update(
{},
{ $set: { 'ParentType.Inventory.$[].UOM':''} }
)
Option 1: ( Update/$set 3.6+ )
db.collection.update({},
{
$set: {
"ParentType.Inventory.$[].UOM": null
}
},
{
multi: true
})
Explained:
Use update() operation with positional operator $[] ( mongoDB 3.6+ )
Use option multi to update all documents in collection matching the filter criteria
Playground
Option 2: ( Update/aggregation 4.2+)
db.collection.update({},
[
{
$addFields: {
"ParentType.Inventory": {
"$map": {
input: "$ParentType.Inventory",
as: "i",
in: {
"$mergeObjects": [
"$$i",
{
UOM: null
}
]
}
}
}
}
}
],
{
multi: true
})
Explained:
Use update with aggregation pipeline ( mongoDB 4.2+ )
In aggregation use the addFileds with mergeObjects so you add the new fields to the array element.
Use multi:true to affect all documents in collection matching the filter criteria
Playground 2

adding data to nested document : mongodb

I want to add new record within 'music'. My document looks similar to
{
"username": "erkin",
"email": "erkin-07#hotmail.com",
"password": "b",
"playlists": [
{
"_id": 58,
"name": "asdsa",
"date": "09-01-15",
"musics": {
"song-one":{
"song-one": "INNA - Cola Song (feat. J Balvin)",
"duration": "3.00"
},
"song-two":{
"song-two": "blabla",
"duration": "3.00"
}
}
}
]
}
After navigating to "music" and then using $set to add/update multiple records at once. But new records is getting added in lexicographical manner(behaviour of $set).
I'm using query similar to (so in my case song-four is coming before song-three) :
db.getCollection('myCollection').update({username:"erkin"},{$set:{"playlists.musics.song-three":{...},"playlists.musics.song-four":{...}}})
Is there any way I can add new records to that location in a way my $set query is arranged ?
As playlists is an array:
Option 1: Update the first document in playlists.
Specify the index: 0 for the first document in playlists.
playlists.0.musics.song-three
db.collection.update({
username: "erkin"
},
{
$set: {
"playlists.0.musics.song-three": {
"song-three": "song-three"
},
"playlists.0.musics.song-four": {
"song-four": "song-four"
}
}
})
Sample Demo on Mongo Playground (Option 1)
Option 2: Update all documents in playlists.
With $[] all positional operator.
playlists.$[].musics.song-three
db.collection.update({
username: "erkin"
},
{
$set: {
"playlists.$[].musics.song-three": {
"song-three": "song-three"
},
"playlists.$[].musics.song-four": {
"song-four": "song-four"
}
}
})
Sample Demo on Mongo Playground (Option 2)
Option 3: Update specified document in playlists.
With $[<identifier>] filtered positional operator.
playlists.$[playlist].musics.song-three
db.collection.update({
username: "erkin"
},
{
$set: {
"playlists.$[playlist].musics.song-three": {
"song-three": "song-three"
},
"playlists.$[playlist].musics.song-four": {
"song-four": "song-four"
}
}
},
{
arrayFilters: [
{
"playlist._id": 58
}
]
})
Sample Demo on Mongo Playground (Option 3)

How can I update a multi level nested array in MongoDB?

How can I update a record in a document with multiple levels of array nesting?
My document structure is the following:
{
"_id": "5bfa09f0a0441f38d45dcc9c",
"nombre": "PROYECTO MAIN",
"area": "Sistemas",
"fecha": "27/01/2018",
"reuniones": [
{
"_id": "5bfa09f0a0441f38d45dcc99",
"objetivo": "Objetivo MODIFICADO",
"fecha": "25/10/2018",
"participantes": [
{
"nomina": 1,
"nombre": "MODIFICADO",
"rol": "rol",
"area": "area",
"firma": "url/imagen.jpg"
},
{
"nomina": 2,
"nombre": "nombre 2",
"rol": "rol",
"area": "area",
"firma": "url/imagen.jpg"
}
]
}
],
"_class": "proyecto"
}
Using the following query, returns me the document mentioned above.
db.proyectos.find({
_id:ObjectId("5bfa09f0a0441f38d45dcc9c"),
"reuniones._id":ObjectId("5bfa09f0a0441f38d45dcc99"),
"reuniones.participantes.nomina":2
})
I want to update the firma field of participant with nomina 2.
Since Mongo 3.6, you can update multi-nested arrays by combining the following operators:
$set (to update a specific field)
$[] (to match any item in an array)
$[<identifier>] (to match specific items in an array)
Example
Here's how you can update a specific proyectos document that has a reuniones array that has a participantes array that has an object with the field nomina equal to 2:
// update a specific proyectos document
// that has a field "reuniones" which is an array
// in which each item is an object with a field "participantes" that is an array
// in which each item is an object that has a field "nomina" equal to 2
db.proyectos.update({
_id: ObjectId("5bfa09f0a0441f38d45dcc9c"),
}, {
$set: {
"reuniones.$[].participantes.$[j].firma": <your update>
},
}, {
arrayFilters: [
{
"j.nomina": 2
}
]
})
If you wanted to limit your query to a specific reunion, you can do:
db.proyectos.update({
_id: ObjectId("5bfa09f0a0441f38d45dcc9c"),
}, {
$set: {
"reuniones.$[i].participantes.$[j].firma": <your update>
},
}, {
arrayFilters: [
{
"i._id": ObjectId("5bfa09f0a0441f38d45dcc99")
},
{
"j.nomina": 2
}
]
})
To update all proyectos satisfying the above condition, just omit the _id query:
// update all proyectos
// that have a field "reuniones" which is an array
// in which each item is an object with a field "participantes" that is an array
// in which each item is an object that has a field "nomina" equal to 2
db.proyectos.update({}, {
$set: {
"reuniones.$[].participantes.$[j].firma": <your update>
},
}, {
arrayFilters: [
{
"j.nomina": 2
}
]
})

Only one element returned in array

I am trying to find elements from my MongoDB database with meteor.
I managed to filter and go through the structure of my array, but the result is a single element, and not all the elements matching the criteria.
Query :
var json = Tests1VerlIR.find({}, {fields: {entries: {$elemMatch: {'payload.id': {$eq: this.params._id}} } } }).fetch();
this.response.setHeader('Content-Type', 'application/json');
this.response.end(JSON.stringify(json));
Data Structure :
{"entries":
[{"method":"POST",
"source":"ex",
"path":"/ex",
"time":1464615406900,
"payload":
{"slot_frame_number":"4",
"slot_HTUTemp":"2306",
"data":"0400f008561655270209a314",
"slot_BMEPres":"10069",
"slot_HTUHumi":"5283",
"slot_BMETemp":"2288",
"time":"1464615404",
"device":"79",
"slot_BMEHumi":"5718",
"signal":"7.22",
"id":"2"},
"_id":"574c41ee578d01af3664cbaf"},
{"method":"POST",
"source":"ex",
"path":"/ex",
"time":1464615406900,
"payload":
{"slot_frame_number":"4",
"slot_HTUTemp":"2306",
"data":"0400f008561655270209a314",
"slot_BMEPres":"10069",
"slot_HTUHumi":"5283",
"slot_BMETemp":"2288",
"time":"1464615404",
"device":"79",
"slot_BMEHumi":"5718",
"signal":"7.22",
"id":"2"},
"_id":"574c41ee578d01af3664cbaf"}, {...}]}
Response :
[
{
"_id":
{
"_str": "576155d7a605348159cd1f1a"
},
"entries":
[
{
"method": "POST",
"source": "ex",
"path": "/ex",
"time": 1464615406900,
"payload":
{
"slot_frame_number":"4",
"slot_HTUTemp":"2306",
"data":"0400f008561655270209a314",
"slot_BMEPres":"10069",
"slot_HTUHumi":"5283",
"slot_BMETemp":"2288",
"time":"1464615404",
"device":"79",
"slot_BMEHumi":"5718",
"signal":"7.22",
"id":"2"
},
"_id": "574c41ee578d01af3664cbaf"
}
]
}
]
You cannot return multiple elements of an array matching your criteria in any form of a basic .find() query. To match more than one element you need to use the .aggregate() method instead.
refer this link.
Tests1VerlIR.aggregate([
{ "$match": { "entries.payload.id": "2" } },
// Unwind the array to denormalize
{ "$unwind": "$entries" },
// Match specific array elements
{ "$match": { "entries.payload.id": "2" } },
// Group back to array form
{ "$group": {
"_id": "$_id",
"entries": { "$push": "$entries" }
}}
])
Solution :
var json = Tests1VerlIR.aggregate({"$unwind": "$entries"}, {$match: {'entries.payload.id': this.params._id} });

Insert object into MongoDB array element

I have to insert an object in every array in the MongoDB document. The items array displayed below has itemList; in every itemList I have to insert a itemSpec. The desired document shape before and after the process is shown below:
Before process
{
"items": [
{
"itemList":{
"rejected": true
},
"fProcBy": "automatic"
},
{
"itemList":{
"rejected": true
},
"fProcBy": "automatic"
}
]
}
After process:
{
"items": [
{
"itemList":{
"rejected": true
},
"itemSpec":{
"approved": true
},
"fProcBy": "automatic"
},
{
"itemList":{
"rejected": true
},
"itemSpec":{
"approved": true
},
"fProcBy": "automatic"
}
]
}
So in each element of the items array there has to be inserted a new object property itemSpec.
I am not aware of a solution which does it in a single run, but with the following simple script the maximal number of iterations is equal to the maximal number of array elements you have in a single documents, which is probably not so high (but it's just a guess as I have no further information about your data):
var q = { "items.itemSpec": null };
var u = {
$set: {
"items.$.itemSpec" : {
"approved": true
}
}
};
var yourColl = db.getCollection('fooBar');
while (yourColl.find(q).count() > 0) {
yourColl.update(q, u, { multi: true });
}
Use $push operator to update array in a document.
Try This:
db.test.update({"_id":"yourUniqueId"},
{$push: { "items":{ "itemSpec":{"approved": true}}}});

Resources