Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 728
Jarki Avatar asked Jul 28 '26 15:07

Jarki


1 Answers

You can try aggregation operator in mquery using $expr expression condition,

  • first of all you can join the array of string by | order symbol and make a string, and use it in $regex search,
  • $filter to iterate loop of ingredients
  • $regexMatch to match element has any matching word
  • $size to get the total size of filtered elements
  • $eq to match filtered result and actual ingredients is equal
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" }
    ]
  }
})

Playground

like image 144
turivishal Avatar answered Jul 30 '26 05:07

turivishal



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!