【问题标题】:MongoDB Find Exact Array Match but order doesn't matterMongoDB 查找精确的数组匹配,但顺序无关紧要
【发布时间】:2015-06-28 17:19:31
【问题描述】:

我正在查询查找精确的数组匹配并成功检索它,但是当我尝试找出具有不同顺序的值的精确数组时,它会失败。

Example

db.coll.insert({"user":"harsh","hobbies":["1","2","3"]})
db.coll.insert({"user":"kaushik","hobbies":["1","2"]})
db.coll.find({"hobbies":["1","2"]})

第二个文档检索成功

db.coll.find({"hobbies":["2","1"]})

什么都不显示

请帮忙

【问题讨论】:

    标签: arrays mongodb mongodb-query


    【解决方案1】:

    currently accepted answer 不能确保您的数组完全匹配,只是大小相同并且该数组与查询数组共享至少一项。

    例如查询

    db.coll.find({ "hobbies": { "$size" : 2, "$in": [ "2", "1", "5", "hamburger" ] }  });
    

    在这种情况下仍会返回用户 kaushik。

    你需要做的就是将$size$all结合起来,就像这样:

    db.coll.find({ "hobbies": { "$size" : 2, "$all": [ "2", "1" ] }  });
    

    但请注意,这可能是一项非常昂贵的操作,具体取决于您的数据量和数据结构。 由于 MongoDB 保持插入数组的顺序稳定,因此在插入数据库时​​确保数组按排序顺序可能会更好,这样在查询时可以依赖静态顺序。

    【讨论】:

      【解决方案2】:

      为了完全匹配数组字段,Mongo 提供了$eq 运算符,它可以像值一样对数组进行操作。

      db.collection.find({ "hobbies": {$eq: [ "singing", "Music" ] }});
      

      $eq 还会检查您指定元素的顺序。

      如果您使用以下查询:

      db.coll.find({ "hobbies": { "$size" : 2, "$all": [ "2", "1" ] }  });
      

      则不会返回完全匹配的内容。假设您查询:

      db.coll.find({ "hobbies": { "$size" : 2, "$all": [ "2", "2" ] }  });
      

      此查询将返回元素为 2 且大小为 2 的所有文档(例如,它还将返回爱好 :[2,1] 的文档)。

      【讨论】:

      • 嗯,你是对的,我不应该在我的查询示例中使用“完全匹配”这个词。您是否知道查询值的确切子集(例如使用 $eq)但不检查顺序的解决方案?
      【解决方案3】:

      Mongodb 完全按数组元素过滤,不考虑顺序或指定顺序。 来源:https://savecode.net/code/javascript/mongodb+filter+by+exactly+array+elements+without+regard+to+order+or+specified+order

      // Insert data
      db.inventory.insertMany([
         { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
         { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
         { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
         { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
         { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
      ]);
      
      // Query 1: filter by exactly array elements without regard to order
      db.inventory.find({ "tags": { "$size" : 2, "$all": [ "red", "blank" ] }  });
      // result:
      [
        {
          _id: ObjectId("6179333c97a0f2eeb98a6e02"),
          item: 'journal',
          qty: 25,
          tags: [ 'blank', 'red' ],
          dim_cm: [ 14, 21 ]
        },
        {
          _id: ObjectId("6179333c97a0f2eeb98a6e03"),
          item: 'notebook',
          qty: 50,
          tags: [ 'red', 'blank' ],
          dim_cm: [ 14, 21 ]
        },
        {
          _id: ObjectId("6179333c97a0f2eeb98a6e05"),
          item: 'planner',
          qty: 75,
          tags: [ 'blank', 'red' ],
          dim_cm: [ 22.85, 30 ]
        }
      ]
      
      // Query 2: filter by exactly array elements in the specified order
      db.inventory.find( { tags: ["blank", "red"] } )
      // result:
      [
        {
          _id: ObjectId("6179333c97a0f2eeb98a6e02"),
          item: 'journal',
          qty: 25,
          tags: [ 'blank', 'red' ],
          dim_cm: [ 14, 21 ]
        },
        {
          _id: ObjectId("6179333c97a0f2eeb98a6e05"),
          item: 'planner',
          qty: 75,
          tags: [ 'blank', 'red' ],
          dim_cm: [ 22.85, 30 ]
        }
      ]
      
      // Query 3: filter by an array that contains both the elements without regard to order or other elements in the array
      db.inventory.find( { tags: { $all: ["red", "blank"] } } )
      // result:
      [
        {
          _id: ObjectId("6179333c97a0f2eeb98a6e02"),
          item: 'journal',
          qty: 25,
          tags: [ 'blank', 'red' ],
          dim_cm: [ 14, 21 ]
        },
        {
          _id: ObjectId("6179333c97a0f2eeb98a6e03"),
          item: 'notebook',
          qty: 50,
          tags: [ 'red', 'blank' ],
          dim_cm: [ 14, 21 ]
        },
        {
          _id: ObjectId("6179333c97a0f2eeb98a6e05"),
          item: 'planner',
          qty: 75,
          tags: [ 'blank', 'red' ],
          dim_cm: [ 22.85, 30 ]
        }
      ]
      

      【讨论】:

        【解决方案4】:

        此查询将找到任何顺序的精确数组。

        let query = {$or: [
        {hobbies:{$eq:["1","2"]}},
        {hobbies:{$eq:["2","1"]}}
        ]};
        
        db.coll.find(query)
        

        【讨论】:

        • 这将是一个不理想的解决方案。使用$or 子句的组合将需要子句的数量以n!(n 阶乘)的速度增长。即使是小型数组,它也会增长得非常快(120 个单独的子句仅用于 5 个数组元素,720 个子句仅用于 6 个数组元素!)。此处较旧的示例处理此问题的效率要高得多,运行时复杂度最多为 O(n^2) 而不是 O(n!)
        • 这与上述评论中提到的不同
        • @B.Fleming 我同意这可能不是理想的解决方案。但是,如果您查看其他答案,那么这就是提出问题的答案。您能否为我们提供一个可行的理想解决方案?
        • @YulePale 我已经为这个问题添加了一个正确的答案,它利用 MongoDB 的聚合框架来确保准确的结果。出于对所有美好事物的热爱,如果您需要解决此问题的方法,请考虑使用我概述的聚合框架解决方案,并避免在程序上生成 $or 子句的 O(n!) 数组。为方便起见,可以在这里找到:stackoverflow.com/a/63915722/8698101
        • @B.Fleming 我听到了。唯一的问题是我的代码会查找匹配项,如果不存在则添加一个匹配项。所以我使用model.update() 方法。所以我想我必须使用两个查询来避免使用'$or 方法'。谢谢你的回答。
        【解决方案5】:

        使用 $all 我们可以做到这一点。 查询:{cast:{$all:["James J. Corbett","George Bickel"]}}

        输出: 演员表:[“George Bickel”,“Emma Carus”,“George M. Cohan”,“James J. Corbett”]

        【讨论】:

          【解决方案6】:

          使用aggregate,这就是我如何更快地熟练掌握我的方法:

           db.collection.aggregate([
           {$unwind: "$array"},
           
            {
                  
              $match: {
                
                "array.field" : "value"
                
              }
            },
          

          然后您可以再次展开它以使其成为平面数组,然后对其进行分组。

          【讨论】:

            【解决方案7】:

            这个问题相当老了,但我被 ping 了,因为另一个答案表明接受的答案对于包含重复值的数组来说是不够的,所以让我们解决这个问题。

            由于我们对查询能够执行的操作有一个基本的潜在限制,因此我们需要避免这些 hacky、容易出错的数组交集。检查两个数组是否包含一组相同的值而不执行每个值的显式计数的最佳方法是对我们要比较的两个数组进行排序,然后比较这些数组的排序版本。据我所知,由于 MongoDB 不支持数组排序,因此我们需要依靠聚合来模拟我们想要的行为:

            // Note: make sure the target_hobbies array is sorted!
            var target_hobbies = [1, 2];
            
            db.coll.aggregate([
              { // Limits the initial pipeline size to only possible candidates.
                $match: {
                  hobbies: {
                    $size: target_hobbies.length,
                    $all: target_hobbies
                  }
                }
              },
              { // Split the hobbies array into individual array elements.
                $unwind: "$hobbies"
              },
              { // Sort the elements into ascending order (do 'hobbies: -1' for descending).
                $sort: {
                  _id: 1,
                  hobbies: 1
                }
              },
              { // Insert all of the elements back into their respective arrays.
                $group: {
                  _id: "$_id",
                  __MY_ROOT: { $first: "$$ROOT" }, // Aids in preserving the other fields.
                  hobbies: {
                    $push: "$hobbies"
                  }
                }
              },
              { // Replaces the root document in the pipeline with the original stored in __MY_ROOT, with the sorted hobbies array applied on top of it.
                // Not strictly necessary, but helpful to have available if desired and much easier than a bunch of 'fieldName: {$first: "$fieldName"}' entries in our $group operation.
                $replaceRoot: {
                  newRoot: {
                    $mergeObjects: [
                      "$__MY_ROOT",
                      {
                        hobbies: "$hobbies"
                      }
                    ]
                  }
                }
              }
              { // Now that the pipeline contains documents with hobbies arrays in ascending sort order, we can simply perform an exact match using the sorted target_hobbies.
                $match: {
                  hobbies: target_hobbies
                }
              }
            ]);
            

            我不能说这个查询的性能,如果初始候选文档太多,很可能会导致管道变得太大。如果您正在处理大型数据集,那么再次按照当前接受的答案状态执行并按排序顺序插入数组元素。通过这样做,您可以执行静态数组匹配,这将更加有效,因为它们可以被正确索引并且不会受到聚合框架的管道大小限制的限制。但作为权宜之计,这应该可以确保更高水平的准确性。

            【讨论】:

              猜你喜欢
              • 2017-11-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-05-19
              • 1970-01-01
              • 1970-01-01
              • 2017-05-03
              相关资源
              最近更新 更多