Find or forEach Inside Aggregate MongoDB - mongodb

Let's say I have two collection in my database call rumahsakit.
First collection called dim_dokter:
[{
"_id": ObjectId("58b22c79e8c1c52bf3fad997"),
"nama_dokter": "Dr. Basuki Hamzah",
"spesialisasi": "Spesialis Farmakologi Klinik",
"alamat": " Jalan Lingkar Ring Road Utara, Yogyakarta "
}, {
"_id": ObjectId("58b22c79e8c1c52bf3fad998"),
"nama_dokter": "Dr. Danie Nukman",
"spesialisasi": "Spesialis Anak",
"alamat": " Jalan Sudirman, Yogyakarta "
}, {
"_id": ObjectId("58b22c79e8c1c52bf3fad999"),
"nama_dokter": "Dr. Bambang Kurnia",
"spesialisasi": "Spesialis Mikrobiologi Klinik",
"alamat": " Jalan Ahmad Yani, Yogyakarta "
}]
Second collection called fact_perawatan:
[{
"_id": ObjectId("58b22d13e8c1c52bf3fad99a"),
"nama_pasien": "Clark",
"detail_perawatan": [{
"id_dokter": ObjectId("58b22c79e8c1c52bf3fad997"),
"jumlah_obat": 1
}, {
"id_dokter": ObjectId("58b22c79e8c1c52bf3fad998"),
"jumlah_obat": 1
}]
}]
Collection fact_perawatan have the id_dokter that is actually point to the dim_dokter._id . I want to do aggregation to show this data in fact_perawatan collection but instead showing just the id_dokter, I want to use nama_dokter from dim_dokter.
This is my code so far:
db.fact_perawatan.aggregate([
{
$match:
{
'_id': db.fact_perawatan.find({"_id" : ObjectId("58b22d13e8c1c52bf3fad99a")})[0]._id
}
},
{
$project:
{
nama_pasien: db.fact_perawatan.find({"_id": ObjectId("58b22d13e8c1c52bf3fad99a")})[0].nama_pasien,
perawatan: [
{
dokter: db.dim_dokter.find({"_id" : db.fact_perawatan.find({"_id": ObjectId("58b22d13e8c1c52bf3fad99a")})[0].detail_perawatan[0].id_dokter})[0].nama_dokter,
},
]
}
}
])
result:
{ "_id" : ObjectId("58b22d13e8c1c52bf3fad99a"), "nama_pasien" : "Clark", "perawatan" : [ { "dokter" : "Dr. Basuki Hamzah" } ] }
Those code can get the nama_dokter from dim_dokter, but only one data. In my case, the data can be up to 5. do the detail_perawatan[0] to [5] is not solution.
So, this code:
db.dim_dokter.find({"_id" : db.fact_perawatan.find({"_id": ObjectId("58b22d13e8c1c52bf3fad99a")})[0].detail_perawatan[0].id_dokter})[0].nama_dokter,
How make the code above to loop as many as the data in there ? so I can get all the data.
Thanks

You can use a $lookup to left join with dim_dokter following by a $group to regroup your data in perawatan field :
db.fact_perawatan.aggregate([{
$match: {
'_id': ObjectId("58b22d13e8c1c52bf3fad99a")
}
}, {
$unwind: "$detail_perawatan"
}, {
$lookup: {
from: "dim_dokter",
localField: "detail_perawatan.id_dokter",
foreignField: "_id",
as: "dim_dokter"
}
}, {
$unwind: "$dim_dokter"
}, {
$group: {
_id: "$_id",
nama_pasien: {
$first: "$nama_pasien"
},
perawatan: {
$push: {
"dokter": "$dim_dokter.nama_dokter"
}
}
}
}])
The $unwind is used to deconstruct the array field detail_perawatan to be able to $lookup afterwards

Related

MongoDB query slow in join using $ne to look for not empty arrays

I'm new to Mongo and I have a slow query when using $ne in a match pipeline (to get the records that match only and not all the ones where the array is empty)
The query is as follow:
db.EN.aggregate([
{
$lookup: {
from: 'csv_import',
let: {pn:'$ICECAT-interface.Product.#Prod_id'},
pipeline: [{
$match: {
$expr: {
$eq: ["$$pn","$part_no"]
}
}
}],
as: 'part_number_info'
}
}, { $match: { part_number_info: { $ne: [] } } }
]).pretty();
When I remove the { $match: { part_number_info: { $ne: [] } } } the query executes in 21 seconds, vs almost 2 hours when executed using the $ne clause.
There's an index already on ICECAT-interface.Product.#Prod_id, and here are the 2 collections structure sample:
csv_import:
{
"_id": "ObjectId(\"6348339cc6e5c8ce0b7da5a4\")",
"index": 23679,
"product_id": 4019734,
"part_no": "CP-HAR-EP-ADVANCED-REN-1Y",
"vendor_standard": "Check Point"
}
EN:
[{
"_id": "1414",
"ICECAT-interface": {
"#xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"#xsi:noNamespaceSchemaLocation": "https://data.icecat.biz/xsd/ICECAT-interface_response.xsd",
"Product": {
"#Code": "1",
"#HighPic": "https://images.icecat.biz/img/norm/high/1414-HP.jpg",
"#HighPicHeight": "400",
"#HighPicSize": "43288",
"#HighPicWidth": "400",
"#ID": "1414",
"#LowPic": "https://images.icecat.biz/img/norm/low/1414-HP.jpg",
"#LowPicHeight": "200",
"#LowPicSize": "17390",
"#LowPicWidth": "200",
"#Name": "C6614NE",
"#IntName": "C6614NE",
"#LocalName": "",
"#Pic500x500": "https://images.icecat.biz/img/gallery_mediums/img_1414_medium_1480667779_072_2323.jpg",
"#Pic500x500Height": "500",
"#Pic500x500Size": "101045",
"#Pic500x500Width": "500",
"#Prod_id": "C6614NE",
SOLUTION
I did add an index on part_no field in csv_import and I changed the order of the query to be smaller to large (EN is 27GB and csv_import is a few MB)
Final query: (includes the suggestion made by nimrod serok
db.csv_import.aggregate([
{
$lookup: {
from: 'EN',
let: {pn:'$part_no'},
pipeline: [{
$match: {
$expr: {
$eq: ["$$pn","$ICECAT-interface.Product.#Prod_id"]
}
}
}],
as: 'part_number_info'
}
},{$match: {"part_number_info.0": {$exists: true}}}
])
A better option is to use:
{$match: {"part_number_info.0": {$exists: true}}}
See how it works on the playground example

If condition in MongoDB for Nested JSON to retrieve a particular value

I've nested JSON like this. I want to retrieve the value of "_value" in second level. i,e. "Living Organisms" This is my JSON document.
{
"name": "Biology Book",
"data": {
"toc": {
"_version": "1",
"ge": [
{
"_name": "The Fundamental Unit of Life",
"_id": "5a",
"ge": [
{
"_value": "Living Organisms",
"_id": "5b"
}
]
}
]
}
}
}
This is what I've tried, using the "_id", I want to retrieve it's "_value"
db.products.aggregate([{"$match":{ "data.toc.ge.ge._id": "5b"}}])
This is the closest I could get to the output you mentioned in the comment above. Hope it helps.
db.collection.aggregate([
{
$match: {
"data.toc.ge.ge._id": "5b"
}
},
{
$unwind: "$data.toc.ge"
},
{
$unwind: "$data.toc.ge.ge"
},
{
$group: {
_id: null,
book: {
$push: "$data.toc.ge.ge._value"
}
}
},
{
$project: {
_id: 0,
first: {
$arrayElemAt: [
"$book",
0
]
},
}
}
])
Output:
[
{
"first": "Living Organisms"
}
]
You can check what I tried here
If you are using Mongoid:
(1..6).inject(Model.where('data.toc.ge.ge._id' => '5b').pluck('data.toc.ge.ge._value').first) { |v| v.values.first rescue v.first rescue v }
# => "Living Organisms"
6 is the number of containers to trim from the output (4 hashes and 2 arrays).
If I understand your question correctly, you only care about _value, so it sounds like you might want to use a projection:
db.products.aggregate([{"$match":{ "data.toc.ge.ge._id": "5b"}}, { "$project": {"data.toc.ge.ge._value": 1}}])

grouping result from multiple queries and aggregating result mongo

I'm new to mongo and I have a document that has an array with the ids of all it's related documents. I need to fetch the document with all it's relateds in a single query. For the moment I fetch the document and I query separatly each of it's related document with there ids.
all my documents are on the same collection documents_nodes and look like so:
{
"id": "document_1",
"name": "flask",
"relateds": [
"document_2",
"document_3",
"document_4"
],
"parents": [
"document1_1"
]
}
The first query is to get the initial document
db.documents_nodes.find({id: document_1})
And then I query it's relateds with a second query
db.documents_nodes.aggregate([{
$match: {
$and: [{
id: {
$in: ["document_2", "document_3", "document_2"]
}
}]
}
}])
is there a way to combine the two queries, I tried this but it doesn't work
db.documents_nodes.aggregate([
{
$match: {
uri: "https://opus.adeo.com/LMFR_prod/3206"
}
},
{
$addFields: {
newRelateds:
{
$match: {
id: {
$in: [ "$relateds" ]
}
}
}
}
}
])
"errmsg" : "Unrecognized expression '$match'",
"code" : 168,
"codeName" : "InvalidPipelineOperator"
I have found a way to do it, in case someone has the same need.
I used the $unwind to flatten the array of documents and then used the $lookup to fetch the documents by their ids and finally I group the result on a new key.
[{
$match: {
id: "document_1"
}
}, {
$unwind: {
path: '$relateds',
}
}, {
$lookup: {
from: 'documents_nodes',
localField: 'relateds',
foreignField: 'id',
as: 'newRelateds'
}
}, {
$group: {
_id: '$id',
relateds: {
'$push': '$newRelateds'
}
}
}]

Mongodb lookup and get output flat tree structure?

I have two collections points collection and users collection here i want to do aggregation based on userid.
here i am trying aggregation and everything is working fine but i need to modify result
db.points.aggregate([
$match: {
store: "001",
date: {$lte: ISODate("2017-11-10T08:15:39.736Z"), $gte: ISODate("2017-11-10T08:15:39.736Z")}
},
{
$lookup: {
from: "users",
localField: "userid",
foreignField: "_id",
as: "user"
}
},
{
$project:
{
_id:0,
"userdata.purchasedetails.purchaseid" : 1,
"userdata.purchasedetails.data" : 1,
usermobinumber: { "$arrayElemAt": [ "$user.mobinumber", 0 ] }
}}
Data stored like this
{
"userpoint": "2",
"date":ISODate("2017-11-10T08:15:39.736Z"),
"store":"001",
"userid":[
objectID("5a7565ug8945rt67"),
objectID("8a35553d3446rt78")
],
"userdata":{
"profile":[],
"purchasedetails":
[{
"purchaseid":"dj04944",
"data":"awe"
}]
}
}
getting result like this :
{
"usermobinumber":"9611",
"userdata":{
"purchasedetails":
[{
"purchaseid":"dj04944",
"data":"awe"
}]
}
my expecting result:
I dont want tree structure output i need like this
{
"usermobinumber":"9611",
"purchaseid":"dj04944",
"data":"awe"
}
how can i do this help me out
You can do something like this here,
db.points.aggregate([
//Whatever you are doing here now (lookup and match)
{
$project:{
"usermobinumber":"$usermobinumber",
"purchaseid":"$userdata.purchasedetails.purchaseid"
}
}
])
This will give you:
{
"usermobinumber" : "9611",
"purchaseid" : [
"dj04944"
]
}
EDIT:
db.points.aggregate([
//Whatever you are doing here now (lookup and match)
{
{
$project:{
"usermobinumber":"$usermobinumber",
"purchaseid":"$userdata.purchasedetails.purchaseid",
"data":"$userdata.purchasedetails.data"
}
}
}
])
This will give you
{
"usermobinumber":"9611",
"purchaseid":["dj04944"],
"data":["awe"]
}
purchaseid and data are array because purchasedetails is an array.
You can add an $unwind stage to flatten the result....

MongoDb how to count fields

I have some data like this:
{
user_id:1,
group_id:123,
discription:null
},
{
user_id:1,
group_id:321,
discription:null
},
{
user_id:1,
group_id:123,
discription:"text"
},
{
user_id:1,
group_id:321,
discription:"another text"
},
{
user_id:1,
group_id:321,
discription:"another another text"
},
etc..
I want get all groups (group by group_id), count of document in each group, and count of documents in that group that have "discription" with null value and not null value.
So i need results like:
[
[group_id:123, count:2, isNull:1, isNotNull:1],
[group_id:321, count:3, isNull:1, isNotNull:2]
]
I know how to group fields by "group_id" and get the "count", but I don't know how to get the info about "description".
db.collection.aggregate([
{
$match:{
user_id:1
}
},
{
$group:{
_id:'$group_id',
group_id:{$first:'$group_id'},
count:{'$sum':1}
}
}
])
Please check this Query
db.testing.aggregate([{
$match:{
user_id:1
}
},
{$project:{_id:0,user_id:1,group_id: 1,description: { $ifNull: [ "$discription", 1 ] }}},
{
$group:{
_id:'$group_id',
group_count:{'$sum':1},
IsNull:{"$sum":"$description"}
}
},
{$project:{_id:0,group_id:"$_id", count:"$group_count", isNull:"$IsNull", isNotNull:{ $subtract:["$group_count","$IsNull"]}}}
])