14

想要对存储在 mongodb 集合中的对象提供 i18n 支持

目前我们的架构是这样的:

{
  _id: "id"
  name: "name"
  localization: [{
    lan: "en-US",
    name: "name_in_english"
  }, {
    lan: "zh-TW",
    name: "name_in_traditional_chinese"
  }]
}

但我的想法是字段“lan”是唯一的,我可以使用这个字段作为键,所以结构是

{
  _id: "id"
  name: "name"
  localization: {
    "en-US": "name_in_english",
    "zh-TW": "name_in_traditional_chinese"
  }
}

这将更整洁,更容易解析(只需本地化 [语言] 将获得我想要的特定语言的值)。

但接下来的问题是:这是在 MongoDB 中存储数据的好习惯吗?以及如何通过 json-schema 检查?

4

3 回答 3

7

将值作为键不是一个好习惯。语言代码是值,正如您所说,您无法根据模式验证它们。它使得查询它变得不可能。例如,您无法确定是否“nl-NL”的语言翻译,因为您无法与键进行比较,也无法轻松地对其进行索引。你应该总是有描述性的键。

但是,正如您所说,将语言作为键可以更轻松地提取数据,因为您可以通过['nl-NL'](或任何您的语言的语法)访问它。

我会建议一个替代模式:

{
    your_id: "id_for_name"
    lan: "en-US",
    name: "name_in_english"
}
{
    your_id: "id_for_name"
    lan: "zh-TW",
    name: "name_in_traditional_chinese"
}

现在你可以 :

  • { your_id: 1, lan: 1 }为快速查找设置索引
  • 单独查询每个翻译并获得该翻译:
    db.so.find( { your_id: "id_for_name", lan: 'en-US' } )
  • 使用相同的索引查询每个 id 的所有版本:
    db.so.find( { your_id: "id_for_name" } )
  • 并且更容易更新特定语言的翻译:

    db.so.update(
        { your_id: "id_for_name", lan: 'en-US' }, 
        { $set: { name: "ooga" } } 
    )
    

对于您建议的模式,这些点都不可能。

于 2013-07-26T11:00:34.283 回答
1

显然,第二个模式示例对您的任务要好得多(当然,如果lan字段是唯一的,正如您所提到的,这对我来说似乎也是如此)。

从中获取元素dictionary/associated array/mapping/whatever_it_is_called_in_your_language比扫描整个值数组便宜得多(在当前情况下,从存储大小的角度来看,它也非常有效(请记住,所有字段都存储在 MongoDB 中as-is,因此每条记录都包含 json 字段的完整键名,不是它的表示或索引或其他)。

我的经验表明 MongoDB 已经足够成熟,可以用作应用程序的主存储,即使在高负载下也是如此(不管它是什么意思 ;)),主要问题是如何对抗数据库级别的锁(好吧,我们将等待承诺的表级锁,它会固定 MongoDB 我希望更多),但如果您的 MongoDB 集群构建不当,可能会丢失数据(在 Internet 上挖掘文档和文章以获取更多信息)。

至于模式检查,您必须在插入记录之前通过应用程序端的编程语言进行检查,是的,这就是为什么调用 Mongo schemaless

于 2013-07-26T09:50:00.203 回答
0

有一种情况,对象必然比数组好:支持将 upserts 插入到集合中。例如,如果您想将具有name“item1”的项目更新为val100,或者如果不存在则插入这样的项目,全部在一个原子操作中完成。使用数组,您必须执行两个操作之一。给定一个类似的模式

{ _id: 'some-id', itemSet: [ { name: 'an-item', val: 123 } ] }

你会有命令

// Update:
db.coll.update(
  { _id: id, 'itemSet.name': 'item1' },
  { $set: { 'itemSet.$.val': 100 } }
);

// Insert:
db.coll.update(
  { _id: id, 'itemSet.name': { $ne: 'item1' } },
  { $addToSet: { 'itemSet': { name: 'item1', val: 100 } } }
);

您必须先查询以了解提前需要哪个,这可能会加剧竞争条件,除非您实施一些版本控制。有了一个对象,你可以简单地做

db.coll.update({
  { _id: id },
  { $set: { 'itemSet.name': 'item1', 'itemSet.val': 100 } }
});

如果这是您的用例,那么您应该使用对象方法。一个缺点是查询特定名称需要扫描。如果还需要,您可以添加一个专门用于索引的单独数组。这是与 MongoDB 的权衡。Upserts 会变成

db.coll.update({
  { _id: id },
  { 
    $set: { 'itemSet.name': 'item1', 'itemSet.val': 100 },
    $addToSet: { itemNames: 'item1' } 
  }
});

然后查询将只是

db.coll.find({ itemNames: 'item1' })

(注意:$位置运算符不支持数组 upserts。)

于 2014-01-25T05:09:55.747 回答