【问题标题】:Efficient way to change the sort index of an element更改元素排序索引的有效方法
【发布时间】:2012-09-25 20:41:02
【问题描述】:

我的非 sql 数据库中有一个非常大的元素列表。

每个元素都有一个从 1 到 N 的排序顺序。这种排序顺序指定了结果在表单上的显示方式。

当在 UI 中触发顺序更改时(将元素 i 放在位置 j)我需要更新它们之间的所有实体。如果元素 1 成为最新的,我需要进行 N 次更新。

有没有一种有效的方法可以降低此操作的成本?有没有一种智能的方法来索引排序值?

一些注意事项:

  • 我正在重新设计我的应用程序,以便能够使用更智能的解决方案重新索引实体。
  • 写入(更新)的成本是提取(读取 1 个实体)的 +-4 倍。
  • 列表很大,无法放入内存。

【问题讨论】:

  • 就 javascript 而言,您对 DOM 所做的更改越少越好。最好在使用 appendChild 之前确定表单元素的顺序。
  • 首先,元素是否必须是实际的随机访问索引,或者只是单调递增的顺序。 (例如,如果您可以在 2.333 和 2.666 位置添加两个元素并让它们显示在 2 和 3 之间,这是否足够好,或者您是否需要知道位置 3 的元素实际上是第 4 个元素而不是6号?)
  • 其次,我假设查找必须比插入/移动更快,但如果这不是真的,那就会打开更广泛的答案,所以值得一问……
  • 第三,您是否保留了一个列表/dict/whatever 元素的(以及其他成员)排序顺序,以及一个单独的列表/数组/任何排序顺序(如 SQL 样式索引)?您使用的实际数据结构是什么?
  • 什么语言?您列出了其中的多个。

标签: java algorithm google-app-engine data-structures google-cloud-datastore


【解决方案1】:
  1. 重新索引您的实体。将 order 属性设置为 double。
  2. 每次用户将一个实体移动到一个新位置时,在其他两个实体之间为其分配一个新的订单属性:

    entityA.setOrder((entityB.getOrder() + entityC.getOrder())/2);

  3. 保存实体 A(属性“订单”应该被索引)。

  4. 当用户请求从 10000 到 10200 的实体时,使用排序顺序在您的 order 属性上构建查询。从 10000 到 10200 检索结果:

    datastore.prepare(q).asList(FetchOptions.Builder.withOffset(10000).limit(200));

  5. 永远不要再重新索引您的实体。每次您保存实体时,Datastore 都会为您执行此操作。

【讨论】:

  • 我同意,这与我的建议几乎相同。虽然双打确实很稀少,但假设你有无限的空间正在招致灾难。假设他做了一个非常成功的推特克隆并且拥有一万亿个实体(如果每条推文都是一个实体,大约需要 3 天的推文)。如果他每秒分类一次,是的,在最坏的情况下,他可能会在大约 8 年内发生碰撞。然而在一个月内,当他拥有 8 倍的数据时,他只需要 1 年的时间就可以撞上。这真的取决于他正在做多少数据和多少排序,但这并非不可能。
  • 您是在暗示他的应用程序将在大约一年内拥有大约 18,000,000 万亿条记录?我们可以放心地假设它不会发生。如果只达到一万亿,他就会在法国的游艇上发推文。
  • 我希望!实际答案是否定的,我不会拥有数万亿个实体,但问题是哪种方式最有效。我们可以讨论很多关于什么是效率的问题。我想说最好的解决方案是它可以用最少的操作处理更多的数据,减少冲突。
  • 在我的解决方案中不会发生冲突,并且成本为零:与仅使用新订单号保存实体相比,没有额外的读取或写入。它不会比这更有效。
【解决方案2】:

我假设您将实体存储在 GAE 数据存储中,并让数据存储为您编制实体索引。数据存储使用类似索引的链表,但您无权访问链表。

我不认为有一个完美的机制,但不是从 1..N 中排序你的 N 个项目,我会使用一大组稀疏的数字(例如,使用浮点数),并将你的实体均匀分布在那个范围。每当您对项目进行排序时,只需在两个新邻居之间生成一个新的索引值。

如果遇到最坏的情况,即邻居太靠近,请为邻居生成新索引,等等。更先进的系统可能会保证每次重新排序后实体之间的空间最小,并主动重新索引一些额外的邻居。

【讨论】:

  • Appengine 将所有浮点数存储为双精度数。我不确定您所说的“太靠近”是什么意思。他需要 10 亿年才能耗尽 double 的精度。
【解决方案3】:

在我看来,您当前的模型没有其他选择。像索引集合一样,您必须在移动元素时“重新索引”元素:减少或增加集合的一部分

更改模型可能是满足您要求的解决方案。您可以尝试将其设计为链表,其中删除/移动/插入操作“更便宜”。每个元素都知道它的下一个(简单)或下一个和上一个元素(双)

【讨论】:

  • 如果我使用链表策略(我猜你的意思是指向下一个元素),那么成本是 2 次操作,但是每次排序我都需要加载所有数据,然后重建列表这样我就可以对其进行排序了。
  • @Jordi 你能解释一下“加载”所有数据并“重建”列表是什么意思吗?
  • 当我想显示从 10000 到 10200 排序的元素时,假设我没有对元素进行物理排序,我需要检索整个数据,构造链表,通过它并得到那些元素。在您的解决方案中,更新成本很低,但阅读起来并不理想。
  • @Jordi,我明白你的意思,但你当前的设计删除第一个元素或将其移动到末尾具有相同的效果。
  • 我同意。你的解决方案比我的便宜 4 倍,因为读取便宜 4 倍,但仍然是 O(N)。
【解决方案4】:

您可以将排序顺序和 UI 数据与每个实体中的其他大量数据分开。后者可以保持不变。

嗯,如果你有这个:

entitles = [bigdata1, bigdata2, bigdata3, ...]
order_numbers = [2, 3, 1, ...]

order_numbers 可以是排序的结果,也可以是任意用户定义的值。

那么你有

display_order = [2, 0, 1, ...]

表示首先显示bigdata3。如果 UI 无论如何都想更改订单,则只有 order_numbers 和 display_order 需要更改,而不是 entitles。这是我的理解。

【讨论】:

  • 我不确定我是否理解您的回答。可以延长吗?
  • 问题不在于 UI。这是重新索引数据存储区中 200 万个实体的有效方法。
  • quote:当在 UI 中触发顺序更改时(将元素 i 放在位置 j)我需要更新它们之间的所有实体。如果元素 1 成为最新的,我需要进行 N 次更新。实体中的哪些值需要更新?它不可能是一切。举个例子吧。
猜你喜欢
  • 2019-01-23
  • 2016-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-29
  • 1970-01-01
  • 2015-09-16
相关资源
最近更新 更多