Mongoose, get child document and group by - javascript

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.

Related

How use aggregate and match in mongodb?

I have two collection ProductInventories and ProductDetails. The ProductInventories has a prodId that is connected to the id of ProductDetails. I want to get all the product that is Active only.
const products = await ProductInventory.find().populate({
path: 'prodId',
model: ProductDetails,
populate: { path: 'category', select: 'category_name', model: Category },
});
I'm using this code to get all the data, but I want to have a condition that it will return the item that is isActicve = True. I'm just using a filter function to filter all active data. I want to learn how to use aggregate and match.
You can first use the lookup aggregation from ProductInventories to ProductDetails based on the prodId field, then do a match aggregation with field isActive = true
db.ProductInventories.aggregate([
{
$lookup: {
from: "ProductDetails",
localField: "prodId",
foreignField: "id",
as: "product_details"
},
$match: {
isActive: true
}
}
])
Refer https://docs.mongodb.com/manual/reference/operator/aggregation/match/
https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
This is my updated code and its working now. Thanks also for helping #smitha t m.
const products = await ProductInventory.aggregate([
{
$lookup: {
from: 'productdetails',
localField: 'prodId',
foreignField: '_id',
as: 'product',
},
},
{
$unwind: '$product',
},
{
$match: { 'product.isActive': true },
},
]);

MongoDb-Node:Agregation node-mongo query for chat

im new on mongo and want to get data for a chat, let me explain.
i have a colection of messages:
_id:id
viewed:boolean
created_at:date
text:String
receiver:ObjectId
emitter:ObjectId
i want all the list of messager for a certain emitter and receiver order by the date (like a normal chat)
i have tryed aggregation like this:
db.messages.aggregate(
[
{
$lookup: {
from: "users",
localField: "emitter", // field in the orders collection
foreignField: "_id", // field in the items collection
as: "fromItems"
}
},
{
$match: {
'emitter':ObjectId("5c8917b4ef9ebf2e608c68dc")
}
}
,
{
$addFields: {
ids: { _id: "$_id" } ,
created: { created_at: "$created_at" }
}
},
{
$group:
{
_id: { tot:["$emitter", "$receiver"] },
text: { $addToSet:"$text"},
}
},
{
$sort: {created_at: 1}
}
]
)
But this gives me an array of messages only of a certain emitter and dont give me the date or the viewed data.
Im really new on mongo and node so if someone can help me with a explain will be great.
Thanks for reading and sory for the bad english
You must include date or the viewed data in $group stage.
Try with this.
{
$group:
{
_id: { tot:["$emitter", "$receiver"] },
text: { $addToSet:{text:"$text",created:"$created.created_at"}},
created_at:{$last:"$created.created_at"}
}
},
Why there is ids and need for tot fields and created as a object ??

How to get matchers work with aggregation for date ranges?

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);
};

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