Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON update has no means to auto-create an updated nested map/array #7720

Open
Gerold103 opened this issue Sep 26, 2022 · 0 comments
Open

JSON update has no means to auto-create an updated nested map/array #7720

Gerold103 opened this issue Sep 26, 2022 · 0 comments
Labels
app feature A new functionality

Comments

@Gerold103
Copy link
Collaborator

There are cases when a user has a map inside tuple's data somewhere, but it is not always created from the beginning. For example, want to create the map on its first update, when a first key is set. Or worse - the map might be somewhere in-place and empty, or sometimes be not here at all. Easily can happen if the map is a nullable field. However it is not possible to create the map + add a key to it atomically. See example (it is about updating tuples directly, but same is true for space updates):

map = setmetatable({}, {__serialize='map'})
t1 = box.tuple.new({map})
t2 = box.tuple.new({nil})
t1:update({{'=', '[1].key', 123}})
t2:update({{'=', '[1].key', 123}})
tarantool> t1:update({{'=', '[1].key', 123}})
---
- [{'key': 123}]
...

tarantool> t2:update({{'=', '[1].key', 123}})
---
- error: Field ''[1].key'' was not found in the tuple
...

Attempt to add a new first operation {'=', '[1]', {}} would loose data if the map was already here and wasn't empty.

As a workaround the user has to get the tuple and either update it manually + make a replace, or add an optional {'=', <path>, {}} operation if the map is not created yet.

All the same is true for arrays.

The possible solutions are below.


New operations to ensure map/array existence
Add new operations which ensure that there is a map/array created. They are nop, if the map/array are already here, or create them if there is nil. Their opcodes could be { and [. Examples:

t = box.tuple.new({{key1 = 1, map1 = {key2 = 2}}})
t:update({{'{', '[1].map1.map2'}, {'=', '[1].map1.map2.key3', 3}})
res = {{key1 = 1, map1 = {key2 = 2, map2 = {key3 = 3}}}}

t = box.tuple.new({{key1 = 1, map1 = {key2 = 2, map2 = {}}}})
t:update({{'{', '[1].map1.map2'}, {'=', '[1].map1.map2.key3', 3}})
-- Same.

Basically, { works like = {} if the map is not here or it is nop. Same for [ and arrays. The operations fail if the field exists but it is not of the needed type ({ on an existing number or an array or other non-map type). They also fail if their JSON path following fails earlier than on the last token.


Create the JSON path parts automatically
If a JSON path following fails during an update, the missing parts are created on fly automatically. Examples:

t = box.tuple.new({{key1 = 1, map1 = {key2 = 2}}})
t:update({{'=', '[1].map1.map2.key3', 3}})
res = {{key1 = 1, map1 = {key2 = 2, map2 = {key3 = 3}}}}

t = box.tuple.new({{key1 = 1, map1 = {key2 = 2}}})
t:update({{'=', '[1].map1.map2.map3.arr1[3].map4.key3', 3}})
res = {{key1 = 1, map1 = {key2 = 2, map2 = {map3 = {arr1 = [nil, nil, {map4 = {key3 = 3}}]}}}}}

The second example demonstrates how multiple missing parts of the path were created. It breaks backward compatibility though. Perhaps could add a new per-operation option like whether it should auto-create the path.


Hybrid solution
Add a new operation which ensures a passed path exists. Can either work like the first option + create all the missing parts of the path instead of failing, or could make a new operation which takes the value for the last token:

-- Create all parts of the <path> if they are missing.
-- Set the value in the end of the path to be a map. Or fail if it already exists and is not a map.
tuple:update({{'/', <path>, '{'}})
-- Same, but the value is an array.
tuple:update({{'/', <path>, '['}})

Other tickets about improving JSON updates: #6006, #5214, #5152, #3388.

@Gerold103 Gerold103 added feature A new functionality app labels Sep 26, 2022
@kyukhin kyukhin added the teamP label Sep 30, 2022
@TarantoolBot TarantoolBot removed the teamP label Jun 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
app feature A new functionality
Projects
None yet
Development

No branches or pull requests

3 participants