查询 MongoDB 中的本地化数据

Querying localized data in MongoDB

有这样的数据'localized':

{
  "cs": [
    {
      "cID": "00001",
      "title": {
        "en": "title en1",
        "de": "title de1"
      },
      "desc": {
        "en": "desc en1",
        "de": "desc de1"
      },
      "startDate": 20210801,
      "endDate": 20210809,
      "numDays": 8
    },
    {
      "cID": "00002",
      "title": {
        "en": "title en2",
        "de": "title de2"
      },
      "desc": {
        "en": "desc en2",
        "de": "desc de2"
      },
      "startDate": 20210701,
      "endDate": 20210715,
      "numDays": 14
    }
  ]
}

考虑到用户区域设置(作为参数传递给查询)的最佳查询方式是什么?例如,如果传递了“en”,查询应该 return this for "cID": "00001" :

    {
      "cID": "00001",
      "title": "title en1",
      "desc": "desc en1",
      "startDate": 20210801,
      "endDate": 20210809,
      "numDays": 8
    }

理想情况下,查询应该是 'generic',而不是专门过滤 'title' 和 'desc' 对象。

我知道:

db.cs.find({
  "cID": "00001"
},
{
  "_id": 0,
  "cID": 1,
  "title": "$title.en",
  "desc": "$desc.en",
  "startDate": 1,
  "endDate": 1,
  "numDays": 1
})

会给我:

[
  {
    "cID": "00001",
    "desc": "desc en1",
    "endDate": 2.0210809e+07,
    "numDays": 8,
    "startDate": 2.0210801e+07,
    "title": "title en1"
  }
]

但是在不同的查询中使用许多语言环境和不同的数据模型来处理它会很棘手。

Mongo 操场:https://mongoplayground.net/p/9erh-VYiOO4

要创建通用查询,您需要聚合框架,因为它有一些方便的运算符可以帮助您完成此操作。首先,您需要将嵌入的文档转换为 key/value 对的数组,然后在传递区域设置作为参数的关键字段上过滤数组。

例如,转换文档

  "title": {
    "en": "title en2",
    "de": "title de2"
  },

到数组

  "title": [
    { "k": "en", '"v": "title en2" },
    { "k": "de", "v": "title de2" }
  ],

使用 $objectToArray operator. You can then filter this array on the key field using the $filter 运算符作为

{
    '$filter': {
        'input': { '$objectToArray': '$title' },
        'cond': { '$eq': ['$$this.k', locale] }
    }
}

其中变量语言环境派生自传递的参数。

获得过滤后的数组后,获取值字段需要在值键上应用 $arrayElemAt 运算符,如

{ 
    '$arrayElemAt': ['$title.v', 0]
}

所以最后你将不得不运行像这样的管道:

var locale = 'en';

db.cs.aggregate([
    { '$match': { "cID" : "00001" } },
    { '$addFields': {
        'title': {
            '$filter': {
                'input': { '$objectToArray': '$title' },
                'cond': { '$eq': ['$$this.k', locale] }
            }
        },
        'desc': {
            '$filter': {
                'input': { '$objectToArray': '$desc' },
                'cond': { '$eq': ['$$this.k', locale] }
            }
        }
    } },

    { '$addFields': {
        'title': { 
            '$arrayElemAt': ['$title.v', 0]
        },
        'desc': { 
            '$arrayElemAt': ['$desc.v', 0]
        }
    } }
]);

并进行一些重构:

var locale = 'en';
var getFilterOperatorExpression = function (field) {
    return {
        '$filter': {
            'input': { '$objectToArray': '$'+ field },
            'cond': { '$eq': ['$$this.k', locale] }
        }
    }
};
var getValueOperatorExpression = function (field) { 
    return { 
        '$arrayElemAt': ['$'+ field +'.v', 0]
    }
};

db.cs.aggregate([
    { '$match': { "cID" : "00001" } },
    { '$addFields': {
        'title': getFilterOperatorExpression('title'),
        'desc': getFilterOperatorExpression('desc'),
    } },

    { '$addFields': {
        'title': getValueOperatorExpression('title'),
        'desc': getValueOperatorExpression('desc')
    } }
]);

你可以试试,

  • $reduce 迭代 title 数组的循环,$cond 匹配 local 和 return 将结果匹配到值,描述数组的过程相同
 {
    $addFields: {
      title: {
        $reduce: {
          input: "$title",
          initialValue: "",
          in: {
            $cond: [{ $eq: ["$$this.locale", "pl"] }, "$$this.value", "$$value"]
          }
        }
      },
      description: {
        $reduce: {
          input: "$description",
          initialValue: "",
          in: {
            $cond: [{ $eq: ["$$this.locale", "pl"] }, "$$this.value", "$$value"]
          }
        }
      }
    }
  }

对于通用选项,您可以创建一个具有两个参数的函数,第一个是带有 $ 符号的输入字段,第二个是区域设置:

function languageFilter(inputField, locale) {
  return {
    $reduce: {
      input: inputField,
      initialValue: "",
      in: {
        $cond: [{ $eq: ["$$this.locale", locale] }, "$$this.value", "$$value"]
      }
    }
  };
}

您的最终查询将是:

let locale = "pl";
db.cs.aggregate([
  { $match: { cID: "00001" } },
  {
    $addFields: {
      title: languageFilter("$title", locale)
      description: languageFilter("$description", locale)
    }
  }
]);