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
Related
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
I am learning MongoDB NoSQL and I am stuck in a problem.
Consider these documents:
{
"_id" : ObjectId("63aad45c008cdce77c2c3f9e"),
"title" : "The Express",
"year" : 2008,
"cast" : "Dennis Quaid",
"genres" : "Sports"
},
{
"_id" : ObjectId("63aad45c008cdce77c2c3fa0"),
"title" : "The Express",
"year" : 2008,
"cast" : "Rob Brown",
"genres" : "Sports"
},
{
"_id" : ObjectId("63aad45c008cdce77c2c3fa2"),
"title" : "The Express",
"year" : 2008,
"cast" : "Omar Benson Miller",
"genres" : "Sports"
},
{
"_id" : ObjectId("63aad45c008cdce77c2c416e"),
"title" : "Semi-Pro",
"year" : 2008,
"cast" : "Will Ferrell",
"genres" : "Sports"
},
{
"_id" : ObjectId("63aad45c008cdce77c2c4170"),
"title" : "Semi-Pro",
"year" : 2008,
"cast" : "Woody Harrelson",
"genres" : "Sports"
},
{
"_id" : ObjectId("63aad45c008cdce77c2c4172"),
"title" : "Semi-Pro",
"year" : 2008,
"cast" : "André Benjamin",
"genres" : "Sports"
}
I am trying to group by "year" and "genres", and count all "title" without repetition.
The code that I try is this:
var query1 = {$group: {"_id": { "year": "$year", "genre": "$genres"}, "count": {$sum:1}}}
var stages = [query1]
db.genres.aggregate(stages)
But this is grouping all the documents and the value of "count" that I get is six when I only have two titles different.
I do not know how to get title with no repeat..
The expect output is as follows:
{
"_id":{
"year": 2008
"genre": "Sports"
},
"count": 2
}
However, with the code that I tried, the output is this:
{
"_id":{
"year": 2008
"genre": "Sports"
},
"count": 6
}
This is wrong, because I only have two different titles in the documents.
How can I solve this? How can I get titles without repetition and with this output?
Thanks so much! Whatever you need to ask, do it please... I am really stuck and I want to learn to do it.
I am trying to group by "year" and "genres", and count all "title" without repetition.
...
But this is grouping all the documents and the value of "count" that I get is six when I only have two titles different.
It sounds to me like you will need to de-duplicate by title before performing this final count. Assuming that different movies never have the same title, something like this would perform that de-duplication:
db.collection.aggregate([
{
$group: {
_id: "$title",
year: {
$first: "$year"
},
genre: {
$first: "genre"
},
}
},
{
$group: {
"_id": {
"year": "$year",
"genre": "$genres",
},
"count": {
$sum: 1
}
}
}
])
The playground demonstration here shows the output is as expected:
[
{
"_id": {
"genre": "Sports",
"year": 2008
},
"count": 2
}
]
Alternatively you could generate an array with distinct values for the movie titles in your current grouping and then calculate its size afterwards. Again with the same assumption about movie titles from above, something like this:
db.collection.aggregate([
{
$group: {
"_id": {
"year": "$year",
"genre": "$genres",
},
"count": {
"$addToSet": "$title"
}
}
},
{
"$addFields": {
"count": {
$size: "$count"
}
}
}
])
Playground demonstration here (with the same output from the previous example).
I have this simple Mongodb document:
{
"_id" : ObjectId("55663d9361cfa81a5c48d54f")
"name" : "Oliver",
"surname" : "Queen",
"age" : 25,
"friends" : [
{
"name" : "Jhon",
"surname" : "Diggle",
"age" : "30"
},
{
"name" : "Barry",
"surname" : "Allen",
"age" : "24"
}
]
}
Is it possbile, using denormalized model as above, to find all Oliver's friends with 24 years old?
I think it's really simple with normalized model; it's enough to do two queries.
For example the following query:
db.collection.find({name:"Oliver", "friends.age":24}, {_id:0, friends:1})
returns an array of Oliver's friends. Is it possible to make a selection of the internal document?
Using aggregation
db.collection.aggregate(
[
{ $match: { "name": "Oliver" }},
{ $unwind: "$friends" },
{ $match: { "friends.age": 24 }},
{ $group: { "_id": "$_id", friends: { "$push": "$friends" }}},
{ $project: { "_id": 0, "friends": 1 }}
]
)
I am trying to formulate a query over the sample bios collection http://docs.mongodb.org/manual/reference/bios-example-collection/:
Retrieve all the persons who received two awards on the same year.
The expected answers are "Ole-Johan Dahl" and "Kristen Nygaard" as for instance the doc for Ole-Johan Dahl is
{
"_id" : 5,
"name" : {
"first" : "Ole-Johan",
"last" : "Dahl"
},
"birth" : ISODate("1931-10-12T04:00:00Z"),
"death" : ISODate("2002-06-29T04:00:00Z"),
"contribs" : [
"OOP",
"Simula"
],
"awards" : [
{
"award" : "Rosing Prize",
"year" : 1999,
"by" : "Norwegian Data Association"
},
{
"award" : "Turing Award",
"year" : 2001,
"by" : "ACM"
},
{
"award" : "IEEE John von Neumann Medal",
"year" : 2001,
"by" : "IEEE"
}
]
}
So far, the best query that I could come up with is the following query using aggregation framework:
db.bios.aggregate([
{$project : { "first_name": "$name.first", "last_name": "$name.last" , "award1" :"$awards", "award2" :"$awards" } },
{$unwind : "$award1"},
{$unwind : "$award2"},
{$project : { "first_name": 1, "last_name": 1, "award1" : 1, "award2" : 1,
"super" : { $and : [ {$eq : ["$award1.year", "$award2.year"]},
{$lt: ["$award1.award", "$award2.award"]}
]
}}
},
{$match : {"super": true}}
])
However I am not happy with this solution because
the query projects awards twice and unwind them in the following step. This will generate quadratic many intermediate documents;
the query computes an auxiliary field "super" which is only used for filtering afterwards.
Is there a better way to formulate this query?
Try the following aggregation pipeline:
db.bios.aggregate([
{
"$unwind": "$awards"
},
{
"$group": {
"_id": {
"year": "$awards.year",
"firstName": "$name.first",
"lastName": "$name.last"
},
"count": { "$sum": 1 },
"award_recepients": { "$push": "$name" }
}
},
{
"$match": { "count": 2 }
},
{
"$project": {
"_id": 0,
"year": "$_id.year",
"award_recepients": 1,
"count": 1
}
}
])
I'm just starting out with mongodb and have been reading through the documentation on aggregation but still struggling to relate equivalent knowledge of sql statements to the methods used in mongo.
I have this data:
{
"_id" : ObjectId("53ac7bce4eaf6de4d5601c19"),
"uid" : ObjectId("53ac7bb84eaf6de4d5601c15"),
"mid" : ObjectId("53ab27504eaf6de4d5601be4"),
"score" : 1
},{
"_id" : ObjectId("53ac7bce4eaf6de4d5601c1a"),
"uid" : ObjectId("53ac7bb84eaf6de4d5601c16"),
"mid" : ObjectId("53ab27504eaf6de4d5601be4"),
"score" : 5
}
...
And I'm trying to get to this result:
{
"uid" : ObjectId("53ac7bb84eaf6de4d5601c15"),
"uid_2" : ObjectId("53ac7bb84eaf6de4d5601c16"),
"mid" : ObjectId("53ab27504eaf6de4d5601be4"),
"score" : 1,
"score_2" : 5,
"difference" : 4
}
...
Where I am comparing every uid against every other uid around a single mid and calculating the difference in their scores (can't be a negative difference, only positive).
Most of the examples I'm running into don't quite fit my requirements and hoping some mongo guru can help me out. Thanks!
As stated, I think your data modelling is a little off here as you need something to "pair" the "matches" as it were. I have a "simplified" case here:
{
"_id" : ObjectId("53ae9da2e24682cac4215e0c"),
"match" : ObjectId("53ae9d78e24682cac4215e0b"),
"score" : 1
}
{
"_id" : ObjectId("53ae9da5e24682cac4215e0d"),
"match" : ObjectId("53ae9d78e24682cac4215e0b"),
"score" : 5
}
{
"_id" : ObjectId("53aea6cde24682cac4215e15"),
"match" : ObjectId("53aea6c1e24682cac4215e14"),
"score" : 2
}
{
"_id" : ObjectId("53aea6e4e24682cac4215e16"),
"match" : ObjectId("53aea6c1e24682cac4215e14"),
"score" : 1
}
{
"_id" : ObjectId("53aea6eae24682cac4215e18"),
"match" : ObjectId("53aea6e6e24682cac4215e17"),
"score" : 2
}
{
"_id" : ObjectId("53aea6ece24682cac4215e19"),
"match" : ObjectId("53aea6e6e24682cac4215e17"),
"score" : 2
}
What that basically represents is the scores for "six" teams in "three" distinct matches.
Given that, my take on getting to results would be this:
db.matches.aggregate([
// Group on matches and find the "min" and "max" score
{ "$group": {
"_id": "$match",
"teams": {
"$push": {
"_id": "$_id",
"score": "$score"
}
},
"minScore": { "$min": "$score" },
"maxScore": { "$max": "$score" }
}},
// Unwind the "teams" array created
{ "$unwind": "$teams" },
// Compare scores for "win", "loss" or "draw"
{ "$group": {
"_id": "$_id",
"win": {
"$min": { "$cond": [
{ "$and": [
{ "$eq": [ "$teams.score", "$maxScore" ] },
{ "$gt": [ "$teams.score", "$minScore" ] }
]},
"$teams",
false
]}
},
"loss": {
"$min": { "$cond": [
{ "$and": [
{ "$eq": [ "$teams.score", "$minScore" ] },
{ "$lt": [ "$teams.score", "$maxScore" ] }
]},
"$teams",
false
]}
},
"draw": {
"$push": { "$cond": [
{ "$eq": [ "$minScore", "$maxScore" ] },
"$teams",
false
]}
},
"difference": {
"$max": { "$subtract": [ "$maxScore", "$minScore" ] }
}
}},
// Just fix up those "draw" results with a [false,false] array
{ "$project": {
"win": 1,
"loss": 1,
"draw": { "$cond": [
{ "$gt": [
{ "$size": { "$setDifference": [ "$draw", [false] ] } },
0
]},
"$draw",
false
]},
"difference": 1
}}
])
And this gives you a quite nice result:
{
"_id" : ObjectId("53ae9d78e24682cac4215e0b"),
"win" : {
"_id" : ObjectId("53ae9da5e24682cac4215e0d"),
"score" : 5
},
"loss" : {
"_id" : ObjectId("53ae9da2e24682cac4215e0c"),
"score" : 1
},
"draw" : false,
"difference" : 4
}
{
"_id" : ObjectId("53aea6c1e24682cac4215e14"),
"win" : {
"_id" : ObjectId("53aea6cde24682cac4215e15"),
"score" : 2
},
"loss" : {
"_id" : ObjectId("53aea6e4e24682cac4215e16"),
"score" : 1
},
"draw" : false,
"difference" : 1
}
{
"_id" : ObjectId("53aea6e6e24682cac4215e17"),
"win" : false,
"loss" : false,
"draw" : [
{
"_id" : ObjectId("53aea6eae24682cac4215e18"),
"score" : 2
},
{
"_id" : ObjectId("53aea6ece24682cac4215e19"),
"score" : 2
}
],
"difference" : 0
}
That is essentially the results per "match" and determines the "difference" between winner and looser while identifying which team "won" or "lost". The final stage there uses some operators only introduced in MongoDB 2.6, but that really is not necessary if you do not have that version available. Or you could actually still do the same thing if you wanted to by using $unwind and some other processing.