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]范围内分数的文档"。换句话说,请过滤掉至少有一个值不在[X-Y]范围内的文档。旁注:在实际数据中,分数是实数,所以在这里做一些$in查询是没有用的。

让我们考虑三个范围:

  • 范围[8,10](一份文件应匹配)
  • Range[0,1](0文档匹配)。
  • Range[5,6](0文档匹配)。

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

db.test.find({
$and: [ 
{ "scores.value": {$gte: 8}}, 
{ "scores.value": {$lte: 10}} 
]
})
  • Case[8,10]: 2条记录。
  • Case [0,1]: 0 records
  • Case[5,6]: 2个记录

所以这里有一些奇怪的东西:第一个结果表明,我的查询实际上返回的分数满足条件中的任何一个(有点像$或而不是$和)。但如果这是真的,我将检索第二个情况[0,1]上的所有记录,而不是0。很明显我在这里用错了,但如果有人能告诉我为什么,我会很高兴的。

下查询:

db.test.find({
"scores": {
$elemMatch: { 
value: {$gte: 6.5, $lte: 6.8} 
}
}
})
  • Case[8,10]: 2条记录。
  • Case [0,1]: 0 records
  • Case [5,6]: 0 records

对于那个,一切都很好,并且按照文档/预期工作,但它不符合我最初的用例。

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

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

"糟糕的;文件是(score <8 OR分数>10):

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

然后您可以使用$not反转条件以获得您期望的结果(拒绝"坏")文档):

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

MongoDB操场

让我们从了解你第一次尝试的错误开始。

你正在使用Mongo的点符号,这是怎么做的?

按从零开始的索引位置指定或访问数组元素

基本上它一次求值所有数组元素

$and: [ 
{ "scores.value": {$gte: 8}}, 
{ "scores.value": {$lte: 10}} 
]

这些表达式中的每一个都查询整个数组,这意味着要满足它的查询,您所需要的只是一个在范围内的单个值。

您可以使用$not操作符并结合$elemMatch查找要排除的元素,如:

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

Mongo操场

相关内容

  • 没有找到相关文章

最新更新