This question already has answers here:
Find in Double Nested Array MongoDB
(2 answers)
Closed 4 years ago.
I have a document that looks like so:
{
"_id": {
"$oid": "5b1586ccf0c56353e89d330b"
},
"address": {
"street": "123 Street",
"address2": "Address 2",
"city": "Some City",
"state": "MI",
"zip": "12345"
},
"subs": [
{
"invoices": [
{
"address": {
"street": "3061 Pine Ave SW",
"city": "Grandville",
"state": "AK",
"zip": "49418"
},
"lineItem": [
{
"images": [
{
"_id": {
"$oid": "5b1fca54e6ee1d80c463612d"
},
"name": "1528810066348_RSA Logo.jpeg",
"url": "https....",
"uploadDate": {
"$date": "2018-06-12T13:27:46.931Z"
},
"size": 91819
}
],
"_id": {
"$oid": "5b1fca54e6ee1d80c463612c"
},
"desc": "2",
"amt": 2
}
],
"_id": {
"$oid": "5b1fca54e6ee1d80c463612b"
}
}
],
"_id": {
"$oid": "5b1fc7f23b595481d4599f58"
},
"email": "a#a.com",
"scope": "Roof",
},
{
"invoices": [
{
"address": {
"street": "3061 Pine Ave SW",
"city": "Grandville",
"state": "AL",
"zip": "49418"
},
"lineItem": [
{
"images": [
{
"_id": {
"$oid": "5b1fca2fe6ee1d80c463612a"
},
"name": "1528810029700_RSA Stamp.png",
"url": "https....",
"uploadDate": {
"$date": "2018-06-12T13:27:10.403Z"
},
"size": 238113
}
],
"_id": {
"$oid": "5b1fca2fe6ee1d80c4636129"
},
"desc": "1",
"amt": 1
}
],
"_id": {
"$oid": "5b1fca2fe6ee1d80c4636128"
}
},
{
"address": {
"street": "3061 Pine Ave SW",
"city": "Grandville",
"state": "AL",
"zip": "49418"
},
"lineItem": [
{
"images": [
{
"_id": {
"$oid": "5b1fd05b0d1f7185e02e9c40"
},
"name": "1528811607099_error page.PNG",
"url": "https....",
"uploadDate": {
"$date": "2018-06-12T13:53:28.080Z"
},
"size": 224772
}
],
"_id": {
"$oid": "5b1fd05b0d1f7185e02e9c3f"
},
"desc": "3",
"amt": 3
}
],
"_id": {
"$oid": "5b1fd05b0d1f7185e02e9c3e"
}
}
],
"_id": {
"$oid": "5b1fc7f23b595481d4599f55"
},
"email": "b#b.com",
"scope": "Siding",
}
],
"firstName": "",
"lastName": "",
}
My issue is that I want to be able to access a specific invoices of a specific subs.
I am new to Mongo/Mongoose so it is possible I am doing something completely wrong and I would be more than happy with any answer/criticism on how I am approaching this.
-- tweaked answer --
Job.aggregate([
{
$match: {
"_id": mongoose.Types.ObjectId(req.body.jobID)
}
},
{
$unwind: "$subs"
},
{
$match: {
"subs._id": mongoose.Types.ObjectId(req.body.subID)
}
},
{
$unwind: "$subs.invoices"
},
{
$match: {
"subs.invoices._id": mongoose.Types.ObjectId(req.body.invID)
}
},
{
$project: {
"_id": 1,
"subs.invoices": 1
}
}
], function(err, job) {
if (err) throw err;
res.send(job);
});
You can try below aggregation...
Here this is a long process of deconstructing an array using $unwind and rebuild the array using $group
db.collection.aggregate([
{ "$match": { "_id": "1111" } },
{ "$unwind": "$subs" },
{ "$match": { "subs._id": "2222" } },
{ "$unwind": "$subs.invoices" },
{ "$match": { "subs.invoices._id": "3333" } },
{ "$group": {
"_id": {
"_id": "$_id",
"subs": "$subs._id"
},
"firstName": { "$first": "$firstName" },
"lastName": { "$first": "$lastName" },
"address": { "$first": "$address" },
"subs": {
"$first": {
"_id": "$subs._id",
"email": "$subs.email",
"venue": "$subs.venue",
"scope": "$subs.scope"
}
},
"invoices": { "$push": "$subs.invoices" }
}},
{ "$group": {
"_id": "$_id._id",
"firstName": { "$first": "$firstName" },
"lastName": { "$first": "$lastName" },
"address": { "$first": "$address" },
"subs": {
"$push": {
"_id": "$subs._id",
"email": "$subs.email",
"venue": "$subs.venue",
"scope": "$subs.scope",
"invoices": "$invoices"
}
}
}}
])
Or you can do this with $filter aggregation as well
db.collection.aggregate([
{ "$match": { "_id": "5b1586ccf0c56353e89d330b" }},
{ "$unwind": "$subs" },
{ "$match": { "subs._id": "5b1fc7f23b595481d4599f58" }},
{ "$project": {
"address": 1, "firstName": 1, "lastName": 1,
"subs.type": "$subs._id",
"subs.status": "$subs.email",
"subs.code": "$subs.scope",
"subs.invoices": {
"$filter": {
"input": "$subs.invoices",
"as": "invoice",
"cond": {
"$eq": [
"$$invoice._id",
"5b1fca54e6ee1d80c463612b"
]
}
}
}
}},
{ "$group": {
"_id": "$_id",
"address": { "$first": "$address" },
"firstName": { "$first": "$firstName" },
"lastName": { "$first": "$lastName" },
"subs": { "$push": "$subs" }
}}
])
Related
I am trying to update the existing document with one extra field as a new requirement in DB needs that
Existing field
[{
"_id": {
"$oid": "62a8ad644035e93c7ff24607"
},
"user_name": "demo2#devia.com",
"password": "1234",
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"basic_details": {
"first_name": "ghgh",
"middle_name": "",
"last_name": "gh",
"email": "demo2#devia.com",
"mobile_number": 1234567890
},
"address_details": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2381c4"
},
"state_code": "MP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21f1a9"
},
"city_name": "Akodia"
},
"address_line1": "",
"address_line2": "",
"pincode": ""
},
"is_verify": false,
"last_accessed": {
"$date": {
"$numberLong": "1655221604144"
}
},
"created_at": {
"$date": {
"$numberLong": "1655221604144"
}
},
"course_details": {}
}]
Needs to update all fields
[{
"_id": {
"$oid": "62bf2cf2a8c782741bbcc398"
},
"user_name": "a#exaple.com",
"password": "$2b$12$NgLNYm5jUBmFUq.w15.qCO35GiTJX09jAnOpoGuPt9G7GNuOkVN7K",
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"basic_details": {
"email": "a#example.com",
"mobile_number": "9898989898",
"first_name": "Fahim",
"middle_name": "",
"last_name": "Ashhab",
"nationality": "Antiguans",
"date_of_birth": "2003-02-04",
"admission_year": "2022-23",
"gender": "Male",
"category": "General",
"para_ability": {
"is_disable": false,
"name_of_disability": ""
}
},
"address_details": {
"communication_address": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2380ef"
},
"state_code": "AP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21ebc8"
},
"city_name": "Akasahebpet"
},
"address_line1": "jjkjk",
"address_line2": "uiui",
"pincode": "989898"
}
},
"is_verify": true,
"last_accessed": {
"$date": {
"$numberLong": "1656696050670"
}
},
"created_at": {
"$date": {
"$numberLong": "1656696050670"
}
},
"allocate_to_counselor": {
"counselor_id": {
"$oid": "62bfd13a5ce8a398ad101bd7"
},
"counselor_name": "test",
"last_update": {
"$date": {
"$numberLong": "1656817948401"
}
}
},
"course_details": {
"BSc": {
"course_id": {
"$oid": "628e03d94e22276b98231407"
},
"course_name": "BSc",
"application_id": {
"$oid": "62bf2cf2a8c782741bbcc399"
},
"status": "Incomplete",
"specs": [
{
"spec_name": "Physician Assistant",
"is_activated": true
}
]
}
}
}]
Query Tried nothing is happening
db.collection.aggregate([
{$match:{"address_details.country": {$exists:true}}},
{$set:{address_details:{communication_address:"$address_details"}}}
])
2nd Query Tried didn't go through
db.studentsPrimaryDetails.updateMany({
"address_details.communication_address": {"$exist":false}},
{$set: {"address_details.communication_address":"$address_details"}})
The desired result needs to be
from this
"address_details": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2381c4"
},
"state_code": "MP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21f1a9"
},
"city_name": "Akodia"
},
"address_line1": "",
"address_line2": "",
"pincode": ""
}
to this
"address_details": {
"communication_address": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2380ef"
},
"state_code": "AP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21ebc8"
},
"city_name": "Akasahebpet"
},
"address_line1": "jjkjk",
"address_line2": "uiui",
"pincode": "989898"
}
}
Don't know what I am doing wrong.
Edit :
Desired full output
[{
"_id": {
"$oid": "62bf2cf2a8c782741bbcc398"
},
"user_name": "a#example.com",
"password": "12345678",
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"basic_details": {
"email": "a#example.com",
"mobile_number": "9898989898",
"first_name": "Fahim",
"middle_name": "",
"last_name": "Ashhab",
"nationality": "Antiguans",
"date_of_birth": "2003-02-04",
"admission_year": "2022-23",
"gender": "Male",
"category": "General",
"para_ability": {
"is_disable": false,
"name_of_disability": ""
}
},
"address_details": {
"communication_address": {
"country": {
"country_id": {
"$oid": "623012f9683f8fb7bd213c36"
},
"country_code": "IN"
},
"state": {
"state_id": {
"$oid": "623013d9683f8fb7bd2380ef"
},
"state_code": "AP"
},
"city": {
"city_id": {
"$oid": "6230139f683f8fb7bd21ebc8"
},
"city_name": "Akasahebpet"
},
"address_line1": "jjkjk",
"address_line2": "uiui",
"pincode": "989898"
}
},
"is_verify": true,
"last_accessed": {
"$date": {
"$numberLong": "1656696050670"
}
},
"created_at": {
"$date": {
"$numberLong": "1656696050670"
}
},
"allocate_to_counselor": {
"counselor_id": {
"$oid": "62bfd13a5ce8a398ad101bd7"
},
"counselor_name": "viru chaudhary",
"last_update": {
"$date": {
"$numberLong": "1656817948401"
}
}
},
"course_details": {
"BSc": {
"course_id": {
"$oid": "628e03d94e22276b98231407"
},
"course_name": "BSc",
"application_id": {
"$oid": "62bf2cf2a8c782741bbcc399"
},
"status": "Incomplete",
"specs": [
{
"spec_name": "Physician Assistant",
"is_activated": true
}
]
}
}
}]```
One option is:
Use $exists instead of $exist
Use pipeline in order to be able to $set the value from another fields' value
db.collection.updateMany(
{"address_details.communication_address": {$exists: false}},
[{$set: {address_detailsB: {communication_address: "$address_details"}}},
{$set: {
address_details: "$address_detailsB",
address_detailsB: "$$REMOVE"}
}
])
See how it works on the playground example
The aggregation query that you first tried, does not not update the db, it only returns data. In order to use it as an updater you need to add a $merge step.
So I have a parent document with users, as well as an array that has users too. I want to add the DisplayName from the nested users array to the aggregation output. Any ideas?
Output I'm looking to achieve:
[
{
"user": {
"_id": "11",
"Name": "Dave",
"DocID": "1",
"DocDisplyName": "ABC"
},
{
"user": {
"_id": "33",
"Name": "Henry",
"DocID": "1",
"DocDisplyName": "ABC",
"BranchDisplayName:"BranchA"
}
}
]
And so on.. So an array of all users and for users that belong to a branch, add the branch display Name to the output.
// Doc 1
{
"_id": "1",
"DisplayName": "ABC",
"Users": [
{ "_id": "11", "Name": "Dave" },
{ "_id": "22", "Name": "Steve" }
],
"Branches": [
{
"_id": "111",
"DisplayName": "BranchA",
"Users": [
{ "_id": "33", "Name": "Henry" },
{ "_id": "44", "Name": "Josh" },
],
},
{
"_id": "222",
"DisplayName": "BranchB",
"Users": [
{ "_id": "55", "Name": "Mark" },
{ "_id": "66", "Name": "Anton" },
],
}
]
}
``Doc 2
{
"_id": "2",
"DisplayName": "DEF",
"Users": [
{ "_id": "77", "Name": "Josh" },
{ "_id": "88", "Name": "Steve" }
],
"Branches": [
{
"_id": "333",
"DisplayName": "BranchA",
"Users": [
{ "_id": "99", "Name": "Henry" },
{ "_id": "10", "Name": "Josh" },
],
},
{
"_id": "444",
"DisplayName": "BranchB",
"Users": [
{ "_id": "112", "Name": "Susan" },
{ "_id": "112", "Name": "Mary" },
],
}
]
}
Collection.aggregate([
{
$addFields: {
branchUsers: {
$reduce: {
input: "$Branches.Users",
initialValue: [],
in: {
$concatArrays: ["$$this", "$$value"],
},
},
},
},
},
{
$addFields: {
user: {
$concatArrays: ["$branchUsers", "$Users"],
},
},
},
{
$addFields: {
"user.DocID": "$_id","user.DocDisaplyName": "$DisplayName"
},
},
{
$unwind: "$user",
},
{
$project: {
_id: 0,
user: 1,
},
}
])
Thanks in advance!
OK I found a solution.
{
$addFields: {
"branchUsers.BranchDisplayName": {
$let: {
vars: {
first: {
$arrayElemAt: [ "$Branches", 0 ]
}
},
in: "$$first.DisplayName"
}
}
}
},
This creates the field only for the users that belong to the branch
I have collections with following values:
reports
{
"_id": { "$oid": "5f05e1d13e0f6637739e215b" },
"testReport": [
{
"name": "Calcium",
"value": "87",
"slug": "ca",
"details": {
"description": "description....",
"recommendation": "recommendation....",
"isNormal": false
}
},
{
"name": "Magnesium",
"value": "-98",
"slug": "mg",
"details": {
"description": "description....",
"recommendation": "recommendation....",
"isNormal": false
}
}
],
"patientName": "Patient Name",
"clinicName": "Clinic",
"gender": "Male",
"bloodGroup": "A",
"createdAt": { "$date": "2020-07-08T15:10:09.612Z" },
"updatedAt": { "$date": "2020-07-08T15:10:09.612Z" }
},
setups
{
"_id": { "$oid": "5efcba7503f4693d164e651d" },
"code": "Ca",
"codeLower": "ca",
"name": "Calcium",
"valueFrom": -75,
"valueTo": -51,
"treatmentDescription": "description...",
"isNormal": false,
"gender": "",
"recommendation": "recommendation...",
"createdAt": { "$date": "2020-07-01T16:31:50.205Z" },
"updatedAt": { "$date": "2020-07-01T16:31:50.205Z" }
},
{
"_id": { "$oid": "5efcba7503f4693d164e651e" }, // <=== should find this for Calcium
"code": "Ca",
"codeLower": "ca",
"name": "Calcium",
"valueFrom": 76,
"valueTo": 100,
"treatmentDescription": "description...",
"isNormal": false,
"gender": "",
"recommendation": "recommendation...",
"createdAt": { "$date": "2020-07-01T16:31:50.205Z" },
"updatedAt": { "$date": "2020-07-01T16:31:50.205Z" }
},
{
"_id": { "$oid": "5efcba7603f4693d164e65bb" }, // <=== should find this for Magnesium
"code": "Mg",
"codeLower": "mg",
"name": "Magnesium",
"valueFrom": -100,
"valueTo": -76,
"treatmentDescription": "description...",
"isNormal": false,
"gender": "",
"recommendation": "recommendation...",
"createdAt": { "$date": "2020-07-01T16:31:50.205Z" },
"updatedAt": { "$date": "2020-07-01T16:31:50.205Z" }
},
{
"_id": { "$oid": "5efcba7503f4693d164e6550" },
"code": "Mg",
"codeLower": "mg",
"name": "Magnesium",
"valueFrom": 76,
"valueTo": 100,
"treatmentDescription": "description...",
"isNormal": false,
"gender": "",
"recommendation": "recommendation...",
"createdAt": { "$date": "2020-07-01T16:31:50.205Z" },
"updatedAt": { "$date": "2020-07-01T16:31:50.205Z" }
}
I want to search the value from reports collection and check whether the value is in range from the setups collection and return the _id and add the returned _ids in setupIds field on reports collection.
I tried with the following aggregation framework:
db.reports.aggegrate([
{
'$match': {
'_id': new ObjectId('5f05e1d13e0f6637739e215b')
}
}, {
'$lookup': {
'from': 'setups',
'let': {
'testValue': '$testReport.value',
'testName': '$testReport.name'
},
'pipeline': [
{
'$match': {
'$expr': {
{
'$and': [
{
'$eq': [
'$name', '$$testName'
]
}, {
'$gte': [
'$valueTo', '$$testValue'
]
}, {
'$lte': [
'$valueFrom', '$$testValue'
]
}
]
}
}
}
}
],
'as': 'setupIds'
}
}
])
This query didn't find the expected results.
This is the updated reports collection I want:
{
"_id": { "$oid": "5f05e1d13e0f6637739e215b" },
"setupIds": [{ "$oid": "5efcba7503f4693d164e651e" }, { "$oid": "5efcba7603f4693d164e65bb" }], // <=== Here, array of the ObjectId (ref: "Setups")
"patientName": "Patient Name",
"clinicName": "Clinic",
"gender": "Male",
"bloodGroup": "A",
"createdAt": { "$date": "2020-07-08T15:10:09.612Z" },
"updatedAt": { "$date": "2020-07-08T15:10:09.612Z" }
},
You can try like following
[{
$match: {
_id: ObjectId('5f05e1d13e0f6637739e215b')
}
}, {
$unwind: {
path: "$testReport"
}
}, {
$lookup: {
from: 'setup',
'let': {
testValue: {
$toInt: '$testReport.value'
},
testName: '$testReport.name'
},
pipeline: [{
$match: {
$expr: {
$and: [{
"$eq": [
"$name",
"$$testName"
]
},
{
"$gte": [
"$valueTo",
"$$testValue"
]
},
{
"$lte": [
"$valueFrom",
"$$testValue"
]
}
]
}
}
}],
as: 'setupIds'
}
}, {
$group: {
_id: "$_id",
patientName: {
$first: "$patientName"
},
clinicName: {
$first: "$clinicName"
},
gender: {
$first: "$gender"
},
bloodGroup: {
$first: "$bloodGroup"
},
createdAt: {
$first: "$createdAt"
},
updatedAt: {
$first: "$updatedAt"
},
setupIds: {
$addToSet: "$setupIds._id"
}
}
}, {
$addFields: {
setupIds: {
$reduce: {
input: "$setupIds",
initialValue: [],
in: {
$setUnion: ["$$this", "$$value"]
}
}
}
}
}]
Working Mongo playground
I have Json which have values like state_city details this contains information like which city belongs to which state -
Need to query it for particular state name which will gives me all cities that belongs to that state.
db.collection.find({
"count": 10,
"state.name": "MP"
})
[
{
"collection": "collection1",
"count": 10,
"state": [
{
"name": "MH",
"city": "Mumbai"
},
{
"name": "MH",
"city": "Pune"
},
{
"name": "UP",
"city": "Kanpur"
},
{
"name": "CG",
"city": "Raipur"
}
]
},
{
"collection": "collection2",
"count": 20,
"state": [
{
"name": "MP",
"city": "Indore"
},
{
"name": "MH",
"city": "Bhopal"
},
{
"name": "UP",
"city": "Kanpur"
},
{
"name": "CG",
"city": "Raipur"
}
]
}
]
You have to use aggregate query to get only matching elements in array :
db.collection.aggregate([{
$unwind: "$content.state"
},
{
$match: {
"content.state.name": "MH",
"count": 10
}
},
{
$group: {
_id: "$content.state.city",
}
},
{
$addFields: {
key: 1
}
},
{
$group: {
_id: "$key",
cities: {
$push: "$_id"
}
}
},
{
$project: {
_id: 0,
cities: 1
}
}
])
This query will return :
{
"cities": [
"Pune",
"Mumbai"
]
}
The following query would be the solution.
db.collection.find({ "count": 10, "state":{"name": "MP"}})
For more complex queries, $elemMatch is also available.
I have a mongodb result like this.
[
{
"_id": {
"_id": "57174838afb8eb97ccd409ca",
"name": "Yet another position",
"description": "description",
"code": "11Y-WK"
},
"votes": [
{
"candidate": {
"_id": "56f19694e84a6bf1b66ad378",
"surname": "XXXXXXXXX",
"firstName": "XXXXXXXXX",
"middleName": "XXXXXXXXX",
"othername": " XXXXXXXXX XXXXXXXXX",
"sc_number": "071050"
},
"count": 3
},
{
"candidate": {
"_id": "56f19690e84a6bf1b66aa558",
"surname": "XXXXXXXXX",
"othername": "XXXXXXXXX XXXXXXXXX",
"sc_number": "034837"
},
"count": 2
},
{
"candidate": {
"_id": "56f19690e84a6bf1b66aa2f3",
"surname": "XXXXXXXXX",
"othername": "XXXXXXXXX XXXXXXXXX",
"sc_number": "008243"
},
"count": 4
}
],
"total_count": 9
},
{
"_id": {
"_id": "571747a8afb8eb97ccd409c7",
"name": "Test Position",
"description": "Description",
"code": "10T-9K"
},
"votes": [
{
"candidate": {
"_id": "56f19690e84a6bf1b66aa3b7",
"surname": "XXXXXXXXX",
"othername": "XXXXXXXXX",
"sc_number": "044660"
},
"count": 1
},
{
"candidate": {
"_id": "56f19690e84a6bf1b66aa6ea",
"surname": "XXXXXXXXX",
"othername": "XXXXXXXXX",
"sc_number": "062444"
},
"count": 5
},
{
"candidate": {
"_id": "56f1968fe84a6bf1b66aa03e",
"surname": "XXXXXXXXX",
"othername": "XXXXXXXXX",
"sc_number": "042357"
},
"count": 3
}
],
"total_count": 9
}
]
I need to sort by count in descending order.
Below is the query that returns the result above.
I've tried applying sort at all stages but with no luck.
Vote.aggregate([
{ "$match": { "_poll" : mongoose.mongo.ObjectID(req.query._poll) } },
{
"$group": {
"_id": {
"_position": '$_position',
"candidate": '$candidate'
},
"voteCount": { "$sum": 1 }
}
},
{
"$group": {
"_id": "$_id._position",
"votes": {
"$push": {
"candidate": "$_id.candidate",
"count": "$voteCount"
}
},
"total_count": { "$sum": "$voteCount" }
}
},
{ "$sort": { "total_count": -1 } }
Where I need to insert sort operation, to sort by count in descending order.
To sort an array during aggregation it is a bit tricky stuff.
So what I am duinfg to solve that is:
unwinding array
apply sort
regroup array
please find mongo shell code which will should give an overview ot this technique:
var group = {$group:{_id:"$type", votes:{$push:"$value" }}}
var unwind={$unwind:"$votes"}
var sort = {$sort:{"votes":-1}}
var reGroup = {$group:{_id:"$_id", votes:{$push:"$votes" }}}
db.readings.aggregate([group,unwind,sort ,reGroup])
any comments welcome!