paginate-1.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. const content = `--[[
  2. Paginate a set or hash
  3. Input:
  4. KEYS[1] key pointing to the set or hash to be paginated.
  5. ARGV[1] page start offset
  6. ARGV[2] page end offset (-1 for all the elements)
  7. ARGV[3] cursor
  8. ARGV[4] offset
  9. ARGV[5] max iterations
  10. ARGV[6] fetch jobs?
  11. Output:
  12. [cursor, offset, items, numItems]
  13. ]]
  14. local rcall = redis.call
  15. -- Includes
  16. --[[
  17. Function to achieve pagination for a set or hash.
  18. This function simulates pagination in the most efficient way possible
  19. for a set using sscan or hscan.
  20. The main limitation is that sets are not order preserving, so the
  21. pagination is not stable. This means that if the set is modified
  22. between pages, the same element may appear in different pages.
  23. ]] -- Maximum number of elements to be returned by sscan per iteration.
  24. local maxCount = 100
  25. -- Finds the cursor, and returns the first elements available for the requested page.
  26. local function findPage(key, command, pageStart, pageSize, cursor, offset,
  27. maxIterations, fetchJobs)
  28. local items = {}
  29. local jobs = {}
  30. local iterations = 0
  31. repeat
  32. -- Iterate over the set using sscan/hscan.
  33. local result = rcall(command, key, cursor, "COUNT", maxCount)
  34. cursor = result[1]
  35. local members = result[2]
  36. local step = 1
  37. if command == "HSCAN" then
  38. step = 2
  39. end
  40. if #members == 0 then
  41. -- If the result is empty, we can return the result.
  42. return cursor, offset, items, jobs
  43. end
  44. local chunkStart = offset
  45. local chunkEnd = offset + #members / step
  46. local pageEnd = pageStart + pageSize
  47. if chunkEnd < pageStart then
  48. -- If the chunk is before the page, we can skip it.
  49. offset = chunkEnd
  50. elseif chunkStart > pageEnd then
  51. -- If the chunk is after the page, we can return the result.
  52. return cursor, offset, items, jobs
  53. else
  54. -- If the chunk is overlapping the page, we need to add the elements to the result.
  55. for i = 1, #members, step do
  56. if offset >= pageEnd then
  57. return cursor, offset, items, jobs
  58. end
  59. if offset >= pageStart then
  60. local index = #items + 1
  61. if fetchJobs ~= nil then
  62. jobs[#jobs+1] = rcall("HGETALL", members[i])
  63. end
  64. if step == 2 then
  65. items[index] = {members[i], members[i + 1]}
  66. else
  67. items[index] = members[i]
  68. end
  69. end
  70. offset = offset + 1
  71. end
  72. end
  73. iterations = iterations + 1
  74. until cursor == "0" or iterations >= maxIterations
  75. return cursor, offset, items, jobs
  76. end
  77. local key = KEYS[1]
  78. local scanCommand = "SSCAN"
  79. local countCommand = "SCARD"
  80. local type = rcall("TYPE", key)["ok"]
  81. if type == "none" then
  82. return {0, 0, {}, 0}
  83. elseif type == "hash" then
  84. scanCommand = "HSCAN"
  85. countCommand = "HLEN"
  86. elseif type ~= "set" then
  87. return
  88. redis.error_reply("Pagination is only supported for sets and hashes.")
  89. end
  90. local numItems = rcall(countCommand, key)
  91. local startOffset = tonumber(ARGV[1])
  92. local endOffset = tonumber(ARGV[2])
  93. if endOffset == -1 then
  94. endOffset = numItems
  95. end
  96. local pageSize = (endOffset - startOffset) + 1
  97. local cursor, offset, items, jobs = findPage(key, scanCommand, startOffset,
  98. pageSize, ARGV[3], tonumber(ARGV[4]),
  99. tonumber(ARGV[5]), ARGV[6])
  100. return {cursor, offset, items, numItems, jobs}
  101. `;
  102. export const paginate = {
  103. name: 'paginate',
  104. content,
  105. keys: 1,
  106. };
  107. //# sourceMappingURL=paginate-1.js.map