1

I have following offers collection

Here readBy contains the _id of the users... Now I want to count the number of unRead offers for the userId = "5add82620d7f5b38240c63d4"

{
    "_id" : ObjectId("5aeaab5ed6a9c97d0209260a"),
    "expiresIn" : ISODate("2018-05-30T18:30:00.000Z"),
    "name" : "Trip ",
    "readBy" : [ 
        ObjectId("5add82620d7f5b38240c63d4"),
        ObjectId("5add82620d7f5b38240c63c6")
    ],
    "__v" : 0
}
{
    "_id" : ObjectId("5aeaab7dd6a9c97d0209260b"),
    "expiresIn" : ISODate("2018-05-29T18:30:00.000Z"),
    "name" : "Trip",
    "readBy" : [ObjectId("5add82620d7f5b38240c63d4")],
    "__v" : 0
}
{
    "_id" : ObjectId("5aeae233d6a9c97d02092622"),
    "expiresIn" : ISODate("2018-05-25T18:30:00.000Z"),
    "name" : "two way off",
    "readBy" : [],
}
{
    "_id" : ObjectId("5aeae49643f10d284726069c"),
    "expiresIn" : ISODate("2018-05-25T18:30:00.000Z"),
    "name" : "two way off",
    "readBy" : [],
}
{
    "_id" : ObjectId("5aeae49743f10d284726069d"),
    "expiresIn" : ISODate("2018-05-25T18:30:00.000Z"),
    "name" : "two way off",
    "readBy" : []
}
{
    "_id" : ObjectId("5aeae49743f10d284726069e"),
    "expiresIn" : ISODate("2018-05-25T18:30:00.000Z"),
    "name" : "two way off",
    "readBy" : []
}

so for the above collection my output should be

[{
  numberOfUnreadOffers: 4
}]

because four of the collection does not have 5add82620d7f5b38240c63d4 in readBy array

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
Ashh
  • 44,693
  • 14
  • 105
  • 132
  • Possible duplicate of [Conditional $sum in MongoDB](https://stackoverflow.com/questions/14102596/conditional-sum-in-mongodb) – Héctor May 07 '18 at 08:53
  • @Héctor with the exception of course that "aggregation" is actually the wrong approach for the most performant solution to this. – Neil Lunn May 07 '18 at 09:05

1 Answers1

2

You basically use $setIsSubset and $cond here:

var userId= "5add82620d7f5b38240c63d4";

Model.aggregate([
  { "$group": {
    "_id": null,
    "numberOfUnreadOffers": {
      "$sum": {
        "$cond": {
          "if": { 
            "$setIsSubset": [[mongoose.Types.ObjectId(userId)], "$readBy"]
          },
          "then": 0,
          "else": 1
        }
      }
    }
  }}
]) 

Of course you also need to "cast" using mongoose.Types.ObjectId from the "string" to a valid ObjectId value.

But really you get better performance from a simple count() instead:

Model.count({ "readBy": { "$ne": userId } })

So you really should not use .aggregate() for this at all.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
  • perfect Neil... where you do test with the queries? I mean there is site like jsFiddle to test mongodb query? – Ashh May 07 '18 at 09:29
  • 1
    @AshishChoudhary Where do "I" test? Usually "nowhere" and this stuff just comes out of my head, which explains why there are usually syntax errors :) If a question correctly shows data and I actually need to think about it for a few moments, then I'll simply copy and paste to insert to a new collection and then run the queries that matter. Always have a MongoDB instance you can trash with rubbish collections and test things I guess. Better than any "fiddle". – Neil Lunn May 07 '18 at 09:32