Skip to content

Commit 27d825f

Browse files
authored
Select: support jsonpath indexes (#158)
1 parent 4f66605 commit 27d825f

File tree

11 files changed

+432
-17
lines changed

11 files changed

+432
-17
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1515
* Ignoring `opts.first` on `crud.pairs` call
1616
* External `keydef` compatibility with built-in `merger`
1717

18+
### Added
19+
20+
* Added jsonpath indexes support for queries.
21+
1822
## [0.7.0] - 2021-05-27
1923

2024
### Fixed

crud/common/compat.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,21 @@ function compat.require(module_name, builtin_module_name)
3030
return module
3131
end
3232

33+
function compat.exists(module_name, builtin_module_name)
34+
local module_cached = rawget(_G, string.format('__crud_%s_cached', module_name))
35+
if module_cached ~= nil then
36+
return true
37+
end
38+
39+
if package.search(module_name) then
40+
return true
41+
end
42+
43+
if package.loaded[builtin_module_name] ~= nil then
44+
return true
45+
end
46+
47+
return false
48+
end
49+
3350
return compat

crud/common/utils.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ local function determine_enabled_features()
205205

206206
-- since Tarantool 2.4
207207
enabled_tarantool_features.uuids = major >= 2 and (minor > 4 or minor == 4 and patch >= 1)
208+
209+
-- since Tarantool 2.6.3 / 2.7.2 / 2.8.1
210+
enabled_tarantool_features.jsonpath_indexes = major >= 3 or (major >= 2 and ((minor >= 6 and patch >= 3)
211+
or (minor >= 7 and patch >= 2) or (minor >= 8 and patch >= 1) or minor >= 9))
208212
end
209213

210214
function utils.tarantool_supports_fieldpaths()
@@ -223,6 +227,14 @@ function utils.tarantool_supports_uuids()
223227
return enabled_tarantool_features.uuids
224228
end
225229

230+
function utils.tarantool_supports_jsonpath_indexes()
231+
if enabled_tarantool_features.jsonpath_indexes == nil then
232+
determine_enabled_features()
233+
end
234+
235+
return enabled_tarantool_features.jsonpath_indexes
236+
end
237+
226238
local function add_nullable_fields_recursive(operations, operations_map, space_format, tuple, id)
227239
if id < 2 or tuple[id - 1] ~= box.NULL then
228240
return operations

crud/compare/comparators.lua

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@ local errors = require('errors')
22

33
local compare_conditions = require('crud.compare.conditions')
44
local type_comparators = require('crud.compare.type_comparators')
5-
local operators = compare_conditions.operators
6-
75
local utils = require('crud.common.utils')
86

7+
local compat = require('crud.common.compat')
8+
local has_keydef = compat.exists('tuple.keydef', 'key_def')
9+
10+
local keydef_lib
11+
if has_keydef then
12+
keydef_lib = compat.require('tuple.keydef', 'key_def')
13+
end
14+
15+
local operators = compare_conditions.operators
16+
917
local ComparatorsError = errors.new_class('ComparatorsError')
1018

1119
local comparators = {}
@@ -102,7 +110,8 @@ function comparators.update_key_parts_by_field_names(space_format, field_names,
102110
local field_name = space_format[part.fieldno].name
103111
local updated_part = {type = part.type,
104112
fieldno = fields_positions[field_name],
105-
is_nullable = part.is_nullable}
113+
is_nullable = part.is_nullable,
114+
path = part.path}
106115
table.insert(updated_key_parts, updated_part)
107116
end
108117

@@ -150,13 +159,22 @@ function comparators.gen_tuples_comparator(cmp_operator, key_parts, field_names,
150159
local updated_key_parts = comparators.update_key_parts_by_field_names(
151160
space_format, field_names, key_parts
152161
)
153-
local keys_comparator = comparators.gen_func(cmp_operator, updated_key_parts)
154162

155-
return function(lhs, rhs)
156-
local lhs_key = utils.extract_key(lhs, updated_key_parts)
157-
local rhs_key = utils.extract_key(rhs, updated_key_parts)
163+
local keys_comparator = comparators.gen_func(cmp_operator, updated_key_parts)
158164

159-
return keys_comparator(lhs_key, rhs_key)
165+
if has_keydef then
166+
local key_def = keydef_lib.new(updated_key_parts)
167+
return function(lhs, rhs)
168+
local lhs_key = key_def:extract_key(lhs)
169+
local rhs_key = key_def:extract_key(rhs)
170+
return keys_comparator(lhs_key, rhs_key)
171+
end
172+
else
173+
return function(lhs, rhs)
174+
local lhs_key = utils.extract_key(lhs, updated_key_parts)
175+
local rhs_key = utils.extract_key(rhs, updated_key_parts)
176+
return keys_comparator(lhs_key, rhs_key)
177+
end
160178
end
161179
end
162180

crud/select/executor.lua

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ local errors = require('errors')
22

33
local dev_checks = require('crud.common.dev_checks')
44
local select_comparators = require('crud.compare.comparators')
5+
local compat = require('crud.common.compat')
6+
local has_keydef = compat.exists('tuple.keydef', 'key_def')
7+
8+
local keydef_lib
9+
if has_keydef then
10+
keydef_lib = compat.require('tuple.keydef', 'key_def')
11+
end
512

613
local utils = require('crud.common.utils')
714

@@ -31,6 +38,26 @@ local function scroll_to_after_tuple(gen, space, scan_index, tarantool_iter, aft
3138
end
3239
end
3340

41+
local generate_value
42+
43+
if has_keydef then
44+
generate_value = function(after_tuple, scan_value, index_parts)
45+
local key_def = keydef_lib.new(index_parts)
46+
if key_def:compare_with_key(after_tuple, scan_value) < 0 then
47+
return key_def:extract_key(after_tuple)
48+
end
49+
end
50+
else
51+
generate_value = function(after_tuple, scan_value, index_parts, tarantool_iter)
52+
local cmp_operator = select_comparators.get_cmp_operator(tarantool_iter)
53+
local scan_comparator = select_comparators.gen_tuples_comparator(cmp_operator, index_parts)
54+
local after_tuple_key = utils.extract_key(after_tuple, index_parts)
55+
if scan_comparator(after_tuple_key, scan_value) then
56+
return after_tuple_key
57+
end
58+
end
59+
end
60+
3461
function executor.execute(space, index, filter_func, opts)
3562
dev_checks('table', 'table', 'function', {
3663
scan_value = 'table',
@@ -53,11 +80,9 @@ function executor.execute(space, index, filter_func, opts)
5380
if value == nil then
5481
value = opts.after_tuple
5582
else
56-
local cmp_operator = select_comparators.get_cmp_operator(opts.tarantool_iter)
57-
local scan_comparator = select_comparators.gen_tuples_comparator(cmp_operator, index.parts)
58-
local after_tuple_key = utils.extract_key(opts.after_tuple, index.parts)
59-
if scan_comparator(after_tuple_key, opts.scan_value) then
60-
value = after_tuple_key
83+
local new_value = generate_value(opts.after_tuple, opts.scan_value, index.parts, opts.tarantool_iter)
84+
if new_value ~= nil then
85+
value = new_value
6186
end
6287
end
6388
end

crud/select/filters.lua

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ local function get_index_fieldnos(index)
4444
local index_fieldnos = {}
4545

4646
for _, part in ipairs(index.parts) do
47-
table.insert(index_fieldnos, part.fieldno)
47+
if part.path ~= nil then
48+
table.insert(index_fieldnos, string.format("[%d]%s", part.fieldno, part.path))
49+
else
50+
table.insert(index_fieldnos, part.fieldno)
51+
end
4852
end
4953

5054
return index_fieldnos
@@ -91,7 +95,6 @@ local function parse(space, conditions, opts)
9195
end
9296

9397
local filter_conditions = {}
94-
9598
for i, condition in ipairs(conditions) do
9699
if i ~= opts.scan_condition_num then
97100
-- Index check (including one and multicolumn)

crud/select/merger.lua

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ local reverse_tarantool_iters = {
136136
local function new(replicasets, space, index_id, func_name, func_args, opts)
137137
opts = opts or {}
138138
local call_opts = opts.call_opts
139-
140139
local mode = call_opts.mode or 'read'
141140
local vshard_call_name = call.get_vshard_call_name(mode, call_opts.prefer_replica, call_opts.balance)
142141

crud/select/plan.lua

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ local compare_conditions = require('crud.compare.conditions')
44
local utils = require('crud.common.utils')
55
local dev_checks = require('crud.common.dev_checks')
66

7+
local compat = require('crud.common.compat')
8+
local has_keydef = compat.exists('tuple.keydef', 'key_def')
9+
10+
local keydef_lib
11+
if has_keydef then
12+
keydef_lib = compat.require('tuple.keydef', 'key_def')
13+
end
14+
715
local select_plan = {}
816

917
local IndexTypeError = errors.new_class('IndexTypeError', {capture_stack = false})
@@ -184,7 +192,12 @@ function select_plan.new(space, conditions, opts)
184192
scan_condition_num = nil
185193

186194
if scan_after_tuple ~= nil then
187-
scan_value = utils.extract_key(scan_after_tuple, scan_index.parts)
195+
if has_keydef then
196+
local key_def = keydef_lib.new(scan_index.parts)
197+
scan_value = key_def:extract_key(scan_after_tuple)
198+
else
199+
scan_value = utils.extract_key(scan_after_tuple, scan_index.parts)
200+
end
188201
else
189202
scan_value = nil
190203
end

test/entrypoint/srv_select.lua

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,43 @@ package.preload['customers-storage'] = function()
151151
unique = false,
152152
if_not_exists = true,
153153
})
154+
155+
if crud_utils.tarantool_supports_jsonpath_indexes() then
156+
local cars_space = box.schema.space.create('cars', {
157+
format = {
158+
{name = 'id', type = 'map'},
159+
{name = 'bucket_id', type = 'unsigned'},
160+
{name = 'age', type = 'number'},
161+
{name = 'manufacturer', type = 'string'},
162+
{name = 'data', type = 'map'}
163+
},
164+
if_not_exists = true,
165+
engine = engine,
166+
})
167+
168+
-- primary index
169+
cars_space:create_index('id_ind', {
170+
parts = {
171+
{1, 'unsigned', path = 'car_id.signed'},
172+
},
173+
if_not_exists = true,
174+
})
175+
176+
cars_space:create_index('bucket_id', {
177+
parts = { 'bucket_id' },
178+
unique = false,
179+
if_not_exists = true,
180+
})
181+
182+
cars_space:create_index('data_index', {
183+
parts = {
184+
{5, 'str', path = 'car.color'},
185+
{5, 'str', path = 'car.model'},
186+
},
187+
unique = false,
188+
if_not_exists = true,
189+
})
190+
end
154191
end,
155192
}
156193
end

0 commit comments

Comments
 (0)