[tarantool-patches] [PATCH v2 5/5] box: specify indexes in user-friendly form

Kirill Shcherbatov kshcherbatov at tarantool.org
Wed Aug 15 15:15:03 MSK 2018


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





More information about the Tarantool-patches mailing list