From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 8A3FA20F56 for ; Wed, 15 Aug 2018 08:15:08 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id em2QhggMYvRw for ; Wed, 15 Aug 2018 08:15:08 -0400 (EDT) Received: from smtp53.i.mail.ru (smtp53.i.mail.ru [94.100.177.113]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id D3BF820DDF for ; Wed, 15 Aug 2018 08:15:07 -0400 (EDT) From: Kirill Shcherbatov Subject: [tarantool-patches] [PATCH v2 5/5] box: specify indexes in user-friendly form Date: Wed, 15 Aug 2018 15:15:03 +0300 Message-Id: <85e01e1462538960a3d14276c6cdb1ebbccad43f.1534332920.git.kshcherbatov@tarantool.org> In-Reply-To: References: In-Reply-To: References: Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org Cc: v.shpilevoy@tarantool.org, Kirill Shcherbatov Since now it is possible to create indexes by JSON-path using field names specified in format. @TarantoolBot document Title: Indexes by JSON path Sometimes field data could have complex document structure. When this structure is consistent across whole document, you are able to create an index by JSON path. Example: s:create_index('json_index', {parts = {{'data.FIO["fname"]', 'str'}}}) Part of #1012. --- src/box/lua/schema.lua | 58 +++++++++++++++++++++++++++++++++++++++------ test/engine/iterator.result | 2 +- test/engine/tuple.result | 36 ++++++++++++++++++++++++++++ test/engine/tuple.test.lua | 10 ++++++++ 4 files changed, 98 insertions(+), 8 deletions(-) diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua index b9b8c90..62b83aa 100644 --- a/src/box/lua/schema.lua +++ b/src/box/lua/schema.lua @@ -556,6 +556,48 @@ 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 + return idx, nil + end + -- try resolve field by name + field_name = string.match(name, "^[a-z][a-z,0-9]*") + if field_name ~= nil then + idx = format_field_index_by_name(format, field_name) + local suffix = string.sub(name, string.len(field_name) + 2) + if idx ~= nil and suffix ~= nil then + return idx, string.format("[%d]%s", idx, suffix) + end + -- simplified json_path + return idx, nil + end + -- try resolve field by index + field_name = string.match(name, "^%[[0-9]*%]") + if field_name ~= nil then + idx = tonumber(string.sub(field_name, 2, -2)) + local suffix = string.sub(name, string.len(field_name) + 2) + if suffix == nil then + -- simplified json_path + return idx, nil + end + return idx, name + end + return nil, nil +end + local function update_index_parts(format, parts) if type(parts) ~= "table" then box.error(box.error.ILLEGAL_PARAMS, @@ -607,16 +649,18 @@ 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 - end - if type(part.field) == 'string' then + 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 part.path ~= nil and part.path ~= path then + box.error(box.error.ILLEGAL_PARAMS, + "options.parts[" .. i .. "]: field path '"..part.path.." doesn't math path resolved by name '" .. part.field .. "'") + 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/iterator.result b/test/engine/iterator.result index 10097ed..0bca87e 100644 --- a/test/engine/iterator.result +++ b/test/engine/iterator.result @@ -4213,7 +4213,7 @@ s:replace{35} ... state, value = gen(param,state) --- -- error: 'builtin/box/schema.lua:1034: usage: next(param, state)' +- error: 'builtin/box/schema.lua:1078: usage: next(param, state)' ... value --- diff --git a/test/engine/tuple.result b/test/engine/tuple.result index e9efb16..775c86b 100644 --- a/test/engine/tuple.result +++ b/test/engine/tuple.result @@ -946,6 +946,42 @@ assert(idx ~= nil) --- - true ... +format = {{'int1', 'unsigned'}, {'int2', 'unsigned'}, {'data', 'array'}, {'int3', 'unsigned'}, {'int4', 'unsigned'}} +--- +... +s:format(format) +--- +- error: Field 3 has type 'map' in one index, but type 'array' in another +... +format = {{'int1', 'unsigned'}, {'int2', 'unsigned'}, {'data', 'map'}, {'int3', 'unsigned'}, {'int4', 'unsigned'}} +--- +... +s:format(format) +--- +... +idx2 = s:create_index('test2', {parts = {{2, 'number'}, {'[3]["FIO"]["fname"]', 'str', path = '[3].FIO.fname'}}}) +--- +- error: 'Illegal parameters, options.parts[2]: field path ''[3].FIO.fname doesn''t + math path resolved by name ''[3]["FIO"]["fname"]''' +... +idx2 = s:create_index('test2', {parts = {{2, 'number'}, {'[3]["FIO"]["fname"]', 'str', path = '[3]["FIO"]["fname"]'}}}) +--- +... +assert(idx2 ~= nil) +--- +- true +... +idx3 = s:create_index('test3', {parts = {{2, 'number'}, {'data.FIO["fname"]', 'str'}}}) +--- +... +assert(idx3 ~= nil) +--- +- true +... +assert(idx2.parts[2].path == "[3][\"FIO\"][\"fname\"]") +--- +- true +... s:insert{7, 7, {town = 'London', FIO = 666}, 4, 5} --- - error: Tuple doesn't math document structure defined as index diff --git a/test/engine/tuple.test.lua b/test/engine/tuple.test.lua index d20a547..53f434b 100644 --- a/test/engine/tuple.test.lua +++ b/test/engine/tuple.test.lua @@ -304,6 +304,16 @@ s:create_index('test1', {parts = {{2, 'number'}, {3, 'str', path = '[2].FIO.fnam s:create_index('test1', {parts = {{2, 'number'}, {3, 'str', path = '[3].FIO....fname'}}}) idx = s:create_index('test1', {parts = {{2, 'number'}, {3, 'str', path = '[3]["FIO"]["fname"]'}, {3, 'str', path = '[3]["FIO"]["sname"]'}}}) assert(idx ~= nil) +format = {{'int1', 'unsigned'}, {'int2', 'unsigned'}, {'data', 'array'}, {'int3', 'unsigned'}, {'int4', 'unsigned'}} +s:format(format) +format = {{'int1', 'unsigned'}, {'int2', 'unsigned'}, {'data', 'map'}, {'int3', 'unsigned'}, {'int4', 'unsigned'}} +s:format(format) +idx2 = s:create_index('test2', {parts = {{2, 'number'}, {'[3]["FIO"]["fname"]', 'str', path = '[3].FIO.fname'}}}) +idx2 = s:create_index('test2', {parts = {{2, 'number'}, {'[3]["FIO"]["fname"]', 'str', path = '[3]["FIO"]["fname"]'}}}) +assert(idx2 ~= nil) +idx3 = s:create_index('test3', {parts = {{2, 'number'}, {'data.FIO["fname"]', 'str'}}}) +assert(idx3 ~= nil) +assert(idx2.parts[2].path == "[3][\"FIO\"][\"fname\"]") 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.7.4