Can I use dynamic variable in $match mongoose - node.js

I want to filtre data using $match and group it using $group
since I use dynamic variable in $match stage I got no result,
let logs = await Promise.all(
tpes.map(async (item) => {
return await this.logModel.aggregate([
{ $match: { terminalId: item } },
{
$group: {
_id: '$outcome',
value: {
$sum: 1,
},
},
},
{
$project: {
name: '$_id',
value: 1,
_id: 0,
},
},
]);
}),
);
console.log('logs', logs);
logs here is an array contain ids from where I want to get data

Related

mongoDB aggregation- unwind, sort and group

Considering I have a users collection contains those documents:
{
_id: 1,
hobbies: ['eat', 'read', 'swim']
},
{
_id: 2,
hobbies: ['eat', 'sleep', 'swim']
},
{
_id: 3,
hobbies: ['code', 'read', 'eat']
}
I want to do an aggregation on this collection so the result will be a distinct list of all those hobbies sorted in alphabetic order, for example:
{
result: [code, eat, read, sleep, swim]
}
I've tried this solution, but it didn't work for me:
{
$unwind: {path: "$hobbies"},
$group: {_id: null, result: {$addToSet: "$hobbies"}}
}
My problem is to sort the result field...
Your approach is very close already. Just remember $unwind and $group are separate pipeline stages. You need to wrap them with individual curly brackets. And for the sorting, you can do a $sortArray at the end of the pipeline.
db.collection.aggregate([
{
$unwind: {
path: "$hobbies"
}
},
{
$group: {
_id: null,
result: {
$addToSet: "$hobbies"
}
}
},
{
$set: {
result: {
$sortArray: {
input: "$result",
sortBy: 1
}
}
}
}
])
Mongo Playground

I have Error Called Arguments must be aggregate pipeline operators

I have some issues with MongoDB aggregate in node.js
Error: Arguments must be aggregate pipeline operators
This is my code
let find_result = await Users.aggregate([
{ $sample: { size: 10 } },
{ $group: { _id: '$_id'} },
{ $project: {
_id : {
$nin: arr2
}
}},
{ $unwind: '$_id' }
])
This code is to output randomly without duplication except for yourself and the person you choose (arr2 contains your _id and the _id of the person you choose)
Remove the comma before unwind.
let find_result = await Users.aggregate([
{ $sample: { size: 10 } }
,{ $group: { _id: '$_id'} },
{ $project: {
"_id" : {
$nin: arr2
}
}
},
{ $unwind: '$_id' },
])

I want to write a MongoDB query in NodeJS where it return the matching documents as well as the count of documents too

I want to write a MongoDB query in NodeJS where it return the matching documents as well as the count of documents too. For ex consider the below code -
const result = await Student.aggregate(
[
{
$match: {
...filter
}
},
{
$project: {
_id: 1,
payment: 1,
type: 1,
BirthDate: 1
}
},
{
$sort: { StudentData: -1 }
},
{
$count: 'count'
},
{
$skip: skip
},
{
$limit: limit
}
]
);
Here I want to save two things in the result variable - the number of documents and individually all the documents.
let [{ totalItems, result }] = await Student.aggregate(
[
{
$match: {
...filter
}
},
{
$project: {
_id: 1,
payment: 1,
type: 1,
BirthDate: 1
}
},
{
$facet: {
result: [
{
$sort: { BirthDate: -1 },
},
{
$skip: skip
},
{
$limit: limit
}
],
totalItems: [{ $count: 'count' }]
}
},
{
$addFields: {
totalItems: {
$arrayElemAt: ["$totalItems.count", 0]
},
}
}
]
);

Mongoose Aggregation not working, tried different ways still not able to solve the problem, even dont know what is problem

I am new to node and mongoose. Tried many ways though none worked for me.
Aggregate query on mongo shell is working, here is query:-
db.collection.aggregate([
{
$match: {
p_id: ObjectId("5d8b4f24d86d9f2400d7ff46"),
m_id: { $in: [ObjectId("5dde14b3f34ac02500a2b0aa")] }
}
},
{ $sort: { updated_at: 1 } },
{ $group: { _id: "$m_id", evaluation: { $last: "$evaluation" } } }
]);
Aggregate query in mongoose:-
Model.aggregate([
{ $match: { p_id: pId, m_id: { $in: mIds } } },
{ $sort: { updated_at: 1 } },
{
$group: {
_id: "$m_id",
evaluation: { $last: "$evaluation" }
}
}
]);
Error:- throwing cursor option is required
Aggregate query after adding cursor optionr:-
Query:-
Model.aggregate(
[
{ $match: { p_id: pId, m_id: { $in: mIds } } },
{ $sort: { updated_at: 1 } },
{
$group: {
_id: "$m_id",
evaluation: { $last: "$evaluation" }
}
}
],
{ cursor: {} }
);
Error: - error_stack=Error: Arguments must be aggregate pipeline operators
Mongoose aggregate api fluent returning aggregationcursor but on doing aggregationcursor.next() a promise is returned in pending state. After adding then() on aggregationcursor.next() a null object is returned:-
Model.aggregate()
.match({ p_id: pId, m_id: { $in: mIds } })
.sort({ $updated_at: 1 })
.group({
_id: "$m_id",
evaluation: { $last: "$evaluation" }
})
.cursor({ batchSize: 1000 });
Schema:
const PSchema = new Schema({
m_id: { type: Schema.Types.ObjectId },
p_id: { type: Schema.Types.ObjectId },
evaluation: { type: String, enum: _.values(EvaluationType) },
created_at: { type: Date },
updated_at: { type: Date }
});
Mongo/Mongoose version:-
"#types/mongoose": "^3.8.36",
"mongodb": "^3.3.5",
"mongoose": "~4.5.9",
I had to try different things, finally following query worked for me.
let cursor = Model.aggregate([
{
$match: {
'p_id': mongoose.Types.ObjectId(providerId) ,'m_id': { $in:objectIds }
}
},
{
$sort: {
'updated_at':1
}
},
{
$group: {
_id: '$m_id',
evaluation: { $last: '$evaluation' }
}
}
]).cursor({async:true});
Three things which i had to do:
I have to pass objectIds only in $match, as in normal find strings work, mongoose is able to convert strings into objectIds internally. For aggregate have to pass objectIds in $match.
Have to pass cursor option. As per [https://docs.mongodb.com/manual/reference/command/aggregate/#dbcmd.aggregate][1] cursor is mandatory after mongodb 3.6 version in aggregate. My mongodb version id 4.0.5 .
Had to pass cursor in async mode cursor({async:true}). Got results from cursor as cursor.toArray().

How to get aggregated sum of values in an array of mongoose subdocuments when query parent?

I'm trying to build some advanced hello world app on top of express and mongoose. Assume I have next Schemas:
const pollOptionsSchema = new Schema({
name: String,
votes: {
type: Number,
default: 0
}
});
const pollSchema = new Schema({
name: String,
dateCreated: { type: Date, default: Date.now },
author: { type: Schema.Types.ObjectId },
options: [pollOptionsSchema]
});
And when I simply call
Poll.findOne({_id: req.params.id}).exec((err, data) => {
if (err) console.log(err);
// I receive next data:
// { _id: 58ef3d2c526ced15688bd1ea,
// name: 'Question',
// author: 58dcdadfaea29624982e2fc6,
// __v: 0,
// options:
// [ { name: 'stack', _id: 58ef3d2c526ced15688bd1ec, votes: 5 },
// { name: 'overflow', _id: 58ef3d2c526ced15688bd1eb, votes: 3 } ],
// dateCreated: 2017-04-13T08:56:12.044Z }
});
The question is how I could receive same data + aggregated number of votes (i.e 8 in case above) after calling some method on Model level, for example:
// I want to receive:
// { _id: 58ef3d2c526ced15688bd1ea,
// name: 'Question',
// author: 58dcdadfaea29624982e2fc6,
// __v: 0,
// totalNumberOfVotes: 8,
// options:
// [ { name: 'stack', _id: 58ef3d2c526ced15688bd1ec, votes: 5 },
// { name: 'overflow', _id: 58ef3d2c526ced15688bd1eb, votes: 3 } ],
// dateCreated: 2017-04-13T08:56:12.044Z }
Or maybe I need to implement some extra method on document level i.e (data.aggregate)?
I've already reviewed:
http://mongoosejs.com/docs/api.html#model_Model.mapReduce
http://mongoosejs.com/docs/api.html#aggregate_Aggregate
https://docs.mongodb.com/manual/core/map-reduce/
https://docs.mongodb.com/manual/tutorial/map-reduce-examples/
But can't utilize it for my case :(
Any advice will be much appreciated. Thanks!
Use $reduce operator within an $addFields pipeline to create the totalNumberOfVotes field. In your aggregate pipeline, the first step is the $match which filters the document stream to allow only matching documents to pass unmodified into the next pipeline stage and uses standard MongoDB queries.
Consider running the following aggregate operation to get the desired result:
Poll.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } },
{
"$addFields": {
"totalNumberOfVotes": {
"$reduce": {
"input": "$options",
"initialValue": 0,
"in": { "$add" : ["$$value", "$$this.votes"] }
}
}
}
}
]).exec((err, data) => {
if (err) console.log(err);
console.log(data);
});
NB: The above will work for MongoDB 3.4 and greater.
For other earlier versions you would need to $unwind the options array first before grouping the denormalised documents within a $group pipeline step and aggregating with the accumulators $sum, $push and $first.
The following example shows this approach:
Poll.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } },
{ "$unwind": { "path": "$options", "preserveNullAndEmptyArrays": true } },
{
"$group": {
"_id": "$_id",
"totalNumberOfVotes": { "$sum": "$options.votes" },
"options": { "$push": "$options" },
"name": { "$first": "$name" },
"dateCreated": { "$first": "$dateCreated" },
"author": { "$first": "$author" }
}
}
]).exec((err, data) => {
if (err) console.log(err);
console.log(data);
});

Resources