lexorank算法:列表拖拽排序 如何高效的更新数据库里的排序值

384 阅读3分钟

推荐使用
mudder、mudderjs

视频简讲
www.youtube.com/watch?v=OjQ…

文章讲解
medium.com/whisperarts…

npm库
www.npmjs.com/package/mud…
www.npmjs.com/package/mud…
www.npmjs.com/package/lex… (一般)

rust库
crates.io/crates/mudd…
crates.io/crates/lexo… (实现不好)

github
github.com/fasiha/mudd…
github.com/kevinhu/mud…

公司实践
support.atlassian.com/jira/kb/und…


原理简单介绍:

通过字符串0-9a-z进行排序,而不是数字。也可以加入大写字母A-Z。 为什么使用字符串:因为字符串可以很长,有base36/62位长度,而且数据库可以根据字符串进行排序。

例:假如有两个排序值aa、bb,如果要插入一个中间值可以使用aan。 并且可以使用bucket来解决没有中间值可插入的情况,桶即在排序值插入0、1、2三个数之一,例:0|aa。 例子:假如有两个排序值且字符长度为2的情况下:0|aa、0|ab,没有中间值可以插入了,则可以把0|ab的桶值加1为1|ab,则0|aa、1|ab之间又可以插入新的中间值(例:0|ac)了

为什么只使用3个桶?chatgpt是这样说的: LexoRank 只用 3 个桶,是为了简单、高效、易维护。 桶是粗粒度的分区,3 个桶足够覆盖前、中、后逻辑,保证大部分插入操作都可以在桶内完成而无需跨桶。 如果你真的有大量频繁插入,增加桶数量是可行的,但实现复杂度会增加。

LeksoRanks based on three things:

  1. Strings are easy to sort alphabetically.
  2. Between two strings you can find the average (not always, and it is not so easy).
  3. If there is no way to find the average — you can use the buckets (sounds weird, yeah).


为什么不使用数字?

如果是列表排序值是按照1、2、3、4、5顺序排列的话,则每拖拽一个列表项,最差的情况需要对每一个列表的排序值在数据库里进行更新。

如果加大数字间隔,比如第一个为1,第二个为1000,则插入的整数中间值为(1+1000)/2=500,以此类推,中间值总有耗尽的那一刻。如果使用分数呢,情况也是如此。网上给出的方案是没有中间值时进行重排,并且最好定期重排。


相比之下,字符串的可插入中间值是海量的,如图所示: 在a和b之间允许插入的中间值会随着字符长度指数增长。

允许长度L可插入数量计算结果
1"a""b"0
236^(2-1) = 36^136
336^2 + 361296 + 36 = 1,332
436^3 + 36^2 + 3646656 + 1296 + 36 = 47,988
536^4 + 36^3 + 36^2 + 361679616 + 46656 + 1296 + 36 = 1,723,604
...
3636^35 + 36^34 + ... + 363,036,108,432,483,867,243,645,927,998,368,073,634,822,868,632,171,448,576


其它方案?

双向链表,缺点:更新操作繁琐,遍历查询,没有分页功能 juejin.cn/post/699997…



题外话:

如果你的数据量比较小而且可以在本地存储为json文件,在js端可以修改其元素位置并覆盖保存。

例:我们想把id=5的元素移动到index=1(第二个位置)

let arr = [
  { id: 1, name: 'A' },
  { id: 2, name: 'B' },
  { id: 3, name: 'C' },
  { id: 4, name: 'D' },
  { id: 5, name: 'E' }
];

// 1. 找到元素索引
const fromIndex = arr.findIndex(item => item.id === 5);
const toIndex = 1;

// 2. 删除该元素
const [element] = arr.splice(fromIndex, 1);

// 3. 插入到指定位置
arr.splice(toIndex, 0, element);

console.log(arr);
[
  { id: 1, name: 'A' },
  { id: 5, name: 'E' },
  { id: 2, name: 'B' },
  { id: 3, name: 'C' },
  { id: 4, name: 'D' }
]


其它资源:
总是把字符串长度保持在最小值的最佳实现:stackoverflow.com/questions/3…