构建分页光标

时间:2013-06-21 11:31:24

标签: pagination cursor cursor-position

我的活动存储在图形数据库中。在某些情况下,多个活动被分组并聚合成1个活动。

已处理的活动Feed可能如下所示:

Activity 1

Activity 2

Grouped Activity
  Activity 3
  Activity 4

Activity 5
  • 活动具有更新的时间戳和唯一ID。

  • 活动按其更新时间排序,如果是分组活动,则使用其子活动中的最新更新时间。

  • 活动可以插入列表中的任何位置(例如,如果我们开始关注某人,他们过去的活动就会被插入到列表中)。

  • 可以从列表中的任何位置删除活动。

  • 由于数据量大,使用带有微秒的时间戳仍然可能导致冲突(2个项目可以具有相同的时间戳)。

  • 游标标识符应该是唯一且稳定的。添加和删​​除Feed项不应更改标识符。

我想介绍基于光标的分页,以允许客户端通过类似于twitter's的Feed进行分页。关于它们是如何构建的似乎没有太多信息,因为我只发现这个blog post正在讨论实现它们。但是,如果光标的标识符恰好指向已删除的项目,则似乎有问题。

通过上述内容,我如何生成一个可用作上述光标的标识符?最初,我考虑将时间戳与唯一ID 1371813798111111.myuniqueid组合在一起。但是,如果1371813798111111.myuniqueid处的项目被删除,我可以获取具有1371813798111111时间戳的项目,但无法确定哪个项目应该以我应该开始的时间戳开始。

我的另一种方法是为每个Feed结果分配一个递增的数字。由于数字是递增的并且按顺序,如果缺少数字/ id,我可以选择下一个。但是,这样做的问题是,如果我开始在Feed中间删除并添加Feed项,则游标ID会发生变化。我遇到这个问题的一个解决方案是每个数字之间存在巨大差距,但很难确定如何以确定的方式将新项目添加到每个数字之间的空间。此外,随着新项目的增加,以及填补的空白,我们最终会遇到同样的问题。

简单地说,如果我有一个项目列表,可以在列表中的任何位置添加和删除项目,为每个列表项生成id的最佳方法是,如果id的项目删除后,我仍然可以确定其在列表中的位置

1 个答案:

答案 0 :(得分:1)

您需要具有其他(或现有)列,该列对于目标表中每个新添加的行都按顺序增加。我们将此列称为 seq_id

当客户端第一次请求光标时:

GET /api/v1/items?sort_by={sortingFieldName}&size={count}

其中sortingFieldName是我们应用排序依据的字段的名称

幕后发生的事情:

SELECT * FROM items
WHERE ...            // apply search params
ORDER BY sortingFieldName, seq_id
LIMIT :count

响应:

{
    "data": [...],
    "cursor": {
        "prev_field_name": "{result[0].sortingFieldName}",
        "prev_id": "{result[0].seq_id}",
        "nextFieldName": "{result[count-1].sortingFieldName}",
        "next_id": "{result[count-1].seq_id}",
        "prev_results_link": "/api/v1/items?size={count}&cursor=bw_{prevFieldName}_{prevId}",
        "next_results_link": "/api/v1/items?size={count}&cursor=fw_{nextFieldName}_{nextId}"       
    }
}

如果我们检索到的行少于计数行,则光标的下一个将不显示。

如果我们在请求中没有游标或没有要返回的数据,则游标的前一部分将不显示。

当客户再次执行请求时-他需要使用游标。前进光标:

GET /api/v1/items?size={count}&cursor=fw_{nextFieldName}_{nextId}

幕后发生的事情:

SELECT * FROM items
WHERE ...            // apply search params
AND ((fieldName = :cursor.nextFieldName AND seq_id > :cursor.nextId) OR 
      fieldName > :cursor.nextFieldName)
ORDER BY sortingFieldName, seq_id
LIMIT :count

或向后光标:

GET /api/v1/items?size={count}&cursor=fw_{prevFieldName}_{prevId}

幕后发生的事情:

SELECT * FROM items
WHERE ...            // apply search params
AND ((fieldName = :cursor.prevFieldName AND seq_id < :cursor.prevId) OR 
      fieldName < :cursor.prevFieldName)
ORDER BY sortingFieldName DESC, seq_id DESC
LIMIT :count

响应将类似于上一个