mongoDB aggregation- unwind, sort and group - node.js

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

Related

Can I use dynamic variable in $match mongoose

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

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' },
])

Pull element from an array of arrays MongoDb

I am struggling to find the format for a query to remove an element (with an _id) from an array of arrays in Mongo.
When looking at the docs I couldn't find anything that was similar to what I have
https://www.mongodb.com/docs/manual/reference/operator/update/pull/#up._S_pull
I know: the _id of the document in MySchema and the _id of the array element in innerArray.
I don't know the outerArray _id
Could someone help point out where I went wrong Thank you!
This is an example of the data (imagine the _id in ObjectId)
{
outerArray:[
{
_id: 1
innerArray: [{_id: 23, name: '123'}, {_id: 13, name: 'asdac'} ]
},
{
_id: 2,
innerArray: [{_id: 16,name:'asf' }, {_id: 18,name:'asf' } ]
},
{
_id: 3,
innerArray: [{_id: 136,name:'asf' }, {_id: 128,name:'asf' } ]
}
]
}
innerIds is an array of mongoose.Types.ObjectId
return MySchema.updateOne(
{
_id: documentId,
},
{ $pull: { outerArray: { innerArray: { _id: { $in: innerIds } } } } },
)
.session(session)
.exec()
db.collection.update({},
{
$pull: {
"outerArray.$[].innerArray": {//$[] does the trick
_id: {
$in: [
16
]
}
}
}
})
playground

Using $elemMatch to compare object fields

I have a need to use $elemMatch in an aggregation pipeline and I need to compare 2 fields of an object from a nested array of objects:
Example collection:
name: 'xxx',
steps: [
{
userId: 'abc',
senderId: 'abc'
},
...
]
What I'm trying to do is return all that have at least 1 step where userId = senderId.
I have tried the following, but I get an error that $expr isn't allowed as a child of $elemMatch:
{
$match: {
steps: {
$elemMatch: {
$expr: { $eq: ['$userId', '$senderId'] },
},
},
},
}
Thanks.
$elemMatch can only be used in projection.
You can workaround for comparing fields in the array as below:
$set - Create new field filteredCount with get the array size $size of filtered array.
$match - Get filteredCount greater than 0.
db.collection.aggregate({
$set: {
filteredCount: {
$size: {
$filter: {
input: "$steps",
cond: {
$eq: [
"$$this.userId",
"$$this.senderId"
]
}
}
}
}
}
},
{
$match: {
"filteredCount": {
$gt: 0
}
}
})
Sample Mongo Playground

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().

Resources