繁体   English   中英

MongoDB:如何返回查询列表中存在的数组元素

[英]Mongodb: how to return elements of array that are present in the query list

我有一个叫做商店的收藏。 结构就像:

[
     {
          '_id' : id1,
          'details' : {name: 'shopA'},
          'products' : [{
               _id: 'p1',
               details:  {
                    'name': 'product1'
               }
          },{
               _id: 'p2',
               details:  {
                    'name': 'product2'
               }
          }, {
               _id: 'p4',
               details:  {
                    'name': 'product4'
               }
          }
     },{
          '_id' : id2,
          'details' : {name: 'shopB'},
          'products' : [{
               _id: 'p1',
               details:  {
                    'name': 'product1'
               }
          },{
               _id: 'p4',
               details:  {
                    'name': 'product4'
               }
          }, {
               _id: 'p5',
               details:  {
                    'name': 'product5'
               }
          }
     },{
          '_id' : id3,
          'details' : {name: 'shopC'},
          'products' : [{
               _id: 'p1',
               details:  {
                    'name': 'product1'
               }
          },{
               _id: 'p2',
               details:  {
                    'name': 'product2'
               }
          }, {
               _id: 'p3',
               details:  {
                    'name': 'product3'
               }
          }
     },{
          '_id' : id4,
          'details' : {name: 'shopOther'},
          'products' : [{
               _id: 'p10',
               details:  {
                    'name': 'product10'
               }
          },{
               _id: 'p12',
               details:  {
                    'name': 'product12'
               }
          }, {
               _id: 'p13',
               details:  {
                    'name': 'product13'
               }
          }
     }
]

现在,用户可以从菜单中选择一些产品,然后尝试找到这些商店。 结果应该是所有提供至少一项选定商品的商店。

例,

假设用户选择['p1', 'p2', 'p3'] //ids那么只会列出三个商店id1,id2,id3(id4没有这些项目),加上这样的结构可以删除其余的结果数组中来自文档的商店产品(未列出)。

有没有办法,我可以直接从mongodb获得这样的结果?

由于您确实提出了很好的要求,而且格式也很合理,因此需要考虑一些类似的答案可能实际上并不适合作为参考,特别是如果您对MongoDB产品的经验水平很低时。

$redact类的选项似乎很简单,通常很适合。 但这不是您需要如何构造语句的情况:

db.collection.aggregate([
  { "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
  { "$redact": {
    "$cond": {
      "if": {
        "$or": [
          { "$eq": [ "$_id", "p1" ] },
          { "$eq": [ "$_id", "p2" ] },
          { "$eq": [ "$_id", "p3" ] }
        ]
      },
      "then": "$$DESCEND",
      "else": "$$PRUNE"
    }
  }}
])

这与$or在聚合运算符中的“不太明显”用法一起使用。 至少就正确的语法和形式而言,但这实际上是“完全失败”。 原因是因为$redact通常是“递归”操作,并且它在文档的“所有级别”进行检查,而不仅仅是在特定级别进行检查。 因此,在您的“顶层”中, _id断言将失败,因为同名的顶层字段将不符合该条件。

实际上,您没有任何其他可以做的事情,但是考虑到数组中的_id实际上是一个“唯一”元素,那么您始终可以在$project阶段借助$map$setDifference来执行此操作:

db.collection.aggregate([
  { "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
  { "$project": {
    "details": 1,
    "products": {
      "$setDifference": [
        { "$map": {
          "input": "$products",
          "as": "el",
          "in": {
            "$cond": {
              "if": { 
                "$or": [
                  { "$eq": [ "$$el._id", "p1" ] },
                  { "$eq": [ "$$el._id", "p2" ] },
                  { "$eq": [ "$$el._id", "p3" ] }
                ]
              },
              "then": "$$el",
              "else": false
            }
          }
        }},
        [false]
      ]
    }
  }}
])

似乎很长,但实际上非常有效。 $map运算符为每个文档“内联”处理数组,并作用于每个元素以产生新的数组。 $setDifference相比,通过考虑结果的“集合”,可以平衡在条件不匹配的$cond下作出的false断言,后者可以有效地“过滤”结果数组中的false结果,仅保留有效的匹配项。

当然,如果_id值或整个对象不是真正的“唯一”,则“集合”将不再有效。 考虑到这一点,以及上述提到的运算符不可用于2.6之前的版本的事实,那么更传统的方法是$unwind数组成员,然后通过$match操作“过滤”它们。

db.collection.aggregate([
  { "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
  { "$unwind": "$products" },
  { "$match": { "products._id": { "$in": ["p1","p2","p3"] } }},
  { "$group": {    
      "_id": "$_id",
      "details": { "$first": "$details" },
      "products": { "$push": "$products" }
  }}
])

考虑到按照其他示例, $match阶段应首先在管道中执行,以减少匹配条件的“可能”文档。 具有$match的“第二”阶段以“非规范化”形式对数组内的文档元素进行实际的“过滤”。

由于数组是由$unwind “解构”的,因此$group的目的是“重建”数组,并从与条件不匹配的元素中“过滤”出来。

MongoDB还提供了位置$运算符,以便从查询条件中选择匹配的数组元素。 像这样:

db.collection.find(
    { "products._id": { "$in": ["p1","p2","p3"] },
    { "details": 1, "products.$": 1 }
)

但是这里的问题是,此运算符仅在查询文档中提供的条件上支持“第一”匹配。 这是设计目的,到目前为止,还没有严格的运算符语法可以满足多个匹配项。

因此,您的最终方法是当前使用.aggregate()方法,以便在所需的内部数组上实际实现匹配过滤。 要么过滤内容,要么在客户端代码中响应自己,这取决于最终对您的可口性。

暂无
暂无

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

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