Updating single object in array in mongoDB - mongodb

My collection is in below format,
{
"_id" : ObjectId("5d01fddd3f21c407582e578d"),
"device_name" : "Test Alarm Device",
"description" : "test",
"parameters" : [
{
"parameter_name" : "CO2",
"status" : "on"
},
{
"parameter_name" : "NO",
"status" : "on"
},
{
"parameter_name" : "O2",
"status" : "on"
}
}
Now I wish to overwrite(update) complete object where "parameter_name" : "O2".
For example, in new collection it will look like...
{
"parameter_name" : "O2",
"status" : "on",
"is_active": true,
"min": 5
}

Use $out to replace existing collection, $addFields to overwrite existing array and $map with $cond to update elements based on some criteria. To build a new object based on existing one you can use $mergeObjects, try:
db.collection.aggregate([
{
$addFields: {
parameters: {
$map: {
input: "$parameters",
in: {
$cond: [
{ "$eq": [ "$$this.parameter_name", "O2" ] },
{ $mergeObjects: [ "$$this", { "is_active": true, "min": 5 } ] },
"$$this"
]
}
}
}
}
},
{ $out: "collection" }
])

This will do, but I don't think it is the best solution:
db.collection.updateOne({
_id: ObjectId("5d01fddd3f21c407582e578d"),
{ $set: { "parameters" : [
{
"parameter_name" : "CO2",
"status" : "on"
},
{
"parameter_name" : "NO",
"status" : "on"
},
{
"parameter_name" : "O2",
"status" : "on",
"is_active": true,
"min": 5
}
]
}
}
)

Related

How to copy a value between fields within each object in an embedded array of objects in MongoDB?

In my 'assemblies' collection, each document contains an embedded array of objects called 'partlist':
{
"_id" : ObjectId("0001"),
"pn" : "01",
"title" : "MyAssembly",
"partlist" : [
{
"id" : "",
"pn" : "1234",
"desc" : "myPart1",
},
{
"id" : "",
"pn" : "5678",
"desc" : "myPart2",
}]
}
Within each object, I need to copy the value from 'partlist.pn' into 'partlist.id'. I used:
db.assemblies.aggregate([{$set:{"partlist.id":"$partlist.pn"}}])
hoping to achieve this:
{
"_id" : ObjectId("0001"),
"pn" : "01",
"title" : "MyAssembly",
"partlist" : [
{
"id" : "1234",
"pn" : "1234",
"desc" : "myPart1",
},
{
"id" : "5678",
"pn" : "5678",
"desc" : "myPart2",
}]
}
Instead it returned to 'id' an array of ALL the 'pn' values in 'partlist':
{
"_id" : ObjectId("0001"),
"pn" : "01",
"title" : "MyAssembly",
"partlist" : [
{
"id" : [
"1234",
"5678"
],
"pn" : "1234",
"desc" : "myPart1",
},
{
"id" : [
"1234",
"5678"
],
"pn" : "5678",
"desc" : "myPart2",
}]
}
What is the correct syntax for copying the one value within each object?
What you can do is, you can use $map to modify each elements and mearg the id with the help of $mergeObject
db.collection.aggregate([
{
$addFields: {
"partlist": {
$map: {
input: "$partlist",
in: {
"$mergeObjects": [
"$$this",
{
id: "$$this.pn"
}
]
}
}
}
}
}
])
Working Mongo playground
If the pn does not exist it retrieves initial object.
db.collection.aggregate([
{
$addFields: {
partlist: {
$map: {
input: "$partlist",
as: "p",
in: {
$cond: [
"$$p.pn",
{
$mergeObjects: [
"$$p",
{
"id": "$$p.pn"
}
]
},
"$$p"
]
}
}
}
}
}
])
Playground

how to sort an array in a nested array which is located under an object in mongodb

I have a collection data like below.
{
"name": "Devices",
"exten": {
"parameters": [{
"name": "Date",
"value": ["5","2"]
}, {
"name": "Time",
"value": ["2"]
}, {
"name": "Season",
"value": ["6"]
}
]
}
}
I want to take all data which is name "Devices" and sort by first index of "Value" which is parameter name is "Date"
ex: mongo will get
name = "devices"
exten.parameters.name = "Date"
will sort it by
exten.parameters.value[0]
in this example it will be sorted by "5".
below query returns 0 record.
db.brand.aggregate(
{ $match: {
"name" : "Devices"
}},
{ $unwind: "$exten.parameters" },
{ $match: {
'exten.parameters.name': 'Date'
}},
{ $sort: {
'exten.parameters.value': -1
}}
)
The following query can get us the expected output:
db.collection.aggregate([
{
$match:{
"name":"Devices"
}
},
{
$unwind:"$exten.parameters"
},
{
$match:{
"exten.parameters.name":"Date"
}
},
{
$project:{
"name":1,
"exten":1,
"firstParam":{
$arrayElemAt:["$exten.parameters.value",0]
}
}
},
{
$sort:{
"firstParam":1
}
},
{
$project:{
"firstParam":0
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5da02fb86472ba670fd8c159"),
"name" : "Devices",
"exten" : {
"parameters" : [
{
"name" : "Date",
"value" : [
"5",
"2"
]
},
{
"name" : "Date",
"value" : [
"2",
"7"
]
},
{
"name" : "Time",
"value" : [
"2"
]
},
{
"name" : "Season",
"value" : [
"6"
]
}
]
}
}
Output:
{
"_id" : ObjectId("5da02fb86472ba670fd8c159"),
"name" : "Devices",
"exten" : {
"parameters" : {
"name" : "Date",
"value" : [
"2",
"7"
]
}
}
}
{
"_id" : ObjectId("5da02fb86472ba670fd8c159"),
"name" : "Devices",
"exten" : {
"parameters" : {
"name" : "Date",
"value" : [
"5",
"2"
]
}
}
}

Extract a value from an object in array matching a condition

I need help to make an aggregation pipeline in mongodb.
The mongodb version i'm using is 4.
The documents stored in database looks like this:
[{
_id : "xxxxxx",
names : [
{ "lang" : "EN", value : "foo" },
{ "lang" : "IT", value : "bar" },
{ "lang" : "NOLANG", value : "baz" }
],
some : "value"
},{
_id : "yyyyyy",
names : [
{ "lang" : "FR", value : "quux" },
{ "lang" : "IT", value : "quuux" },
{ "lang" : "NOLANG", value : "quuuux" }
],
some : "value"
}]
I need to add a field with aggregation that contains the value of a certain language (for this example i'll take "EN"), if no element with requested language is found i need to get the "NOLANG" object value.
So, the result of the aggregation must looks like:
[{
_id : "xxxxxx",
name : "foo",
some : "value"
},{
_id : "yyyyyy",
name : "quuuux",
some : "value"
}]
This is the pipeline i wrote:
[
{
$project : {
names : 0,
name: {
$filter: {
input: '$names',
as: 'name',
cond: {
$switch: {
$branches: [
{
case : {
$eq : ["$$name.lang", "EN"]
},
then : "$$name.value"
} ,{
case : {
$eq : ["$$name.lang", "NOLANG"]
},
then : "$$name.value"
}
],
default : ''
}
}
}
}
}
}
]
It gives me the error: Expected "[" or AggregationStage but "{" found.
What i'm doing wrong? Someone can help me please?
Thanks
You can use below aggregation
db.collection.aggregate([
{ "$project": {
"some": 1,
"name": {
"$arrayElemAt": [
"$names.value",
{
"$cond": [
{
"$ne": [
{ "$indexOfArray": ["$names.lang", "EN"] },
-1
]
},
{ "$indexOfArray": ["$names.lang", "EN"] },
{ "$indexOfArray": ["$names.lang", "NOLANG"] }
]
}
]
}
}}
])
Output
[
{
"_id": "xxxxxx",
"name": "baz",
"some": "value"
},
{
"_id": "yyyyyy",
"name": "quuuux",
"some": "value"
}
]

Issue retrieving subdocuments from MongoDB

I have the following dataset:
{
"_id" : ObjectId("59668a22734d1d48cf34de08"),
"name" : "Nobody Cares",
"menus" : [
{
"_id" : "menu_123",
"name" : "Weekend Menu",
"description" : "A menu for the weekend",
"groups" : [
{
"name" : "Spirits",
"has_mixers" : true,
"sizes" : [
"Single",
"Double"
],
"categories" : [
{
"name" : "Vodka",
"description" : "Maybe not necessary?",
"drinks" : [
{
"_id" : "drink_123",
"name" : "Absolut",
"description" : "Fancy ass vodka",
"sizes" : [
{
"_id" : "size_123",
"size" : "Single",
"price" : 300
}
]
}
]
}
]
}
],
"mixers" : [
{
"_id" : "mixer_1",
"name" : "Coca Cola",
"price" : 150
},
{
"_id" : "mixer_2",
"name" : "Lemonade",
"price" : 120
}
]
}
]
}
And I'm attempting to retrieve a single drink from that dataset, I'm using the following aggregate query:
db.getCollection('places').aggregate([
{ $match : {"menus.groups.categories.drinks._id" : "drink_123"} },
{ $unwind: "$menus" },
{ $project: { "_id": 1, "menus": { "groups": { "categories": { "drinks": { "name": 1 } } } } } }
])
However, it's returning the full structure of the dataset along with the correct data.
So instead of:
{
"_id": "drink_123",
"name": "Absolut"
}
I get:
{
"_id": ObjectId("59668a22734d1d48cf34de08"),
"menus": {
"groups": {
"categories": {
"drinks": { "name": "Absolut" }
}
}
}
}
For example. Any ideas how to just retrieve the subdocument?
If you need to retain the deeply nested model then this call will produce the desired output:
db.getCollection('places').aggregate([
{ $match : {"menus.groups.categories.drinks._id" : "drink_123"} },
{ $project: {"_id": '$menus.groups.categories.drinks._id', name: '$menus.groups.categories.drinks.name'}},
{ $unwind: "$name" },
{ $unwind: "$name" },
{ $unwind: "$name" },
{ $unwind: "$name" },
{ $unwind: "$_id" },
{ $unwind: "$_id" },
{ $unwind: "$_id" },
{ $unwind: "$_id" }
])
The numerous unwinds are the result of the deep nesting of the drinks subdocuments.
Though, FWIW, this sort of query does perhaps suggest that the model isn't 'read friendly'.

How sort according to given order and calculation 1st document with second one in MongoDB?

I want to get which members not present when state change and which member is present.
My given array for state order like:
[{order:1,text:'CS'}, {order:2,text:'IP'}, {order:3,text:'AC'}]
so I want to sort according to this array want to perform some operation with each pare of documents
My documents like:
{
"count" : 2,
"state" : "CS",
"members" : [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
},
{
"email" : "bactv.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
]
},
{
"count" : 1,
"state" : "AC",
"members" : [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
}
]
},
{
"count" : 3,
"state" : "IP",
"members" : [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
},
{
"email" : "bactv.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
},
{
"email" : "abc.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
]
}
So I want to know which members are not present from one state to another state and which members are present .
In my exam 1st state to 2nd state means (CS - IP): present 2 members and not present 1 member
{
"state": "CS_IP",
"present": [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
},
{
"email" : "bactv.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
],
"notPresent": [
{
"email" : "abc.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
]
}
and 2nd state to 3rd state means (IP - AC): present 1 members and not present 2 member
{"state": "IP_AC",
"present": [
{
"email" : "builuu1998#gmail.com",
"date" : ISODate("2016-12-24T03:39:05.720Z")
}
],
"notPresent": [
{
"email" : "bactv.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
},
{
"email" : "abc.hn#gmail.com",
"date" : ISODate("2016-12-25T02:32:48.698Z")
}
]}
How can I achieve this using aggregate query because I need some aggregate operation after this stage complete
You can perform one aggregation request per "couple" of state like this for ["IP", "AC"] :
db.exams.aggregate([{
$match: {
"state": {
$in: ["IP", "AC"]
}
}
}, {
"$group": {
"_id": 1,
"group1": { "$first": "$members" },
"group2": { "$last": "$members" }
}
}, {
"$project": {
"present": { "$setIntersection": ["$group1", "$group2"] },
"notPresent": {
$setUnion: [
{ "$setDifference": ["$group1", "$group2"] },
{ "$setDifference": ["$group2", "$group1"] }
]
}
}
}])
Note that this will only work for 2 elements to match (here ["IP", "AC"]) since we have to create two new fields group1 & group2 to $setIntersection and $setDifference (as there is no $group with $setIntersection and $setDifference)
The query above will give :
{
"_id": 1,
"present": [
{ "email": "builuu1998#gmail.com", "date": ISODate("2016-12-24T03:39:05.720Z") }
],
"notPresent": [
{ "email": "abc.hn#gmail.com", "date": ISODate("2016-12-25T02:32:48.698Z") },
{ "email": "bactv.hn#gmail.com", "date": ISODate("2016-12-25T02:32:48.698Z") }
]
}