skip to Main Content

Given documents like the one below, I’d like to return _id only if all elements of the input_array matches the list of available objects. For these examples, the first object should match while the second does not because key3 in first element is "P" instead of "c" and key2 for second element cannot be found in the second lookup object.

{
    _id: ObjectId('abcd1234001'),
    input_array: [
        {
            "key1": "a",
            "key2": "b",
        },
        {
            "key1": "x"
        }
    ]
},
{
    _id: ObjectId('abcd1234002'),
    input_array: [
        {
            "key1": "a",
            "key2": "b",
            "key3": "P",
        },
        {
            "key1": "x",
            "key2": "y",
        }
    ]
}

list of available objects are:

[
   {
      "key1": "a",
      "key2": "b",
      "key3": "c",
   },
   {
      "key1": "x"
   }
]

2

Answers


  1. Definitely not the best looking query, but the idea is to iterate over every item in the input array and try to find an element that matches it, if every single element matches this filter then the document will be matched, like so:

    (notice i changed the input structure, this is just for connivence of the query, you can do it yourself in code or in mongo )

    const input = [
       ["a", "b", "c"],
       ["x"]
    ];
    
    db.collection.aggregate([
      {
        $match: {
          $expr: {
            $eq: [
              {
                $size: "$input_array"
              },
              {
                $size: {
                  $filter: {
                    input: {
                      $map: {
                        input: "$input_array",
                        as: "outer",
                        in: {
                          $map: {
                            input: {
                              "$objectToArray": "$$outer"
                            },
                            in: "$$this.v"
                          }
                        }
                      }
                    },
                    as: "outer",
                    cond: {
                      $gt: [
                        {
                          $size: {
                            $filter: {
                              input: input,
                              cond: {
                                $eq: [
                                  {
                                    $size: {
                                      "$setDifference": [
                                        "$$outer",
                                        "$$this"
                                      ]
                                    }
                                  },
                                  0
                                ]
                              }
                            }
                          }
                        },
                        0
                      ]
                    }
                  }
                }
              }
            ]
          }
        }
      }
    ])
    

    Mongo Playground

    Login or Signup to reply.
  2. If the array is fixed to 2 elements, try this.
    I convert data in each element to array by $objectToArray and combine it by $concatArrays and matched by $setIsSubset.

    const listof = [{ "key1": "a", "key2": "b", "key3": "c", }, { "key1": "x" }];
    
    db.collection.aggregate([
        {
            $match: {
                $expr: {
                    $eq: [
                        {
                            $setIsSubset: [
                                {
                                    $concatArrays: [
                                        { $objectToArray: { $arrayElemAt: ['$input_array', 0] } },
                                        { $objectToArray: { $arrayElemAt: ['$input_array', 1] } }
                                    ]
                                },{
                                    $concatArrays: [
                                        { $objectToArray: { $arrayElemAt: [listof, 0] } },
                                        { $objectToArray: { $arrayElemAt: [listof, 1] } }
                                    ]
                                }
                            ]
                        }
                        , true]
                }
            }
        }
    ])
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search