How to display "hardest category" based on in which "study" size of notLearnedWords was the highest. MongoDB Aggregation
I have these 3 models:
Study
WordSet
Category
Study model has reference into WordSet, then WordSet has reference into Category.
And based on Studies i'm displaying statistics.
How i can display "The hardest category" based on size of "notLearnedWords" was the highest?
I don't know on which place i should start with that querying.
For now i display "hardestCategory" as element that is most used.
I think that condition would look something like this:
{ $max: { $size: '$notLearnedWords' } } // size of the study with most notLearnedWords
I would achieve a response like this:
"stats": [
{
"_id": null,
"numberOfStudies": 4,
"averageStudyTime": 82.5,
"allStudyTime": 330,
"longestStudy": 120,
"allLearnedWords": 8
"hardestCategory": "Work" // only this field is missing
}
]
I've tried to do it like this:
const stats = await Study.aggregate([
{ $match: { user: new ObjectID(currentUserId) } },
{
$lookup: {
from: 'users',
localField: 'user',
foreignField: '_id',
as: 'currentUser',
},
},
{
$lookup: {
from: 'wordsets',
let: { wordSetId: '$learnedWordSet' },
pipeline: [
{ $match: { $expr: { $eq: ['$_id', '$$wordSetId'] } } },
{
$project: {
_id: 0,
category: 1,
},
},
{ $unwind: '$category' },
{
$group: {
_id: '$category',
count: { $sum: 1 },
},
},
{ $sort: { count: -1 } },
{ $limit: 1 },
{
$lookup: {
from: 'categories',
localField: '_id',
foreignField: '_id',
as: 'category',
},
},
{
$project: {
_id: 0,
category: { $arrayElemAt: ['$category.name', 0] },
},
},
],
as: 'wordSet',
},
},
{
$group: {
_id: null,
numberOfStudies: { $sum: 1 },
averageStudyTime: { $avg: '$studyTime' },
allStudyTime: { $sum: '$studyTime' },
longestStudy: { $max: '$studyTime' },
allLearnedWords: {
$sum: { $size: '$learnedWords' },
},
hardestCategory: {
$first: {
$first: '$wordSet.category',
},
},
studyWithMostNotLearnedWords: { $max: { $size: '$notLearnedWords' } },
},
},
]);
Study
const studySchema = new mongoose.Schema({
name: {
type: String,
},
studyTime: {
type: Number,
},
learnedWords: [String],
notLearnedWords: [String],
learnedWordSet: {
type: mongoose.Schema.Types.ObjectId,
ref: 'WordSet',
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
});
WordSet
const wordSetSchema = new mongoose.Schema({
name: {
type: String,
},
category: {
type: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Category',
required: true,
},
],
},
});
Category
const categorySchema = new mongoose.Schema({
name: {
type: String,
},
});
Related
So I have 3 models user, property, and testimonials.
Testimonials have a propertyId, message & userId. I've been able to get all the testimonials for each property with a pipeline.
Property.aggregate([
{ $match: { _id: ObjectId(propertyId) } },
{
$lookup: {
from: 'propertytestimonials',
let: { propPropertyId: '$_id' },
pipeline: [
{
$match: {
$expr: {
$and: [{ $eq: ['$propertyId', '$$propPropertyId'] }],
},
},
},
],
as: 'testimonials',
},
},
])
The returned property looks like this
{
.... other property info,
testimonials: [
{
_id: '6124bbd2f8eacfa2ca662f35',
userId: '6124bbd2f8eacfa2ca662f29',
message: 'Amazing property',
propertyId: '6124bbd2f8eacfa2ca662f2f',
},
{
_id: '6124bbd2f8eacfa2ca662f35',
userId: '6124bbd2f8eacfa2ca662f34',
message: 'Worth the price',
propertyId: '6124bbd2f8eacfa2ca662f2f',
},
]
}
User schema
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
Property schema
name: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
location: {
type: String,
required: true,
},
Testimonial schema
propertyId: {
type: ObjectId,
required: true,
},
userId: {
type: ObjectId,
required: true,
},
testimonial: {
type: String,
required: true,
},
Now the question is how do I $lookup the userId from each testimonial so as to show the user's info and not just the id in each testimonial?
I want my response structured like this
{
_id: '6124bbd2f8eacfa2ca662f34',
name: 'Maisonette',
price: 100000,
testimonials: [
{
_id: '6124bbd2f8eacfa2ca662f35',
userId: '6124bbd2f8eacfa2ca662f29',
testimonial: 'Amazing property',
propertyId: '6124bbd2f8eacfa2ca662f34',
user: {
_id: '6124bbd2f8eacfa2ca662f29',
firstName: 'John',
lastName: 'Doe',
email: 'jd#mail.com',
}
},
{
_id: '6124bbd2f8eacfa2ca662f35',
userId: '6124bbd2f8eacfa2ca662f27',
testimonial: 'Worth the price',
propertyId: '6124bbd2f8eacfa2ca662f34',
user: {
_id: '6124bbd2f8eacfa2ca662f27',
firstName: 'Sam',
lastName: 'Ben',
email: 'sb#mail.com',
}
}
]
}
You can put $lookup stage inside pipeline,
$lookup with users collection
$addFields, $arrayElemAt to get first element from above user lookup result
Property.aggregate([
{ $match: { _id: ObjectId(propertyId) } },
{
$lookup: {
from: "propertytestimonials",
let: { propPropertyId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$propertyId", "$$propPropertyId"] }
}
},
{
$lookup: {
from: "users",
localField: "userId",
foreignField: "_id",
as: "user"
}
},
{
$addFields: {
user: { $arrayElemAt: ["$user", 0] }
}
}
],
as: "testimonials"
}
}
])
Playground
I'm building a comment system for my website. I've created this schema for the article and it's comments:
let articleSchema = mongoose.Schema({
language: {
type: String,
default: "english"
},
slug: {
type: String,
required: true
},
image: {
type: String,
required: true
},
title: {
type: String,
required: true
},
tags: {
type: [String],
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true
},
date: {
type: Date,
default: Date.now()
},
description: {
type: String,
required: false
},
body: {
type: String,
required: true
},
translation: [
{
language: {
type: String,
required: true
},
title: {
type: String,
required: true
},
tags: {
type: [String],
required: true
},
description: {
type: String,
required: false
},
body: {
type: String,
required: true
}
}
],
comments: [{
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
date: {
type: Date,
default: Date.now()
},
body: {
type: String,
required: true
}
}]
});
Now what I want to do is to populate every comment author field.
Let's say this is the article with a comment:
{
"comments": [
{
"author": "5f71d3b575f9f800943903af",
"date": "some date here",
"body": "comment body"
}
]
}
Now what I want to get after I populated all of this is this:
{
"comments": [
{
"author": {
"username": "Username",
"avatar": "fox"
},
"date": "some date here",
"body": "comment body"
}
]
}
How can I do that?
Here's my current code:
router.get('/article/:id', async (req, res) => {
const { id: articleId } = req.params;
const lang = req.i18n.language;
const langName = new Intl.DisplayNames(['en'], { type: "language" }).of(lang).toLowerCase();
try {
console.log(langName);
if (req.i18n.language === "en") {
var article = await Article.findById(articleId)
.populate([
{path:'comments.author', select:'+photo +username'},
{path:'author', select:'+photo +username'}
])
.select("title image tags author date description body comments");
} else {
var article = await Article
.aggregate([
{
$match: {
"_id": ObjectId(articleId)
}
}, {
$unwind: "$translation"
}, {
$match: {
"translation.language": langName
}
}, {
$group: {
_id: "$_id",
image: { $first: "$image" },
title: { $first: "$translation.title" },
tags: { $first: "$translation.tags" },
author: { $first: "$author" },
date: { $first: "$date" },
description: { $first: "$translation.description" },
body: { $first: "$translation.body" },
comments: { $first: "$comments" }
}
},
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "author"
}
},
{
$unwind: {
path: "$author"
}
},
{ ///////////////////// Here I need to populate the comment author. How can I do it?
$lookup: {
from: "users",
localField: "comments.author",
foreignField: "_id",
as: "comments.author"
}
},
{
$unwind: {
path: "$comments.author"
}
},
{
$project: {
image: 1,
title: 1,
tags: 1,
date: 1,
description: 1,
body: 1,
// comments: 1,
"comments.date": 1,
"comments.body": 1,
"comments.author.username": 1,
"comments.author.avatar": 1,
"author.username": 1,
"author.avatar": 1
}
}
], function(err, result) {
console.log(err)
return result;
});
console.log(article[0].comments[0]);
console.log(article);
}
if (!article) throw new Error();
} catch {
return res.status(404).render('errors/404');
}
res.render('articles/article', {
article: article[0]
});
});
So after some digging I came up with this solution:
router.get('/article/:id', async (req, res) => {
const { id: articleId } = req.params;
const lang = req.i18n.language;
const langName = new Intl.DisplayNames(['en'], { type: "language" }).of(lang).toLowerCase();
try {
if (req.i18n.language === "en") {
var article = await Article.findById(articleId)
.populate([
{path:'comments.author', select:'+photo +username'},
{path:'author', select:'+photo +username'}
])
.select("title image tags author date description body comments");
} else {
var article = await Article
.aggregate([
{
$match: {
"_id": ObjectId(articleId)
}
}, {
$unwind: "$translation"
}, {
$match: {
"translation.language": langName
}
},
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "author"
}
},
{
$unwind: {
path: "$author"
}
},
{
$unwind: {
path: "$comments"
}
},
{
$lookup: {
from: "users",
localField: "comments.author",
foreignField: "_id",
as: "comments.author"
}
},
{
$group: {
_id: "$_id",
root: { $mergeObjects: '$$ROOT' },
image: { $first: "$image" },
title: { $first: "$translation.title" },
tags: { $first: "$translation.tags" },
author: { $first: "$author" },
date: { $first: "$date" },
description: { $first: "$translation.description" },
body: { $first: "$translation.body" },
comments: { $push: "$comments" }
}
},
{
$project: {
image: 1,
title: 1,
tags: 1,
date: 1,
description: 1,
body: 1,
"comments.date": 1,
"comments.body": 1,
"comments.author.username": 1,
"comments.author.avatar": 1,
"author.username": 1,
"author.avatar": 1
}
}
]);
}
if (!article) throw new Error();
} catch {
return res.status(404).render('errors/404');
}
res.render('articles/article', {
article: article[0]
});
});
I'm trying to join two schema and summarize the total price.
This is the schema:
const Product = new mongoose.Schema({
name: { type: String, required: true },
price: Number
})
const Order = new mongoose.Schema({
fullname: { type: String, required: true },
address: { type: String, required: true },
products: [
{
product: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Product',
},
quantity: Number,
},
],
})
I want to create aggregation to get orders with total price.so it could be like:
[
{
fullname: 'jhon doe',
address: 'NY 1030',
products: [
{
product: {
name: 'Piano',
price: 50
},
quantity: 10,
},
],
price: 500
}
]
I try to use aggregation framework without any success, any idea?
Updated
As the question needs the price to be calculated by sum of the multiplication of quantity and product price, It can be done with below code:
db.getCollection('orders').aggregate([
{ $unwind: { path: '$products' } },
{
$lookup: {
from: 'products',
localField: 'products.product',
foreignField: '_id',
as: 'p',
},
},
{ $unwind: { path: '$p' } },
{
$group: {
_id: '$_id',
price: { $sum: { $multiply: ['$p.price', '$products.quantity'] } },
fullname: { $first: '$fullname' },
address: { $first: '$address' },
products: { $push: { product: '$p', quantity: '$products.quantity' } },
},
},
])
----------------------------------------------------------------------------------------------------------------------------
You can use $lookup in aggregation as below:
db.getCollection('orders').aggregate([
{
$lookup: {
from: 'products',
localField: 'products.product',
foreignField: '_id',
as: 'p',
},
},
{ $unwind: { path: '$p' } },
{
$project: {
fullname: 1,
address: 1,
products: {
product: '$p',
quantity: 1,
},
price: 1,
},
},
])
I am trying to create a social networking application which can have connect (followers, following), posts, comments, likes, shares, etc. This is a MVP project, but still i wanted to explore mongoDB for this use case. I am having some doubt regarding the performance of this application.
I have three collection:
Posts: This is where a new post shall be added. This collection contains all the details related to a post.
Schema:
const postSchema = new mongoose.Schema({
user_id: String,
title: String,
text: String,
type: { type: String, enum: ["music", "movie", "tv"] },
mediaUrl: String,
thumbnailUrl: String,
accessType: { type: Number, enum: [1, 2, 3] },
tags: [String],
like_count: Number,
comment_count: Number,
share_count: Number,
insertDate: {
type: Date,
default: () => {
return new Date();
}
}
});
Feeds: This collection just add a metadata of user and post including tags. This i intend to use to get the relevant feed for a particular user.
Schema:
const feedSchema = new mongoose.Schema({
post_id: String,
user_id: String,
isTag: Boolean,
isPublic: Boolean,
insertDate: {
type: Date,
default: () => {
return new Date();
}
},
modifiedDate: {
type: Date,
default: () => {
return new Date();
}
}
});
Connects: This collection is for the relationship of users.
Schema:
const connectSchema = new mongoose.Schema({
followed_by: String,
user_id: String,
insertDate: {
type: Date,
default: () => {
return new Date();
}
}
});
My approach was to first find the posts from feeds collection basis users whom I am following, then fetching the posts from post collection.
Here goes my attempted query:
db.connects.aggregate([
{ $match: { followed_by: "5cbefd61d3b53a4aaa9a2b16" } },
{
$lookup: {
from: "feeds",
let: { user_id: "$user_id" },
pipeline: [{ $match: { $expr: { $or: [{ $eq: ["$user_id", "$$user_id"] }, { isPublic: true }] } } }],
as: "feedData"
}
},
{ $unwind: "$feedData" },
{ $replaceRoot: { newRoot: "$feedData" } },
{ $group: { _id: { post_id: { $toObjectId: "$post_id" }, modifiedDate: { $toLong: "$modifiedDate" } } } },
{ $replaceRoot: { newRoot: "$_id" } },
{ $sort: { modifiedDate: -1 } },
{ $skip: 0 },
{ $limit: 10 },
{
$lookup: { from: "posts", localField: "post_id", foreignField: "_id", as: "postData" }
},
{ $unwind: "$postData" },
{ $replaceRoot: { newRoot: "$postData" } },
{ $addFields: { userId: { $toObjectId: "$user_id" } } },
{
$lookup: { from: "users", localField: "userId", foreignField: "_id", as: "userData" }
},
{
$project: {
post_id: "$_id",
user_id: 1,
title: 1,
text: 1,
typeaccessType: 1,
mediaUrl: 1,
thumbnailUrl: 1,
insertDate: { $toLong: "$insertDate" },
like_count: 1,
comment_count: 1,
share_count: 1,
user_email: { $arrayElemAt: ["$userData.email", 0] },
user_profile_pic: { $arrayElemAt: ["$userData.profile_pic", 0] },
username: { $arrayElemAt: ["$userData.firstname", 0] }
}
}
]).pretty();
Please share your feedback on:
Which index should I use to boost up the performance?
Is there is a better way of doing the same in mongoDB. Also if any part of the query can be optimised?
I'm working on a project where a user can place bets about a match and then earns points if he has bet for the winning team.
I'm building a leaderboard where I need to choose the 50 best players on the platform (those who have the most points).
Processing points dynamically compelled me to look at the aggregate method in order to calculate the user points from these Models:
const UserSchema = new mongoose.Schema({
admin: { type: Boolean, default: true },
username: String,
password: String,
pronos: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Prono', default: [] }],
});
const PronoSchema = new mongoose.Schema({
match: { type: mongoose.Schema.Types.ObjectId, ref: 'Match' },
local: Number,
guest: Number,
coeff: Number,
});
const StepSchema = new mongoose.Schema({
matchs: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Match', default: [] }],
name: String,
});
const CompetitionSchema = new mongoose.Schema({
steps: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Step', default: [] }],
start: { type: mongoose.Schema.Types.Date },
name: String,
});
const MatchSchema = new mongoose.Schema({
local: { type: mongoose.Schema.Types.ObjectId, ref: 'Team' },
guest: { type: mongoose.Schema.Types.ObjectId, ref: 'Team' },
localScore: { type: Number, default: -1 },
guestScore: { type: Number, default: -1 },
date: { type: mongoose.Schema.Types.Date },
});
To sum up all these code:
A player places bets, called pronos on Matches that are inside Steps, them being inside a Competition. So every steps of the competition has its matches.
I've been producing this to calculate the points for the players and I would like to know if I'm heading to the right direction:
const users = await User.aggregate([
{ $unwind: '$pronos' },
{
$lookup: {
from: 'pronos',
localField: 'pronos',
foreignField: '_id',
as: 'pronoObjects',
}
},
{ $unwind: '$pronoObjects' },
{
$lookup: {
from: 'matches',
localField: 'pronoObjects.match',
foreignField: '_id',
as: 'matches',
}
},
{ $unwind: '$matches' },
{
$addFields: {
pointsEarned: {
$switch: {
branches: [
{
case: {
$and: [
{ $eq: ['$pronoObjects.local', '$matches.localScore'] },
{ $eq: ['$pronoObjects.guest', '$matches.guestScore'] },
],
},
then: 3,
},
{
case: {
$and: [
{ $lt: [{ $subtract: ['$pronoObjects.local', '$pronoObjects.guest'] }, 0] },
{ $lt: [{ $subtract: ['$matches.localScore', '$matches.guestScore'] }, 0] }
]
},
then: 1,
},
{
case: {
$and: [
{ $gt: [{ $subtract: ['$pronoObjects.local', '$pronoObjects.guest'] }, 0] },
{ $gt: [{ $subtract: ['$matches.localScore', '$matches.guestScore'] }, 0] }
]
},
then: 1,
},
],
default: 0,
}
}
}
},
{
$group: {
_id: '$_id',
points: { $sum: '$pointsEarned' }
}
},
{
$lookup: {
from: 'users',
localField: '_id',
foreignField: '_id',
as: 'user',
}
}
]);
Since it's working the way I want, I planned to build up a ranking by Competition, where a user can select a competition ID and see the ranking for it.
When trying to achieve that, I've been using so many unwind methods that my response was 10k lines long before grouping. Thus I would like to know if anyone can hint me about the right way to achieve this.
I'm not looking for the complete answer, I'm new to mongo and would like to know about good practices or learn new aggregation methods.
Thanks in advance.
EDIT:
I managed to get the points for every user with this aggregation. The problem is that it takes more than 10 seconds to run over 400 matches with only 2 users.
Am I missing something ?
Here is the request I use:
return await Competition.aggregate([
{
$match: {
$expr: {
$eq: ['$_id', { $toObjectId: compId }],
}
}
},
{ $unwind: '$steps' },
{
$lookup: {
from: 'steps',
as: 'stepsObject',
localField: 'steps',
foreignField: '_id',
}
},
{ $unwind: '$stepsObject' },
{ $unwind: '$stepsObject.matchs' },
{
$project: {
_id: 1,
stepsObject: 1,
}
},
{
$lookup: {
from: 'matches',
as: 'matchesObject',
let: { otherid: '$stepsObject.matchs' },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ['$$otherid', '$_id'] },
{ $ne: ['$localScore', -1] },
]
}
}
}
],
},
},
{ $unwind: '$matchesObject' },
{
$lookup: {
from: 'users',
as: 'users',
pipeline: [
{
$project: {
_id: 1,
pronos: 1,
}
}
],
}
},
{ $unwind: '$users' },
{ $unwind: '$users.pronos' },
{
$lookup: {
from: 'pronos',
as: 'pronosObject',
let: { matchid: '$matchesObject._id', knownpronos: '$users.pronos' },
pipeline: [
{
$match: {
$expr:
{
$and: [
{ $eq: ['$$matchid', '$match'], },
{ $eq: ['$$knownpronos', '$_id'] },
]
}
}
}
]
// localField: 'users.pronos',
// foreignField: '_id',
}
},
{ $unwind: '$pronosObject' },
{
$addFields: {
pointsEarned: {
$switch: {
branches: [
{
case: {
$and: [
{ $eq: ['$pronosObject.local', '$matchesObject.localScore'] },
{ $eq: ['$pronosObject.guest', '$matchesObject.guestScore'] },
],
},
then: { $multiply: [3, '$pronosObject.coeff'] },
},
{
case: {
$and: [
{ $lt: [{ $subtract: ['$pronosObject.local', '$pronosObject.guest'] }, 0] },
{ $lt: [{ $subtract: ['$matchesObject.localScore', '$matchesObject.guestScore'] }, 0] }
]
},
then: { $multiply: [1, '$pronosObject.coeff'] },
},
{
case: {
$and: [
{ $gt: [{ $subtract: ['$pronosObject.local', '$pronosObject.guest'] }, 0] },
{ $gt: [{ $subtract: ['$matchesObject.localScore', '$matchesObject.guestScore'] }, 0] }
]
},
then: { $multiply: [1, '$pronosObject.coeff'] },
},
],
default: 0,
}
}
}
},
{
$group: {
_id: '$users._id',
pointsEarned: { $sum: '$pointsEarned' },
}
}
]);
EDIT 2:
I rewrote the entire pipeline with a different approach much faster. I still have a tiny problem. If I get rid of the final group, the request only take 4 milliseconds to run, but the final group to group points by id takes it up to 550ms. I don't know how can I optimize this since it's the final addition of all points for every user.
Here is what I came with:
return await Competition.aggregate([
{
$match: {
$expr: {
$eq: ['$_id', { $toObjectId: compId }],
}
}
},
{ $unwind: '$steps' },
{
$lookup: {
from: 'steps',
as: 'stepsObject',
localField: 'steps',
foreignField: '_id',
}
},
{ $unwind: '$stepsObject' },
{
$project: {
_id: 1,
stepsObject: 1,
}
},
{
$lookup: {
from: 'matches',
as: 'matchesObject',
let: { otherid: '$stepsObject.matchs' },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $in: ['$_id', '$$otherid'] },
{ $ne: ['$localScore', -1] },
]
}
}
}
],
},
},
{
$lookup: {
from: 'users',
as: 'users',
pipeline: [
{
$project: {
_id: 1,
pronos: 1,
}
}
],
}
},
{ $unwind: '$users' },
{ $unwind: '$users.pronos' },
{
$lookup: {
from: 'pronos',
as: 'pronosObject',
localField: 'users.pronos',
foreignField: '_id',
}
},
{ $unwind: '$pronosObject' },
{
$project: {
user_id: '$users._id',
pronosObject: 1,
matchesObjects: {
$arrayElemAt: [
{
$filter: {
input: '$matchesObject',
as: 'matchesObjects',
cond: { $eq: ['$$matchesObjects._id', '$pronosObject.match'] }
}
}, 0
]
},
}
},
{
$addFields: {
pointsEarned: {
$switch: {
branches: [
{
case: {
$and: [
{ $eq: ['$pronosObject.local', '$matchesObjects.localScore'] },
{ $eq: ['$pronosObject.guest', '$matchesObjects.guestScore'] },
],
},
then: { $multiply: [3, '$pronosObject.coeff'] },
},
{
case: {
$and: [
{ $lt: [{ $subtract: ['$pronosObject.local', '$pronosObject.guest'] }, 0] },
{ $lt: [{ $subtract: ['$matchesObjects.localScore', '$matchesObjects.guestScore'] }, 0] }
]
},
then: { $multiply: [1, '$pronosObject.coeff'] },
},
{
case: {
$and: [
{ $gt: [{ $subtract: ['$pronosObject.local', '$pronosObject.guest'] }, 0] },
{ $gt: [{ $subtract: ['$matchesObjects.localScore', '$matchesObjects.guestScore'] }, 0] }
]
},
then: { $multiply: [1, '$pronosObject.coeff'] },
},
],
default: 0,
}
}
}
},
{
$group: { // This is causing me trouble
_id: '$user_id',
pointsEarned: { $sum: '$pointsEarned' },
}
}
]);