From: Kirill Shcherbatov <kshcherbatov@tarantool.org> To: tarantool-patches@freelists.org, vdavydov.dev@gmail.com Cc: kostja@tarantool.org, Kirill Shcherbatov <kshcherbatov@tarantool.org> Subject: [PATCH v8 5/5] box: specify indexes in user-friendly form Date: Wed, 16 Jan 2019 16:44:43 +0300 [thread overview] Message-ID: <a9e76ac945ba8dde15c1fa1bd170baf61c3835f6.1547645795.git.kshcherbatov@tarantool.org> (raw) In-Reply-To: <cover.1547645795.git.kshcherbatov@tarantool.org> Implemented a more convenient interface for creating an index by JSON path. Instead of specifying fieldno and relative path it comes possible to pass full JSON path to data. Closes #1012 @TarantoolBot document Title: Indexes by JSON path Sometimes field data could have complex document structure. When this structure is consistent across whole space, you are able to create an index by JSON path. Example: s = box.schema.space.create('sample') format = {{'id', 'unsigned'}, {'data', 'map'}} s:format(format) -- explicit JSON index creation age_idx = s:create_index('age', {{2, 'number', path = "age"}}) -- user-friendly syntax for JSON index creation parts = {{'data.FIO["fname"]', 'str'}, {'data.FIO["sname"]', 'str'}, {'data.age', 'number'}} info_idx = s:create_index('info', {parts = parts}}) s:insert({1, {FIO={fname="James", sname="Bond"}, age=35}}) --- src/box/lua/schema.lua | 60 ++++++++++++++++++++++++++++++++++----- test/engine/json.result | 42 +++++++++++++++++++++++++++ test/engine/json.test.lua | 12 ++++++++ 3 files changed, 107 insertions(+), 7 deletions(-) diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index 8a804f0ba..a55ef5b22 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -575,6 +575,49 @@ local function update_index_parts_1_6_0(parts) return result end +local function format_field_index_by_name(format, name) + for k,v in pairs(format) do + if v.name == name then + return k + end + end + return nil +end + +local function format_field_resolve(format, name) + local idx = nil + local field_name = nil + -- try resolve whole name + idx = format_field_index_by_name(format, name) + if idx ~= nil then + print("Case 1 "..idx) + return idx, nil + end + -- try resolve [%d] + field_name = string.match(name, "^%[(%d+)%]") + idx = tonumber(field_name) + if idx ~= nil then + print("Case 2 "..idx..string.sub(name, string.len(field_name) + 3)) + return idx, string.sub(name, string.len(field_name) + 3) + end + -- try resolve ["%s"] or ['%s'] + field_name = string.match(name, "^%[\"(.*)\"%]") or + string.match(name, "^%[\'(.*)\'%]") + idx = format_field_index_by_name(format, field_name) + if idx ~= nil then + print("Case 3 "..idx..string.sub(name, string.len(field_name) + 5)) + return idx, string.sub(name, string.len(field_name) + 5) + end + -- try to resolve .*[ and .*. + field_name = string.match(name, "^([^.%[]-)[.%[]") + idx = format_field_index_by_name(format, field_name) + if idx ~= nil then + print("Case 4 "..idx..string.sub(name, string.len(field_name) + 1)) + return idx, string.sub(name, string.len(field_name) + 1) + end + return nil, nil +end + local function update_index_parts(format, parts) if type(parts) ~= "table" then box.error(box.error.ILLEGAL_PARAMS, @@ -626,16 +669,19 @@ local function update_index_parts(format, parts) box.error(box.error.ILLEGAL_PARAMS, "options.parts[" .. i .. "]: field (name or number) is expected") elseif type(part.field) == 'string' then - for k,v in pairs(format) do - if v.name == part.field then - part.field = k - break - end + local idx, path = format_field_resolve(format, part.field) + if idx == nil then + box.error(box.error.ILLEGAL_PARAMS, + "options.parts[" .. i .. "]: field was not found by name '" .. part.field .. "'") end - if type(part.field) == 'string' then + if part.path ~= nil and part.path ~= path then box.error(box.error.ILLEGAL_PARAMS, - "options.parts[" .. i .. "]: field was not found by name '" .. part.field .. "'") + "options.parts[" .. i .. "]: field path '".. + path.." doesn't math the path '" ..part.path.."'") end + parts_can_be_simplified = parts_can_be_simplified and path == nil + part.field = idx + part.path = path or part.path elseif part.field == 0 then box.error(box.error.ILLEGAL_PARAMS, "options.parts[" .. i .. "]: field (number) must be one-based") diff --git a/test/engine/json.result b/test/engine/json.result index 9e5accb0b..32f787a3d 100644 --- a/test/engine/json.result +++ b/test/engine/json.result @@ -70,6 +70,48 @@ s:create_index('test2', {parts = {{2, 'number'}, {3, 'number', path = 'FIO.fname - error: Field [3]["FIO"]["fname"] has type 'string' in one index, but type 'number' in another ... +s:create_index('test3', {parts = {{2, 'number'}, {']sad.FIO["fname"]', 'str'}}}) +--- +- error: 'Illegal parameters, options.parts[2]: field was not found by name '']sad.FIO["fname"]''' +... +s:create_index('test3', {parts = {{2, 'number'}, {'[3].FIO["fname"]', 'str', path = "FIO[\"sname\"]"}}}) +--- +- error: 'Illegal parameters, options.parts[2]: field path ''.FIO["fname"] doesn''t + math the path ''FIO["sname"]''' +... +idx3 = s:create_index('test3', {parts = {{2, 'number'}, {'[3].FIO["fname"]', 'str'}}}) +--- +... +idx3 ~= nil +--- +- true +... +idx3.parts[2].path == ".FIO[\"fname\"]" +--- +- true +... +s:create_index('test4', {parts = {{2, 'number'}, {'invalid.FIO["fname"]', 'str'}}}) +--- +- error: 'Illegal parameters, options.parts[2]: field was not found by name ''invalid.FIO["fname"]''' +... +idx4 = s:create_index('test4', {parts = {{2, 'number'}, {'data.FIO["fname"]', 'str'}}}) +--- +... +idx4 ~= nil +--- +- true +... +idx4.parts[2].path == ".FIO[\"fname\"]" +--- +- true +... +-- Vinyl has optimizations that omit index checks, so errors could differ. +idx3:drop() +--- +... +idx4:drop() +--- +... s:insert{7, 7, {town = 'London', FIO = 666}, 4, 5} --- - error: 'Tuple field [3]["FIO"] type does not match one required by operation: expected diff --git a/test/engine/json.test.lua b/test/engine/json.test.lua index a876ff1ed..cfed275b0 100644 --- a/test/engine/json.test.lua +++ b/test/engine/json.test.lua @@ -19,6 +19,18 @@ s:format(format) format = {{'id', 'unsigned'}, {'meta', 'unsigned'}, {'data', 'map'}, {'age', 'unsigned'}, {'level', 'unsigned'}} s:format(format) s:create_index('test2', {parts = {{2, 'number'}, {3, 'number', path = 'FIO.fname'}, {3, 'str', path = '["FIO"]["sname"]'}}}) +s:create_index('test3', {parts = {{2, 'number'}, {']sad.FIO["fname"]', 'str'}}}) +s:create_index('test3', {parts = {{2, 'number'}, {'[3].FIO["fname"]', 'str', path = "FIO[\"sname\"]"}}}) +idx3 = s:create_index('test3', {parts = {{2, 'number'}, {'[3].FIO["fname"]', 'str'}}}) +idx3 ~= nil +idx3.parts[2].path == ".FIO[\"fname\"]" +s:create_index('test4', {parts = {{2, 'number'}, {'invalid.FIO["fname"]', 'str'}}}) +idx4 = s:create_index('test4', {parts = {{2, 'number'}, {'data.FIO["fname"]', 'str'}}}) +idx4 ~= nil +idx4.parts[2].path == ".FIO[\"fname\"]" +-- Vinyl has optimizations that omit index checks, so errors could differ. +idx3:drop() +idx4:drop() s:insert{7, 7, {town = 'London', FIO = 666}, 4, 5} s:insert{7, 7, {town = 'London', FIO = {fname = 666, sname = 'Bond'}}, 4, 5} s:insert{7, 7, {town = 'London', FIO = {fname = "James"}}, 4, 5} -- 2.19.2
next prev parent reply other threads:[~2019-01-16 13:44 UTC|newest] Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-01-16 13:44 [PATCH v8 0/5] box: Indexes by JSON path Kirill Shcherbatov 2019-01-16 13:44 ` [PATCH v8 1/5] lib: updated msgpuck library version Kirill Shcherbatov 2019-01-23 12:53 ` Vladimir Davydov 2019-01-16 13:44 ` [PATCH v8 2/5] box: refactor tuple_field_raw_by_path routine Kirill Shcherbatov 2019-01-23 13:15 ` Vladimir Davydov 2019-01-16 13:44 ` [PATCH v8 3/5] box: introduce JSON Indexes Kirill Shcherbatov 2019-01-23 13:49 ` Vladimir Davydov 2019-01-16 13:44 ` [PATCH v8 4/5] box: introduce offset_slot cache in key_part Kirill Shcherbatov 2019-01-23 14:23 ` Vladimir Davydov 2019-01-16 13:44 ` Kirill Shcherbatov [this message] 2019-01-23 15:29 ` [PATCH v8 5/5] box: specify indexes in user-friendly form Vladimir Davydov 2019-01-16 15:35 ` [tarantool-patches] [PATCH v8 6/6] box: introduce has_json_paths flag in templates Kirill Shcherbatov 2019-01-23 14:15 ` Vladimir Davydov
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=a9e76ac945ba8dde15c1fa1bd170baf61c3835f6.1547645795.git.kshcherbatov@tarantool.org \ --to=kshcherbatov@tarantool.org \ --cc=kostja@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=vdavydov.dev@gmail.com \ --subject='Re: [PATCH v8 5/5] box: specify indexes in user-friendly form' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox