2020 年了,现在对于拖拽排序有什么更好的存储方案?

2020-05-27 15:11:37 +08:00
 raysonlu

记得以前设计 CMS 系统的时候接触过这个问题,

文章表加一个 sort,然后让用户自己填写 sort 值,越大就越靠前,再加个 id 排序解决同样 sort 值问题,这种很适合翻页排序(特别是翻页后不显示其他页内容),但需要用户自己填写数值感觉不太友好。

后来出现了“拖拽排序”的交互,这种填值方式就走不通了,置顶和置尾还可以前端自动填值,中间的移动就没办法了,除非移动后把后面所有文章的 sort 值都 update 一次,少量文章还可以,量大了而且还可能涉及到多人同时操作的话,要不得。

寻求过其他方法,很多都说用“除二法”, 就是每个文章排序号中间有间距( 100 和 200),每往中间拖一个,sort 值就改为前后间距的二分一(150),如此类推,一直除下去将会是浮点数。 且不说浮点数精准度会不会出现问题,乍看这个方案貌似有“次数限制”,如果不停地往“某个中间”拖入一个,浮点数的长度就是限制?后来想到一个法子规避这个次数限制,用定时任务方法定期优化一下排序数字,这算是个可行但很难的方案吧~~~

现在又遇到一个类似的开发需求,想咨询一下 2020 年了有无更好的解决方案?

6007 次点击
所在节点    程序员
36 条回复
weixiangzhe
2020-05-27 15:32:10 +08:00
那不用数字成不成
1. 原始序号是 1,2,3,4,5
2. ‘5’拉到 1,2 之间变为 1@1
3. ‘4’再到 1,2 之间变为 1@2
4. ‘3’ 再拉到'1','2'之间为 1@1@1
5. 最后再按 @分割排序
keepeye
2020-05-27 15:40:13 +08:00
其实也不用更新太多吧?一页能有几条数据,只要更新两个位置中间的行就可以了
Shy07
2020-05-27 16:07:08 +08:00
文章 id 单独保存一个排序数组,简单暴力
flipped598
2020-05-27 16:14:56 +08:00
同意二楼所说的,不用更新后边全部的,只更新两个位置中间所说的就行了,一页也没有多少数据。我们系统的后台就有这样的需求,由于基本上没有并发操作,目前也是这么实现的。
star7th
2020-05-27 16:35:14 +08:00
我还真的是这么做的,不觉得有什么问题。我在 showdoc 的实现排序机制有两种,github.com/star7th/showdoc ,一种是用户操作后,就把目录 /页面的数字排序都更新一遍。一种是项目排序,简单地保存整个数组,然后读取的时候,在代码里根据这个数组进行重排。目前没遇到啥问题。你上面考虑的性能问题,算法问题,我觉得是多虑了的。不会有那么多人并发操作,也不会 update 得特别慢。就这么简单处理就好。
fancy2020
2020-05-27 16:48:44 +08:00
正好现在做的项目<橙子简历> https://wonderfulcv.com 在编辑简历的部分有拖拽排序的功能,我是单独用一张表存储排序 id,数据量不大的话这样做比较省心
damngood
2020-05-27 16:53:36 +08:00
我一般是用 double
index90
2020-05-27 17:08:38 +08:00
你拖拽的跨度有多大?
假设你一个页面有一百条数据,那么每次拖拽用交换排序,最多更新一百条数据的索引值。
yc8332
2020-05-27 17:28:07 +08:00
如果都有排序,其实只要更新交换的两个而已,如果没有排序或者有些值是一样的,那可能会多更新几个。。。这个我做过
SakuraKuma
2020-05-27 17:33:13 +08:00
拖拽排序问题不大, 对比原序号和新序号 patch 一下就好, 现系统就是这么搞的.
问题是跨分页排序, 不分页嘛, 数据太多, 分页嘛, 跨分页拖拽不了.

有大佬提供下好思路嘛~
lookas2001
2020-05-27 17:38:57 +08:00
实现一个序列,支持 O(logn)插入、删除(类似文字编辑器),典型的算法题啊,用平衡树,即使在最坏情况下也能保证时间。
不过我觉得一般情况下,你最后提出的解决方案已经能很好的解决问题了,码量也不大。
lookas2001
2020-05-27 17:42:13 +08:00
平衡树也有实现,如果使用 js,可以看看这个库 https://github.com/mourner/bbtree
hronro
2020-05-27 17:45:24 +08:00
@lookas2001 问的是储存方案,最后应该是要和数据库挂钩的
raysonlu
2020-05-27 17:58:18 +08:00
@keepeye
@Shy07
“只更新两个位置中间”,是指那个“除二法”吗?那个确实没有批量更新和并发锁行的问题,但理论上会出现了次数限制啊,另外这个法子和一页多少条数据无关吧,只要往同一个位置拖入,那个位置不断除二,就出现次数限制了。
raysonlu
2020-05-27 18:02:42 +08:00
@star7th
@fanchangyong
@index90
当数据量大,比如有 10W(或更多)个文章,把开头第十个拖到第五个下面,那么就要更新其他文章( 10W-5 个)的排序号?
raysonlu
2020-05-27 18:09:36 +08:00
@lookas2001 定时器方案确实能解决,但这的确太奇葩,我思考着我以后项目交接接手人看到这个定时任务会一脸懵逼,毕竟我刚在小组提出这个问题,几乎全部人一来就表示“这不是很简单吗”。平衡树我也有用来解决过一些需求,但这里无法应用啊,毕竟基本场景是数据库查表。

@hronro 当然如果有其他曲线方案能解决也比较好,比如数据库纯天然般支持这种排序需求
fancy2020
2020-05-27 18:12:50 +08:00
@raysonlu 我的回复里说过了,我的数据量不大所以才这样做,我需要排序的数据只有个位数(每个简历里的教育经历等的排序)。十万这种的肯定就需要其他方案了
DavidNineRoc
2020-05-27 19:01:32 +08:00
数据量大要处理的问题:
加入排序位置是
1 10 100 100

1. 把第三位放到第一位的处理方案
2. 之后把末尾放到第一位的解决方案
3. 重复以上两个步骤一百次.
DavidNineRoc
2020-05-27 19:02:39 +08:00
如果对于用户级别的排序没有分页可以考虑使用双链表, 全部的排序另说
index90
2020-05-27 19:08:36 +08:00
@raysonlu 怎么会呢?
假设你当前页有十条数据,你现在把第 7 条数据拖到第 4 条数据前,那么需要变更的数据有 D,E,F,G:
1 A--A
2 B--B
3 C--C
4 D--G
5 E--D
6 F--E
7 G--F
8 H--H
9 I--I
10 J--J

所以结合场景,如果你一页所展示的数据大小为 100 条数据,那么最坏的情况下,你要变更 100 条数据,无论你的总量是多少。
但如果你支持跨页拖拽,你就当我没说。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/675986

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX