Query nested array from document - mongodb

Given the following document data in collection called 'blah'...
[
{
"_id" : ObjectId("60913f55987438922d5f0db6"),
"procedureCode" : "code1",
"description" : "Description 1",
"coding" : [
{
"system" : "ABC",
"code" : "L111"
},
{
"system" : "DEFG",
"code" : "S222"
}
]
},
{
"_id" : ObjectId("60913f55987438922d5f0dbc"),
"procedureCode" : "code2",
"description" : "Description 2",
"coding" : [
{
"system" : "ABC",
"code" : "L999"
},
{
"system" : "DEFG",
"code" : "X3333"
}
]
}
]
What I want to get is all of the coding elements where system is ABC for all parents, and an array of codes like so.
[
{ "code": "L111" },
{ "code": "L999" },
]
If I use db.getCollection('blah').find({"coding.system": "ABC"}) I get the parent document with any child in the coding array of ICD.
If I use...
db.getCollection("blah")
.find({ "coding.system": "ABC" })
.projection({ "coding.code": 1 })
I do get the parent documents which have a child with a system of "ABC", but the coding for "DEFG" seems to come along for the ride too.
{
"_id" : ObjectId("60913f55987438922d5f0db6"),
"coding" : [
{
"code" : "L989"
},
{
"code" : "S102"
}
]
},
{
"_id" : ObjectId("60913f55987438922d5f0dbc"),
"coding" : [
{
"code" : "L989"
},
{
"code" : "X382"
}
]
}
I have also tried experimenting with:
db.getCollection("blah").aggregate(
{ $unwind: "$coding" },
{ $match: { "system": "ICD" } }
);
.. as per this page: mongoDB query to find the document in nested array
... but go no where fast with that approach. i.e. no records at all.
What query do I need, please, to achieve something like this..?
[
{ "code": "L111" },
{ "code": "L999" },
...
]
or even better, this..?
[
"L111",
"L999",
...
]

db.collection.aggregate([
{
$match: { "coding.system": "ABC" }
},
{
$unwind: "$coding"
},
{
$match: { "coding.system": "ABC" }
},
{
$project: { code: "$coding.code" }
}
])
mongoplayground
db.collection.aggregate([
{
$match: { "coding.system": "ABC" }
},
{
$unwind: "$coding"
},
{
$match: { "coding.system": "ABC" }
},
{
$group: {
_id: null,
coding: { $push: "$coding.code" }
}
}
])
mongoplayground

Instead of $unwind, $match you can also use $filter:
db.collection.aggregate([
{ $match: { "coding.system": "ABC" } },
{
$project: {
coding: {
$filter: {
input: "$coding",
cond: { $eq: [ "$$this.system", "ABC" ] }
}
}
}
}
])

Related

Project boolean value if any of the nested array item satisfies a condition in mongoDB

An example structure of my collection is below
{
"_id" : ObjectId("5f631d6f3792ae9ce5e35ddd"),
"from" : "kathy",
"content" : "hello",
"to" : [
{
"name" : "david",
"isfavorite" : true
},
{
"name" : "james",
"isfavorite" : false
},
{
"name" : "steve",
"isfavorite" : true
}
]
}
{
"_id" : ObjectId("5f631d6f3792ae9ce5e35dde"),
"from" : "kathy",
"content" : "hey",
"to" : [
{
"name" : "david",
"isfavorite" : false
},
{
"name" : "john",
"isfavorite" : false
},
{
"name" : "roy",
"isfavorite" : true
}
]
}
I am trying to get messages received by a particular person. In this case if we show steve's messages I need to know if it is set as favorite by him. What I need is to project the favorite flag on the outer document itself if it matches some condition
I tried this but not working
db.getCollection('Messages_Collection').aggregate([
{
$match:
{
"to":
{
$elemMatch:
{
"name":"steve"
}
}
}
},
{
$project:
{
"_id":1,
"from":1,
"to":1,
"isfavorite":
{
$cond:
{
if:
{
"$to":
{
$elemMatch:
{
"isfavorite":true,
"name":"david"
}
},
then:true,
else:false
}
}
}
}
}])
Mongo version :4.0.13
You can simply use $filter to filter the particular person and get.
[
{
$match: {
"to.name": "steve"
}
},
{
$addFields: {
isfavorite: {
"$arrayElemAt": [
{
$filter: {
input: "$to",
cond: {
$eq: [
"$$this.name",
"steve"
]
}
}
},
0
]
}
}
},
{
$addFields: {
isfavorite: "$isfavorite.isfavorite"
}
}
]
Working Mongo playground

Lookup and aggregate multiple levels of subdocument in Mongodb

I've tried many answers to similar problems using $lookup, $unwind, and $match, but I can't get this to work for my sub-sub-subdocument situation.
I have this collection, Things:
{
"_id" : ObjectId("5a7241f7912cfc256468cb27"),
"name" : "Fortress of Solitude",
"alias" : "fortress_of_solitude",
},
{
"_id" : ObjectId("5a7247ec548c9ad042f579e2"),
"name" : "Batcave",
"alias" : "batcave",
},
{
"_id" : ObjectId("6a7247bc548c9ad042f579e8"),
"name" : "Oz",
"alias" : "oz",
},
and this one-document collection, Venues:
{
"_id" : ObjectId("5b9acabbbf71f39223f8de6e"),
"name" : "The Office",
"floors" : [
{
"name" : "1st Floor",
"places" : [
{
"name" : "Front Entrance",
"alias" : "front_entrance"
}
]
},
{
"name" : "2nd Floor",
"places" : [
{
"name" : "Batcave",
"alias" : "batcave"
},
{
"name" : "Oz",
"alias" : "oz"
}
]
}
]
}
I want to return all the Things, but with the Venue's floors.places.name aggregated with each Thing if it exists if the aliases match between Things and Venues. So, I want to return:
{
"_id" : ObjectId("5a7241f7912cfc256468cb27"),
"name" : "Fortress of Solitude",
"alias" : "fortress_of_solitude",
<-- nothing added here because
<-- it's not found in Venues
},
{
"_id" : ObjectId("5a7247ec548c9ad042f579e2"),
"name" : "Batcave",
"alias" : "batcave",
"floors" : [ <-- this should be
{ <-- returned
"places" : [ <-- because
{ <-- the alias
name" : "Batcave" <-- matches
} <-- in Venues
] <--
} <--
] <--
},
{
"_id" : ObjectId("6a7247bc548c9ad042f579e8"),
"name" : "Oz",
"alias" : "oz",
"floors" : [ <-- this should be
{ <-- returned
"places" : [ <-- because
{ <-- the alias
name" : "Oz" <-- matches
} <-- in Venues
] <--
} <--
] <--
}
I've gotten as far as the following query, but it only returns the entire Venues.floors array as an aggregate onto each Thing, which is way too much extraneous data aggregated. I just want to merge each relevant floor.place sub-subsubdocument from Venues into its corresponding Thing if it exists in Venues.
db.getCollection('things').aggregate([
{$lookup: {from: "venues",localField: "alias",foreignField: "floors.places.alias",as: "matches"}},
{
$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$matches", 0 ] }, "$$ROOT" ] } }
},
{ $project: { matches: 0 } }
])
I'm struggling with existing answers, which seem to change at MongoDB version 3.2, 3.4, 3.6, or 4.2 to include or not include $unwind, $pipeline, and other terms. Can someone explain how to get a sub-sub-subdocument aggregated like this? Thanks!
You can try this :
db.things.aggregate([
{
$lookup:
{
from: "venues",
let: { alias: "$alias" },
pipeline: [
{ $unwind: { path: "$floors", preserveNullAndEmptyArrays: true } },
{ $match: { $expr: { $in: ['$$alias', '$floors.places.alias'] } } },
/** Below stages are only if you've docs like doc 2 in Venues */
{ $addFields: { 'floors.places': { $filter: { input: '$floors.places', cond: { $eq: ['$$this.alias', '$$alias'] } } } } },
{ $group: { _id: '$_id', name: { $first: '$name' }, floors: { $push: '$floors' } } },
{$project : {'floors.places.alias': 1, _id :0}} // Optional
],
as: "matches"
}
}
])
Test : MongoDB-Playground
Since MongoDB v3.6, we may perform uncorrelated sub-queries which gives us more flexibility to join two collections.
Try this:
db.things.aggregate([
{
$lookup: {
from: "venues",
let: {
"alias": "$alias"
},
pipeline: [
{
$unwind: "$floors"
},
{
$project: {
_id: 0,
places: {
$filter: {
input: "$floors.places",
cond: {
$eq: [
"$$alias",
"$$this.alias"
]
}
}
}
}
},
{
$match: {
"places.0": {
$exists: true
}
}
},
{
$unset: "places.name"
}
],
as: "floors"
}
}
])
MongoPlayground

Filter keys not in collection

How do we find keys which do not exist in collection.
Given an input list of keys ['3321', '2121', '5647'] , i want to return those that do not exist in the collection :
{ "_id" : { "$oid" : "5e2993b61886a22f400ea319" }, "scrip" : "5647" }
{ "_id" : { "$oid" : "5e2993b61886a22f400ea31a" }, "scrip" : "3553" }
So the expected output is ['3321', '2121']
This aggregation gets the desired output (works with MongoDB version 3.4 or later):
INPUT_ARRAY = ['3321', '2121', '5647']
db.test.aggregate( [
{
$match: {
scrip: {
$in: INPUT_ARRAY
}
}
},
{
$group: {
_id: null,
matches: { $push: "$scrip" }
}
},
{
$project: {
scrips_not_exist: { $setDifference: [ INPUT_ARRAY, "$matches" ] },
_id: 0
}
}
] )
The output:
{ "scrips_not_exist" : [ "3321", "2121" ] }

How to get nested 3 label array object in Mongo Query?

Basically the structure is :
{
"_id" : ObjectId("123123"),
"stores" : [
{
"messages" : [
{
"updated_time" : "2018-05-15T05:12:25+0000",
"message_count" : 4,
"thread_id" : "123",
"messages" : [
{
"message" : "Hi User ",
"created_time" : "2018-05-15T05:12:25+0000",
"message_id" : "111",
},
{
"message" : "This is tes",
"created_time" : "2018-05-15T05:12:21+0000",
"message_id" : "222",
}
]
},
],
"store_id" : "123"
}
]
}
I have these values to get message_id object : 111. So how to get this object, any idea or help will be appreciated. THanks
store_id: 123,
thread_id:123,
message_id:111
The simplest way would be to $unwind all the nested arrays and then use $match to get single document. You can also add $replaceRoot to get only nested document. Try:
db.collection.aggregate([
{ $unwind: "$stores" },
{ $unwind: "$stores.messages" },
{ $unwind: "$stores.messages.messages" },
{ $match: { "stores.store_id": "123", "stores.messages.thread_id": "123", "stores.messages.messages.message_id": "111" } },
{ $replaceRoot: { newRoot: "$stores.messages.messages" } }
])
Prints:
{
"created_time": "2018-05-15T05:12:25+0000",
"message": "Hi User ",
"message_id": "111"
}
To improve the performance you can use $match after every $unwind to filter out unnecessary data as soon as possible, try:
db.collection.aggregate([
{ $unwind: "$stores" },
{ $match: { "stores.store_id": "123" } },
{ $unwind: "$stores.messages" },
{ $match: { "stores.messages.thread_id": "123" } },
{ $unwind: "$stores.messages.messages" },
{ $match: { "stores.messages.messages.message_id": "111" } },
{ $replaceRoot: { newRoot: "$stores.messages.messages" } }
])

removing uncessary keys in result mongoose

I am currently getting a response in mongoose like this
{
"_id" : "5a0be40836341c8ef9dc6d9d",
"Username" : "Adkins Daugherty",
"ContactDetail" : {
"ApiID" : "5a0be408ed6983954888bdb5",
"ContactName" : "Pace Roach",
}
}
But i want like this :
{
"_id" : "5a0be40836341c8ef9dc6d9d",
"Username" : "Adkins Daugherty",
"ApiID" : "5a0be408ed6983954888bdb5",
"ContactName" : "Pace Roach",
}
How can i do this using mongoose. My collection name is contacts
Thanks
EDIT-1
db.contacts.aggregate([{
$match: { Username: 'year_Adkins Daugherty' } },
{ "$unwind": "$ContactDetail" },
{
"$group": { "_id": "$_id",
"ApiID": "$ContactDetail.ApiID"
}
}
])
I think I got an answer in $project
return Contact.aggregate(
[
{ $match: { Username: regex } },
{
$project:{
Username:1,
ApiID:"$ContactDetail.ApiID",
Name:"$ContactDetail.ContactName",
ProfileImageUrl:"$ContactDetail.ProfileImageUrl",
OutletName:"$ContactDetail.OutletName"
}
}
]
).exec()
The result I got is
{
"_id": "5a0be40836341c8ef9dc6d9d",
"Username": "year_Adkins Daugherty",
"ApiID": "5a0be408ed6983954888bdb5",
"Name": "Pace Roach",
"ProfileImageUrl": "http://placehold.it/256x256",
"OutletName": "Codehow___kjvolyi3ye"
}