mongo $和选择器如何工作?我无法获得正确的结果。
假设我有一个像这样的集合:
{ "_id" : "F7mdaZC2eBQDXA5wx", "quantity" : 5 }
{ "_id" : "F7mdaZC2eBQDXA5wx", "quantity" : 9 }
{ "_id" : "F7mdaZC2eBQDXA5wx", "quantity" : 34 }
{ "_id" : "F7mdaZC2eBQDXA5wx", "quantity" : 66 }
我运行查询:
var selectorMin = 9;
var selectorMax = 42;
ScrapReport.find({ $and: [ { quantity: { $gte: selectorMin }, quantity: { $lte: selectorMax } } ] })
我希望mongo只返回9和34但是由于某种原因它也会返回5和66.
我的查询有什么问题?
答案 0 :(得分:4)
您的查询返回该示例中的所有文档,因为它首先查找的文档quantity >= 9
即9,34和66 AND 将该查询与{{1}的文档相结合即34,9和5.它没有查找特定范围内的文档,但您的查询明确查找满足两个范围的所有文档,即
quantity <= 42
不是
Documents which satisfy "quantity >= 9"
+
Documents which satisfy "quantity <= 42"
只需将查询简化为
即可Documents which satisfy "9 <= quantity <= 42"
这样,您可以指定MongoDB使用例如
过滤文档的范围ScrapReport.find({ "quantity": { "$gte": selectorMin, "$lte": selectorMax } })
指定逗号分隔的表达式列表意味着隐式AND操作,并且当必须在多个表达式中指定相同的字段或运算符时,使用带 $and
运算符的显式AND。
答案 1 :(得分:3)
使用隐含的AND操作,就像建议的其他答案一样。但我想深入研究具体细节。 为什么您的查询无法正常工作?
为什么?为什么这个看似正确的查询对您的回复不是那么正确的结果?毕竟,无论您使用隐式还是显式AND操作都应该是您的选择的问题,无论您使用哪种形式,您都应该能够实现目标。 如何使用明确的AND操作进行查询?
让我们看一下AND操作的语法。
{ $and: [ { <expression1> }, { <expression2> } , ... , { <expressionN> } ] }
AND运算符的值应该是一个包含要在其上执行AND运算的表达式的数组。
第一眼看到你的查询后,一切看起来都很好。但是如果你花一点时间来深入研究,你会发现你的查询与AND语法不完全匹配。它在语法上仍然是正确的。毫无疑问。但它在逻辑上是不正确的。我将解释如何。
这是您的$and
运算符值
{ $and: [ { quantity: { $gte: selectorMin }, quantity: { $lte: selectorMax } } ] }
您认为您有一个表达式quantity: { $gte: selectorMin }
和一个表达式2 quantity: { $lte: selectorMax }
。使用这些表达式的AND操作应返回数量为9和34的文档。但实际上,您拥有的只是一个表达式。密切注意牙套。您已在单个{}
块中添加了这两个表达式。你看到了吗?如此有效,AND运算符没有第二个表达式可供使用。但AND运算符需要两个或多个表达式才能正常运行。
因此您的查询格式为
{ $and: [ { <expression1> } ] }
如果表单不正确,结果也会不正确。使用显式AND操作的正确查询将是
ScrapReport.find({ $and: [ { quantity: { $gte: selectorMin } }, { quantity: { $lte: selectorMax } } ] })
你看到了区别吗?尝试此查询,您将获得您期望的结果。
如果您对答案感到不满意并且很想知道Mongo如何解释您的第一个查询,请继续阅读。
考虑此查询
ScrapReport.find({ quantity: 9 })
您期望结果如何?如果您希望Mongo返回单个文档,其数量字段中的值为9,那么您是对的。这正是结果。现在考虑相同的查询,但有一点小麻烦。
ScrapReport.find({ quantity: 9, quantity: 5 })
现在的结果是什么?事情现在变得有趣了,是吧?如果执行此查询并查看结果,您仍将只看到一个文档。但是数量字段中的值是5.现在这很有趣!
ScrapReport.find({ quantity: 9, quantity: 5, quantity: 34 })
这个怎么样?结果仍然是单个文档,其数量字段中的值为34.您可以尝试其他组合。你会发现这是什么 -
在表达式中,如果多次引用某个字段,结果将由该表达式中该字段的最后一个引用确定。
现在将此概念应用于原始查询。已经指出,您有一个单表达式,其中包含quantity: { $gte: selectorMin }
和quantity: { $lte: selectorMax }
两个部分。因为在表达式中,您指的是同一个字段两次,所以只有最后一个字段是相关的。选择标准为quantity: { $lte: selectorMax }
。结果将是3份文件,数量值为5,9和34.
如果您更换订单,即先写quantity: { $lte: selectorMax }
然后再写quantity: { $gte: selectorMin }
,现在将由quantity: { $gte: selectorMin }
确定选择条件。结果将是3份文件,数量值为9,34和66。
虽然这不是您的意图,但您的原始查询实际上是
ScrapReport.find({ quantity: { $gte: selectorMin }, quantity: { $lte: selectorMax } })
当你错过大括号或将它们添加到错误的位置时,它可以完全改变查询的含义。
道德 - 密切关注您在复杂查询中放置大括号的位置。
答案 2 :(得分:1)
实际上你有两个问题:
您的查询等同于以下内容:
ScrapReport.find( { "$and": [{ "quantity": { "$lte": selectorMax } } ] } )
甚至更好:
ScrapReport.find( { "quantity": { "$lte": selectorMax } } )
原因是因为JSON文档中允许使用重复键,但保留了给定键的最后一个值。
所以这只会返回“数量”小于或等于selectorMax
的所有文件。
@ chridam的答案中已经提到了第二个问题,所以正确的查询是:
ScrapReport.find({ "quantity": { "$gte": selectorMin, "$lte": selectorMax } })