MongoDB 找到精确的数组匹配但顺序无关紧要

MongoDB Find Exact Array Match but order doesn't matter

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

Example

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

第 2 个文件检索成功

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

什么都不显示

请帮忙

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

例如查询

db.coll.find({ "hobbies": { "$size" : 2, "$in": [ "2", "1", "5", "hamburger" ] }  });
在这种情况下,

仍会 return 用户 kaushik。

要实现完全匹配,您需要做的是合并 $size with $all,如下所示:

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

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

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

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

同时 $eq 检查您指定元素的顺序。

如果您使用以下查询:

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

那么将不会 return 编辑完全匹配。假设你查询:

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

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

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

let query = {$or: [
{hobbies:{$eq:["1","2"]}},
{hobbies:{$eq:["2","1"]}}
]};

db.coll.find(query)

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

输出: 转换:["George Bickel","Emma Carus","George M. Cohan","James J. Corbett"]

使用 aggregate 这就是我如何变得熟练和更快的:

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

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

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

由于我们对查询的能力有一个基本的潜在限制,我们需要避免这些 hacky,error-prone 数组交叉。在不对每个值执行显式计数的情况下检查两个数组是否包含一组相同的值的最佳方法是对我们要比较的两个数组进行排序,然后比较这些数组的排序版本。据我所知,由于 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
    }
  }
]);

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

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 ]
  }
]