Query Comparing Arrays of Objects

2019-02-20 11:38发布

问题:

I'm wondering how I can compare arrays of (nested) objects in Mongoose.

Considering the data below, I would like to get results when the name properties match. Could anyone help me with this?

    Organisation.find( { 
        $or: [ 
          { "category_list": { $in: cat_list } }, 
          { "place_topics.data": { $in: place_tops } } 
        ] 
      } 
    )

Let's say that this is the data stored in my MongoDB:

  "category_list": [
      {
          "id": "197750126917541",
          "name": "Pool & Billiard Hall"
      },
      {
          "id": "197871390225897",
          "name": "Cafe"
      },
      {
          "id": "218693881483234",
          "name": "Pub"
      }
  ],
  "place_topics": {
      "data": [
          {
              "name": "Pool & Billiard Hall",
              "id": "197750126917541"
          },
          {
              "name": "Pub",
              "id": "218693881483234"
          }
      ]
  }

And let's say that these are the arrays I want to compare against (almost the same data):

let cat_list = [
            {
                "id": "197750126917541",
                "name": "Pool & Billiard Hall"
            },
            {
                "id": "197871390225897",
                "name": "Cafe"
            },
            {
                "id": "218693881483234",
                "name": "Pub"
            }
        ]
let place_tops = [
                {
                    "name": "Pool & Billiard Hall",
                    "id": "197750126917541"
                },
                {
                    "name": "Pub",
                    "id": "218693881483234"
                }
            ]

回答1:

When there are "multiple conditions" required for each array element is when you actually use $elemMatch, and in fact "need to" otherwise you don't match the correct element.

So to apply multiple conditions, you would rather make an array of conditions for $or instead of shortcuts with $in:

Organizations.find({
  "$or": [].concat(
    cat_list.map( c => ({ "category_list": { "$elemMatch": c } }) ),
    place_tops.map( p => ({ "place_topics": { "$elemMatch": p } }) )
  )
})

However, if you take a step back and think logically about it, you actually named one of the properties "id". This would generally imply in all good practice that the value is in fact ""unique".

Therefore, all you really should need to do is simply extract those values and stick with the original query form:

Organizations.find({
  "$or": [
    { "category_list.id": { "$in": cat_list.map(c => c.id) } },
    { "place_topics.id": { "$in": place_tops.map(p => p.id) } }
  ]
})

So simply mapping both the values and the property to "match" onto the "id" value instead. This is a simple "dot notation" form that generally suffices when you have one condition per array element to test/match.

That is generally the most logical approach given the data, and you should apply which one of these actually suits the data conditions you need. For "multiple" use $elemMatch. But if you don't need multiple because there is a singular match, then simply do the singular match