1

hotdeal:

{ "property" : "ATL-D406" } 

translation:

{ "property" : "ATL-D406", "language" : "gb", "txt": [{"aaa":"hi", "bbb":"bye"}] }
{ "property" : "ATL-D406", "language" : "ru", "txt": [{"aaa":"priviet", "bbb":"baka"}] } 
{ "property" : "ATL-D406", "language" : "cn", "txt": [{"aaa":"??", "bbb":"??"}] } 

Current result:

{ "property":"ATL-D406", "language":[ "gb", "ru", "cn" ] }

What I would like:

{ "property":"ATL-D406", "language":"gb", "aaa":"hi", "bbb":"bye" }

I cannot understand why my $elemMatch is not working, shouldn't it isolate that particular element in the result from "translation"?

Also once I got it to only take the "gb" language, how do I project "aaa" and "bbb" without them being inside an array? I tried $resolve but then I get no data at all. Can you $resolve an array in an array? ($resolve "txt")?

db.hotdeal.aggregate([ 
    { '$match': {} },
    { '$lookup': { 
        from: 'translation', 
        localField: 'property', 
        foreignField: 'property', 
        as: 'translations' 
    } },

    { '$match': 
        { 'translations': 
            { '$elemMatch': { 'language': 'gb' } }
        } 
    }, 

    { '$project': { 
        _id: 0, 
        property: 1, 
        language: '$translations.language'
    }}
])

2 Answers 2

1

You are doing the wrong operation. You want $filter, $map, and also $arrayElemAt by the look of it. The $elemMatch operator is a "query" operation for "selection of data", and is not used to "filter" array content.

db.hotdeal.aggregate([ 
    { '$match': {} },
    { '$lookup': { 
        'from': 'translation', 
        'localField': 'property', 
        'foreignField': 'property', 
        'as': 'translations' 
    } },
    { '$project': { 
        '_id': 0, 
        'property': 1, 
        'translation': {
         '$arrrayElemAt': [
           { '$map': {
             'input': {
               '$filter': {
                 'input': '$translations'
                 'as': 't',
                 'cond': { '$eq': [ '$$t.language', 'gb' ] }
               },
             },
             'as': 't',
             'in': {
               'language': '$$t',
               'aaa': { '$arrayElemAt': [ '$$t.txt.aaa', 0 ] },
               'bbb': { '$arrayElemAt': [ '$$t.txt.bbb', 0 ] }
             }
           }},
           0
         ]
       }
    }},
    { '$project': {
      'property': 1,
      'language': '$translation.language',
      'aaa': '$translation.aaa',
      'bbb': '$translation.bbb'
    }}
])

I generally find that these sort of "trival" structural changes are best handled in "client code" rather than mangling through the aggregation framework anyway.

So simply do the "join" and then let the "client" process the alteration:

db.hotdeal.aggregate([ 
    { '$match': {} },
    { '$lookup': { 
        'from': 'translation', 
        'localField': 'property', 
        'foreignField': 'property', 
        'as': 'translations' 
    } }
]).forEach( doc => {
  let translation = doc.translations.find( t => t.language === 'gb' );
  doc.language = translation.language;
  doc.aaa = translation.txt[0].aaa;
  doc.bbb = translation.txt[0].bbb;

  delete doc.translations;
  delete doc._id;
  printjson(doc);
});

So that is doing the same thing, yet is far less obtuse.

Sign up to request clarification or add additional context in comments.

9 Comments

Hi Neil. Interesting but a quite complex way to do it. Are you sure that you cannot use $elemMatch inside a $match operation on the data you got from the $lookup? I believe I have done so before.
@torbenrudgaard Read it all. $elemMatch would only "confirm" that the document has the matching property in an element of the array which resulted from $lookup. It does not remove it. That is what you are asking. What I also conclude when you read everything is that the "complex" operation is completely unnecessary. Falls under the category of "Just because you can do it, does not mean you should". The swiss army chainsaw and all that. Don't overuse aggregation functions when you are not "aggregating".
Neil, I just managed to do it like this.... argh! I cannot format code here - let me post it as an answer so you can see it.
Neil have a look at my answer - it actually gave the desired result.
@torbenrudgaard $unwind is not an efficient way of processing this. It does work, but there is a large cost with large collections and arrays
|
1
db.hotdeal.aggregate([ 
    { '$match': {} },
    { '$lookup': { 
        from: 'translation', 
        localField: 'property', 
        foreignField: 'property', 
        as: 'translations' 
    } },

    { '$unwind': '$translations' },

    { '$match': { 'translations.language': 'gb' } }, 

    { '$project': { 
        _id: 0, 
        property: 1, 
        language: '$translations.language',
        aaa: '$translations.txt.aaa',
        bbb: '$translations.txt.bbb'
    }}
])

Comments

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.