mongodbmongodb-querynosqlnode-mongodb-native

Dynamic from in $lookup


I am trying to see if i can change the from in the $lookup or rearrange my query to somehow retrieve from three potential collections. So far i have managed to set up the query like so:

const search = db.collection("search");

search.aggregate([
  {
    '$match': {
      'id_int': 0
    }
  }, {
    '$project': {
      '_id': 0, 
      'collection': 1, 
      'id_int': 1
    }
  }, {
    '$lookup': {
      'from': 'arxiv', 
      'localField': 'id_int', 
      'foreignField': 'id_int', 
      'as': 'arxiv'
    }
  }
], function(err, cursor) ... )

The $match and then $project pipeline stages return a result with the following properties:

collection:"arxiv"
id_int:0 

The collection value will always be one of three arxiv, crossref or pmc_test. Therefore i'd like my $lookup from to use this property value programmatically as opposed having it hard coded.

'$lookup': {
   'from': 'arxiv' or 'crossref' or 'pmc_test', // Dynamic based on result
   ...
}

Thanks

Edit

id_int will get passed in and collection will not, thats why a query is made to the search collection.


Solution

  • Sadly this is not possible currently, there is an open feature request on it here so you can keep track of it if you wish.

    Right now thought you have two options.

    1. Split your call into 2 queries and add that bit of logic to your code, which is what i personally recommend.

    2. Use this aggregate which looks up all 3 collections:

    search.aggregate([
        {
            '$match': {
                'id_int': 0
            }
        },
        {
            '$project': {
                '_id': 0,
                'collection': 1,
                'id_int': 1
            }
        },
        {
            "$facet": {
                "arxiv": [
                    {
                        "$lookup": {
                            "from": "arxiv",
                            "localField": "id_int",
                            "foreignField": "id_int",
                            "as": "arxiv"
                        }
                    }
                ],
                "crossref": [
                    {
                        "$lookup": {
                            "from": "crossref",
                            "localField": "id_int",
                            "foreignField": "id_int",
                            "as": "crossref"
                        }
                    }
                ],
                "pmc_test": [
                    {
                        "$lookup": {
                            "from": "pmc_test",
                            "localField": "id_int",
                            "foreignField": "id_int",
                            "as": "pmc_test"
                        }
                    }
                ]
            }
        },
        {
            "$addFields": {
                "newRoot": [
                    {
                        "k": "$collection",
                        "v": {
                            "$cond": [
                                {
                                    "$eq": [
                                        "$collection",
                                        "arxiv"
                                    ]
                                },
                                "$arxiv",
                                {
                                    "$cond": [
                                        {
                                            "$eq": [
                                                "$collection",
                                                "crossref"
                                            ]
                                        },
                                        "$crossref",
                                        "$pmc_test"
                                    ]
                                }
                            ]
                        }
                    },
                    {
                        "k": "collection", "v": "$collection"
                    },
                    {
                        "k": "id_int", "v": "$id_int"
                    }
                ]
            }
        },
        {
            "$replaceRoot": {
                "newRoot": {
                    "$arrayToObject": {
                        "$concatArrays": "$newRoot"
                    }
                }
            }
        }
    ])
    

    As you might have noticed the pipeline isn't exactly sexy, if you don't care about the field name in the end result you can dump most of it.