paginate-1.js 3.9 KB

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