首页 > 解决方案 > Mongoose get all documents matching array intersection

问题描述

By array intersection I mean, the inventory has a lot more elements than each document ingredients array, and the result I want to get from the query is all documents which all array elements are contained within the inventory. $all will get me zero results since the inventory has more elements than can be found in ingredients even if all ingredients are found within the inventory,

I have thousands of docs that have an array field of strings

{
 ...
  recipe: "recipe1",
  ingredients: [ "1 cup cooked quinoa", "6 tbsp butter", "1 large egg" ]
 ...
},
{
 ...
  recipe: "recipe2",
  ingredients: [ "2 lemons", "2 tbsp butter", "1 large egg" ]
 ...
}
{
 ...
  recipe: "recipe3",
  ingredients: [ "1lb salmon", "1 pinch pepper", "4 spears asparagus" ]
 ...
}

and I'm trying to find all documents where all elements in the ingredients array are contained in a sample array that contains lots of elements, lets say for the case this only contains this:

inventory = [ "lemons", "butter", "egg", "milk", "bread", "salmon", "asparagus", "pepper" ]

With this inventory array, I want to get recipe2 and recipe3.
Right now I have this inventory array and query (thanks to turivishal):

    let inventory = ["lemons", "butter", "egg", "milk", "bread", "salmon", "asparagus", "pepper"];
inventory = inventory.map((i) => new RegExp(i, "i"));

query:

    Recipe.find({
  ingredients: { $all: inventory }
})

Expected result:

{
 ...
  recipe: "recipe2",
  ingredients: [ "2 lemons", "2 tbsp butter", "1 large egg" ]
 ...
}
{
 ...
  recipe: "recipe3",
  ingredients: [ "1lb salmon", "1 pinch pepper", "4 spears asparagus" ]
 ...
}

But I'm getting zero results

标签: javascriptmongodbmongoose

解决方案


$expr您可以使用表达式条件在 mquery 中尝试聚合运算符,

  • 首先,您可以按|顺序符号加入字符串数组并制作一个字符串,并在$regex搜索中使用它,
  • $filter迭代循环ingredients
  • $regexMatch匹配元素有任何匹配的词
  • $size获取过滤元素的总大小
  • $eq匹配过滤结果和实际ingredients相等
let inventory = ["lemons", "butter", "egg", "milk", "bread", "salmon", "asparagus", "pepper"];
let inventoryStr = inventory.join("|");
// "lemons|butter|egg|milk|bread|salmon|asparagus|pepper"

Recipe.find({
  $expr: {
    $eq: [
      {
        $size: {
          $filter: {
            input: "$ingredients",
            cond: {
              $regexMatch: {
                input: "$$this",
                regex: inventoryStr,
                options: "i"
              }
            }
          }
        }
      },
      { $size: "$ingredients" }
    ]
  }
})

操场


推荐阅读