MongoDB average aggregation doesn't work properly - mongodb

I'm newbie to NoSQL databases. What I want is to show the title, url and the avg(ratings).
The sample data looks like the following:
{
"_id" : ObjectId("52b3833bd3e98582d2bfb628"),
"author" : {
"name" : "Graydon Hoare",
"email" : "graydon#gmail.com"
},
"title" : "Why Rust ditched pure functions",
"body" : "sth",
"url" : "http://thread.gmane.org/gmane.comp.lang.rust.devel/3674/focus=3855",
"date" : ISODate("2013-04-30T13:23:00.000Z"),
"starred" : 105,
"ratings" : [
3,
5,
3,
2,
4,
1,
3,
3,
3,
2,
3
],
"comments" : [
{
"user" : "tr0lltherapy",
"upVotes" : 18,
"downVotes" : 2,
"text" : "something",
"replies" : [
{
"user" : "thedeemon",
"upVotes" : 10,
"downVotes" : 0,
"text" : "something"
},
{
"user" : "mcandre",
"upVotes" : 0,
"downVotes" : 5,
"text" : "Performance? There are already a slew of performant languages. Assembler, C, C++, Go. What does Rust actually offer that's new and useful in this category, other than using my favorite abbreviation for the named function keyword, fn?"
},
{
"user" : "lacosaes0",
"upVotes" : 30,
"downVotes" : 6,
"text" : "Particular emphasis on memory safety."
}
]
},
{
"user" : "hypster",
"upVotes" : 30,
"downVotes" : 2,
"text" : "tl;dr everybody was type-fu fighting",
"replies" : [
{
"user" : "homoiconic",
"upVotes" : 15,
"downVotes" : 0,
"text" : "Here comes the Big Boss, Hu! Simon Peyton-Jones."
}
]
}
],
"tags" : [
"Rust",
"Computer",
"Programming"
],
"draft" : true,
"published" : true
}
I have tried the following query, but it doesn't work properly and puts null value in the average. I don't know how should I fix it.
db.getCollection('links').aggregate(
[
{
$match: {
"author.email": /#gmail.com$/
}
},
{
$project: {
_id: 0,
title: 1,
url: 1,
avgRatings: {
$avg: "$Ratings"
}
}
}
])
Expected output is:
title: "Why Rust ditched pure functions", url: "http://thread.gmane.org/gmane.comp.lang.rust.devel/3674/focus=3855",
avgRatings: 2.90

You have a typo, $Ratings; use $ratings as below. Aggregation syntax is case sensitive.
db.getCollection('links').aggregate(
[
{
$match: {
"author.email": /#gmail.com$/
}
},
{
$project: {
_id: 0,
title: 1,
url: 1,
avgRatings: {
$avg: "$ratings"
}
}
}
])

Related

Mongodb query to Display the number of movies based on the number of directors a movie has using split

I have a movieDetails.json database and the collection name is a movie then our teacher wants us to Display the number of movies based on the number of directors a movie has
a. Hint you might have to use the javascript function split
Since the director is not an array, it is only a string so I can't count how many directors are there because it will always come out as 1 since its a string so I want to split that string so that I can get an array so that I can count it. But unfortunately, I don't know the query that will solve this problem.
ex. of expected output:
{_id:1 , value: 100}
{_id:2 , value: 200} etc.
_id being the number of directors a document has
db.movie.find().pretty()
{
"_id" : ObjectId("5b107bec1d2952d0da9046e1"),
"title" : "A Million Ways to Die in the West",
"year" : 2014,
"rated" : "R",
"runtime" : 116,
"countries" : [
"USA"
],
"genres" : [
"Comedy",
"Western"
],
"director" : "Seth MacFarlane",
"writers" : [
"Seth MacFarlane",
"Alec Sulkin",
"Wellesley Wild"
],
"actors" : [
"Seth MacFarlane",
"Charlize Theron",
"Amanda Seyfried",
"Liam Neeson"
],
"plot" : "As a cowardly farmer begins to fall for the mysterious new woman in town, he must put his new-found courage to the test when her husband, a notorious gun-slinger, announces his arrival.",
"poster" : "http://ia.media-imdb.com/images/M/MV5BMTQ0NDcyNjg0MV5BMl5BanBnXkFtZTgwMzk4NTA4MTE#._V1_SX300.jpg",
"imdb" : {
"id" : "tt2557490",
"rating" : 6.1,
"votes" : 126592
},
"tomato" : {
"meter" : 33,
"image" : "rotten",
"rating" : 4.9,
"reviews" : 188,
"fresh" : 62,
"consensus" : "While it offers a few laughs and boasts a talented cast, Seth MacFarlane's overlong, aimless A Million Ways to Die in the West is a disappointingly scattershot affair.",
"userMeter" : 40,
"userRating" : 3,
"userReviews" : 62945
},
"metacritic" : 44,
"awards" : {
"wins" : 0,
"nominations" : 6,
"text" : "6 nominations."
},
"type" : "movie"
}
{
"_id" : ObjectId("5b107bec1d2952d0da9046e3"),
"title" : "West Side Story",
"year" : 1961,
"rated" : "UNRATED",
"runtime" : 152,
"countries" : [
"USA"
],
"genres" : [
"Crime",
"Drama",
"Musical"
],
"director" : "Jerome Robbins, Robert Wise",
"writers" : [
"Ernest Lehman",
"Arthur Laurents",
"Jerome Robbins"
],
"actors" : [
"Natalie Wood",
"Richard Beymer",
"Russ Tamblyn",
"Rita Moreno"
],
"plot": "Two youngsters from rival New York City gangs fall in love, but tensions between their respective friends build toward tragedy.",
"poster" : "http://ia.media-imdb.com/images/M/MV5BMTM0NDAxOTI5MF5BMl5BanBnXkFtZTcwNjI4Mjg3NA##._V1_SX300.jpg",
"imdb" : {
"id" : "tt0055614",
"rating" : 7.6,
"votes" : 67824
},
"awards" : {
"wins" : 18,
"nominations" : 11,
"text" : "Won 10 Oscars. Another 18 wins & 11 nominations."
},
"type" : "movie"
}
"director" : "Sergio Leone",
"writers" : [
"Sergio Donati",
"Sergio Leone",
"Dario Argento",
"Bernardo Bertolucci",
"Sergio Leone"
],
"actors" : [
"Claudia Cardinale",
"Henry Fonda",
"Jason Robards",
"Charles Bronson"
],
"plot": "Epic story of a mysterious stranger with a harmonica who joins forces with a notorious desperado to protect a beautiful widow from a ruthless assassin working for the railroad.",
"poster" : "http://ia.media-imdb.com/images/M/MV5BMTEyODQzNDkzNjVeQTJeQWpwZ15BbWU4MDgyODk1NDEx._V1_SX300.jpg",
"imdb" : {
"id" : "tt0064116",
"rating" : 8.6,
"votes" : 201283
},
"tomato" : {
"meter" : 98,
"image" : "certified",
"rating" : 9,
"reviews" : 54,
"fresh" : 53,
"consensus" : "A landmark Sergio Leone spaghetti western masterpiece featuring a classic Morricone score.",
"userMeter" : 95,
"userRating" : 4.3,
"userReviews" : 64006
},
"metacritic" : 80,
"awards" : {
"wins" : 4,
"nominations" : 5,
"text" : "4 wins & 5 nominations."
},
"type" : "movie"
}
Demo - https://mongoplayground.net/p/y3kvFnocWKn
Use aggregation query
Read the below links for better understanding
$set
$group
$split
$size
db.movie.aggregate([
{
$match: { director: { $ne: null } }
},
{
$set: { // set directorsCount
directorsCount: {
$size: { // get the size of the array
$split: ["$director", ","] // split by comma will return array
}
}
}
},
{
$group: {
_id: "$directorsCount", // group by directorsCount
value: { $sum: 1 } // count
}
}
])
Shorter version
Demo - https://mongoplayground.net/p/Nt-NDBpN4Ad
db.movie.aggregate([
{
$match: { director: { $ne: null } }
},
{
$group: {
_id: { $size: { $split: [ "$director", "," ] } },
value: { $sum: 1 }
}
}
])
Demo - https://mongoplayground.net/p/f8fuZVIjc-_
If you want to count records where directors are 0
db.movie.aggregate([
{
$group: {
_id: {
$size: {
$ifNull: [
{ $split: [ "$director", "," ] },
[]
]
}
},
value: { $sum: 1 }
}
}
])

Get the maximum of difference of two fields in mongodb

I'm newbie to mongoDB. Here I face with an error while I want to get the maximum value of the difference of two fields.
Here is the structure of data saved in database:
{
"_id" : ObjectId("52b3833bd3e98582d2bfb628"),
"author" : {
"name" : "Graydon Hoare",
"email" : "graydon#gmail.com"
},
"title" : "Why Rust ditched pure functions",
"body" : "sth",
"url" : "http://thread.gmane.org/gmane.comp.lang.rust.devel/3674/focus=3855",
"date" : ISODate("2013-04-30T13:23:00.000Z"),
"starred" : 105,
"ratings" : [
3,
5,
3,
2,
4,
1,
3,
3,
3,
2,
3
],
"comments" : [
{
"user" : "tr0lltherapy",
"upVotes" : 18,
"downVotes" : 2,
"text" : "sth",
"replies" : [
{
"user" : "thedeemon",
"upVotes" : 10,
"downVotes" : 0,
"text" : "sth"
},
{
"user" : "mcandre",
"upVotes" : 0,
"downVotes" : 5,
"text" : "sth"
},
{
"user" : "lacosaes0",
"upVotes" : 30,
"downVotes" : 6,
"text" : "Particular emphasis on memory safety."
}
]
},
{
"user" : "hypster",
"upVotes" : 30,
"downVotes" : 2,
"text" : "tl;dr everybody was type-fu fighting",
"replies" : [
{
"user" : "homoiconic",
"upVotes" : 15,
"downVotes" : 0,
"text" : "Here comes the Big Boss, Hu! Simon Peyton-Jones."
}
]
}
],
"tags" : [
"Rust",
"Computer",
"Programming"
],
"draft" : true,
"published" : true
}
What I want is to get the value of maximum of the subtract of upVotes and downVotes in replies and comments.
db.getCollection('links').aggregate([
{$project: {
_id: "$author",
maxVote: $max: {
$subtract: ["$comments.upVotes", "$comments.downVotes"]
}
}
}
])
I don't know how to fix it!
You can use $map to get the difference for each comment (using $subtract) and then run $max on the output from mapped comments. Additionally you need another nested $max to get the differences from replies, try:
db.col.aggregate([
{
$project: {
maxVote: {
$max: {
$map: {
input: "$comments",
as: "comment",
in: {
$max: {
$concatArrays: [
[ { $subtract: [ "$$comment.upVotes", "$$comment.downVotes" ] } ],
{
$map: {
input: "$$comment.replies",
as: "reply",
in: { $subtract: [ "$$reply.upVotes", "$$reply.downVotes" ] }
}
}
]
}
}
}
}
}
}
}
])
prints:
{ "_id" : ObjectId("..."), "maxVote" : 28 }

MongoDB: How To use $group aggregation to get all data using similar field?

I am trying to perform a query using golang mgo to effectively get similar values from a join.
My structure is like this:
result: [
{
"_id" : 1,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158790),
"arrival_time" : NumberLong(1541158863)
},
{
"_id" : 3,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158908),
},
{
"_id" : 4,
"booking_id" : 95,
"provider_id" : 20,
"type" : "abc",
"time" : NumberLong(1541163544),
"location" : {
"lat" : 30.711858,
"lng" : 76.729649
},
},
{
"_id" : 8,
"booking_id" : 95,
"provider_id" : 20,
"type" : "aaa",
}
]
I have to group data of similar booking_id, Now how can I get a data of simlilar booking id using $group aggregation.I want a data in a following structure::
result: [
0:[
{
"_id" : 1,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158790),
"arrival_time" : NumberLong(1541158863)
},
{
"_id" : 3,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158908),
},
],
1:[
{
"_id" : 4,
"booking_id" : 95,
"provider_id" : 20,
"type" : "abc",
"time" : NumberLong(1541163544),
"location" : {
"lat" : 30.711858,
"lng" : 76.729649
},
},
{
"_id" : 8,
"booking_id" : 95,
"provider_id" : 20,
"type" : "aaa",
}
]
]
I have created a function which is returing result of this collection and using $group like this:
query := []bson.M{
{"$group": bson.M{
"_id": bson.M{"booking_id": "$booking_id"},
"count": bson.M{"$sum": 1}}}}
pipe := getCollection.Pipe(query)
err = pipe.All(&result)
But it will return this output to me:
[
{
"id": 0,
"booking_id": 0,
"provider_id": 0
}
]
Here I am mentioning only two booking ids data, I have 1000 of booking id records in my database.
I want to show data grouped by booking id, Is it possible using mongodb $group aggregation?? Or if not then how can I achieve this thing in mongodb using mgo package for golang.
You can use $group and $$ROOT, which references the document currently being processed in the pipeline.
Your aggregation would be something like:
{
$group: {
_id: '$booking_id',
items: {
$push: '$$ROOT'
}
}
}
Which would result in this:
[
{
"_id": 95,
"items": [
{
"_id" : 1,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158790),
"arrival_time" : NumberLong(1541158863)
},
{
"_id" : 3,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158908),
},
]
}
],
...
EDIT:
If you want to show both booking_id and provider_id, you can group by these both fields and project the data the way you want. Like this:
[
{
$group: {
_id: { 'booking_id': '$booking_id', 'provider_id': '$provider_id' },
items: { $push: '$$ROOT' }
}
},
{
$project: {
_id: 0,
booking_id: '$_id.booking_id',
provider_id: '$_id.provider_id',
items: 1
}
}
]
Giving this structure:
[
{
"booking_id": 96,
"provider_id": 20,
"items": [
{
"_id" : 1,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158790),
"arrival_time" : NumberLong(1541158863)
},
{
"_id" : 3,
"booking_id" : 96,
"provider_id" : 20,
"time" : NumberLong(1541158908),
},
]
}
],
...

How sum & group with mongodb?

I need to retrieve information that is in the mongoDB, but I'm not sure how to do it :(
Here's the structure part of my collection that I have:
[{
"points" : 171,
"superPoints" : 2228,
"username" : "ammanda",
"posts" : [{
"comments" : [ { "username" : "jamlabtra", "comment" : "Top" }, ... ],
"likes" : { "users" : ['jamlabtra', 'mrcbrandon','pauljames'], "qtty" : 67 },
"type" : "feedback"
},{
"comments" : [ { "username" : "mrcbrandon", "comment" : "I liked it" }, ... ],
"likes" : {"users" : ['mrcbrandon','pauljames'], "qtty" : 46 },
"type" : "suggestion"
}],
},{
"points" : 23,
"superPoints" : 423,
"username" : "pauljames",
"posts" : [ {
"comments" : [ { "username" : "jamlabtra", "comment" : "Top" }, ... ],
"likes" : { "users" : ['mrcbrandon'], "qtty" : 12 },
"type" : "feedback"
}, {
"comments" : [ { "username" : "jamlabtra", "comment" : "Cool!!" }, ... ],
"likes" : {"users" : ['pauljames'], "qtty" : 3 },
"type" : "suggestion"
}],
}]
I need this result (requesting by username):
{
'username': 'ammanda',
'posts': {
'feedbackTotal': 58,
'suggestionTotal': 6,
},
'likes': {
'total': 3266,
'likers': 32,
'perParticipant': 3,
'perPost': 2.17
},
'comments': {
'total': 123,
'commenters': 546,
'perParticipant': 1.3,
'perPost': 3.3
},
'points': {
'total': 32145,
},
}
I've already been able to do some things:
mongodb.collection('evaluation').aggregate([
{ $match: { username: 'ammanda' } },
{
$project: {
_id: 0,
username: 1,
points: 1,
posts: {
$size: '$posts'
},
likes: {
$sum: '$posts.likes.qtty'
},
},
},
])
But I can not get the rest of the information. Could you help me, please?

mongodb unwind array nested inside an array of documents

In MongoDB, I need to be able to unwind nested an array in a document inside an array inside the main document.
{
"_id" : ObjectId("5808d700536d1a3d69f4cf51"),
"last_name" : "Maity",
"xiith_mark" : 58,
"id" : "3539488",
"first_name" : "Harshavardhan",
"course_name" : "BE/B.Tech",
"institute_name_string" : "Abhayapuri College, P.O. Abhayapuri",
"profile_percentage" : 45,
"xiith_mark_type" : "Percentage",
"xth_mark_type" : "Percentage",
"date_of_birth" : "14-April-1993",
"xth_mark" : 30,
"last_login" : 1470827224,
"percentage" : 55,
"job_details" : [
{
"status" : NumberLong(6),
"applied_date" : NumberLong(1470831441),
"job_id" : NumberLong(92928),
"contact_viwed_status" : 0,
"label_name" : [
"shortlisted",
"rejected"
],
"questionnaire_status" : 0,
"batch_id" : NumberLong(6),
"call_letter" : NumberLong(812)
},
{
"status" : NumberLong(6),
"applied_date" : NumberLong(1470831441),
"job_id" : NumberLong(92928),
"contact_viwed_status" : 0,
"label_name" : [
"shortlisted",
"rejected"
],
"questionnaire_status" : 0,
"batch_id" : NumberLong(6),
"call_letter" : NumberLong(812)
}
],
"branch_name" : "Applied Electronics",
"candidate_state_name" : "West Bengal",
"candidate_city_name_string" : "Kolkata",
"10" : 10,
"12" : 12,
"skills" : "",
"gender" : "Male",
"fw_id" : "FW15884830",
"cgpa" : 0,
"picture_path" : "",
"hq_passout_year" : 2019
}
Based on the record above I need to count the job labels (job_details.label_name).
I have tried the following query:
db.response.aggregate(
{"$match":type_match},
{"$unwind": "$job_details" },
{"$group":
{
"_id":"$job_details.label_name",
"count": {"$sum": 1 }
}
}
])
The output is:
{
"count": 2,
"_id": [
"shortlisted",
"rejected"
]
}
But I want the output to be:
[
{
"count": 1,
"_id": "shortlisted"
},
{
"count": 1,
"_id": "rejected"
}
]
How can I get this output?
In unwind stage, field should be an array field. If not array field, it treats it as array of 1 element.
From the docs:
Changed in version 3.2: $unwind stage no longer errors on non-array operands. If the operand does not resolve to an array but is not missing, null, or an empty array, $unwind treats the operand as a single element array.
Answer to your query:
db.response.aggregate([
{
$project:
{
"job_details.label_name":1,
_id:0
}
},
{
$unwind:"$job_details.label_name"
},
{
$group:
{
_id:"$job_details.label_name",
count:{$sum:1}
}
}
])
Refer Shell Output