MongoDB Join Fields Aggregation - mongodb

In MongoDB, I have a collection of different movies with their years.
Consider these documents:
{
"_id" : ObjectId("63a994974ac549c5ea982d2b"),
"title" : "Destroyer",
"year" : 1907
},
{
"_id" : ObjectId("63a994974ac549c5ea982d2a"),
"title" : "Aquaman",
"year" : 1902
},
{
"_id" : ObjectId("63a994974ac549c5ea982d29"),
"title" : "On the Basis of Sex",
"year" : 1907
},
{
"_id" : ObjectId("63a994974ac549c5ea982d28"),
"title" : "Holmes and Watson",
"year" : 1902
},
{
"_id" : ObjectId("63a994974ac549c5ea982d27"),
"title" : "Conundrum: Secrets Among Friends",
"year" : 1902
},
{
"_id" : ObjectId("63a994974ac549c5ea982d26"),
"title" : "Welcome to Marwen",
"year" : 1907
},
{
"_id" : ObjectId("63a994974ac549c5ea982d25"),
"title" : "Mary Poppins Returns",
"year" : 1997
},
{
"_id" : ObjectId("63a994974ac549c5ea982d24"),
"title" : "Bumblebee",
"year" : 2004
}
I want to show the year or years with the fewest movies showing the number of movies from that year. So, with the previous documents, you can see there are 2 years with the same count of movies. Years: 1907 and 1902.
Therefore, I want to join those years in a single document. I tried this code:
var query1 = {$group: {"_id": "$year",
"movies": {$sum:1},
"Years": {$addToSet:"$year"},
}}
var stages = [query1]
db.movies.aggregate(stages)
However, the output is this:
{
"_id" : 1907,
"movies" : 3,
"Years" : [ 1907 ]
},
{
"_id" : 1902,
"movies" : 3,
"Years" : [ 1902 ]
},
I do not want that. The expect output that I want is this:
{
"_id" : 1902,
"movies" : 3,
"Years" : [ 1907, 1902 ]
}
Once you get that, what I want to show as a final output is this:
{
"_id" : [1907, 1902],
"movies" : 3
}
I do not know how to do that. I cannot join all these years in an array...
How can I get that? How can I obtain the previous output?
Thanks so much for your attention. Whatever you need, ask it pls...

One option is to do what #Joe suggested:
db.collection.aggregate([
{$group: {
_id: "$year",
count: {$sum: 1}
}},
{$group: {
_id: "$count",
years: {$push: "$_id"}
}},
{$sort: {_id: -1}},
{$limit: 1},
{$project: {_id: 0, count: "$_id", years: 1}}
])
See how it works on the playground example

If two values are Equal make a Condition and change datatype after trying to concatenates save in Same array or use trim to four letter ',' and store Database then Fetch the movies value and this new Concatenate array.
I am not sure This I just give my IdEA

The _id for the group is $year, so every year will have a separate document.
What you should do is 2 group stages, the first with _id: "$year" to count the number of movies per year, and the second, with the _id: $movies with the addToSet to group years with the same number of movies

Related

MongoDB Aggregation leap-year

I am learning MongoDB NoSQL, and I have a problema about it.
Consider these documents:
{
"_id" : ObjectId("63a994974ac549c5ea982d2b"),
"title" : "Destroyer",
"year" : 2018
},
{
"_id" : ObjectId("63a994974ac549c5ea982d2a"),
"title" : "Aquaman",
"year" : 2014
},
{
"_id" : ObjectId("63a994974ac549c5ea982d29"),
"title" : "On the Basis of Sex",
"year" : 1996
},
{
"_id" : ObjectId("63a994974ac549c5ea982d28"),
"title" : "Holmes and Watson",
"year" : 1940
},
{
"_id" : ObjectId("63a994974ac549c5ea982d27"),
"title" : "Conundrum: Secrets Among Friends",
"year" : 1957
},
{
"_id" : ObjectId("63a994974ac549c5ea982d26"),
"title" : "Welcome to Marwen",
"year" : 2000
},
{
"_id" : ObjectId("63a994974ac549c5ea982d25"),
"title" : "Mary Poppins Returns",
"year" : 1997
},
{
"_id" : ObjectId("63a994974ac549c5ea982d24"),
"title" : "Bumblebee",
"year" : 2004
},
I am trying to get all title that they have a leap year and I want to get the "count" of all title.
So, I tried this code:
var q1 = {$project: {
leap: {
"$and": [
"$eq": ["$divide"["$year", 4], 0],
{
"$or":[{"$ne": ["$divide"["$year",100],0]},
{"$eq": ["$divide"["$year", 400],0]}]
}
]
}
}}
var q2 = {$group: {"_id": null, "total": {$sum:1}}}
var etapas = [q1,q2]
db.genres.aggregate(etapas)
But, with this code, I only get in 'q1', the variable 'leap', all false conditionals. So, I do not know how to get the year when the logical operator is true or false. Even more, I want to count all the leap years of the documents I have been given before.
The expect output for the documents before is this:
{
"_id": leap year
"count": 3
}
How can I do that? How can I type when I've got a false result?
Thanks so much for your attention in this problem. Whatever ask you need, do it without any problem.
To check if a number is divisible by another, you need to see if the remainder is 0 using $mod, such as:
db.collection.aggregate([
{$project: {
leap: {
"$and": [
{"$eq": [{"$mod": ["$year",4]}, 0]},
{"$or": [
{"$ne": [{"$mod": ["$year", 100]}, 0]},
{"$eq": [{"$mod": ["$year", 400]}, 0] }
]}
]
}
}},
{$group: {
"_id": "$leap",
"total": {$sum: 1}
}}
])
Playground

How to sort element in array of arrays in MongoDB?

I'm learning MongoDB's sorting. I have a collection with documents that look like this:
{
"_id" : ObjectId("5d0c13fbfdca455311248d6f"),
"borough" : "Brooklyn",
"grades" :
[
{ "date" : ISODate("2014-04-16T00:00:00Z"), "grade" : "A", "score" : 5 },
{ "date" : ISODate("2013-04-23T00:00:00Z"), "grade" : "B", "score" : 2 },
{ "date" : ISODate("2012-04-24T00:00:00Z"), "grade" : "A", "score" : 4 }
],
"name" : "C & C Catering Service",
"restaurant_id" : "40357437"
}
And I want to sort all restaurants in Brooklyn by their most recent score.
Right now I have:
db.restaurants.find({borough: "Brooklyn"}).sort()
But I don't know how to proceed. Any help on how to sort this by most recent score, which is the first entry in grades?
This is not possible in mongo with a find query, you'll have to use an aggregation like this one:
db.collection.aggregate([
{
$unwind: "$grades"
},
{
$sort: {"grades.date": -1}
},
{
$group: {
_id:"$_id",
grades: {$push:"$grades"},
resturant_id: {$first: "$resturant_id",
name: {$first: "$name"},
borough: {$first: "$borough"}
}
}
]);
EDIT:
collection.find({}).sort({'grades.0.date': -1});

mongodb group on month part of date

I have the data below. I want to run a query to group my results by category and month and return a total.
The first desired output is a nested array of month names with aggregated totals for all 12 months by category. Months that are not present in the data will still be returned but have 0 as the total.
{"category":"Auto","month":{"Jan":9.12,"Feb":9.12,"Mar":0,...}},
{"category":"Fees","month":{..."Apr":0,"May":4.56,"Jun":0,...}},
{"category":"Travel","month":{..."Oct":0,"Nov":4.56,"Dec":0}}
The second desired output is an array that doesn't have nested months...
{"category":"Auto","Jan":4.56,"Feb":4.56,"Mar":0,...},
{"category":"Fees",..."Apr":0,"May":4.56,"Jun":0,...},
{"category":"Travel",..."Oct":0,"Nov":0,"Dec":4.56,}
How can these results be queried with Mongodb? Here is the sample input data:
{
"_id" : ObjectId("583f6e6d14c8042dd7c153f1"),
"transid" : 1,
"category": "Auto",
"postdate" : ISODate("2016-01-28T05:00:00.000Z"),
"total" : 4.56 }
{
"_id" : ObjectId("583f6e6d14c8042dd7c153f2"),
"transid" : 5,
"category": "Auto",
"postdate" : ISODate("2016-01-31T05:00:00.000Z"),
"total" : 4.56 }
{
"_id" : ObjectId("583f6e6d14c8042dd7c153f3"),
"transid" : 3,
"category": "Auto",
"postdate" : ISODate("2016-02-28T05:00:00.000Z"),
"total" : 4.56 }
{
"_id" : ObjectId("583f6e6d14c8042dd7c153f4"),
"transid" : 2,
"category": "Auto",
"postdate" : ISODate("2016-02-31T05:00:00.000Z"),
"total" : 4.56 }
{
"_id" : ObjectId("583f6e6d14c8042dd7c153f5"),
"transid" : 6,
"category": "Fees",
"postdate" : ISODate("2016-05-16T05:00:00.000Z"),
"total" : 4.56 }
{
"_id" : ObjectId("583f6e6d14c8042dd7c153f6"),
"transid" : 7,
"category": "Travel",
"postdate" : ISODate("2016-11-13T05:00:00.000Z"),
"total" : 4.56 }
I'm new to mongodb and come from a sql background so I feel I've been thinking about all this in sql terms.
Below is what I've tried so far based on reading through the mongodb documentation and attempting to translate "sql think". I'm essentially trying to filter to a specified year (in this case 2016). I'm then grouping by category and date. And in the last step I plan to use project and the $cond keyword to "subaggregate" on month by specifying the start and end dates of each month and then assign the month name as Jan, Feb, etc... I have syntax errors and I don't know if this is the right or best approach.
db.transactions.aggregate(
[
{ $match: { "postdate": {$gte: new Date("2016-01-01")}} },
{ $group: { _id: {"category":"$category","postdate":"$postdate"} , "total": { $sum: "$debit" } } },
{ $project: {"_id":0,"category":"$_id.category",
"month":{$cond: {
$and:
[
{ $gte: ["$_id.postdate", new Date("2016-01-01")] },
{ $lt: ["$_id.postdate", new Date("2016-02-01")] },
]
},"Jan":"$sum"}
//repeat for all other 11 months...
}}
]
)
If you want to group by month you can use month operator. eg:
db.transaction.aggregate([{$group:{_id:{ $month:"$postdate"}, "total":{$sum:1}}}])
I am not sure what project is doing for you.

MongoDB group by subdocument counts and year

Here is a sample of my document from collection called products:
{
"_id" : "B000KIT6LQ",
"brand" : "unknown",
"category" : "Electronics",
"price" : "11.99",
"title" : "Scosche KA2067B 2005..."
"reviews" : [
{
"date" : ISODate("1969-12-31T23:59:59Z"),
"score" : 5,
"user_id" : "AK7M5Y7E9O3L7",
"sentiment" : 0.5,
"text" : "Bought this so I ...",
"user_gender" : "female",
"voted_total" : 0,
"voted_helpful" : 0,
"user_name" : "Alex",
"summary" : "It is what it is"
},
{
"date" : ISODate("1969-12-31T23:59:59Z"),
"score" : 5,
"user_id" : "A26VRLMPEA8IDR",
"sentiment" : 0.352,
"text" : "Years ago I worked as an...",
"user_gender" : "male",
"voted_total" : 0,
"voted_helpful" : 0,
"user_name" : "Jack R. Smith",
"summary" : "Great Kit"
},
{
"date" : ISODate("1969-12-31T23:59:59Z"),
"score" : 4,
"user_id" : "A1TGBDVX3QXCRH",
"sentiment" : 0.19318181818181818,
"text" : "This insert works great in my ...",
"user_gender" : "female",
"voted_total" : 0,
"voted_helpful" : 0,
"user_name" : "J. Reed",
"summary" : "Fits great in my 2006 Spectra5"
}
]
}
I have many documents with multiple categories. I am trying to create a mongo query which will result in all categories with the number of reviews (subdocument) per year. I have to group by categories and year, and get the count for number of reviews.
This is the query that I have got so far:
db.products.aggregate([
{ $unwind : "$reviews" },
{ $group: {
_id: {category: "$category", date: "$reviews.date.getFullYear()"},
count: { $sum: 1 }}},
{$sort:{"count": -1}}
])
For some reason the getFullYear() method is not working for me. If I group by $reviews.date I get the results.
Any pointers on getting the query right is appreciated.
Thanks
You can't use JavaScript functions like getFullYear() in your aggregate pipeline, you need to use the equivalent aggregation Date operator, which in this case is $year.
db.products.aggregate([
{ $unwind : "$reviews" },
{ $group: {
_id: {category: "$category", date: {$year: "$reviews.date"}},
count: { $sum: 1 }}},
{$sort:{"count": -1}}
])

Order by Array of documents in aggregation mongodb

I have a collection with documents like these
{
"Day" : 1
"Name" : "Fred",
"Badges" : ["STAR", "FAST"]
}
{
"Day" : 2
"Name" : "Fred",
"Badges" : ["STAR", "NEWBIE", "SLOW"]
}
{
"Day" : 3
"Name" : "Fred",
"Badges" : ["STAR", "NEWBIE", "FAST"]
}
{
"Day" : 1
"Name" : "Frank",
"Badges" : ["STAR","GO"]
}
{
"Day" : 2
"Name" : "Frank",
"Badges" : ["STAR"]
}
I aggregated these documents for all month in order to return a statistic like this
{
Name : "Fred"
Badges : [{Name : "STAR", Count : 3},
{Name : "NEWBIE", Count : 2},
{Name : "FAST", Count : 2},
{Name : "SLOW", Count : 1}
]
}
and the same thing for Frank.
{
Name : "Fred"
Badges : [{Name : "STAR", Count : 2},
{Name : "GO", Count : 1}
]
}
I have a problem with the sort .
How can I order for badge value STAR ?
Could I change the aggregation query in order to produce different result ?
I think that a structure like this can be good solution but I don't understand how to obtain it
{Name : "Fred"
STAR : 2
GO : 1}
Thanks
db.a.aggregate([
{$unwind: "$Badges"},
{$group: {_id: {name: "$Name", badge: "$Badges"}, numberOfBadges: { $sum: 1}}},
{$project: {_id: 0,name: "$_id.name", badge: "$_id.badge", numberOfBadges:1, orderField: {$cond: [{$eq: ["$_id.badge", "STAR"]}, "$numberOfBadges", 0]}}},
{$sort: {orderField:-1}}])
As far as I know you cannot have dynamic field names in aggregation framework so you can't have a field name as STAR. But you can make a trick by adding a new field to your document such as orderField and set the number of numberOfBadges to that document if the badge is STAR, otherwise set 0 to that field. Then sort on orderField.