0

I am trying to count the number of documents with a matching criteria in a nested field.

My schema is defined as follows:

let userSchema = new Schema({
  //...
  interests: [{
    type: Schema.Types.ObjectId,
    ref: 'Interests'
  }],
  //...
});

let interestSchema = new Schema({
  id: ObjectId,
  name: {
    type: String,
    required: true
  },
  //...
});

The count must reflect how many times an interest with the same name is choosed by the users.

For example, I must have a result of 2 with the interest 'coding' in the following documents:

{
  //Other Fields of user 1
  "interests": [
     {
       "id": "XXX"
       "name": "coding"
     }, 
     {
       "id": "YYY"
       "name": "surfing"
     }]
}

{
  //Other Fields of user 2
  "interests": [
     {
       "id": "ZZZ"
       "name": "coding"
     }
   ]
}

I looked into the countDocuments method, but it doesn't seem to allow this kind of count.


EDIT + First solution:

This is how I managed to solve it:

const res = await UserModel.aggregate([
  {
    $unwind: '$interests'
  },
  {
    $lookup: {
      from: "interests",
      localField: "interests",
      foreignField: "_id",
      as: "interests"
    }
  },
  {
    $match:{
      "interests.name": name
    }
  },
  {
    $count: "count"
  }
]);
return res[0].count;

The fact that the interests is a referenced type, I can not query for its name unless I pass the lookup stage. I am not sure if this is a good solution regarding performance, since the unwind stage must pass through all the users of the database and create a new element for each of their interests. This why I am not posting it as an answer

Strider
  • 3,539
  • 5
  • 32
  • 60
  • 1
    Try to use aggregation to unwind interests and group by name. Show your attempts and errors, if you have any. – Alex Blex Jul 23 '18 at 12:04
  • Use `model.where({ 'interests.name': 'coding' }).count();` – s7vr Jul 23 '18 at 12:07
  • A simple count query can do `db.collection.count({ interests: { $elemMatch: { name: "coding" } } })`... https://stackoverflow.com/questions/50210367/count-where-value-does-not-match-array – Ashh Jul 23 '18 at 12:10

1 Answers1

0

To work with elemMatch, I had to change the schema in order to embed the Interest in the User instead of referencing it:

let userSchema = new Schema({
  //...
  interests:  [InterestSchema],
  //...
});

let InterestSchema = new Schema({
  id: ObjectId,
  name: {
    type: String,
    required: true
  },
  //...
});

This is how I used the elemMatch:

const count = UserModel
  .where('interests').elemMatch( interest => {
    interest.where({ name: name });
  })
  .count();

As I mentioned in my question, the aggregate method works but I am not sure about its performance since I was using a referenced array instead of a sub-document, this is why I had to change the schema of my collection

Strider
  • 3,539
  • 5
  • 32
  • 60