2

I'm joining two collections, but the result of trends is an array, each item always has only one trend, how do I remove the trend inside array?

'Items' collection:

{
 "itemid" : "370",
 "name" : "A"
},
{
"itemid" : "378",
 "name" : "B"
}

'Trends' collection

{
 "itemid" : "370",
 "max" : "715705",
},
{
 "itemid" : "378",
 "max" : "35346",
}

Command executed:

db.items.aggregate([
{
    $lookup: {
            from: "trends",
            localField: "itemid",
            foreignField: "itemid",
            as: "trend"
            }
    }
])

Result:

{
 "itemid" : "370",
 "name" : "A",
 "trend" : [       // unexpected array, the result is always a single 'trend'
        {
          "itemid" : "370",
          "max" : "715705",
        }
    ]
},
...

Expected:

{
 "itemid" : "370",
 "name" : "A",
 "trend" : {      // yeah, without array          
             "itemid" : "370",
             "max" : "715705",                    
            }
},
...

2 Answers 2

3

You can do it with $unwind pipeline stage

db.items.aggregate([
{
    $lookup: {
            from: "trends",
            localField: "itemid",
            foreignField: "itemid",
            as: "trend"
            }
    },
    $unwind:"$trend"
])
Sign up to request clarification or add additional context in comments.

Comments

2

It's not an unexpected result, as $lookup would result in retrieving all matched documents into an array i.e; trend will be an array in your case. It's for a reason, Assume if you're joining collection A with collection B & documents in collection A might multiple matching documents in collection B then all those documents from collection B are pulled into an array of that relative document of collection A in your case trend field. Here if you wanted to get object inside it you need to do one of these ::

db.items.aggregate([
{
    $lookup: {
            from: "trends",
            localField: "itemid",
            foreignField: "itemid",
            as: "trend"
            }
    },
    { $addFields: { trend: {$arrayElemAt : ["$trend", 0] } } } // This stage is going re-write trend field with first object from trend array created by $lookup stage.
])

(Or)

db.items.aggregate([
{
    $lookup: {
            from: "trends",
            localField: "itemid",
            foreignField: "itemid",
            as: "trend"
            }
    },
     { $unwind: { path: "$trend", preserveNullAndEmptyArrays: true } } 
])

Note : When you use $unwind you need to be cautious that you need to use preserveNullAndEmptyArrays option as if trend is an [] - which means no match found in collection B, then $unwind is going to remove all collection A documents with trend :[] in the final aggregation result.

1 Comment

@ViniciusRaupp : Yes it might work, but I'm giving you all the possible scenarios.If you might need all docs from collection A though there is no match in collection B i.e; trend : [], So here $unwind will remove this kind of docs. That's why we need preserveNullAndEmptyArrays: true. As $unwind can be tricky cause if your aggregation returns 100s or 1000s of documents then you might not be able to validate whether query really did work to return all docs or not. So it's better to use it rather than loose few docs :-)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.