skip to Main Content

I have a collection of a few thousand characters. Each character has eight localized fields and nine different supported languages. Below is an example of the schema I am using populated with sample data (just with a smaller document).

db.characters.insertMany([
    {
        "key": 123,
        "attack": 1337,
        "numberField3": 30
        "translations": {
            "en": {
                "name": "Englishh name"
                "description": "English description"
            },
            "es": {
                "name": "Spanish name"
                "description": "Spanish description"
            }
        }
    },...
])

How can I make a query that returns a document containing the non-localized fields and the localized fields from the specified language?
pseudo-query: db.characteres.find({"key":123}).addAll("translations.en") would return:

{
    "key": 123,
    "attack": 1337,
    "numberField3": 30
    "name": "Englishh name"
    "description": "English description"
}

Thank you for looking at this. First time mongodb user and data-modeling noob here
UPDATE
I just found an example (thanks ChatGPT) that would do what I need using $project. Is there a more performant way of doing this? or possibly shorter solution (each document has 8 localized fields I will have to add into $project); can I project the fields I want then add_all fields from translation.en to the root level? that would be a more generalized way to achieve what I am trying to do with localization in a query

db.characters.aggregate([
    {
        $match: {
            "key": 123
        }
    },
    {
        $project: {
            "_id": 0,
            "key": 1,
            "attack": 1,
            "numberField3": 1,
            "name": "$translations.en.name",
            "description": "$translations.en.description"
        }
    }
])

UPDATE #2
Upon further prodding ChatGPT gave me this. Can anyone confirm this is a proper pipeline or know of a better way to do this? I can not currently connect to the db due to other reasons

db.characters.aggregate([
    {
        $match: {
            "key": 123
        }
    },
    {
        $replaceRoot: {
            newRoot: {
                $mergeObjects: ["$$ROOT", "$translations.en"]
            }
        }
    },
    {
        $project: {
            "translations": 0
        }
    }
])

2

Answers


  1. Chosen as BEST ANSWER
    1. $replaceRoot { newRoot { mergeObjects }} to merge translations.en into the root level
    2. $unset to remove translations
    db.characters.aggregate([
        {
            $match: {
                "key": 123
            }
        },
        {
            $replaceRoot: {
                newRoot: {
                    $mergeObjects: ["$$ROOT", "$translations.en"]
                }
            }
        },
        {
            $unset: ["translations"]
        }
    ])
    

  2. The $project in your pipeline won’t produce the result you expect: it will just return the _id of the document, that’s it. You have to use $unset for that.

    A potentially slightly faster aggregation can be achieved using $addFields. This would merge your last two pipeline steps (not verified the syntax, but should give a hint):

    b.characters.aggregate([
        {
            $match: {
                "key": 123
            }
        },
        {
            $addFields: {
                translations: "$translations.en"
            }
        }
    ])
    

    If you want a different name:

    b.characters.aggregate([
        {
            $match: {
                "key": 123
            }
        },
        {
            $addFields: {
                translation: "$translations.en"
            }
        },
        {
            $unset: "translations"
        }
    ])
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search