Search code examples
node.jsmongodbmongodb-querynodesmongojs

MongoDB Multilingual - Switch Case


I've recently started with MongoDB on Node.js and I need help from fellow folks on the below situation.

I've already referred Multilingual data modeling on MongoDB which is also similar Schema structure I am following.

I'm creating the list of countries in multi language format and schema as given below.

var CountrySchema = new Schema({
  name: {
    type: Object,
    required: 'Kindly enter the name of the country'
  },
  Short_code: {
    type: String,
    required: 'Please enter the country code'
  },
  Created_date: {
    type: Date,
    default: Date.now
  },
  status: {
    type: [
      {
        type: String,
        enum: ['published', 'unpublished', 'deleted']
      }
    ],
    default: ['published']
  }
})

I would like to fetch data in such a way that, when the translation is missing, it should take the default one i.e. en

The equallent SQL is given below.

SELECT Short_code, 
CASE
  WHEN name.es IS NOT NULL THEN name.es
  WHEN name.es IS NULL and name.en IS NOT NULL name.en
END AS name
FROM countries;

Also, it will be greatful if I could pass the es value dynamically to the query.


Solution

  • So let us start off with some countries in our database collection

    db.countries.insertMany([
       {
           "name" : {
               "en": "Germany",
               "es": "Alemania",
    
           },
           "Short_code" : "DEU"
       },
       {
           "name" : {
               "en": "Spain",
               "es": "España",
    
           },
           "Short_code" : "ESP"
       },
       {
           "name" : {
               "en": "England"
    
           },
           "Short_code" : "GB"
       }
    ]);
    

    Notice that DEU and ESP have Spanish translation, however GB does not.

    Now we can write a simple aggregation query taking advantage of $ifNull operator (https://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/) to take es value if it exists or en if not.

    for example querying ESP will return the es name:

    > db.countries.aggregate([
    ... { $match: { "Short_code" : "ESP" } },
    ... { $project: { "name" : { $ifNull: ["$name.es", "$name.en"] }}}
    ... ]);
    { "_id" : ObjectId("5b923f4b1f269cd18cd2f209"), "name" : "España" }
    

    however querying GB will return the en name:

    > db.countries.aggregate([
    ... { $match: { "Short_code" : "GB" } },
    ... { $project: { "name" : { $ifNull: ["$name.es", "$name.en"] }}}
    ... ]);
    { "_id" : ObjectId("5b923f4b1f269cd18cd2f20a"), "name" : "England" }
    

    To make the es field dynamic you'd just build up the "$name.es" expression of what you want to select within your application, for example:

    var userLanguage = "es";
    
    { $project: { "name" : { $ifNull: ["$name." + userLanguage, "$name.en"] }}}
    

    To get only "Short_code" & "Country Name", code is

    {
      $project: {
        "_id": 0,
        "Short_code": 1,
        "name" : { $ifNull: ["$name." + userLanguage, "$name.en"] }
      }
    }