MongoDB:混合$ all和$ elemMatch

rah*_*ave 6 mongodb

我有一个包含5个项目的"项目",每个项目都有一个id和一个数组

{ _id: ObjectId("51c21bec162c138d9d0001a7"), 
    tags: [ { name: "a", type: "tag" }, { name: "b", type: "tag" }, { name: "c", type: "note" } ] 
}
{ _id: ObjectId("51c21ca22c69904840000178"), 
    tags: [ { name: "a", type: "tag" }, { name: "d", type: "tag" }, { name: "c", type: "note" } ] 
}
{ _id: ObjectId("51c21cc1478cf6691a0001b4"), 
    tags: [ { name: "d", type: "tag" }, { name: "b", type: "tag" }, { name: "c", type: "note" } ] 
}
{ _id: ObjectId("51c22292162c13b1ff000222"), 
    tags: [ { name: "a", type: "tag" }, { name: "d", type: "tag" }, { name: "e", type: "note" } ] 
}
{ _id: ObjectId("51c222e926d602a57d0001d8"), 
    tags: [ { name: "a", type: "tag" }, { name: "d", type: "note" }, { name: "c", type: "note" } ] 
}
Run Code Online (Sandbox Code Playgroud)

这里的目标是返回所有标记为"a"和"d"的项目,其中标记的类型为"tag".您可能认为这样做会:

find({"tags.name":{"$all":["a","d"]}, "tags.type":'tag'})
Run Code Online (Sandbox Code Playgroud)

返回3个文档,这是错误的,但我已经知道这个查询做了一个或.那么我尝试使用'$ elemMatch'来实现这一点,我认为这是最直观的方式,但是

find({"tags":{"$elemMatch":{'name':{'$all':["a","d"]}, "type":'tag'}}})
Run Code Online (Sandbox Code Playgroud)

不返回任何文档.

相同的查询,如果我只想标记'a'的项目有效:

find({"tags":{"$elemMatch":{'name':{'$all':["a"]}, "type":'tag'}}})
Run Code Online (Sandbox Code Playgroud)

大概是因为$ all被映射到类似$ eq的东西.

最终我发现以下是我需要为我的原始目标做的事情:

find({"tags":{"$all":[{'$elemMatch':{'name':"a", "type":'tag'}}, {'$elemMatch':{'name':"d", "type":'tag'}} ]}})
Run Code Online (Sandbox Code Playgroud)

返回正确的两个文档.

但这种语法太可怕了!我必须将数组["a","d"]自己扩展到查询中.如果我正在编写通用查询引擎并且我想说嵌入式doc的多个字段是数组,并且我想要每个数组的某个值的子集,该怎么办?

有一个更好的方法吗?更好的语法?

Nat*_*Nat 5

您最终得出的是正确的解决方案。以编程方式构造规则数组可能是一种改进$elemMatch

考虑以下Python:

match_rules = []
for tag in query_tags:
  match_rules.append({
    '$elemMatch': {
      'name': tag
      'type': 'tag'
    }
  })

collection.find({"tags":{"$all": match_rules}})
Run Code Online (Sandbox Code Playgroud)