Skip to content

Commit f130542

Browse files
authored
Fix pagination + multipart indexes (#177)
Fixed invalid results for pagination queries when filtering on a parts of a composite index. Now, instead of "scrolling" to the desired tuple (during pagination) using the ``scroll_to_after_tuple`` function, we change the iterators (``EQ`` -> ``GE``, ``REQ`` -> ``LT``) to use them in ``index:pairs`` function, together with a combination with additional filtration. Closes #170
1 parent f065552 commit f130542

File tree

4 files changed

+89
-6
lines changed

4 files changed

+89
-6
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
* Invalid results for pagination queries when filtering on
13+
a part of a composite index
14+
1015
### Added
1116

1217
* Added jsonpath indexes support for queries

crud/select/plan.lua

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,16 @@ function select_plan.new(space, conditions, opts)
182182
return nil, err
183183
end
184184

185+
-- Since we use pagination we should continue iteration since after tuple.
186+
-- Such iterator could be run only with index:pairs(key(after_tuple), 'GT/LT').
187+
-- To preserve original condition we should manually inject it in `filter_conditions`
188+
-- See function `parse` in crud/select/filters.lua file for details.
189+
if scan_after_tuple ~= nil then
190+
if scan_iter == box.index.REQ or scan_iter == box.index.EQ then
191+
table.insert(conditions, conditions[scan_condition_num])
192+
end
193+
end
194+
185195
if opts.first ~= nil then
186196
total_tuples_count = math.abs(opts.first)
187197

@@ -204,6 +214,18 @@ function select_plan.new(space, conditions, opts)
204214
end
205215
end
206216

217+
-- Moreover, for correct pagination we change iterator
218+
-- to continue iterating in direct and reverse order.
219+
if scan_after_tuple ~= nil then
220+
if scan_iter == box.index.LE then
221+
scan_iter = box.index.LT
222+
elseif scan_iter == box.index.EQ then
223+
scan_iter = box.index.GE
224+
elseif scan_iter == box.index.REQ then
225+
scan_iter = box.index.LT
226+
end
227+
end
228+
207229
local sharding_index = primary_index -- XXX: only sharding by primary key is supported
208230

209231
-- get sharding key value

test/integration/select_test.lua

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -898,16 +898,53 @@ pgroup:add('test_multipart_primary_index', function(g)
898898
})
899899

900900
local conditions = {{'=', 'primary', 0}}
901-
local result, err = g.cluster.main_server.net_box:call('crud.select', {'coord', conditions})
901+
local result_0, err = g.cluster.main_server.net_box:call('crud.select', {'coord', conditions})
902902
t.assert_equals(err, nil)
903-
local objects = crud.unflatten_rows(result.rows, result.metadata)
903+
local objects = crud.unflatten_rows(result_0.rows, result_0.metadata)
904904
t.assert_equals(objects, helpers.get_objects_by_idxs(coords, {1, 2, 3}))
905905

906+
local result, err = g.cluster.main_server.net_box:call('crud.select', {'coord', conditions,
907+
{after = result_0.rows[1]}})
908+
t.assert_equals(err, nil)
909+
local objects = crud.unflatten_rows(result.rows, result.metadata)
910+
t.assert_equals(objects, helpers.get_objects_by_idxs(coords, {2, 3}))
911+
912+
local result, err = g.cluster.main_server.net_box:call('crud.select', {'coord', conditions,
913+
{after = result_0.rows[3], first = -2}})
914+
t.assert_equals(err, nil)
915+
local objects = crud.unflatten_rows(result.rows, result.metadata)
916+
t.assert_equals(objects, helpers.get_objects_by_idxs(coords, {1, 2}))
917+
918+
local new_conditions = {{'=', 'y', 1}, {'=', 'primary', 0}}
919+
local result, err = g.cluster.main_server.net_box:call('crud.select', {'coord', new_conditions,
920+
{after = result_0.rows[3], first = -2}})
921+
t.assert_equals(err, nil)
922+
local objects = crud.unflatten_rows(result.rows, result.metadata)
923+
t.assert_equals(objects, helpers.get_objects_by_idxs(coords, {2}))
924+
906925
local conditions = {{'=', 'primary', {0, 2}}}
907926
local result, err = g.cluster.main_server.net_box:call('crud.select', {'coord', conditions})
908927
t.assert_equals(err, nil)
909928
local objects = crud.unflatten_rows(result.rows, result.metadata)
910929
t.assert_equals(objects, helpers.get_objects_by_idxs(coords, {3}))
930+
931+
local conditions_ge = {{'>=', 'primary', 0}}
932+
local result_ge_0, err = g.cluster.main_server.net_box:call('crud.select', {'coord', conditions_ge})
933+
t.assert_equals(err, nil)
934+
local objects = crud.unflatten_rows(result_ge_0.rows, result_ge_0.metadata)
935+
t.assert_equals(objects, helpers.get_objects_by_idxs(coords, {1, 2, 3, 4, 5}))
936+
937+
local result, err = g.cluster.main_server.net_box:call('crud.select', {'coord', conditions_ge,
938+
{after = result_ge_0.rows[1]}})
939+
t.assert_equals(err, nil)
940+
local objects = crud.unflatten_rows(result.rows, result.metadata)
941+
t.assert_equals(objects, helpers.get_objects_by_idxs(coords, {2, 3, 4, 5}))
942+
943+
local result, err = g.cluster.main_server.net_box:call('crud.select', {'coord', conditions_ge,
944+
{after = result_ge_0.rows[3], first = -3}})
945+
t.assert_equals(err, nil)
946+
local objects = crud.unflatten_rows(result.rows, result.metadata)
947+
t.assert_equals(objects, helpers.get_objects_by_idxs(coords, {1, 2}))
911948
end)
912949

913950
pgroup:add('test_select_partial_result_bad_input', function(g)
@@ -1424,8 +1461,6 @@ pgroup:add('test_jsonpath_index_field_pagination', function(g)
14241461
local objects = crud.unflatten_rows(result.rows, result.metadata)
14251462
t.assert_equals(objects, helpers.get_objects_by_idxs(cars, {1, 2}))
14261463

1427-
--[==[
1428-
-- Uncomment after https://github.com/tarantool/crud/issues/170
14291464
-- Pagination (secondary index - 1 field)
14301465
local result, err = g.cluster.main_server.net_box:call('crud.select',
14311466
{'cars', {{'==', 'data_index', 'Yellow'}}, {first = 2}})
@@ -1445,7 +1480,6 @@ pgroup:add('test_jsonpath_index_field_pagination', function(g)
14451480
local result, err = g.cluster.main_server.net_box:call('crud.select',
14461481
{'cars', {{'==', 'data_index', 'Yellow'}}, {first = -2, after = result.rows[1]}})
14471482
t.assert_equals(err, nil)
1448-
]==]
14491483

14501484
local objects = crud.unflatten_rows(result.rows, result.metadata)
14511485
t.assert_equals(objects, helpers.get_objects_by_idxs(cars, {1, 2}))

test/unit/select_plan_test.lua

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,29 @@ g.test_first = function()
256256
t.assert_equals(plan.scan_value, {777}) -- after_tuple id
257257
t.assert_equals(plan.after_tuple, after_tuple)
258258
t.assert_equals(plan.scan_condition_num, nil)
259-
t.assert_equals(plan.tarantool_iter, box.index.LE) -- inverted iterator
259+
t.assert_equals(plan.tarantool_iter, box.index.LT) -- inverted iterator
260+
t.assert_equals(plan.total_tuples_count, 10)
261+
t.assert_equals(plan.sharding_key, nil)
262+
t.assert_equals(plan.field_names, nil)
263+
264+
-- select by primary key, eq condition
265+
-- first 10 after_tuple 777
266+
local conditions = { cond_funcs.eq('age', 90) }
267+
local plan, err = select_plan.new(box.space.customers, conditions, {
268+
first = 10,
269+
after_tuple = after_tuple,
270+
})
271+
272+
t.assert_equals(err, nil)
273+
t.assert_type(plan, 'table')
274+
275+
t.assert_equals(plan.conditions, conditions)
276+
t.assert_equals(plan.space_name, 'customers')
277+
t.assert_equals(plan.index_id, 1)
278+
t.assert_equals(plan.scan_value, {90}) -- after_tuple id
279+
t.assert_equals(plan.after_tuple, after_tuple)
280+
t.assert_equals(plan.scan_condition_num, 1)
281+
t.assert_equals(plan.tarantool_iter, box.index.GE) -- inverted iterator
260282
t.assert_equals(plan.total_tuples_count, 10)
261283
t.assert_equals(plan.sharding_key, nil)
262284
t.assert_equals(plan.field_names, nil)

0 commit comments

Comments
 (0)