1

I have a user model in MongoDB looks like such:

{ _id: <some_id>,
  name: <some_name>,
  preferences: <this is an object with 1-20 key values pair>
}

I would like to get the count of the users with all preferences keys present (# of users with 20 keys in the preferences.) how would I go about querying that?

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
WABBIT0111
  • 2,233
  • 2
  • 18
  • 30

1 Answers1

1

If you have the latest MongoDB 3.4 then you can do this in an aggregation statement with $objectToArray:

Model.aggregate([
  { "$group": {
    "_id": null,
    "count": {
      "$sum": {
        "$cond": {
          "if": { "$eq": [ 
             { "$size": { 
               "$objectToArray": { "$ifNull": [ "$preferences", { } ] }
             }}, 
             20
          ]},
          "then": 1,
          "else": 0
        }
      }
    }
  }}
])

The new operator $objectToArray will take an object of "key/value" pairs and turn this into an array. So this:

 { "a": 1, "b": 2 }

Becomes this:

 [ { "k": "a", "v": 1 }, { "k": "b", "v": 2 } ]

As an array, you can use operators like $size to count the number of "keys", which is now equal to the entries in the array.

For earlier versions you can use a $where condition in JavaScript evaluation:

Model.find(function() {
  return (this.hasOwnProperty("preferences")) ?
    Object.keys(this.preferences).length == 20 : false;
}).count()

The latter not really being an "aggregation", but the cursor count is really all you can get from a JavaScript evaluation.

Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
  • What happen if some of the keys have null value. how should i go about eliminating those? I guess I can use something here https://stackoverflow.com/questions/33123396/group-in-mongo-excluding-null-values but was wondering if you know something even more elegant? – WABBIT0111 Jun 06 '17 at 02:27
  • @WABBIT0111 Sure. You can add `null` checking where the property is not present, which is added in the answer. Or are you talking about the "inner" keys of the "preferences" property? In the latter case you can add `$filter` or `.filter()` operations depending on which operation is used. And by that I interpret the latter as meaning something like `{ "preferences": { "a": 1, "b": null } }` where you are asking how to "exclude" the "b" key since it's value is `null`? It really comes down to perspective, but at brief glance at the referenced link, I guess anything is more elegant than that. – Neil Lunn Jun 06 '17 at 03:35
  • Thx for the thorough explanation for both scenario. I think both scenario are very useful and i learned a lot. thank you ! – WABBIT0111 Jun 06 '17 at 16:06