How to get matchers work with aggregation for date ranges? - javascript

I have a model that has author (that is mongoose.Schema.ObjectId), and I have a date field on that model (type Date).
It works when I do this, the data comes back:
MyModel.find({
author: req.user._id,
started: {
$gte: start,
$lte: end
}
});
But I have an aggregation function in my Schema, this is how it looks like:
myModelSchema.statics.getSomeData = function getSomeData(id, start, end) {
const query = [
{ $sort: { started: 1 } },
{
$lookup: {
from: 'projects',
localField: 'project',
foreignField: '_id',
as: 'projectdata'
}
},
{
$lookup: {
from: 'tasks',
localField: 'task',
foreignField: '_id',
as: 'taskdata'
}
}
];
if (start && end) {
query.unshift({
$match: {
$and: [{ started: { $gte: start, $lte: end } }, { author: id }]
}
});
} else {
query.unshift({ $match: { author: id } });
}
return this.aggregate(query);
};
Note: The part where I push the matcher for the author, that gets me all the data with the current author works, but the part where I also want the data to come back within date range doesn't work, the data comes empty, when the start and end dates are passed to the static function.
Date format I pass to the aggregation function: '2018-11-04 06:00:00' (a string).

Actually you are matching a date object with string value that wouldn't work at all, So value also should be a date object
myModelSchema.statics.getSomeData = function getSomeData(id, start, end) {
const query = [
{ $sort: { started: 1 } },
{
$lookup: {
from: 'projects',
localField: 'project',
foreignField: '_id',
as: 'projectdata'
}
},
{
$lookup: {
from: 'tasks',
localField: 'task',
foreignField: '_id',
as: 'taskdata'
}
}
];
if (start && end) {
query.unshift({
$match: {
$and: [{ started: { $gte: new Date(start), $lte: new Date(end) } }, { author: id }]
}
});
} else {
query.unshift({ $match: { author: id } });
}
return this.aggregate(query);
};

Related

MongoDB - How to combine findOne (in array) with aggregate

I currently have a Mongo query that looks like this:
const user = await User.findOne({ userId }).lean() || []
const contributions = await Launch.aggregate([
{ $sort: { addedAt: -1 } },
{ $limit: 10 },
{
$match: {
_id: { $in: user.contributions }
}
},
{
$addFields: {
activity: 'contribution',
launchName: '$name',
launchId: '$_id',
date: '$addedAt',
content: '$description'
}
}
])
But instead of having two different Mongo queries (findOne and aggregate), how can I combine them into one query?
I tried this but it just errors out immediately in the lookup part:
const contributions = await Launch.aggregate([
{ $sort: { addedAt: -1 } },
{ $limit: 10 },
{
$lookup: {
from: 'user',
let: { id: $user.contributions },
pipeline: [
{ $match: { $expr: { $in: [$_id, $$user.contributions] } } }
],
localField: '_id',
foreignField: 'userId',
as: 'user'
}
},
{
$addFields: {
activity: 'contribution',
launchName: '$name',
launchId: '$_id',
date: '$addedAt',
content: '$description'
}
}
])
I've never used the pipeline option so a little confused onn how to fix this problem?
Enclose these $user.contributions, $_id with quotes in order to make the query valid.
Since you declare the id variable with the value of user.contributions. You should use the variable with $$id instead of $$user.contributions.
I don't think the localField and foreignField are needed as you are mapping/joining with pipeline.
Your aggregation query should be looked as below:
const contributions = await Launch.aggregate([
{ $sort: { addedAt: -1 } },
{ $limit: 10 },
{
$lookup: {
from: 'user',
let: { id: "$user.contributions" },
pipeline: [
{ $match: { $expr: { $in: ["$_id", "$$id"] } } }
],
as: 'user'
}
},
{
$addFields: {
activity: 'contribution',
launchName: '$name',
launchId: '$_id',
date: '$addedAt',
content: '$description'
}
}
])

How to use MongoDB aggregate to look up an ARRAY of objects with NESTED OBJECT

I am currently searching MongoDB to find all the Posts and showing all the COMMENTS array. Each COMMENT has a postedBy nested object ID that refers to the USERS model.
At the moment when I $lookup, I loose all my current data. I wasnt to keep the initial data but then add on the details about the postedBy.
Similar to what population would do on a Post.find().
const bigPosts= await Post.aggregate([
//Code works good
{
$addFields: {
winAmount: {
$round: { $multiply: ['$betAmount', '$odds'] },
},
},
},
{
$lookup: {
from: 'users',
localField: 'postedBy',
foreignField: '_id',
as: 'postedBy',
},
},
{
$unwind: { path: '$postedBy', preserveNullAndEmptyArrays: true },
},
// this is where the issues start
{
$lookup: {
from: 'users',
localField: 'comments.postedBy',
foreignField: '_id',
as: 'comments.postedBy',
},
},
{
$unwind: {
path: '$comments.postedBy',
preserveNullAndEmptyArrays: true,
},
},
{
$project: {
comments: 1,
winAmount: 1,
'postedBy._id': 1,
'postedBy.name': 1,
'postedBy.username': 1,
'postedBy.image': 1,
},
},
])

Mongoose, get child document and group by

I'm trying to get some results and group the by an id, but I don't know a way to do that, I was trying with $unwind and $lookup, but when I use the $group, it doesn't give me results, so, I will appreciate any feedback.
usercalification model
{
calification: Number,
status: Number,
place: {
type: Schema.ObjectId,
ref: 'Place',
}
}
place model
{
name: String,
description: String
}
I was trying to do something like this:
mongoose.model('usercalification').aggregate([
{
$lookup: {
from: 'places',
localField: 'place',
foreignField: '_id',
as: 'places'
}
},
{
{
$unwind: '$places'
}
}
]);
It works like it should, the issue is when I try to $groupBy
mongoose.model('usercalification').aggregate([
{
$lookup: {
from: 'places',
localField: 'place',
foreignField: '_id',
as: 'places'
}
},
{
{
$unwind: '$places'
}
},
{
$group: {
_id: '$place',
calification: { $avg: '$calification' },
count: {$sum: 1}
}
}
]);
I'm grouping by $place cuz that's the Place Id that I need to group by.
I'll appreciate any feedback.
The issue was that those were not ordered in the correct way.

convert a $lookup result to an object instead of array

I'm doing a $lookup from a _id. So the result is always 1 document. Hence, I want the result to be an object instead an array with one item.
let query = mongoose.model('Discipline').aggregate([
{
$match: {
project: mongoose.Types.ObjectId(req.params.projectId)
},
},
{
$lookup: {
from: "typecategories",
localField: "typeCategory",
foreignField: "_id",
as: "typeCategory"
}
},
{
$project: {
title: 1, typeCategory: "$typeCategory[0]"
}
}
]);
This notation: "$typeCategory[0]" is not working. Is there any smart way of doing this?
You can just use $unwind. It deconstructs an array field from the input documents to output a document for each element
let query = mongoose.model('Discipline').aggregate([
{
$match: {
project: mongoose.Types.ObjectId(req.params.projectId)
},
},
{
$lookup: {
from: "typecategories",
localField: "typeCategory",
foreignField: "_id",
as: "typeCategory"
}
},
{$unwind: '$typeCategory'},
{
$project: {
title: 1, typeCategory: "$typeCategory"
}
}
]);
You can use $arrayElemAt in $project stage.
Syntax of $arrayElemAt is { $arrayElemAt: [ <array>, <idxexOfArray> ] }
like:
mongoose.model('Discipline').aggregate([
{
$match: {
project: mongoose.Types.ObjectId(req.params.projectId)
},
},
{
$lookup: {
from: "typecategories",
localField: "typeCategory",
foreignField: "_id",
as: "typeCategory"
}
},
{
$project: {
name: 1, typeCategory: {$arrayElemAt:["$typeCategory",0]}
}
}
]);
Use $first to return the first element in the array:
$project: {
title: 1,
typeCategory: {$first: "$typeCategory"}
}
For merging two collections:
{$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$typeCategory", 0 ] }, "$$ROOT" ] } } },
{ $project: { typeCategory: 0 } }

Filter array during publication

I have a document that looks as follows.
{
_id: "1234",
orderIds: [1,2,3,4,5]
}
I want to publish this document, however, I want to lookup and filter this reactively. With an aggregation in 3.2, it would be something like this:
var id = "1234"
db.users.aggregate([
{ $match: { _id: id } },
{ $unwind: '$orderIds' },
{
$lookup: {
from: 'orders',
as: 'orders',
localField: 'orderIds',
foreignField: '_id'
}
},
{ $match: { 'orders.status': { $ne: 'closed' } } },
{
$group: {
_id: "$_id",
orders: { $push: '$orders' }
}
}
])
However, I can't find a way to do this with plain publications, and mongo 2.6.7 that meteor ships with. Is there a way to do it?

Categories