Rohit Chattopadhyay
Rohit Chattopadhyay

Reputation: 67

Nested MongoDB query in javascript

This is a GraphQL resolver. The problem is with promise handling using async/await.

I have tried to implement promise handling, but I am not able to set it the right way, I do not have much experience in promise handling, some study materials will do a great help.

My understanding was that the script will stop where await is called and will continue after the await call is finished. but it is bypassing the await. await call is finished after the value is returned

    allDocs: async (args, context) => context().then(async client => {
        let db = client.db(dbName)
        const id = args.identifier.trim()
        let nameArr = []
        return await db.collection("collection").find({
            $or: [{
                "a.iden": id
            }, {
                "b.iden": id
            }]
        }).toArray().then(async (arr) => {
            let year
            arr.map(async element => {
                let pmcid = element.pmc_id.toUpperCase()      
                try {
                    year = await db.collection("Another_collection").findOne({"pmcid" : query})["year"]
                } catch (e) {
                    year = null
                }
                element["publication_year"] = year
            })
            return await arr
        }).then((arr)=>{
            client.close()
            return {
                "documents":  arr,
                "count": arr.length,
                "searchkey": id
            }
        })
    }),

The expected return value should have "publication_year" as some year, it is giving null now.

Thanks for help

Upvotes: 2

Views: 238

Answers (1)

chridam
chridam

Reputation: 103305

You seem to mixing Promises with async/await and it's a bit hard to follow. Always better to break down your code by changing the Promise bits to use async/await as this will help you narrow down the issue. You can as well wrap the whole block in a try/catch which makes it relatively easy to handle both synchronous and asynchronous errors.

So to begin with, you can change the context function call which returns a promise to use async await as

allDocs: async (args, context) => {
    try {
        const client = await context()

        ....
    } catch(err) {

    }
}

Then do the same with the toArray() function call which returns a promise that can be resolved with async/await:

allDocs: async (args, context) => {
    try {
        const client = await context()
        const db = client.db(dbName)
        const id = args.identifier.trim()
        const results = await db.collection('collection').find({
            '$or': [
                { 'a.iden': id }, 
                { 'b.iden': id }
            ]
        }).toArray()

        const arr = results.map(async doc => {
            const pmcid = doc.pmc_id.toUpperCase()
            const { year } = await db.collection('Another_collection')
                                     .findOne({'pmcid' : pmcid })

            return {
                ...doc,
                publication_year: year
            }
        })

        client.close()

        return {
            'documents':  arr,
            'count': arr.length,
            'searchkey': id
        }
    } catch(err) {
        // handle error
    }
}

The call to the other collection to get the publication_year can be done using a $lookup pipeline in a single call rather than in a map loop. Consider the following pipeline

allDocs: async (args, context) => {
    try {
        const client = await context()
        const db = client.db(dbName)
        const id = args.identifier.trim()
        const pipeline = [
            { '$match': {
                '$or': [
                    { 'a.iden': id }, 
                    { 'b.iden': id }
                ]
            } },
            { '$lookup': {
                'from': 'Another_collection',
                'let': { 'pmcId': '$pmc_id' },
                'pipeline': [
                    { '$match': { 
                        '$expr': { 
                            '$eq': [
                                '$pmcid', 
                                { '$toUpper': '$$pmcId' }
                            ] 
                        }
                    } }
                ],
                'as': 'pmc'
            } },
            { '$addFields': {
                'publication_year': { 
                    '$arrayElemAt': [ '$pmc.year', 0 ]
                }
            } },
            { '$project': { 'pmc': 0 } }
        ]
        const arr = await db.collection('collection').aggregate(pipeline).toArray()

        client.close()

        return {
            'documents':  arr,
            'count': arr.length,
            'searchkey': id
        }
    } catch(err) {
        // handle error
    }
}

Upvotes: 1

Related Questions