在CouchDB上的文档之间的关系建模?

问题描述 投票:11回答:1

我正在尝试在CouchDB中建立一个相当简单的关系的模型,但是我无法确定实现此目标的最佳方法。我希望用户能够创建视频游戏对象列表。我已经用"type":"game"将视频游戏文档存储在DB中。我希望能够(通过视图)查询列表对象的ID,并获取列表的元数据(标题,创建日期等)和游戏文档的某些部分(如标题和发行日期)。此外,我希望能够将游戏添加到列表中/从列表中删除游戏,而无需下载整个列表文档并将其发布回去(因此,我不能简单地将游戏信息存储在列表文档中)最终希望支持对同一列表做出贡献的多个用户,并且我不想引入冲突。

阅读了EntityRelationships上的CouchDB Wiki之后,我确定设置关系文档可能是最好的解决方案。

游戏:

{
    "_id": "2600emu",
    "type": "game"
}

列表:

{
    "_id": 123,
    "title": "Emulators",
    "user_id": "dstaley",
    "type": "list"
}

游戏列表关系:

{
    "_id": "98765456789876543",
    "type": "relationship",
    "list_id": 123,
    "game_id": "2600emu"
}

但是,据我了解,这不允许我在一个请求中获取列表的元数据和游戏的元数据。有什么建议吗?

couchdb cloudant
1个回答
12
投票

很好的问题。您指出了一些非常重要的原因,即使用“规范化”数据模型(带有链接的不同文档类型)是最佳模型的原因:

  1. 您在用户<==>列表<==>游戏之间有多对多关系。
  2. 一对多关系很容易在使用容器作为“许多”部分的单个文档中表示,但它们会变得很大,并且您可能会发生并发冲突。
  3. 扩展单文档模型以存储多对多关系是站不住脚的。
  4. 通常,文档不变性非常适合于并发系统。在CouchDB中,您可以按照您所说的完全做到这一点,方法是存储代表图形边缘的“一次写入”文档,然后使用二级索引来重建所需链接的各个部分,并在一个单一的索引中获取所需的信息。 API查询调用。

您也很对,这里的解决方案是'map-side-join'(从hadoop社区借来的)。基本上,您想在地图输出中使用不同的行来表示不同的信息。然后,您可以使用范围查询(开始键/结束键)仅查询所需地图结果的一部分,并查询“联接”表的实例化视图。但是,您在文档中找不到的难题之一是:

3.2.3。 Joins With Views

3.2.3.1。链接文件

[如果您的地图函数发出一个具有{'_id': XXX}的对象值,并且您使用include_docs=true参数查询视图,则CouchDB将获取ID为XXX的文档,而不是经过处理以发出键/值对的文档。

全部说明。这样,您便可以通过外键解除对指向已存储链接文档的指针的引用。然后,您可以使用复合键(是JS数组的键)和view collation rules来结合使用。

以便您的视图行的排序方式如下:

["list_1"], null
["list_1", "game"], {"_id":"game_1234"}
["list_1", "game"], {"_id":"game_5678"}
["list_2"], null
["list_2","game"], {"_id":"game1234"}
["list_3"], null
...

将其与您现有的数据模型放在一起,下面是一些(未经测试的)伪代码应该可以解决的问题:

function(doc) {
    if (doc.type=="list") {
        //this is the one in the one-to-many
        emit( [doc._id]),);
    }
    else if (doc.type=="relationship") {
        //this is the many in the one-to-many
        //doc.list_id is our foreign key to the list.  We use that as the key
        //doc.game_id is the foreign key to the game.  We use that as the value
        emit( [doc.list_id,'game'],  {'_id': doc.game_id});
    }
}   

最后,您将使用startkey / endkey进行查询,以便获得所有您感兴趣的以list_id开头的行。它看起来像是:

curl -g 'https://usr:[email protected]/db/_design/design_doc_name/_view/view_name?startkey=["123"]&endkey=["123",{}]&include_docs=true'

-g选项告诉curl不要泛滥,这意味着您不必取消引用方括号等,并且include_docs=true选项将跟随指针指向您在game_id中指定的外键relationship文档。

分析:

  1. 您正在使用本质上不可变的文档来存储状态更改,并且让数据库为您计算聚合状态。这是一个很好的模型,是我们最成功的模式之一。
  2. 非常有效地添加或删除列表。
  3. 高并发下的出色缩放属性
  4. 在Cloudant(和CouchDB v2.0)上,我们还没有二级索引的'读-写'一致性。在优先级列表中它很高,但是在潜在情况下,在失败情况或高负载下,您可能看不到主索引和辅助索引之间的即时一致性。长话短说,仲裁是用于主索引的,但是仲裁不是用于二级索引的可行模型,因此正在开发另一种一致性策略。
© www.soinside.com 2019 - 2024. All rights reserved.