MongoDB - How to update a document with multi-level nesting - mongodb

This is my document:
{
"_id": NumberLong(861),
"Name": "李四",
"Number": "030404",
"Answers": {
"ModuleItems": [
{
"ModuleType": NumberInt(1),
"AnswerListItems": [
{
"ModuleItemId": "111",
"SRUrl": "https://file-public-ashermed.oss-cn-shanghai.aliyuncs.com/XXX.mp3",
"ModuleItemState": NumberInt(0),
"TestType": NumberInt(2),
"AnswerItems": [
{
"_id": "222",
"Answer": "some questions1",
"AnswerState": NumberInt(0)
},
]
},
{
"ModuleItemId": "444",
"SRUrl": "https://file-public-ashermed.oss-cn-shanghai.aliyuncs.com/XXXX.mp3",
"ModuleItemState": NumberInt(0),
"TestType": NumberInt(2),
"AnswerItems": [
{
"_id": "666",
"Answer": "",
"AnswerState": NumberInt(1)
},
]
},
],
"LastModuleItemId": "666"
},
{
"ModuleType": NumberInt(2),
"AnswerListItems": [],
"LastModuleItemId": "2555"
},
],
"TotalScore": "79分"
},
"VisitRecordState": NumberInt(2),
}
My filters:
"_id" : NumberLong(861),Answers.ModuleItems.ModuleType = NumberInt(1), ModuleItemId= 444
I want to modify the SRUrl field of this document's elements which ModuleItemId equals 444.

You need positional operator $[] and arrayFilters to update element in nested arrays.
db.collection.update({
_id: NumberLong(861)
},
{
$set: {
"Answers.ModuleItems.$[mi].AnswerListItems.$[ali].SRUrl": /* New SRUrl */
}
},
{
arrayFilters: [
{
"mi.ModuleType": 1
},
{
"ali.ModuleItemId": "444"
}
]
})
Sample Mongo Playground

Related

how to get data in single array in mongodb?

I want to achieve my documents in a single array
My data is this:
"summary_data" : [
{
"_id" : ObjectId("60418b730ba57044ec0afddd"),
"category" : "Entertainment",
"name" : "Alia bhatt",
"profession" : "acting"
},
{
"_id" : ObjectId("60418bc838497d42a80879e3"),
"category" : "Entertainment",
"name" : "priyanka chopra",
"profession" : "acting"
},
{
"_id" : ObjectId("60418bef38497d42a80879eb"),
"category" : "Entertainment",
"name" : "radhika apte",
"profession" : "acting"
},
]
}
And i want data like this having min 10(limit) category data of each category.How can achieve this:
"summary_data" : [
{
"_id": ObjectId("60418b730ba57044ec0afddd"),
"data": [{
"Entertainment": [{
"_id": ObjectId("60418ce138497d42a8087a03"),
"category": "Entertainment",
"name": "Alia bhatt",
"profession": "acting"
}],
}]
i have tried many queries some of them are:
db.getCollection('experts').aggregate([
{
$match: {
$or: [
{ category: "Sports" },
{ category: "other" },
{ category: "Entertainment" }
]
}
},
{ $limit: 10 },
{
$group:
{
_id: null,
summary_data: {
$push: {
_id: "$_id",
category: "$category",
name: "$name",
profession: "$profession",
}
},
}
},
])
Please help me to build this query. Thanks in advance.

MongoDB Aggregation function

I have the following JSON Documents in Mongo collection named "Movies"
{
"_id": "5ed0c9700b9e8b0e2c542054",
"movie_name": "Jake 123",
"score": 20,
"director": "Jake"
},
{
"_id": "5ed0a9840b9e8b0e2c542053",
"movie_name": "Avatar",
"director": "James Cameroon",
"score": 50,
"boxoffice": [
{
"territory": "US",
"gross": 2000
},
{
"territory": "UK",
"gross": 1000
}
]
},
{
"_id": "5ed0a9630b9e8b0e2c542052",
"movie_name": "Titanic",
"score": 100,
"director": "James Cameroon",
"boxoffice": [
{
"territory": "US",
"gross": 1000
},
{
"territory": "UK",
"gross": 500
}
],
"actors": [
"Kate Winselet",
"Leonardo De Caprio",
"Rajinikanth",
"Kamalhaasan"
]
}
I run the below query which finds the maximum collection of a country of various movies. My intention is to find the maximum collection and the corresponding territory.
db.movies.aggregate([
{$match: {"boxoffice" : { $exists: true, $ne : []}}},
{$project: {
"title":"$movie_name", "max_boxoffice": {$max : "$boxoffice.gross"},
"territory" : "$boxoffice.territory" } }
])
I get the result as follows. How do I get the correct territory that corresponds to the collection?
{
"_id" : ObjectId("5ed0a9630b9e8b0e2c542052"),
"title" : "Titanic",
"max_boxoffice" : 1000,
"territory" : [
"US",
"UK"
]
},
{
"_id" : ObjectId("5ed0a9840b9e8b0e2c542053"),
"title" : "Avatar",
"max_boxoffice" : 2000,
"territory" : [
"US",
"UK"
]
}
Expected output:
Avatar and Titanic has collected more money in US. I wanted territories to display the values of them
{
"_id" : ObjectId("5ed0a9630b9e8b0e2c542052"),
"title" : "Titanic",
"max_boxoffice" : 1000,
"territory" : "US"
},
{
"_id" : ObjectId("5ed0a9840b9e8b0e2c542053"),
"title" : "Avatar",
"max_boxoffice" : 2000,
"territory" : "US"
}
For this specific requirement, you can use $set (aggregation). $set appends new fields to existing documents. and we can include one or more $set stages in an aggregation operation to achieve this like:
db.movies.aggregate([
{
$match: { "boxoffice": { $exists: true, $ne: [] } }
},
{
$set: {
boxoffice: {
$filter: {
input: "$boxoffice",
cond: { $eq: ["$$this.gross", { $max: "$boxoffice.gross" }]}
}
}
}
},
{
$set: {
boxoffice: { $arrayElemAt: ["$boxoffice", 0] }
}
},
{
$project: {
"title": "$movie_name",
"max_boxoffice": "$boxoffice.gross",
"territory": "$boxoffice.territory"
}
}
])
Mongo Playground

Forming an array with aggregation in MongoDB

I have a document in MongoDB 3.4 with the following structure:
{
"_id" : ObjectId("5e3419e468d01013eadb83dc"),
"id_station" : "62",
"fiware_service" : null,
"fiware_servicepath" : null,
"id_fiware_name" : "CE_del_medio",
"attrName" : "15",
"attrType" : "float",
"attrValue" : 0.33,
"id_sensor_station_absolute" : "15_62",
"recvTimeTs" : 1580387045,
"recvTime" : "2020-01-30T12:24:05.00Z",
"id_fiware" : "15",
"sensor_type" : [
{
"name" : "id",
"type" : "String",
"value" : "15"
},
{
"name" : "img",
"type" : "String",
"value" : "assets/img/contrast.png"
},
{
"name" : "manufacturer",
"type" : "String",
"value" : "Hortisis"
},
{
"name" : "medida",
"type" : "String",
"value" : "mS/cm"
},
{
"name" : "name_comun",
"type" : "String",
"value" : "CE del medio"
},
{
"name" : "place",
"type" : "String",
"value" : "interior"
},
{
"name" : "timestamp",
"type" : "DateTime",
"value" : "2020-01-30T12:24:05.00Z"
},
{
"name" : "type",
"type" : "String",
"value" : "fertigation"
}
]
}
I need to convert the sensor_type field to an array with only one object, as follows:
{
"_id":"15_62",
"medidas":[
{
"_id":"5e3419e468d01013eadb83dc",
"marca":"Hortisis",
"modelo":"Estacion",
"fabricante":"Hortisis",
"id_station":"15",
"sensor_type":[
{
"name":"15",
"type":"fertigation",
"place":"interior",
"img":"assets/img/contrast.png",
"name_comun":"Temp. Suelo",
"medida":"mS/cm"
}
],
"attrName":"15",
"attrValue":0.33,
"recvTimeTs":1580387045,
"recvTime":"2020-01-30T12:24:05.00Z",
"id_sensor_station_absolute":"15_62"
}
]
}
As you can really see it is formatting the sensor_type field = name : value.
I'm working with NODEJS and mongoose.
This is my query: (first I search, sort, only show the first value and then with the project I give format, the problem is that I don't know how to tell the project to put that format if I put "sensor_type": "$latest.attributes.name") it only shows the names and I don't know how to put it in the mentioned format.
Datagreenhouse.aggregate([
{ "$match": { "id_sensor_station_absolute": { "$in": array3 } } }, // "id_station": { "$in": id_station },
{ "$sort": { "recvTime": -1 } },
{
"$group": {
"_id": "$id_sensor_station_absolute",
"latest": { "$first": "$$ROOT" },
}
},
{
"$project": {
"_id": 1,
"id_station": "$latest.id_station",
//"id_sensor_station_absolute": "$id_sensor_station_absolute",
"attrName": "$latest.attrName",
"attrValue": "$latest.attrValue",
"recvTimeTs": "$latest.recvTimeTs",
"recvTime": "$latest.recvTime",
"id_sensor_station_absolute": "$latest.id_sensor_station_absolute",
"sensor_type": "$latest.attributes",
"name": { $arrayElemAt: ["$latest.attributes", 0] },
"type": { $arrayElemAt: ["$latest.attributes", 1] },
"place": { $arrayElemAt: ["$latest.attributes", 2] },
"img": { $arrayElemAt: ["$latest.attributes", 1] },
"name_comun": { $arrayElemAt: ["$latest.attributes", 4] },
"medida": { $arrayElemAt: ["$latest.attributes", 3] },
"interfaz": { $arrayElemAt: ["$latest.attributes", 6] },
}
}
], (err, DatagreenhouseRecuperado) => {
if (err) return res.status(500).send({ message: 'Error al realizar la peticion' + err })
if (!DatagreenhouseRecuperado) return res.status(404).send({ message: 'Error el usuario no existe' })
res.status(200).send({ DatagreenhouseRecuperado })
})
Thank you for your help. Best regards.
Since version 3.4.4, MongoDB introduced a magnific operator: $arrayToObject
This operator allows us transmute array key:value pair into object.
Syntax
RAW DATA $map $arrayToObject
sensor_type : [ sensor_type : [ sensor_type : {
{ \ { \
"name" : "manufacturer", ----> k: "manufacturer", --->
"type" : "String", / v: "Hortisis" / "manufacturer" : "Hortisis"
"value" : "Hortisis"
} }
] ] }
db.datagreenhouses.aggregate([
{
"$match": {} // setup your match criteria
},
{
"$sort": {
"recvTime": -1
}
},
{
$group: {
_id: "$id_sensor_station_absolute",
medidas: {
$push: {
_id: "$_id",
"marca": "Hortisis", // don't know where you get this value
"modelo": "Estacion", // don't know where you get this value
"id_station": "$id_station",
"attrName": "$attrName",
"attrValue": "$attrValue",
"recvTimeTs": "$recvTimeTs",
"recvTime": "$recvTime",
"id_sensor_station_absolute": "$id_sensor_station_absolute",
"sensor_type": {
$arrayToObject: {
$map: {
input: "$sensor_type",
in: {
k: "$$this.name",
v: "$$this.value"
}
}
}
}
}
}
}
}
])
MongoPlayground
[
{
"_id": "15_62",
"medidas": [
{
"_id": ObjectId("5e3419e468d01013eadb83dc"),
"attrName": "15",
"attrValue": 0.33,
"id_sensor_station_absolute": "15_62",
"id_station": "62",
"marca": "Hortisis",
"modelo": "Estacion",
"recvTime": "2020-01-30T12:24:05.00Z",
"recvTimeTs": 1.580387045e+09,
"sensor_type": {
"id": "15",
"img": "assets/img/contrast.png",
"manufacturer": "Hortisis",
"medida": "mS/cm",
"name_comun": "CE del medio",
"place": "interior",
"timestamp": "2020-01-30T12:24:05.00Z",
"type": "fertigation"
}
}
]
}
]
All you need to do is transform data to the desired result with an easy to handle object ($unwind medidas field, transform and then $group again)
Note: If your MongoDB is earlier 3.4.4 version, follow update procedure:
Install MongoDB 3.4.4 or newer
Make mongodump with new version MongoBD
Stop old MongoBD
Remove /data directory (make backup)
Start new MongoDB and run mongorestore

Join Same Collection in Mongo

Below is the sample collection document record that i want to join the same collection with different child array elements.
Sample Collection Record :
{
"_id": "052dc2aa-043b-4cd7-a3f2-f3fe6540ae52",
"Details": [
{
"Id": "104b0bb1-d4a5-469b-b1fd-b4822e96dcb0",
"Number": "12345",
"Percentages": [
{
"Code": "55555",
"Percentage": "45"
},
{
"Code": "55333",
"Percentage": "50"
}
]
},
{
"Id": "104b0bb1-d4a5-469b-b1fd-b4822e96dcb0",
"Number": "55555",
"Percentages": [
{
"Code": "55555",
"Percentage": "45"
}
]
}
],
"Payments": [
{
"Id": "61ee1a6f-3334-4f33-ab6c-51c646b75c41",
"Number": "12345"
}
]
}
The mongo Pipeline query which i would like to fetch the Percentages Array with matched conditions whose Details.Number and Payment.Number should be same
Result:
"Percentages": [
{
"Code": "55555",
"Percentage": "45"
},
{
"Code": "55333",
"Percentage": "50"
}]
How to bring the result by joining the same collections child elements using aggregate ?
Following query does what you want:
db.collection.aggregate([
{$unwind : "$Details"},
{$unwind : "$Details.Percentages"},
{$unwind : "$Payments"}, // $unwind all your arrays
{
$addFields : { //This include new `isMatch` field, which is gonna be true, only if Details.Number = Payment.Number
"isMatch" : {$cond: { if: { $eq: [ "$Details.Number", "$Payments.Number" ] }, then: true, else: false }}
}
},
{
$match : { // This ignores all others, for which Details.Number != Payment.Number
"isMatch" : true
}
},
{
$group : { // This will return only the Percentage objects
_id : null,
"Percentages" : {$push : "$Details.Percentages"}
}
},
{
$project : { // To ignore "_id" field
_id : 0,
"Percentages" : 1
}
}
])
Result:
{
"Percentages" : [
{
"Code" : "55555",
"Percentage" : "45"
},
{
"Code" : "55333",
"Percentage" : "50"
}
]
}
Hope this helps!

$lookup and get count under certain conditions MongoDB

Have 2 collections for handling chat
For chat rooms
For chat Messages
Sample data for chatRooms is as follows
{
"data": [
{
"_id": "5a606ab0116e2c164b25ef33",
"topic": "akhil Ben chat",
"topicDesc": "question 1",
"roomName": "benakhil777akhil",
"createdOn": "2018-01-18T09:36:48.231Z",
"participants": [
"ben",
"akhil777"
],
"__v": 0
},
{
"_id": "5a4dbdaab46b426863e7ead3",
"topic": "test",
"topicDesc": "test123",
"roomName": "benakhil777test",
"createdOn": "2018-01-04T05:37:46.088Z",
"participants": [
"ben",
"akhil777"
],
"__v": 0
}
]}
Sample Data for chatMessages is as follows
{"data": [
{
"_id": "5a62281ea0652120a6668bae",
"topic": "akhil Ben chat",
"roomName": "benakhil777akhil",
"message": "test 1",
"__v": 0,
"readStatus": [
{
"recipient": "ben",
"_id": "5a62281ea0652120a6668bb0",
"status": true
},
{
"recipient": "akhil777",
"_id": "5a62281ea0652120a6668baf",
"status": true
}
],
"createdOn": "2018-01-19T17:17:18.456Z"
},
{
"_id": "5a622866a0652120a6668bb1",
"topic": "akhil Ben chat",
"roomName": "benakhil777akhil",
"message": "Test 2",
"__v": 0,
"readStatus": [
{
"recipient": "ben",
"_id": "5a622866a0652120a6668bb3",
"status": false
},
{
"recipient": "akhil777",
"_id": "5a622866a0652120a6668bb2",
"status": true
}
],
"createdOn": "2018-01-19T17:18:30.396Z"
},
{
"_id": "5a62287ca0652120a6668bb4",
"topic": "akhil Ben chat",
"roomName": "benakhil777akhil",
"message": "test 3",
"__v": 0,
"readStatus": [
{
"recipient": "ben",
"_id": "5a62287ca0652120a6668bb6",
"status": false
},
{
"recipient": "akhil777",
"_id": "5a62287ca0652120a6668bb5",
"status": true
}
],
"createdOn": "2018-01-19T17:18:52.018Z"
}
]}
In the above JSON readStatus store the status, which the user read the message or not. so that i can count the unread messages by a user for each chat room.
The status inside the readStatus holds the read status of message, true for message is read.
There are two rooms benakhil777akhil and benakhil777test.
What i want to get is the number of unread messages for each room by a user say ben
Also there is userDetails collection
say,
[{
"_id": "59e6d6ba02e11e1814481022",
"username": "ben",
"name": "Ben S",
"email": "qwerty#123.com",
},{
"_id": "59e6d6ba02e11e1814481022",
"username": "akhil777",
"name": "Akhil Clement",
"email": "qwerty#123.com",
}]
this will be the user details collection
and output JSON i need is like.
{
"data": [
{
"_id": "5a606ab0116e2c164b25ef33",
"topic": "akhil Ben chat",
"topicDesc": "question 1",
"roomName": "benakhil777akhil",
"createdOn": "2018-01-18T09:36:48.231Z",
"participants": [
"ben",
"akhil777"
],
"participantDetails":[{
"_id": "59e6d6ba02e11e1814481022",
"username": "ben",
"name": "Ben S",
"email": "qwerty#123.com",
},{
"_id": "59e6d6ba02e11e1814481022",
"username": "akhil777",
"name": "Akhil Clement",
"email": "qwerty#123.com",
}],
"unreadCount": 2,
"__v": 0
},
{
"_id": "5a4dbdaab46b426863e7ead3",
"topic": "test",
"topicDesc": "test123",
"roomName": "benakhil777test",
"createdOn": "2018-01-04T05:37:46.088Z",
"participants": [
"ben",
"akhil777"
],
"participantDetails":[{
"_id": "59e6d6ba02e11e1814481022",
"username": "ben",
"name": "Ben S",
"email": "qwerty#123.com",
},{
"_id": "59e6d6ba02e11e1814481022",
"username": "akhil777",
"name": "Akhil Clement",
"email": "qwerty#123.com",
}],
"unreadCount": 0,
"__v": 0
}
]}
Please try this aggregation pipeline
db.rooms.aggregate(
[
{$match : {participants : 'ben'}},
{$lookup : {
from : "chats",
localField : "roomName",
foreignField:"roomName",
as :"out"
}
},
{$unwind : {
path: "$out",
preserveNullAndEmptyArrays: true
}
},
{$unwind : {
path: "$out.readStatus",
preserveNullAndEmptyArrays: true
}
},
{$addFields : {
isMatch : { $and : [
{ $eq : ["$out.readStatus.recipient" , "ben" ] } , { $eq : [ "$out.readStatus.status" , false ] } ]
}
}
},
{$group : {
_id : {
_id : "$_id" ,
topic : "$topic",
topicDesc : "$topicDesc",
createdOn : "$createdOn",
participants : "$participants",
roomName : "$roomName"
},
unreadCount : { $sum : { $cond : [ "$isMatch" , 1, 0 ] } }
}
},
{$sort : {unreadCount : -1}}
]
).pretty()
result
{
"_id" : {
"_id" : "5a606ab0116e2c164b25ef33",
"topic" : "akhil Ben chat",
"topicDesc" : "question 1",
"createdOn" : "2018-01-18T09:36:48.231Z",
"participants" : [
"ben",
"akhil777"
],
"roomName" : "benakhil777akhil"
},
"unreadCount" : 2
}
{
"_id" : {
"_id" : "5a4dbdaab46b426863e7ead3",
"topic" : "test",
"topicDesc" : "test123",
"createdOn" : "2018-01-04T05:37:46.088Z",
"participants" : [
"ben",
"akhil777"
],
"roomName" : "benakhil777test"
},
"unreadCount" : 0
}
EDIT since addFields is not available in 3.2.17
{$group : {
_id : {
_id : "$_id" ,
topic : "$topic",
topicDesc : "$topicDesc",
createdOn : "$createdOn",
participants : "$participants",
roomName : "$roomName"
},
unreadCount : { $sum : { $cond : [ { $and : [
{ $eq : ["$out.readStatus.recipient" , "ben" ] } , { $eq : [ "$out.readStatus.status" , false ] } ]
} , 1, 0 ] } }
}
}
EDIT-2 added $project
{$project :
{
"_id" : "$_id._id",
"topic" : "$_id.topic",
"topicDesc" : "$_id.topicDesc",
"createdOn" : "$_id.createdOn",
"participants" : "$_id.participants",
"roomName" : "$_id.roomName",
"unreadCount" : "$unreadCount"
}
}
You can simplify your code to use below aggregation.
$cond with input criteria to check for read status flag, output 1 when false 0 when true.
inner $sum to count unread values in each chat message with outer $sum to sum the unread values across all matching chat messages.
db.chatRooms.aggregate(
[{
"$match":{"participants":"ben"}},
{"$lookup":{
"from":"chatMessages",
"localField":"roomName",
"foreignField":"roomName",
"as":"chatMessages"
}},
{"$project":{
"topic":1,
"topicDesc":1,
"roomName":1,
"createdOn":1,
"participants":1,
"unreadCount":{
"$sum":{
"$map":{
"input":"$chatMessages",
"as":"chatMessage",
"in":{
"$sum":{
"$map":{
"input":"$$chatMessage.readStatus",
"as":"mChatMessage",
"in":{"$cond":[{"$eq":["$$mChatMessage.status",false]},1,0]}
}
}
}
}
}
}
}}
])
result JSON with user details.
db.chatRooms.aggregate(
[
{$match : {participants : 'ben'}},
{ $unwind : {
path: "$participants",
preserveNullAndEmptyArrays: true
}
},
{ $lookup: {
from:"users",
localField:"participants",
foreignField:"username",
as:"userData"
}
},
{ $lookup: {
from:"chatmessages",
localField:"roomName",
foreignField:"roomName",
as:"out"
}
},
{ $unwind : {
path: "$out",
preserveNullAndEmptyArrays: true
}
},
{ $unwind : {
path: "$out.readStatus",
preserveNullAndEmptyArrays: true
}
},
{ $group : {
_id : {
_id : "$_id" ,
topic : "$topic",
topicDesc : "$topicDesc",
createdOn : "$createdOn",
roomName : "$roomName"
},
participants : {$addToSet : "$participants" } ,
participantDetails : {$addToSet : {$arrayElemAt : ["$userData", 0]}},
unreadCount : {
$sum : {
$cond : [ {
$and : [
{ $eq : ["$out.readStatus.recipient" , "ben" ] } ,
{ $eq : [ "$out.readStatus.status" , false ] }
]
} , 1, 0
]
}
}
}
}
,
{ $project :
{
_id : "$_id._id",
topic : "$_id.topic",
topicDesc : "$_id.topicDesc",
createdOn : "$_id.createdOn",
participants : "$_id.participants",
roomName : "$_id.roomName",
unreadCount : "$unreadCount",
participants : 1 ,
participantDetails : 1
}
}
])