【问题标题】:Querying localized data in MongoDB在 MongoDB 中查询本地化数据
【发布时间】:2020-11-21 22:44:39
【问题描述】:

像这样“本地化”数据:

{
  "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”,则查询应该为“cID”返回这个:“00001”:

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

理想情况下,查询应该是“通用的”,而不是专门过滤“标题”和“描述”对象。

我知道:

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

【问题讨论】:

  • 你在使用mongoose os 类似的东西吗?您使用哪种语言?还是只是一个mongo 查询?
  • 这是一个 node.js / express 应用程序。我没有使用猫鼬。我正在使用 MongoDB Node.JS 驱动程序。

标签: node.js mongodb localization aggregation-framework


【解决方案1】:

要创建通用查询,您需要聚合框架,因为它有一些方便的运算符来帮助您。首先,您需要将嵌入的文档转换为键/值对数组,然后在作为参数传入语言环境的键字段上过滤该数组。

例如转换文档

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

到一个数组

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

使用$objectToArray 运算符。然后,您可以使用 $filter 运算符作为关键字字段过滤此数组

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

变量语言环境是从传递的参数派生的。

获得过滤后的数组后,获取 value 字段需要将 $arrayElemAt 运算符应用于 value 键

{ 
    '$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')
    } }
]);

【讨论】:

  • 如果我们将 'title' 指定为数组 [],则不需要 $objectToArray。除此之外,它有效。谢谢。
  • @Przemek 在您的原始问题中没有提到将title 指定为数组,因此使用$objectToArray 的这种解决方案直接解决了title 是嵌入式文档的问题。更改我的答案不是一个好主意,因为它会误导那些过来的人,看到提供的答案与您的要求不符。
  • 您能看看mongoplayground.net/p/hvbMNKAuPw4 并帮我弄清楚如何在roomDetails 对象中做同样的事情吗?我迷失在这些 mongo 管道中
  • 在您的解决方案中,您建议将“title”更改为数组。我这样做了。当我使用 objectToArray 函数执行此操作时,它会返回一个错误,因为它已经获取了数组。删除此功能使解决方案工作。再次感谢。
  • 您能否为roomDetails 查询创建一个新问题?这样,您不仅可以从我那里获得足够的帮助,还可以从更广泛的 SO 社区获得足够的帮助。
【解决方案2】:

你可以试试,

  • $reduce循环title数组,$cond匹配local并将匹配结果返回值,描述数组的相同过程
 {
    $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)
    }
  }
]);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-06
    • 2023-03-11
    • 2021-07-12
    • 2011-11-15
    • 1970-01-01
    相关资源
    最近更新 更多