CouchDB/PouchDB中的任意文档排序

5
我正在使用CouchDB/PouchDB构建一个幻灯片应用程序:每个“幻灯片”都是自己的Couch文档,可以重新排序或删除幻灯片,并且可以在现有幻灯片之间或幻灯片的开头或结尾添加新幻灯片。一个幻灯片集可以从一个到约10,000张幻灯片,因此我对空间和时间效率非常敏感。
我首先制作了幻灯片创建/编辑功能,完全低估了跟踪幻灯片排序的难度。这很难做到,因为每个幻灯片文档的顺序完全独立于幻灯片文档本身,即它不是我可以按时间或文档中包含的某个数字进行排序的东西。我在StackOverflow上看到了许多关于如何在关系数据库中跟踪排序的问题:

但所有这些都涉及以下内容:

  1. 使用浮点型的次要键进行重新排序/创建/删除,定期规范化索引(例如,假设两个文档的排序索引为1.0和2.0,然后插入一个介于它们之间的第三个文档并获得关键字1.5,然后第四个文档获得1.25,直到插入约31个文档并出现浮点精度问题);
  2. 使用链接列表方法,其中幻灯片文档具有包含其两侧文档的主键的“previous”和“next”字段;
  3. 每次文档重新排序/插入/删除都更新所有文档的非常简单的方法。

这些方法都不适用于CouchDB:#1在SQL或CouchDB中会产生大量的附加复杂性。#2由于缺乏原子事务而不可靠(CouchDB可能会更新先前文档的新“下一个”,但另一个客户端可能已经同时更新了新的下一个文档,因此更新新的下一个文档将失败并导致链接列表处于不一致状态)。出于同样的原因,#3是完全不可行的。


我正在评估的一种面向CouchDB的方法是创建一个仅包含幻灯片排序的文档:它可能包含一个主键到排序号的哈希对象以及一个将排序号转换为主键的数组,并且在重新排序/插入/删除幻灯片时只需更新此对象。缺点是Couch会为每个排序更改(重新排序/插入/删除)保留此潜在大的文档的副本,CouchDB不支持紧缩单个文档,而且我不想对整个数据库运行压缩,因为我喜欢保留每个幻灯片文档的历史记录。另一个缺点是,在成千上万张幻灯片之后,每次更改排序都需要从PouchDB/客户端传输整个对象(数百千字节)到Couch。
对此方法进行微调的方法是创建第二个数据库来保存此排序文档,并在其上启用自动压缩。维护两个数据库连接会更费力,而且最终我将不得不通过网络传输大量数据,但我将拥有一种在CouchDB中对文档进行排序的稳健方法。
所以我的问题是:CouchDB的人通常如何存储文档的顺序?更有经验的CouchDB人员是否能看出我上面概述的方法中存在任何缺陷?

2
可能感兴趣的内容:https://dev59.com/YFkT5IYBdhLWcg3wNs3- - Lyn Headley
1
@LynHeadley 谢谢你,我正在开发一个超级版本的m69的答案,我认为这将与CouchDB对查询上一个/下一个主ID的良好支持非常契合! - Ahmed Fasih
太棒了!我也一直在思考这个问题,但在网上找不到任何好的答案。也许我们正在找到一些东西... - Lyn Headley
@LynHeadley 我认为使用一个函数,它接受两个字符串并返回一个在它们之间按字典顺序排序的字符串(理想情况下靠近它们的“中点”)就可以解决问题了。而m69提供了这样的代码,我只是稍微改进了一下(使用base-62可以为大量文档提供非常短的键)。这将很容易地进行插入操作。移动文档会稍微不太优雅:将文档复制到新的主键(在新邻居之间按字典顺序),然后删除旧的主键。没有要跟踪顺序的大型文档,很好地利用了CouchDB的特性...或者我漏掉了什么? - Ahmed Fasih
@LynHeadley,花了一点时间,但我把那个库泛化了@m69的答案,并且它很好用!请看我的回答https://dev59.com/Sprga4cB1Zd3GeqPiSXV#44448718 - Ahmed Fasih
我不确定这里描述的使用分数的高级技术是否可以在CouchDB中使用,但无论如何都是一篇相关的阅读文章:https://begriffs.com/posts/2018-03-20-user-defined-order.html - Ahmed Fasih
2个回答

5

感谢@LynHeadley的提示,我编写了一个可以细分字符串之间词典顺序间隔的库:Mudder.js。这使得我能够无限制地在CouchDB中插入和移动文档,通过随意创建新的键而没有存储排序的二级文档的任何开销。我认为这是解决这个问题的正确方法!


3
根据我所了解的,我会选择“订购文档”方法。(即:幻灯片文档,每个幻灯片文档有一个id数组)这很简单明了,可以完成使用情况,所以我不会让这些问题妨碍干净/直观的代码。
你是对的,这个文档可能会变得非常大,尤其是特定文档的写入重负性。这就是为什么压缩存在并且是解决方案的原因,所以你不应该在这一点上与CouchDB对抗。
普遍误解是,您可以使用CouchDB的修订历史记录来保持数据库的全面历史记录。修订只是为了帮助写并发性,而不是作为完整的版本控制系统。
CouchDB默认启用自动压缩,如果没有它,您的数据库将无限制地增长。因此,您应该放弃使用这种方法跟踪文档历史记录,并采用另一种更安全的替代方法。(这些替代方法的列表超出了本答案的范围)

当你说“CouchDB默认启用自动压缩”时,你指的是_revs_limit选项,它的默认值为1000,也就是说,CouchDB不会保留超过1000个修订版本。虽然1000还是很多,但自动压缩(在每次写入后立即丢弃非叶节点)仍然很重要,因此需要第二个数据库吗? - Ahmed Fasih
请问,您能否评论或提供有关在CouchDB之上(或之外)使用“适当”的版本控制的指针?如果Couch的修订系统无法提供它,我计划将其用作非常温和的“撤消”系统,在灾难发生时,我至少可以阅读旧版本的文档,但是您对此类问题的评论使我认为我不应该期望能够做到这一点。 - Ahmed Fasih
1
我建议阅读他们关于压缩的文档,特别是这个关于自动压缩的部分。 - Dominic Barnes
至于另一个问题,我将简单地提供另一个问题的链接,在那里我已经回答了这个问题:https://dev59.com/Tofca4cB1Zd3GeqPk5hL#28357763 - Dominic Barnes
谢谢。https://wiki.apache.org/couchdb/How_to_design_for_replication 对于理解处理语义更新的策略也很有帮助。 - Ahmed Fasih

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接