简体   繁体   English

查询以找到所有数组属性元素都在数组中的文档

[英]Query to find documents where all array property elements are in array

I have a collection with documents like this one: 我有一个像这样的文档集合:

{
_id : "1",
arrayProperty : ["1","2","3"]
}

I want to find documents which having all elements of arrayProperty contained in some array. 我想找到在某些数组中包含arrayProperty的所有元素的文档。

Suppose I have this collection : 假设我有这个集合:

{_id : "1", arrayProperty : ["1", "2"]}
{_id : "2", arrayProperty : ["1", "4"]}
{_id : "3", arrayProperty : ["1", "7", "8"]}
{_id : "4", arrayProperty : ["1", "9"]}

and I want to find the documents which having All their arrayProperty elements contained in ["1", "2", "3", "4"] 并且我想找到在["1", "2", "3", "4"]包含其所有arrayProperty元素的文档

It should return : 它应该返回:

{_id : "1", arrayProperty : ["1", "2"]}
{_id : "2", arrayProperty : ["1", "4"]}

The basic concept here is to look for things which are NOT in the list of possible values per array element and then "exclude" that document. 这里的基本概念是查找不在每个数组元素可能值列表中的内容,然后“排除”该文档。 Which means using $elemMatch with $nin for the list and $not to reverse the logic: 这意味着将$elemMatch$nin用于列表,而$not颠倒逻辑:

db.collection.find({ 
  "arrayProperty": { 
    "$not": { "$elemMatch": { "$nin": ["1", "2", "3", "4"] } } 
  }
})

Which returns the correct documents: 返回正确的文档:

{ "_id" : "1", "arrayProperty" : [ "1", "2" ] }
{ "_id" : "2", "arrayProperty" : [ "1", "4" ] }

That actually uses the native operators in the "query engine" to evaluate the expression as opposed to "forced calculation" via $expr or $where which we will mention later. 实际上,它通过$expr$where使用“查询引擎”中的机运算符来评估表达式,而不是“强制计算” ,我们将在后面提到。 It's the right results, but the only problem here is the operator pattern actually negates the usage of any index. 这是正确的结果,但是这里唯一的问题是运算符模式实际上否定了任何索引的使用。 Fortunately there is something we can do about that: 幸运的是,我们可以做一些事情:

db.collection.find({
  "arrayProperty": {
    "$in": ["1", "2", "3", "4"],
    "$not": { "$elemMatch": { "$nin": ["1", "2", "3", "4"] } }
  } 
})

Whilst it might seem a little funny at first, adding the $in here is a valid condition. 虽然乍一看似乎有点可笑,但$in此处添加$in是有效的条件。 What it does for the query is enforce that an index is actually used in selection of the valid documents. 对于查询的作用是强制在选择有效文档时实际使用索引。 In the question sample that is still "ALL" of the documents presented, but in the real world not all things will typically match the list of arguments. 在问题样本中,仍然是所提供文档的“全部”,但在现实世界中,并非所有事物都通常会匹配参数列表。

Essentially it changes the parsed query conditions from this: 从本质上说,它从以下更改已解析的查询条件:

"winningPlan" : { "stage" : "COLLSCAN"

To this: 对此:

"winningPlan" : { "stage" : "FETCH",

"inputStage" : { "stage" : "IXSCAN",

That makes $in a worthwhile filter to add to the expression and the "native query operator" expression is the fastest way to do this. 这使得$in成为添加到表达式$in的有价值的过滤器,而“本地查询运算符”表达式是执行此操作的最快方法。

The problem with $expr ( aside from being only available from MongoDB 3.6 ) is that it means the "whole collection" needs to be scanned in order to apply the "aggregation operator expression" which it contains. $expr的问题(除了只能从MongoDB 3.6中使用),这意味着需要扫描“整个集合”才能应用其中包含的“聚合运算符”。 Of course, we also just learned what $in adds to the query 当然,我们也刚刚了解了$in给查询添加了什么

db.collection.find({
   "arrayProperty": { "$in": ["1", "2", "3", "4"] },
   "$expr": { "$setIsSubset": ["$arrayProperty", ["1", "2", "3", "4"]] }
})

This has a similar IXSCAN input where an index is present because of the $in and only using the $setIsSubset boolean condition in order to reject the other documents found in the index selection. 这具有类似的IXSCAN输入,其中由于$in而存在索引$in并且仅使用$setIsSubset布尔条件来拒绝在索引选择中找到的其他文档。

Earlier forms of usage with prior MongoDB release are less ideal: MongoDB先前版本的较早使用形式不太理想:

db.collection.aggregate([
  { "$match": { "$in": ["1", "2", "3", "4"] } },
  { "$redact": {
    "if": { "$setIsSubset": ["$arrayProperty", ["1", "2", "3", "4"]] },
    "then": "$$KEEP",
    "else": "$$PRUNE"
  }}
])

Or using $where : 或使用$where

db.collection.find({
  "arrayProperty": { "$in": ["1", "2", "3", "4"] },
  "$where": function() {
    return this.arrayProperty.every(a => ["1", "2", "3", "4"].some(s => a === s))
  }
})

So all actually get the job done but the combination of the $elemMatch with $nin and $not , also including the $in operator for index selection is actually what you really want. 因此,实际上所有工作都可以完成,但是$elemMatch$nin$not的组合(实际上还包括$in运算符用于索引选择)实际上是您真正想要的。 And it's supported in all versions as well. 而且所有版本都支持它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM