【问题标题】:JSON Schema with dynamic key field in MongoDBMongoDB中具有动态键字段的JSON模式
【发布时间】:2013-07-26 09:23:44
【问题描述】:

希望 i18n 支持存储在 mongodb 集合中的对象

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

{
  _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检查?

【问题讨论】:

    标签: mongodb jsonschema


    【解决方案1】:

    将值作为键不是一个好习惯。语言代码是值,正如您所说,您无法根据模式验证它们。它使得查询它变得不可能。例如,您无法确定您是否拥有“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" } } 
      )
      

    对于您建议的架构,这两点都不可能。

    【讨论】:

    • 废话。 “单独查询翻译”:db.so.find({'localization.lan': 'en-US'})(此处有索引会有所帮助)或db.so.find({'localization.en-US': { $exists: true } })。 “查询每个 id 的所有翻译”:db.so.find({_id:'id'})。如果您只需要翻译,请添加字段说明符。 “更新特定翻译”:db.so.update({_id:'id','localization.lan':'en-US'},{$set:{'localization.$.name':'ooga'}})db.so.update({_id:'id','localization.en-US':{$exists:true}},{$set:{'localization.en-US.name':'ooga'}})。每种方法都有独特的特点;没有一个是“不好的做法”。
    【解决方案2】:

    显然,第二个架构示例更适合您的任务(当然,如果您提到的 lan 字段是唯一的,那对我来说也是如此)。

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

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

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

    【讨论】:

      【解决方案3】:

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

      的架构
      { _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' })
      

      (注意:$ 位置运算符不支持数组更新插入。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-07-21
        • 1970-01-01
        • 2017-05-14
        • 2010-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多