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 }
Related
My primary goal is to print titles are having number of grades greater than four, i can achieve it with below query,
db.students.aggregate({$project : { title:1 ,_id : 0, count: {$size : "$grades"}}},{$match: {"count": {$gt:4}}})
But if grades array have empty values how can i remove them, tried this but not giving correct output.
db.students.aggregate({$project : { title:1 ,_id : 0, count: {$size : "$grades"}}},{$match: {"count": {$gt:4},grades : {$ne:''}}})
You can use $filter to remove empty grades before you run $size:
db.students.aggregate([
{$project : { title:1 ,_id : 0, count: { $size : { $filter: { input: "$grades", cond: { $ne: [ "$$this", '' ] } } }}}},
{$match: {"count": {$gt:4}}}
])
Let's explain this with step by step of different different queries:
All possible values in the collection grades:
> db.grades.find()
{ "_id" : ObjectId("5cb2ff50d33f6ed856afe577"), "title" : "abc", "grades" : [ 12, 23, 1 ] }
{ "_id" : ObjectId("5cb2ff55d33f6ed856afe578"), "title" : "abc", "grades" : [ 12, 23 ] }
{ "_id" : ObjectId("5cb2ff5cd33f6ed856afe579"), "title" : "abc", "grades" : [ 12, 23, 10, 100, 34 ] }
{ "_id" : ObjectId("5cb2ff63d33f6ed856afe57a"), "title" : "abc", "grades" : "" }
{ "_id" : ObjectId("5cb2ff66d33f6ed856afe57b"), "title" : "abc", "grades" : [ ] }
{ "_id" : ObjectId("5cb2ff6bd33f6ed856afe57c"), "title" : "abc", "grades" : [ 1, 2, 3, 4, 5 ] }
Just filtered empty grades records as:
> db.grades.aggregate([{$match: {grades: {$ne:''}} }])
{ "_id" : ObjectId("5cb2ff50d33f6ed856afe577"), "title" : "abc", "grades" : [ 12, 23, 1 ] }
{ "_id" : ObjectId("5cb2ff55d33f6ed856afe578"), "title" : "abc", "grades" : [ 12, 23 ] }
{ "_id" : ObjectId("5cb2ff5cd33f6ed856afe579"), "title" : "abc", "grades" : [ 12, 23, 10, 100, 34 ] }
{ "_id" : ObjectId("5cb2ff66d33f6ed856afe57b"), "title" : "abc", "grades" : [ ] }
{ "_id" : ObjectId("5cb2ff6bd33f6ed856afe57c"), "title" : "abc", "grades" : [ 1, 2, 3, 4, 5 ] }
Now project the grades count values in a variable along with required other columns.
> db.grades.aggregate([{$match: {grades: {$ne:''}} }, {$project: {_id:0, title:1, count: {$size: "$grades"} } }])
{ "title" : "abc", "count" : 3 }
{ "title" : "abc", "count" : 2 }
{ "title" : "abc", "count" : 5 }
{ "title" : "abc", "count" : 0 }
{ "title" : "abc", "count" : 5 }
Now match required condition of grades array count greater than 4 as below:
> db.grades.aggregate([{$match: {grades: {$ne:''}} }, {$project: {_id:0, title:1, count: {$size: "$grades"} } }, {$match: {count: {$gte: 4}}} ])
{ "title" : "abc", "count" : 5 }
{ "title" : "abc", "count" : 5 }
>
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"
}
}
}
])
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?
Considering the following document in my mongo DB instance :
{
"_id": 1,
"people": [
{"id": 1, "name": "foo"},
{"id": 2, "name": "bar"},
/.../
],
"stats": [
{"peopleId": 1, "workHours": 24},
{"peopleId": 2, "workHours": 36},
/.../
}
Each element in my collection represent the work of every employee in my company, each weeks. As an important note, peopleId may change from one week to another !
I would like to get all weeks where foo worked more than 24 hours. As you can see, the format is kinda annoying since the people name and the work hours are separated in my database. A simple $and is not enough.
I wonder if, using some $ and $elemMatch I can achieve doing this query.
Can I use this to group the "people" entities with "stats" entities ?
Query to get foo worked more than 24 hours.
db.collection.aggregate([
{$unwind: { path : "$people"}},
{$unwind: { path : "$stats"}},
{$match: { "people.name" : "foo"}},
{$group: {
_id: "$_id",
peopleIdMoreThan24: { $addToSet: {
$cond : { if : { $and : [ {"$eq" : ["$people.id", "$stats.peopleId" ] },
{"$gt" : ["$stats.workHours", 24] }]} , then : "$people.id", else: "Not satisfying the condition"}}}
}
},
{$unwind: { path : "$peopleIdMoreThan24" }},
{$match: { "peopleIdMoreThan24" : {$nin : [ "Not satisfying the condition"]}}},
]);
Data in collection:-
/* 1 */
{
"_id" : 1,
"people" : [
{
"id" : 1,
"name" : "foo"
},
{
"id" : 2,
"name" : "bar"
}
],
"stats" : [
{
"peopleId" : 1,
"workHours" : 24
},
{
"peopleId" : 2,
"workHours" : 36
}
]
}
/* 2 */
{
"_id" : 2,
"people" : [
{
"id" : 1,
"name" : "foo"
},
{
"id" : 2,
"name" : "bar"
}
],
"stats" : [
{
"peopleId" : 1,
"workHours" : 25
},
{
"peopleId" : 2,
"workHours" : 36
}
]
}
/* 3 */
{
"_id" : 3,
"people" : [
{
"id" : 1,
"name" : "foo"
},
{
"id" : 2,
"name" : "bar"
}
],
"stats" : [
{
"peopleId" : 1,
"workHours" : 25
},
{
"peopleId" : 2,
"workHours" : 36
}
]
}
Output:-
The output has document id and people id of foo worked more than 24 hours.
/* 1 */
{
"_id" : 3,
"peopleIdMoreThan24" : 1
}
/* 2 */
{
"_id" : 2,
"peopleIdMoreThan24" : 1
}
I have records in a collection of the following format.
//One parent record
{
"_id" : "someDocID",
"title" : "some title",
"analytics" : [
{
"_id" : "analyticsID1",
"timeSpent" : [
{
"time" : 14,
"pageNo" : 1
},
{
"time" : 4,
"pageNo" : 2
},
{
"time" : 3,
"pageNo" : 1
},
{
"time" : 1,
"pageNo" : 2
}
]
},
{
"_id" : "analyticsID2",
"timeSpent" : [
{
"time" : 12,
"pageNo" : 10
},
{
"time" : 15,
"pageNo" : 11
},
{
"time" : 26,
"pageNo" : 12
},
{
"time" : 13,
"pageNo" : 11
},
{
"time" : 17,
"pageNo" : 10
},
{
"time" : 30,
"pageNo" : 11
}
]
}
]
}
The "pageNo" field contains repeated values. I need to group the pageNo field with adding their respective "time".
This is my required output. ( after "$unwind" operation on analytics )
//Two records after "$unwind" on analytics
{
"_id" : "someDocID",
"title" : "some title",
"analytics" : {
"_id" : "analyticsID1",
"timeSpent" : [
{
"time" : 17, //14+3
"pageNo" : 1
},
{
"time" : 5, //4+1
"pageNo" : 2
}
]
}
}
{
"_id" : "someDocID",
"title" : "some title",
"analytics" : {
"_id" : "analyticsID2",
"timeSpent" : [
{
"time" : 29, //12+17
"pageNo" : 10
},
{
"time" : 58, //15+13+30
"pageNo" : 11
},
{
"time" : 26,
"pageNo" : 12
}
]
}
}
I've tried various combinations of aggregate, group, unwind and project but still can't quite get there and would really appreciate any suggestions.
Here is an aggregate I came up with to provide the output that you mentioned in your comment above. As an FYI, the more elements you have in an array that needs to be unwound, the more memory usage you'll have, and it will take an exponentially amount of time based on array sizes. I would highly recommend you structure your data differently if your arrays are not limited in length.
var aggregrate = [{
$unwind: '$analytics'
}, {
$unwind: '$analytics.timeSpent'
}, {
$group: {
_id: {
analytics_id: '$analytics._id',
pageNo: '$analytics.timeSpent.pageNo'
},
title:{$first:'$title'},
time: {
$sum: '$analytics.timeSpent.time'
},
}
}, {
$group: {
_id: '$_id.analytics_id',
title:{$first:'$title'},
timeSpent: {
$push: {
time: '$time',
pageNo: '$_id.pageNo'
}
}
}
}, ];
This Outputs:
[{
"_id": "analyticsID1",
"title" : "some title",
"timeSpent": [{
"time": NumberInt(17),
"pageNo": NumberInt(1)
}, {
"time": NumberInt(5),
"pageNo": NumberInt(2)
}]
}, {
"_id": "analyticsID2",
"title" : "some title",
"timeSpent": [{
"time": NumberInt(26),
"pageNo": NumberInt(12)
}, {
"time": NumberInt(29),
"pageNo": NumberInt(10)
}, {
"time": NumberInt(58),
"pageNo": NumberInt(11)
}]
}]