Here is my json
[
{
id:1,
name:'rakib'
},
{
id:2,
name:'sakib'
},
{
id:3,
name:'sakib'
},
{
id:4,
name:'akib'
}
]
In this, I want to count the number of names but not duplicate by node js in MongoDB
My count will be: 3 ('rakib', 'sakib', 'akib')
$group - Group by null and with $addToSet to add name to names array without duplicates.
$project - Decorate the output documents. With $size to count the size of the names array.
db.collection.aggregate([
{
$group: {
_id: null,
names: {
$addToSet: "$name"
}
}
},
{
$project: {
count: {
$size: "$names"
}
}
}
])
Sample Mongo Playground
Related
So what I want to do is group all documents having same hash whose count is more than 1 and only keep the oldest record according to startDate
My db structure is as follows:
[{
"_id": "82bacef1915f4a75e6a18406",
"Hash": "cdb3d507734383260b1d26bd3edcdfac",
"duration": 12,
"price": 999,
"purchaseType": "Complementary",
"startDate": {
"$date": {
"$numberLong": "1656409841000"
}
},
"endDate": {
"$date": {
"$numberLong": "1687859441000"
}
}
}]
I was using this query which I created
db.Mydb.aggregate([
{
"$group": {
_id: {hash: "$Hash"},
dups: { $addToSet: "$_id" } ,
count: { $sum : 1 }
}
},{"$sort":{startDate:-1}},
{
"$match": {
count: { "$gt": 1 }
}
}
]).forEach(function(doc) {
doc.dups.shift();
db.Mydb.deleteMany({
_id: {$in: doc.dups}
});
})
this gives a result like this:
{ _id: { hash: '1c01ef475d072f207c4485d0a6448334' },
dups:
[ '6307501ca03c94389f09b782',
'6307501ca03c94389f09b783',
'62bacef1915f4a75e6a18l06' ],
count: 3 }
The problem with this is that the _id's in dups array are random everytime I run this query i.e. not sorted according to startDate field.
What can be done here?
Any help is appreciated. Thanks!
After $group stage, startDate field will not pre present in the results, so you can not sort based on that field. So, as stated in the comments, you should put $sort stage first in the Aggregation pipeline.
db.Mydb.aggregate([
{
"$sort": { startDate: -1}
},
{
"$group": {
_id: {hash: "$Hash"},
dups: { $addToSet: "$_id" } ,
count: { $sum : 1 }
},
{
"$match": { count: { "$gt": 1 }
}
]
Got the solution. I was using $addToSet in the group pipeline stage which does not allow duplicate values. Instead, I used $push which allows duplicate elements in the array or set.
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
Is it possible to retrieve the total fields count with respect to fields name in a single query from a collection?
I am expecting a result like this
{
"field_1":101,
"field_2":93,
"field_3":1
}
You can try,
$project convert root object to array using $objectToArray, this will create k(key) and v(value) structure of array
$unwind deconstruct root array
$group by k(key) and get total fields count using $sum
$group by null and construct an array with k(key) and v(value) fields format
$replaceRoot to replace object after converting from array using $arrayToObject
db.collection.aggregate([
{ $project: { root: { $objectToArray: "$$ROOT" } } },
{ $unwind: "$root" },
{
$group: {
_id: "$root.k",
total: { $sum: 1 }
}
},
{
$group: {
_id: null,
root: {
$push: { k: "$_id", v: "$total" }
}
}
},
{
$replaceRoot: {
newRoot: {
$arrayToObject: "$root"
}
}
}
])
Playground
Yes
Playground
db.collection.aggregate({
$group: {
_id: null,
field_2: {
$push: {
$cond: [
"$field_2",
1,
0
]
}
},
field_1: {
$push: {
$cond: [
"$field_1",
1,
0
]
}
}
}
},
{
$project: {
_id:0,
field_1: {
$sum: "$field_1"
},
field_2: {
$sum: "$field_2"
}
}
})
Be aware:
When is true, $exists matches the documents that contain the field, including documents where the field value is null
I need to get docs from MongoDB collection where ID's are in array:
[
'5f80a44d0179262f7c2e6a42',
'5f8c00762fae890e9c4d029c',
'5f802cf8abac1116a46bf9d4'
]
The issue is, docs are not coming in sequence of my array ID's. They are coming (1, 0, 2) for above array ID's.
How can I make them in sequence of my ID's array? I am using, NodeJs + Mongoose.
My code:
var ids = ['5f80a44d0179262f7c2e6a42','5f8c00762fae890e9c4d029c','5f802cf8abac1116a46bf9d4']
Product.find({
_id: {
$in: ids
}
})
I don't think its possible with find(), or any functionality available in MongoDB related to this,
It is possible with aggregate() but this will just fulfil your expected result,
I am not recommending to use this because this will affect performance of query response, this could be a heavy transaction.
$match your conditions
$group by null and make array of all matching objects in root
$addFields to add ids array that we search for
$unwind deconstruct ids array, this will deconstruct in sequence as per we provided in ids array
$project, $reduce input as root array and check condition if id match then return object
$replaceWith to replace root object to root
var ids = [
ObjectId("5f802cf8abac1116a46bf9d4"),
ObjectId("5f8c00762fae890e9c4d029c"),
ObjectId("5f80a44d0179262f7c2e6a42")
];
Product.aggregate([
{ $match: { _id: { $in: ids } } },
{
$group: {
_id: null,
root: { $push: "$$ROOT" }
}
},
{ $addFields: { ids: ids } },
{ $unwind: "$ids" },
{
$project: {
root: {
$reduce: {
input: "$root",
initialValue: {},
in: { $cond: [{ $eq: ["$$this._id", "$ids"] }, "$$this", "$$value"] }
}
}
}
},
{ $replaceWith: "$root" }
])
Playground
I have a collection with name post and I have one doc and its replicated doc but in replicated doc we have one field different ,some doc don't have replicated doc and that situation depends on an array field of the doc ,if that field have the userId of user then the replicated doc will exist otherwise it will not exist.
So what I want if the doc array have that id then get the replicated post and if not then original post
I have made the query but showing error I am using $exist in $cond ?
Post.aggregate([
{
$match: {
socomo_visibility: socomoId
}
},
{
$project: {
"post_stream_type": {
$cond: {
if: {
'following_users_list': {
$exist: [userId]
}
},
then: constants.POST_STREAM_TYPE.FOLLOW.value,
else: constants.POST_STREAM_TYPE.SOCIAL_CURRY_CHANNEL.value
}
}
}
}
]
You can check whether your array has some value in the boolean-expression in such way:
Do intersection of array and value using $setIntersection.
Check size of that intersection array using $size.
If the size is greater than 0 then value is present in the array. $gt will do this check.
Try the following code:
Post.aggregate([
{
$project: {
"post_stream_type": {
$cond: {
if: {$gt: [{$size: {$setIntersection: ["$following_users_list", [userId]] } }, 0] },
then: constants.POST_STREAM_TYPE.FOLLOW.value,
else: constants.POST_STREAM_TYPE.SOCIAL_CURRY_CHANNEL.value
}
}
}
}
])
okay finally I have done this without using the aggregation .
my answer for the query is
Post.find({
$or: [{
socomo_visibility: {
$elemMatch: {
$eq: socomoId
}
},
post_stream_type: constants.POST_STREAM_TYPE.SOCIAL_CURRY_CHANNEL.value,
following_users_list: {
$exists: true,
$nin: [userId]
}
},
{
socomo_visibility: {
$elemMatch: {
$eq: socomoId
}
},
post_stream_type: constants.POST_STREAM_TYPE.FOLLOW.value,
following_users_list: {
$elemMatch: {
$eq: userId
}
}
}]
})