Nested grouping in Mongodb - mongodb

I am working on grouping the MongoDB documents and updating the sample in the below link
https://mongoplayground.net/p/c2s8KmavPBp
I am expecting the output to look like
[
{
"group" : "one",
"details":[{
"avgtime" : 9.359833333333333,
"title": "Scence 1"
},{
"avgtime" : 9.359833333333333,
"title": "Scence 2"
},{
"avgtime" : 9.359833333333333,
"title": "Scence 3"
}]
},
{
"group" : "two",
"details":[{
"avgtime" : 9.359833333333333,
"title": "Scence 1"
}]
}
]
How to rename the field _id to the group and merge the two elements containing title: scence 1 and display their average time
Note: Question and sample link updated

Query
group by both field, to do the calculation
group by the one field and use the calculated avgtime
group the specific(here 2 fields) first and then the more general, using the values from the specific
PlayMongo
aggregate(
[{"$group":
{"_id": {"group": "$group", "title": "$details.title"},
"avgtime":
{"$avg":
{"$divide": [{"$subtract": ["$endTime", "$startTime"]}, 60000]}}}},
{"$group":
{"_id": "$_id.group",
"group": {"$first": "$_id.group"},
"details": {"$push": {"title": "$_id.title", "avgtime": "$avgtime"}}}},
{"$unset": ["_id"]}])

Related

how to sort mongodb document to appear document with certain key value to appear first,

// i want to appear document with isFeatured:"true" at first
{ "_id": "625c13ea5c5d3f49f152783b",
"name": "helmet 1",
"description": "gold",
"category": [
"helmet"
],
"price": "25000",
"stock": 25,
"user": "",
"isFeatured": true // with this property to come first and "isFeatured:false" later
}
You can invoke sort on the cursor to sort the documents in myCollection on the isFeatured field in descending order.
db.myCollection.find().sort({ isFeatured: -1 })
Or on aggregation pipeline query
db.myCollection.aggregate([{ $sort: { isFeatured: -1 } }])

Bucketing based on whether a field exists or not

I have data that looks like this:
{ fname: "bob", age: 33, description: "brown eyes"}
{ fname: "sally", age: 20, description: "tall"}
{ fname: "jim", age: 48 }
I'm trying to, in one query, get results that look something like this:
{
hasDescription: ["bob", "sally"],
noDescription: ["jim"]
}
I've been playing around with $bucketAuto with something like this:
{
groupBy: { "$cond": [{ "$eq": [ "description", null ] }, true, false ] },
buckets: 2,
}
but am not having any success. Any guidance would be appreciated!
Query
group with expression on group, to make 2 groups, one that have description and one that doesnt (instead of a field to create the group, we create 2 groups based on a condtion)
the other 3 stages is to fix the structure to be like the expected output
*i don't think you need bucket, bucket is for range of values
*instead of the bellow facet could be used also, but facet is like running the aggregation 1 time per field so its slower
Playmongo (mouse to the end of each stage => see in/out of each stage)
aggregate(
[{"$group":
{"_id":
{"$cond":
[{"$ne": [{"$type": "$description"}, "missing"]}, "hasDescription",
"noDescription"]},
"names": {"$push": "$fname"}}},
{"$replaceRoot":
{"newRoot":
{"$cond":
[{"$eq": ["$_id", "noDescription"]}, {"noDescription": "$names"},
{"hasDescription": "$names"}]}}},
{"$group": {"_id": null, "docs": {"$mergeObjects": "$$ROOT"}}},
{"$replaceRoot": {"newRoot": "$docs"}}])

MongoDB get all documents with highest value in collection

Context:
I have a MongoDB full of Documents like this:
[
{
"_id": "615dc97907f597330c510279",
"code": "SDFSDFSDF",
"location": "ABC1",
"week_number": 39,
"year": 2020,
"region": "NA"
},
....
{
"_id": "615dc97907f597330c51027a",
"code": "SDFSGSGR",
"location": "ABC1",
"week_number": 42,
"year": 2021,
"region": "EU"
},
....
{
"_id": "615dc97607f597330c50ff50",
"code": "GGSFHSFS",
"location": "DEF2",
"week_number": 42,
"year": 2021,
"region": "EU",
"audit_result": {
"issues_found": true,
"comment": "comment."
}
}
]
Problem
I am trying to write an aggregation which should return all object with the highest "week_number" and highest "year". So with the example above, I want to return the full documents of _id "615dc97907f597330c51027a" and "615dc97607f597330c50ff50".
I tried multiple approaches. like first sorting and then grouping but with no success.
currently I have something like this, which seemed logical, but it returns all documents not only the one with the highest week and year
[
{
'$match': {
'$expr': {
'$max': '$week_number',
'$max': '$year'
}
}
}
]
You can do the followings in an aggregation pipeline:
$group by year and week_number; push the _id into an array for future lookup
$sort by year: -1 and week_number: -1
$limit to get the first grouping, which is the one with max year and week_number
$lookup the original documents using the previously stored array of _id in step 1
$replaceRoot to get back the documents
Here is the Mongo playground for your reference.

mongo aggregate group and find one

I have collections in mongodb: which stores as:
{"tag":"count1","value":100,"ts":1544423706} {"tag":"count2","value":1002,"ts":1544423706} {"tag":"count1","value":101,"ts":1544423806} {"tag":"count2","value":1003,"ts":1544423806} {"tag":"count1","value":102,"ts":1544423906} {"tag":"count2","value":1004,"ts":1544423906}
so my problem is how can I get the result out of "tag" is count1 , "ts" is larger than 1544423800's first item. As I describled: I want to find the result as:
{"tag":"count1","value":101,"ts":1544423806} {"tag":"count2","value":1003,"ts":1544423806}
do I need use aggregate to group the tag and then get the first item which larger than given "ts", I am new to aggregate function in MongoDB.
db.index.aggregate([{"$match": {"tag": {"$in":["count1","count2"]},"ts": {"$gt":1544423800}}
},
{"$group": {"_id": "$tag",
"tags": {"$push": "$$ROOT"}}
},
])
which the result is not one item for each tag so I want to limit one item , what am I have to do
thank you I have solve this by :
db.index.aggregate(
[
{"$match": {"tag": {"$in":["count1","count2"]},
"ts": {"$gt":1545730000}}
},
{"$group": {"_id": "$tag",
"value": {"$first": "$value"}
}
},
]
)

Combined lookup and embed in the same MongoDB collection depending on root document structure

I want to make a conditional lookup with Mongo in the following way: a root document contains either a link (customerId) to the customers collection or directly embeds a customer, like this:
{
"_id" : 1,
"item": "item1",
"customer": { _id: 1, "name": "Jane Doe" }
},
{
"_id": 2,
"item": "item2",
"customerId": 1
}
customers collection:
{ _id: 1, "name": "Jane Johnson" }
The customers collection stores the current versions of customers; to maintain consistency, members of the items collection will contain just ids of customer. But if I want to freeze an item so it holds the version of its customer at a certain time, I will embed that customer directly into the item in question.
When searching for items I want them to appear uniformly (i.e. regardless whether customer is looked up or embedded it will appear as embedded field):
e.g.
[{
"_id" : 1,
"item": "item1",
"customer": { _id: 1, "name": "Jane Doe" } // historical version of Jane (embedded)
},
{
"_id": 2,
"item": "item2",
"customer": { _id: 1, "name": "Jane Johnson" } // current version of Jane by lookup
}]
Question 1: is this the right approach and if not what is the best practice for handling cases like this?
Question 2: if my approach is correct, how to best use aggregation framework to achieve this?
Thanks!
The answers:
Yes, you can achieve it with aggregation framework and it is one of the possible solutions (the other possible solution would be to implement it in your program).
Just use $lookup (for data merging from other collection) and $project (for final result generation) pipeline stages.
Query example:
db.getCollection('items').aggregate([
{
$lookup: {
from: "customers",
localField: "customerId",
foreignField: "_id",
as: "customerData"
}
},
{
$project: {
"item": 1,
"customer": {
$cond: {
if: {$gt: [{$size: "$customerData"}, 0]},
then: {$arrayElemAt: ["$customerData", 0]},
else: "$customer"
}
}
}
}
]);