首页 > 解决方案 > MongoDB:通过对嵌套文档数组执行的范围过滤器查找文档

问题描述

考虑将分数收集为嵌套文档数组的文档集合,如下所示(我使用的是 MongoDb 4.2):

{
    "scores" : [
      { "value" : 2 }, 
      { "value" : 3 },
      { "value" : 4 }
    ]
}, {
    "scores" : [
      { "value" : 8  }, 
      { "value" : 9  },
      { "value" : 10 }
    ]
}, {
    "scores" : [
      { "value" : 7  }, 
      { "value" : 7 },
      { "value" : 10 }
    ]
}

现在考虑以下用例:“给我所有只包含 [X, Y] 范围内分数的文档”。换句话说,请过滤掉至少有一个值不在 [XY] 范围内的文档。旁注:在实际数据中,分数是实数,所以在这里做一些 $in 查询将无济于事。

让我们考虑三个范围:

作为 mongo 的非专家,我的第一次尝试是这样做:

db.test.find({
  $and: [ 
    { "scores.value": {$gte: 8}}, 
    { "scores.value": {$lte: 10}} 
  ]
})

所以这里有一些奇怪的东西:第一个结果表明我的查询实际上返回了满足任一条件的分数(有点像 $or 而不是 $and)。但这是真的吗,我会在第二种情况下检索所有记录 [0, 1] 而不是 0。显然我在这里使用了错误的方式,但如果有人能解释我为什么,我会很高兴.

下一个查询:

db.test.find({
  "scores": {
    $elemMatch: { 
      value: {$gte: 6.5, $lte: 6.8} 
    }
  }
})

对于那个,一切都很好,并且按记录/预期工作,但它没有回答我原来的用例。

所以除了理解使用第一个查询发生了什么之外,有没有人知道实现我想要做的事情的方法?

标签: arraysmongodbfilter

解决方案


以 [8-10] 为例,正如您所提到的,您可以编写一个查询来查找至少分数不在 [8-10] 范围内的所有“坏”文档:这就是elementMatch.

“坏”文件是(分数 < 8 或分数 > 10):

db.collection.find({
  "scores": {
    $elemMatch: {
      $or: [ { value: { $lt: 8 }}, { value: { $gt: 10 }} ]
    }
  }
})

然后你可以用 a 反转条件$not以获得你期望的结果(拒绝“坏”文件):

db.collection.find({
  "scores": { 
    $not: {
      $elemMatch: {
        $or: [ { value: { $lt: 8 }}, { value: { $gt: 10 }} ]
      }
    }
  }
})

MongoDB游乐场


推荐阅读