简体   繁体   中英

Why is pipeline inside a lookup in mongoDB aggregation doesn't work when matched with a field in array?

I had been trying to write an aggregation pipeline in MongoDB which uses a lookup stage with a sub pipeline. In that pipeline, I am trying to match a field in an array in the document with an array of strings. But it seems to be not working. I get an empty result(No documents).

Following is a document from the collection called 'card':

{
"_id": "5fbff18be1157d5f8c6089f2",
"keywords": [{
    "type": "topic",
    "value": "benny"
  }, {
    "type": "tag",
    "value": "bo"
  }, {
    "type": "tag",
    "value": "bo"
  }],
"name": "tasty_steel_car"}

In this, I am trying to match the field: 'value' inside the keywords with the following query:

{
 'from': 'cards', 
 'let': {
   'keywordList': '$keywords'
 }, 
 'pipeline': [
  { 
   "$match": {
     "$expr": { 
       $and: [
         { "$in": [ "$keywords.value", ['benny'] ]  }
       ]
     } 
    
    } 
  
   }
  ], 
 'as': 'cards'
}

What am I doing wrong here?

The following part doesn't seem to work:

{ "$in": [ "$keywords.value", ['benny'] ]  }

Even the following doesn't seem to be working:

db.cards.find({ 

  $expr: {
      $in: [ "$keywords.value", ["benny"] ]
  }

})

Assuming you are trying to lookup all cards documents which contains a specific keyword you want(eg "benny" in your example"), you may try following code:

db.getCollection(<collection name>).aggregate([
    {
        $lookup: {
            from: "card",
            let: {
                keywordList: "$keywords"
            },
            // this pipeline is used to filter only a list with the keyword
            pipeline : [
                {
                    $project : {
                        keywords : { 
                            $filter: { 
                                input: "$keywords", 
                                as: "keyword", 
                                cond: {
                                    $eq : ["$$keyword.value", "benny"]
                                } 
                            }
                        }
                    }
                }
            ],
            as: "filteredCardLookups"
        }
    },
    {
        $addFields : {
            // this will help to filter out card collection lookup that contains no keyword
            filteredCardLookups : { 
                $filter: { 
                    input: "$filteredCardLookups", 
                    as: "filteredCardLookup", 
                    cond: {
                        $ne : ["$$filteredCardLookup.keywords", []]
                    } 
                }
            }
        }    
    }
]);

your final result may look like this:

{
    ...
    "filteredCardLookups" : [ 
        {
            "_id" : "5fbff18be1157d5f8c6089f2",
            "keywords" : [ 
                {
                    "type" : "topic",
                    "value" : "benny"
                }
            ]
        }
    ]
    ...
}

2 different things to change here. According to the documentation , $in takes the foillowing arguments:

[ <expression>, <array expression> ]

So you have to switch your arguments.

Second thing is that "benny" must not be an array, because each element of 'keywords.value' will be a string.

So the good syntax of your lookup stage is

{
    "$lookup": {
      "from": "cards",
      "let": {
        "keywordList": "$keywords.value"
      },
      "pipeline": [
        {
          "$match": {
            "$expr": {
              $and: [
                {
                  "$in": [
                    "benny",
                    "$keywords.value"
                  ]
                }
              ]
            }
          }
        }
      ],
      "as": "cards"
    }
  }

You can test it here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM