How can I find the following output using a mongo query - mongodb

How can I find the following output using a mongo query
{
"_id" : ObjectId("5ec387342971ea13815924e0"),
"fullName" : "pqr",
"pendingRequest" : [],
},
{
"_id" : ObjectId("5ec387552971ea13815924e1"),
"fullName" : "xyz",
"pendingRequest" : [],
},
{
"_id" : ObjectId("5ec3876f2971ea13815924e2"),
"fullName" : "abc",
"pendingRequest" : [
{
"_id" : "5ec387342971ea13815924e0"
},
{
"_id" : "5ec387552971ea13815924e1"
}
],
}
This is my data looks like.I want to retrieve "fullName" whose "_id" is in the "pendingRequest" array of "abc" user.I have tried using "$match", "$lookup" stages but didn't get expected output.

To make use of lookup you need to store the "_id" in pending request as ObjectId's.
Try following code it will work.
db.collectionName.aggregate([
{
$match: {fullName: "abc"}
},
{
$unwind: "$pendingRequest"
},
{
$lookup: {from: "so", localField: "pendingRequest._id", foreignField: "_id", as: "a"}
}
])
Also, one more suggestion, it would be better to use pendingRequest as array instead of object array.
pendingRequest: [ObjectId("5ec387342971ea13815924e0"),ObjectId("5ec387552971ea13815924e1")]

Related

MongoDB Aggregation - How can I" $lookup" a nested document "_id"?

I successfully thanks to the help of the people here managed to $lookup two IDs in my document with their representive document in another collection. The next step I need to take is to further lookup a "nested" ID (refering to a document in another collection).
I tried to simply put another $lookup pipeline up but that just worked part-wise.
So it happens that an "empty" document was included into the chieftain attributes and all other attributes of chieftain where somewhat removed.
See my current aggregate:
db.getCollection('village').aggregate([
{
"$match": { _id: "111" }
},
{
"$lookup": {
from: "character",
localField: "chieftainId",
foreignField: "_id",
as: "chieftain"
}
},
{
"$lookup": {
from: "character",
localField: "villagerIds",
foreignField: "_id",
as: "villagers"
}
},
{
"$lookup": {
from: "bloodline",
localField: "chieftain.bloodline",
foreignField: "_id",
as: "chieftain.bloodline"
}
},
{ "$project" : { "villagerIds" : 0, "chieftainId" : 0}},
{ "$unwind" : "$chieftain" }
])
The result of that is the following:
{
"_id" : "111",
"name" : "MyVillage",
"reputation" : 0,
"chieftain" : {
"bloodline" : []
},
"villagers" : [
{
"_id" : "333",
"name" : "Bortan",
"age" : 21,
"bloodlineId" : "7f02191f-90af-406e-87ff-41d5b4387999",
"villageId" : "foovillage",
"professionId" : "02cbb10a-6c0f-4249-a932-3f40e12d32c5"
},
{
"_id" : "444",
"name" : "Blendi",
"age" : 21,
"bloodlineId" : "b3a8ffeb-27aa-4e2e-a8e6-b382554f326a",
"villageId" : "foovillage",
"professionId" : "45dc9350-c84a-491d-a49a-524834dd5773"
}
]
}
I expected the chieftain part to look like this (this is how the chieftain document looks like without the 'last' $lookup I added):
"chieftain" : {
"_id" : "222",
"name" : "Bolzan",
"age" : 21,
"bloodlineId" : "7c2926f9-2f20-4ccf-846a-c9966970fa9b", // this should be resolved/lookedup
"villageId" : "foovillage",
},
At the point of the lookup, chieftan is an array, so setting the chieftan.bloodline replaces the array with an object containing only the bloodline field.
Move the { "$unwind" : "$chieftain" } stage to before the bloodline lookup stage so the lookup is dealing with an object.

How can I use MongoDB's aggregate with $lookup to replace an attribute that holds an id with the whole document?

I have two collections:
user:
{
"_id" : "9efb42e5-514d-44bd-a4b8-6f74e6313ec2",
"name" : "Haralt",
"age" : 21,
"bloodlineId" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"villageId" : "foovillage"
}
bloodlines:
{
"_id" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"name" : "Tevla",
"legacy" : 0
}
Now I'd like to do an aggregate to replace user.bloodlineId with the whole bloodline document.
This is what I tried to far:
db.getCollection('character').aggregate([
{
"$match": { _id: "9efb42e5-514d-44bd-a4b8-6f74e6313ec2" }
},
{
"$lookup": {
from: "bloodline",
localField: "bloodlineId",
foreignField: "_id",
as: "bloodline"
}
}])
The result is almost where I want it:
{
"_id" : "9efb42e5-514d-44bd-a4b8-6f74e6313ec2",
"name" : "Haralt",
"age" : 21,
"bloodlineId" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"villageId" : "foovillage",
"bloodline" : [
{
"_id" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"name" : "Tevla",
"legacy" : 0
}
]
}
Only two issues here. The first is that bloodlineId is still there and bloodline was just added to the result. I'd like to have bloodline replace the bloodlineId attribute.
The second problem is that bloodline is an array. I'd love to have it a single object.
I think this pipeline might do the trick:
[
{
"$match": {
_id: "9efb42e5-514d-44bd-a4b8-6f74e6313ec2"
}
},
{
"$lookup": {
from: "bloodlines",
localField: "bloodlineId",
foreignField: "_id",
as: "bloodline"
}
},
{
$project: {
"age": 1,
"bloodlineId": {
$arrayElemAt: [
"$bloodline",
0
]
},
"name": 1,
"villageId": 1
}
}
]
Mongo Playground
If there's anything I'm missing, please let me know!

In MongoDB, how to calculate an average ($avg) per class ($group) after joining several collections ($lookup)

Here I have two collections :
conso
{
"_id" : ObjectId("5684f3c454b1fd6926c322fd"),
"client" : "1",
"conso" : "4"
}
{
"_id" : ObjectId("56d82612b63f1f31cf906003"),
"client" : "1",
"conso" : "2"
}
{
"_id" : ObjectId("56d82612b63f1c31cf906004"),
"client" : "2",
"conso" : "10"
}
{
"_id" : ObjectId("59301c39028afaf3450e2890"),
"client" : "2",
"conso" : "20"
}
{
"_id" : ObjectId("59301c39029afaf4450e2885"),
"client" : "3",
"conso" : "18"
}
{
"_id" : ObjectId("59301c39030afaf4450e2885"),
"client" : "3",
"conso" : "12"
}
class
{
"_id" : ObjectId("5f6d219d345f0066299c1fd6"),
"CLIENT" : "1",
"class" : "A"
}
{
"_id" : ObjectId("5e6d219c345f0066299c1fd6"),
"CLIENT" : "2",
"class" : "A"
}
{
"_id" : ObjectId("5e6d02c97d9426227fa00401"),
"CLIENT" : "3",
"class" : "B"
}
I would like to have :
{"class" : "A", avg : 9}
{"class" : "B", avg : 15}
My code :
db.conso.aggregate([
{$group: {_id : {class: "$class"}, avg: { $avg: "$conso" }}},
{$lookup: {from: "class", localField: "client", foreignField: "CLIENT", as: "class_info"}}
]);
and I obtain :
{ "_id" : { "class" : null }, "avg" : 11, "class_info" : [ ] }
After having the below changes to your conso collection and your query, we can get the desired result.
Modification in conso collection
The attribute conso in the conso collection takes a string value which we need to change it into number, so the modified collection will look like
{"_id":"5684f3c454b1fd6926c322fd","client":"1","conso":4},
{"_id":"56d82612b63f1f31cf906003","client":"1","conso":2},
{"_id":"56d82612b63f1c31cf906004","client":"2","conso":10},
{"_id":"59301c39028afaf3450e2890","client":"2","conso":20},
{"_id":"59301c39029afaf4450e2885","client":"3","conso":18},
{"_id":"59301c39030afaf4450e2885","client":"3","conso":12}
If you are not willing to modify your collection then you can use $toInt to convert the conso string into number, this step will add another pipeline stage( $project ) to our existing query between $lookup and $group. Please note that $toInt will be giving error if the value cannot be converted to int.
Modification to the query
Do the $lookup first and then perform $group and in the group stage _id part need to changed from class: "$class" to class: "$class_info.class"
Modified query will be
db.conso.aggregate([
{ $lookup: {
from: "class",
localField: "client",
foreignField: "CLIENT",
as: "class_info"}
},
{ $group: {_id : {class: "$class_info.class"}, avg: { $avg: "$conso" }}},
]);
Hope it Helps!

Mongodb Aggregate calculate average and add it to the document

I have websites which contains 2 documents:
{
"_id" : ObjectId("58503934034b512b419a6eab"),
"website" : "https://www.stackoverflow.com",
"name" : "Stack Exchange",
"keywords" : [
"helping",
"C#",
"PYTHON"
]
}
{
"_id" : ObjectId("58503934034b512b419a6eab"),
"website" : "https://www.google.com.com",
"name" : "Stack Exchange",
"keywords" : [
"search",
"engine",
]
}
I also have another seo_tracking which contains:
{
"_id" : ObjectId("587373d6f6325811c8a0b3ad"),
"position" : "2",
"real_url" : "https://www.stackoverflow.com",
"created_at" : ISODate("2017-01-09T11:28:22.104Z"),
"keyword" : "helping"
},
{
"_id" : ObjectId("587373d6f6325811c8a0b3ad"),
"position" : "4",
"real_url" : "https://www.stackoverflow.com",
"created_at" : ISODate("2017-01-09T11:28:22.104Z"),
"keyword" : "C#"
}
etc.. This contains around 100+ documents
What I want to do is is aggregate the seo_tracking with website on the specific URL (www.stackexchange (in websites) would match www.stackoverflow.com in (seo_tracking)) which I can do fine. However, I would like to return for each of the websites the following:
{
"_id" : ObjectId("587373d6f6325811c8a0b3ad"),
"website":"https://www.stackoverflow.com",
"avg_position" : "2"
}
Then for Google etc.. Even if the avg_position is 0 .. I have tried the following:
db.seo_tracking.aggregate([
{
$lookup:
{
from: "websites",
localField: "real_url",
foreignField: "website",
as: "post_websites"
},
},
{
"$group": {
_id:null,
avg_position:{$avg:"$position"}
}
}
])
However, this just produces:
{
"_id" : null,
"avg_position" : 2.0
}
What I need to do is have website and ideally also need the ID
Any ideas to where I'm going wrong here?
You can try something like this. You'll need to $unwind to access the fields from joined collection and change your grouping key to use the _id from joined collection to get average for each website:
db.seo_tracking.aggregate([{
$lookup: {
from: "website",
localField: "real_url",
foreignField: "website",
as: "post_websites"
},
}, {
$unwind: "$post_websites"
}, {
"$group": {
_id: "$post_websites._id",
avg_position: {
$avg: "$position"
},
website: {
$first: "$real_url"
}
}
}])

Mongodb:Right way to collect data from two collections?

I have two collections: one is items and the second one is user_item_history. I want to fetch items with their status. Status of each item is stored in user_item_history, and other details of the item are in the items collection. we have to filter data for particular user and category of item. so user_id and category is in user_item_history collection.
user_item_history:
{
"_id" : NumberLong(25424),
"_class" : "com.samepinch.domain.registration.UserItemHistory",
"user_id" : NumberLong(25416),
"item_id" : NumberLong(26220),
"catagoryPreference" : "BOTH",
"preference" : 0.6546536707079772,
"catagory" : "FOOD",
"status" : 1,
"createdDate" : ISODate("2015-09-02T07:50:36.760Z"),
"updatedDate" : ISODate("2015-09-02T07:55:24.105Z")
}
items:
{
"_id" : NumberLong(26220),
"_class" : "com.samepinch.domain.item.Item",
"itemName" : "Shoes",
"categoryName" : "SHOPPING",
"attributes" : [
"WESTERN",
"CASUAL",
"ELEGANT",
"LATEST"
],
"isAccessed" : false,
"imageUrl" : "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg",
"catagoryPreference" : "FEMALE",
"startDate" : ISODate("2015-11-26T18:30:00Z"),
"endDate" : ISODate("2015-11-27T18:30:00Z"),
"location" : {
"coordinates" : [
77.24149558372778,
28.56973445677584
],
"type" : "Point",
"radius" : 2000
},
"createdDate" : ISODate("2015-11-16T10:49:11.858Z"),
"updatedDate" : ISODate("2015-11-16T10:49:11.858Z")
}
As the final result, I would like to have documents of this format:
{
item_id:26220,
status:1,
imageUrl: "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg"
}
Update to MongoDB 3.2 and you'll be able to use the $lookup aggregation stage, which works similarly to SQL joins.
One-to-many relationship
If there are many corresponding user_item_history documents for each items document, you can get a list of item statuses as an array.
Query
db.items.aggregate([
{
$lookup:
{
from: "user_item_history",
localField: "_id",
foreignField: "item_id",
as: "item_history"
}
},
{
$project:
{
item_id: 1,
status: "$item_history.status",
imageUrl: 1
}
}])
Example Output
{
"_id" : NumberLong(26220),
"imageUrl" : "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg",
"status" : [ 1 ]
},
{
"_id" : NumberLong(26233),
"imageUrl" : "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg",
"status" : [ 1, 2 ]
}
One-to-one relationship
If there's only one corresponding history document for every item, you can use the following approach to get the exact format you requested:
Query
db.items.aggregate([
{
$lookup:
{
from: "user_item_history",
localField: "_id",
foreignField: "item_id",
as: "item_history"
}
},
{
$unwind: "$item_history"
},
{
$project:
{
item_id: 1,
status: "$item_history.status",
imageUrl: 1
}
}])
Example Output
{
"_id" : NumberLong(26220),
"imageUrl" : "0bd2838e-9349-432a-a200-6e6b659e853eitemcompressed.jpg",
"status" : 1
}
Please bear in mind that with every additional aggregation pipeline stage you add, the performance deteriorates. So you may prefer the one-to-many query even if you have a one-to-one relationship.
Applying filtering
In your edit, you added this:
we have to filter data for particular user and category of item. so user_id and category is in user_item_history collection
To filter your results, you should add a $match step to your query:
db.items.aggregate([
{
$lookup:
{
from: "user_item_history",
localField: "_id",
foreignField: "item_id",
as: "item_history"
}
},
{
$unwind: "$item_history"
},
{
$match:
{
"item_history.user_id": NumberLong(25416),
"item_history.catagory": "FOOD"
}
},
{
$project:
{
item_id: 1,
status: "$item_history.status",
imageUrl: 1
}
}])
Please note that "category" is misspelled as "catagory" in your example data, so I also had to misspell it in the query above.