Combining 2 collections after filtering 2nd one based on ids from first one - mongodb

I have 2 collections like this:
Batches:
{
"userId": "",
"productId: "",
}
Products:
{
"_id": "",
"name": "",
}
What i want to do is filter the batches by userId first. And then get all products which i get ids in filtered elements of batches. I have seen a lot of examples but mostly goes the opposite way.
My final result i would like to look like this:
[{
name: "product 1",
batches: [...]
}]

You can try lookup with aggregation pipeline,
$lookup in branches collection, pass productId in let, and match expression condition for for productId and filter userId
db.products.aggregate([
{
$lookup: {
from: "branches",
let: { productId: "$_id" },
pipeline: [
{
$match: {
userId: "1", // filter user id here
$expr: { $eq: ["$$productId", "$productId"] }
}
}
],
as: "brnaches"
}
}
])
Playground

Maybe something like this:
mongos> db.Batches.find()
{ "_id" : ObjectId("5ff9a66fda146da4e1359dc9"), "userId" : "User1", "productId" : "product1" }
{ "_id" : ObjectId("5ff9a675da146da4e1359dca"), "userId" : "User2", "productId" : "product1" }
{ "_id" : ObjectId("5ff9a682da146da4e1359dcb"), "userId" : "User3", "productId" : "product2" }
{ "_id" : ObjectId("5ff9a6deda146da4e1359dcc"), "userId" : "User3", "productId" : "product1" }
mongos> db.Products.find()
{ "_id" : "product1", "name" : "product 1" }
{ "_id" : "product2", "name" : "product 2" }
{ "_id" : "product3", "name" : "product 3" }
{ "_id" : "product4", "name" : "product 4" }
mongos> db.Batches.aggregate([ {$match:{"userId":{$in:["User3","User1"]}}}, { $lookup:{ from:"Products" , localField:"productId" , foreignField:"_id" , as:"match" } } , {$unwind:"$match"} , {$project:{ name:"$match.name" , batches :{userId:"$userId" , productId:"$productId"} }} , {$group:{_id:"$name" , batches:{$addToSet:"$batches"}}} , {$project:{_id:0, name:"$_id" , batches:1}} ])
{ "batches" : [ { "userId" : "User1", "productId" : "product1" }, { "userId" : "User3", "productId" : "product1" } ], "name" : "product 1" }
{ "batches" : [ { "userId" : "User3", "productId" : "product2" } ], "name" : "product 2" }
mongos>

Related

Spring Data Mongo - How to get the nested distinct array for nested value?

I'm taking a reference from : Spring Data Mongo - Perform Distinct, but doesn't wants to pull embedded documents in results and asking another questions.
I want to find technology list where "subdeptCd" : "1D". How can we do that ?
{
"firstName" : "Laxmi",
"lastName" : "Dekate",
.....
.......
.....
"departments" : {
"deptCd" : "Tax",
"deptName" : "Tax Handling Dept",
"status" : "A",
"subdepts" : [
{
"subdeptCd" : "1D",
"subdeptName" : "Tax Clearning",
"desc" : "",
"status" : "A"
"technology" : [
{
"technologyCd" : "4C",
"technologyName" : "Cloud Computing",
"desc" : "This is best certficate",
"status" : "A"
}
]
}
]
},
},
{
"firstName" : "Neha",
"lastName" : "Parate",
.....
.......
.....
"departments" : {
"deptCd" : "Tax Rebate",
"deptName" : "Tax Rebate Handling Dept",
"status" : "A",
"subdepts" : [
{
"subdeptCd" : "1D",
"subdeptName" : "Tax Clearning",
"desc" : "",
"status" : "A"
"technology" : [
{
"technologyCd" : "9C",
"technologyName" : "Spring Cloud",
"desc" : "This is best certficate post Google",
"status" : "A"
}
]
}
]
},
}
You can get distinct technologies (technology array elements) with this aggregation:
db.depts.aggregate( [
{
$unwind: "$departments.subdepts"
},
{
$unwind: "$departments.subdepts.technology"
},
{
$match: { "departments.subdepts.subdeptCd": "1D" }
},
{
$group: { _id: "$departments.subdepts.technology.technologyCd", tech: { $first: "$departments.subdepts.technology" } }
},
{
$replaceRoot: { newRoot: "$tech" }
}
] )

MongoDB - double $group make 2nd ObjectID a nested document

I have a collection that after unwind has this structure (I've deleted information which I think is not relevant to the question)
{
"_id" : ObjectId("1"),
"Members" : {
"City" : "New York"
},
"Group_name" : "Group A"
}
{
"_id" : ObjectId("2"),
"Members" : {
"City" : "Seattle"
},
"Group_name" : "Group A"
}
{
"_id" : ObjectId("3"),
"Members" : {
"City" : "Seattle"
},
"Group_name" : "Group A"
}
{
"_id" : ObjectId("4"),
"Members" : {
"City" : "New York"
},
"Group_name" : "Group B"
}
{
"_id" : ObjectId("5"),
"Members" : {
"City" : "Los Angeles"
},
"Group_name" : "Group B"
}
{
"_id" : ObjectId("6"),
"Members" : {
"City" : "Los Angeles"
},
"Group_name" : "Group B"
}
I have used double Object Id to get a result like this:
{ "_id" : { "group" : "A", "city" : "New York" }, "totalMembers" : 1 }
{ "_id" : { "group" : "A", "city" : "Seattle" }, "totalMembers" : 2 }
{ "_id" : { "group" : "B", "city" : "New York" }, "totalMembers" : 1 }
{ "_id" : { "group" : "B", "city" : "Los Angeles" }, "totalMembers" : 2 }
I want to be able to obtain a document with the following structure:
{
"_id" : "A",
"Cities" : {
"New York" : 1,
"Seattle" : 2
}
}
{
"_id" : "B",
"Cities" : {
"New York" : 1,
"Los Angeles" : 2
}
}
This is my code so far, I haven't been able to group by 'group' and then by 'city'
db.Users_Group.aggregate([
{"$unwind":"$Members"},
{"$group":{"_id":{"group":"$Group_Name","City":"$Members.City"},"totalUsers":{"$sum":1}}},
{"$group":{"_id":"$_id.grupo","total":{"$sum":1}}}
] )
With this I get the sum of all members in that group not separated by cities. How can I nest a document of the cities with the total of users of that city within each group? Appreciate any help on this. Thanks in advance.
You need to run one more $group and prepare the data for $arrayToObject which takes an array of k-v pairs:
db.collection.aggregate([
// your current aggregation stages
{
$group: {
_id: "$_id.group",
Cities: { $push: { k: "$_id.city", v: "$totalMembers" } }
}
},
{
$project: {
_id: 1,
Cities: { $arrayToObject: "$Cities" }
}
}
])
Mongo Playground

Problems aggregating MongoDB

I am having problems aggregating my Product Document in MongoDB.
My Product Document is:
{
"_id" : ObjectId("5d81171c2c69f45ef459e0af"),
"type" : "T-Shirt",
"name" : "Panda",
"description" : "Panda's are cool.",
"image" : ObjectId("5d81171c2c69f45ef459e0ad"),
"created_at" : ISODate("2019-09-17T18:25:48.026+01:00"),
"is_featured" : false,
"sizes" : [
"XS",
"S",
"M",
"L",
"XL"
],
"tags" : [ ],
"pricing" : {
"price" : 26,
"sale_price" : 8
},
"categories" : [
ObjectId("5d81171b2c69f45ef459e086"),
ObjectId("5d81171b2c69f45ef459e087")
],
"sku" : "5d81171c2c69f45ef459e0af"
},
And my Category Document is:
{
"_id" : ObjectId("5d81171b2c69f45ef459e087"),
"name" : "Art",
"description" : "These items are our artsy options.",
"created_at" : ISODate("2019-09-17T18:25:47.196+01:00")
},
My aim is to perform aggregation on the Product Document in order to count the number of items within each Category. So I have the Category "Art", I need to count the products are in the "Art" Category:
My current aggregate:
db.product.aggregate(
{ $unwind : "$categories" },
{
$group : {
"_id" : { "name" : "$name" },
"doc" : { $push : { "category" : "$categories" } },
}
},
{ $unwind : "$doc" },
{
$project : {
"_id" : 0,
"name" : "$name",
"category" : "$doc.category"
}
},
{
$group : {
"_id" : "$category",
"name": { "$first": "$name" },
"items_in_cat" : { $sum : 1 }
}
},
{ "$sort" : { "items_in_cat" : -1 } },
)
Which does actually work but not as I need:
{
"_id" : ObjectId("5d81171b2c69f45ef459e082"),
"name" : null, // Why is the name of the category no here?
"items_in_cat" : 4
},
As we can see the name is null. How can I aggregate the output to be:
{
"_id" : ObjectId("5d81171b2c69f45ef459e082"),
"name" : "Art",
"items_in_cat" : 4
},
We need to use $lookup to fetch the name from Category collection.
The following query can get us the expected output:
db.product.aggregate([
{
$unwind:"$categories"
},
{
$group:{
"_id":"$categories",
"items_in_cat":{
$sum:1
}
}
},
{
$lookup:{
"from":"category",
"let":{
"id":"$_id"
},
"pipeline":[
{
$match:{
$expr:{
$eq:["$_id","$$id"]
}
}
},
{
$project:{
"_id":0,
"name":1
}
}
],
"as":"categoryLookup"
}
},
{
$unwind:{
"path":"$categoryLookup",
"preserveNullAndEmptyArrays":true
}
},
{
$project:{
"_id":1,
"name":{
$ifNull:["$categoryLookup.name","NA"]
},
"items_in_cat":1
}
}
]).pretty()
Data set:
Collection: product
{
"_id" : ObjectId("5d81171c2c69f45ef459e0af"),
"type" : "T-Shirt",
"name" : "Panda",
"description" : "Panda's are cool.",
"image" : ObjectId("5d81171c2c69f45ef459e0ad"),
"created_at" : ISODate("2019-09-17T17:25:48.026Z"),
"is_featured" : false,
"sizes" : [
"XS",
"S",
"M",
"L",
"XL"
],
"tags" : [ ],
"pricing" : {
"price" : 26,
"sale_price" : 8
},
"categories" : [
ObjectId("5d81171b2c69f45ef459e086"),
ObjectId("5d81171b2c69f45ef459e087")
],
"sku" : "5d81171c2c69f45ef459e0af"
}
Collection: category
{
"_id" : ObjectId("5d81171b2c69f45ef459e086"),
"name" : "Art",
"description" : "These items are our artsy options.",
"created_at" : ISODate("2019-09-17T17:25:47.196Z")
}
{
"_id" : ObjectId("5d81171b2c69f45ef459e087"),
"name" : "Craft",
"description" : "These items are our artsy options.",
"created_at" : ISODate("2019-09-17T17:25:47.196Z")
}
Output:
{
"_id" : ObjectId("5d81171b2c69f45ef459e087"),
"items_in_cat" : 1,
"name" : "Craft"
}
{
"_id" : ObjectId("5d81171b2c69f45ef459e086"),
"items_in_cat" : 1,
"name" : "Art"
}

MongoDB query with multiple conditions

I have data with multiple documents :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e487"),
"empId" : "2"
"type" : "Admin",
"city" : "Mumbai"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e488"),
"empId" : "3"
"type" : "Admin",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e489"),
"empId" : "4"
"type" : "User",
"city" : "Mumbai"
}
I want to get data according to my multiple conditions :
condition 1:- {"type" : "WebUser", "city" : "Pune"}
condition 2:- {"type" : "WebUser", "city" : "Pune"} & {"type" : "User", "city" : "Mumbai"}
I want below result when run condition 1 :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
When I run second condition :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e489"),
"empId" : "4"
"type" : "User",
"city" : "Mumbai"
}
I want above result by one query,
Currently I am using below aggregate query,
db.emp.aggregate([
{ $match: { '$and': [
{"type" : "WebUser", "city" : "Pune"},
{"type" : "User", "city" : "Mumbai"}
] } },
{ $group: { _id: 1, ids: { $push: "$empId" } } }
])
Above query work for first condition & fails for other. Please help me.
For the second condition, you can use the $in operator in your query as:
db.emp.find({
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
})
If you want to use in aggregation:
db.emp.aggregate([
{
"$match": {
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
}
},
{ "$group": { "_id": null, "ids": { "$push": "$empId" } } }
])
or simply use the distinct() method to return an array of distinct empIds that match the above query as:
var employeeIds = db.emp.distinct("empId", {
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
});
If you are looking for the AND operator
This example checks if a field exists AND is null
db.getCollection('TheCollection').find({
$and: [
{'the_key': { $exists: true }},
{'the_key': null}
]
})
This example checks if a field has 'value1' OR 'value2'
db.getCollection('TheCollection').find({
$or: [
{'the_key': 'value1'},
{`the_key': 'value2'}
]
})
When just checking for null, the return contains non-existing fields plus fields with value null
db.getCollection('TheCollection').find({'the_key': null})
You can use mongo db $or operator.
db.emp.find({ $or: [
{ "type": "WebUser", "city": "Pune" },
{ "type": "user", "city": "Mumbai"}
]})
You can pass conditions in the array.
For more reference see mongo docs
Display the document where in the “StudName” has value “Ajay Rathod”.
db.Student.find({name:"ajay rathod"})
{ "_id" : ObjectId("5fdd895cd2d5a20ee8cea0de"), "
Retrieve only Student Name and Grade.
db.Student.find({},{name:1,grade:1,_id:0})
{ "name" : "dhruv", "grade" : "A" }
{ "name" : "jay", "grade" : "B" }
{ "name" : "abhi", "grade" : "C" }
{ "name" : "aayush", "grade" : "A" }
{ "name" : "sukhdev", "grade" : "B" }
{ "name" : "dhruval", "grade" : "B" }
{ "name" : "ajay rathod", "grade" : "D" }

How to count occurrences in nested document in mongodb?

In the situation where there is an array nested within an array, how would I count the number of a specific value? For example, I want to count the number of "answers" in the document below. query should return that there are 2 apples and 1 banana.
{
"_id" : ObjectId("52c1d909fc7fc68ddd999a73"),
"name" : "Some survey",
"questions" : [
{
"_id" : ObjectId("52c1e250fc7fc68ddd999a75"),
"answers" :
[
{
"userId" : "some GUIDs",
"answer" : "apple"
},
{
"userId" : "some GUID",
"answer" : "apple"
},
{
"userId" : "some GUID",
"answer" : "banana"
}
],
"questionText" : "blah blah blah...",
"questionType" : "multiple choice"
}
]
}
There are a few ways to approach this depending on how much data you need to process. You could use the Aggregation Framework in MongoDB 2.2+, or possibly Map/Reduce. See Aggregation Commands Comparison for a summary of the features and limitations.
Here's an example using the Aggregation Framework:
db.fruit.aggregate(
// Limit matching documents (can take advantage of index)
{ $match: {
"_id" : ObjectId("52c1d909fc7fc68ddd999a73")
}},
// Unpack the question & answer arrays
{ $unwind: "$questions" },
{ $unwind: "$questions.answers" },
// Group by the answer values
{ $group: {
_id: "$questions.answers.answer",
count: { $sum: 1 }
}}
)
For your sample document this returns:
{
"result" : [
{
"_id" : "banana",
"count" : 1
},
{
"_id" : "apple",
"count" : 2
}
],
"ok" : 1
}
Here is one way to skin your cat using the aggregate framework. Once you learn it you can do a lot of good stuff with you data.
db.so.aggregate([{$unwind:"$questions"}, {$unwind:"$questions.answers"}, {$group:{_id:"$questions.answers.answer", fruit_count:{$sum:1}}}])
Gives me this:
{
"result" : [
{
"_id" : "banana",
"fruit_count" : 1
},
{
"_id" : "apple",
"fruit_count" : 2
}
],
"ok" : 1
To be doubly sure, I added this doc:
db.so.insert({
"_id" : ObjectId("52c1d909fc7fc68ddd999a75"),
"name" : "Some survey",
"questions" : [
{
"_id" : ObjectId("52c1e250fc7fc68ddd999a75"),
"answers" :
[
{
"userId" : "some GUIDs",
"answer" : "orange"
},
{
"userId" : "some GUID",
"answer" : "orange"
},
{
"userId" : "some GUID",
"answer" : "banana"
}
],
"questionText" : "blah blah blah...",
"questionType" : "multiple choice"
}
]
})
My query now gives me:
{
"result" : [
{
"_id" : "orange",
"fruit_count" : 2
},
{
"_id" : "banana",
"fruit_count" : 2
},
{
"_id" : "apple",
"fruit_count" : 2
}
],
"ok" : 1
}