How to add new field to Tarantool space - lua

I have a following space schema in Tarantool
box.schema.space.create('customer')
format = {
{name = 'id', type = 'string'},
{name = 'last_name', type = 'string'},
}
box.space.customer:format(format)
box.space.customer:create_index('id', {parts = {{field = 'id', is_nullable = false}}})
box.space.customer:replace({'1', 'Ivanov'})
I want to add the new field first_name to this space. What's the way I can do that?

Before answering a question we should discuss a format method.
format - it’s a space option that allows you to get value from a tuple by name. Actually tuple is a “list” of values and any field could be accessed by field number.
What’s next? E.g. you have a simple schema.
box.schema.space.create('customer')
box.space.customer:format(format)
box.space.customer:create_index('id', {parts = {{field = 'id', is_nullable = false}}})
box.space.customer:replace({'1', 'Ivanov'})
Let’s define new format that has the third field - first_name.
new_format = {
{name = 'id', type = 'string'},
{name = 'last_name', type = 'string'},
{name = 'first_name', type = 'string'},
}
box.space.customer:format(new_format) -- error: our tuple have only two fields
tarantool> box.space.customer:format(new_format)
- --
- error: Tuple field 3 required by space format is missing
...
There are two ways to fix it.
Add new field to the end of tuple with default value.
box.space.customer:update({'1'}, {{'=', 3, 'Ivan'}})
box.space.customer:format(new_format) -- OK
Define new field as nullable
new_format = {
{name = 'id', type = 'string'},
{name = 'last_name', type = 'string'},
{name = 'first_name', type = 'string', is_nullable = true},
}
box.space.customer:format(new_format) -- OK: absence of the third value is acceptable
You can choose one described above variant.
I’ve just add some notes:
You can’t add some value through the absence field (e.g. you have first and second values you should add the third before adding the fourth)
tarantool> box.tuple.new({'1', 'Ivanov'}):update({{'=', 4, 'value'}})
- --
- error: Field 4 was not found in the tuple
...
tarantool> box.tuple.new({'1', 'Ivanov'}):update({{'=', 3, box.NULL}, {'=', 4, 'value'}})
- --
- ['1', 'Ivanov', null, 'value']
...
Filling in the field with the default value can be quite a long operation if you have a lot of data. Please be careful when apply any migratons.
Read more about format method in the documentation.

Related

How to create a space in Tarantool?

I've started Tarantool and have called box.cfg{} for the first configuring.
The next step: I want to create a space in Tarantool.
I read the documentation but I didn't quite understand everything.
How I should do it?
You don't need to create the sequence manually; just pass true and tarantool will create a sequence and even delete it when you drop the space. You can also skip the parts option as it defaults to {1, 'unsigned'}
box.space.users:create_index("pk", { if_not_exists = true, sequence = true })
Create it via Box API:
box.schema.sequence.create('user_seq', { if_not_exists = true })
box.schema.create_space('users', { if_not_exists = true, format={
{ name = 'id', type = 'unsigned'},
{ name = 'name', type = 'string'},
{ name = 'age', type = 'unsigned'}}
})
box.space.users:create_index('pk', { parts = { 'id' }, if_not_exists = true })
With if_not_exists, tarantool won't try to create space if it already exists.
Creating the index is mandatory because Tarantool doesn't allow you to insert data in space without any indexes.
After creating the space, you can insert and select data:
box.space.users:insert({ box.sequence.user_seq:next(), 'Artur Barsegyan', 24 })
box.space.users:get({1})
---
- - [1, 'Artur Barsegyan']
...
You can read more in the documentation.

How to make a select via secondary indexes in Tarantool?

I create a space with two indexes — primary and secondary:
box.schema.sequence.create('user_seq', { if_not_exists = true })
box.schema.space.create('user', {
if_not_exists = true,
format = {
{ name = 'id', type = 'unsigned'},
{ name = 'bio', type = 'string'}
}
})
box.space.user:create_index('id', {
sequence = 'user_seq',
parts = {'id'}
})
box.space.user:create_index('bio', {
parts = {'bio'},
if_not_exists = true,
unique = false
})
Insert the tuple:
tarantool> box.space.user:insert({ box.sequence.user_seq:next(), 'other stuff'})
---
- [1, 'other stuff']
...
I've tried to search like this:
box.space.user:select({'other stuff'})
And got the error:
- error: 'Supplied key type of part 0 does not match index part type: expected unsigned'
How I should search via secondary indexes?
The documentation said:
index.index-name is optional. If it is omitted, then the assumed index is the first (primary-key) index. Therefore, for the example above, box.space.tester:select({1}, {iterator = 'GT'}) would have returned the same two rows, via the ‘primary’ index.
Use that secondary index explicitly:
tarantool> box.space.user.index.bio:select({'other stuff'})
---
- - [1, 'other stuff']
...
Read more about it in the documentation.

Lua - Insert table values

I got a table of form let's say:
house = {
["Street 22"] = {
{name = "George", age = 20},
{name = "Pete", age = 25}
},
["Street 30"] = {
{name = "John", age = 32},
}
}
And I want to insert programmatically a third house, that is key "Street 35", with a person's details, Nick and 30 let's say. I am relatively new to lua and don't know how to do this, I must use table.insert but I am having troubles following the above format... Some help please?
Do it simple so:
house["Street 52"] = {{name = "Nick", age = 30}}
You could also mutate the third element of the houses table like this:
house[3]={name = "Nick", age = 30}
did you read this and get confused?
just try table.insert(house, {name = "Nick", age = 30}) and house[3] now contains the new element.

Set F# list as null for serialisation

I'm using F# alongside a JSON data-store making use of the JSON.NET library. I'm trying to utilise F# structures and types where possible and have run into the following issue. Say I wish to store the following data structure,
type A = {
id : int
name : string
posts : string list
}
Creation works fine, but to update just the stored name field I need to send a JSON record that omits the posts field. Using the empty list won't work as the persistence system will assume that I wish to replace the existing posts with an empty list and thus overwrite them. From the JSON.NET docs I've read a field can be omitted from serialisation by setting it to null,
let updatedEntry = { id : 0, name : "Fred", posts = null }
However the F# compiler will give an error stating that the type list can not be set to null. Is there anyway to accomplish this from within F#, perhaps an attribute I'm unaware of? Thanks
There are two ways you could do this easily:
Option 1
Use the System.Collections.Generic.List type, which can be null:
> type A = {id: int; name:string; posts: System.Collections.Generic.List<string> };;
type A =
{id: int;
name: string;
posts: System.Collections.Generic.List<string>;}
> let a = {id=5; name="hello"; posts=null};;
val a : A = {id = 5;
name = "hello";
posts = null;}
Option 2
The other, more idiomatic way, would be to use the Option type:
> type A = {id: int; name:string; posts: string list option };;
type A =
{id: int;
name: string;
posts: string list option;}
> let a = {id=5; name="there"; posts=None};;
val a : A = {id = 5;
name = "there";
posts = null;}
Note that you'd compare the posts member to None rather than null.
Handy reading: Option types
Edit
(After some searching & experimentation) you could use boxing to still use F# types as the values:
> type A = {id: int; name:string; posts: System.Object };;
type A =
{id: int;
name: string;
posts: Object;}
> let a = {id=5; name="foo"; posts=null};;
val a : A = {id = 5;
name = "foo";
posts = null;}
> let b = {id=6; name="bar"; posts=(box [])};;
val b : A = {id = 6;
name = "bar";
posts = [];}
But I'd stick with the Option type, personally

Lua: implicit table creation with string keys - why the extra brackets?

Say that you want to create a Lua table, and all its keys are valid lua identifiers. Then you can use the key=value syntax:
local niceTable = { I=1, like=1, this=1, syntax=1 }
If however your strings are not "identifiable", then you have to use the ['key']=value syntax:
local operators = { ['*']="Why", ['+']="the", ['/']="brackets", ['?']='?' }
I'm a bit baffled about this. What are those brackets doing there? What do they mean?
They identify the contained string as a key in the resulting table. The first form, you could consider as equal to
local niceTable = {}
niceTable.I = 1;
niceTable.like = 1;
The second form is equal to
local operators = {}
operators['*'] = "Why";
operators['+'] = "The";
The difference is purely syntactic sugar, except where the first one uses identifiers, so it has to follow the identifier rules, such as doesn't start with a number and interpret-time constant, and the second form uses any old string, so it can be determined at runtime, for example, and a string that's not a legal identifier. However, the result is fundamentally the same. The need for the brackets is easily explained.
local var = 5;
local table = {
var = 5;
};
-- table.var = 5;
Here, var is the identifier, not the variable.
local table = {
[var] = 5;
};
-- table[5] = 5;
Here, var is the variable, not the identifier.
The normal syntax for indexing a table is t[val]. For string keys only, Lua provides an alternate syntax, where t.foo is exactly equivalent to t["foo"]. This is purely a syntactical convenience, so-called 'syntax sugar'. It doesn't add functionality, it just gives you a less cluttered syntax for using strings as named fields.
There are a lot of strings keys this won't work for:
t["hello_world"] => t.hello_world -- works
t["hello world"] => t.hello world -- oops, space in the string
t["5 * 3"] => t.5 * 3 -- oops
t['[10]'] => t.[10] -- oops
Basically it only works if the string key would be a valid identifier.
Again, tables are indexed via [], and in most cases you need to use them:
t = {
-- [key] = value
[10] = "ten", -- number key, string value
["print function"] = print, -- string key, function value
["sub table"] = {}, -- string key, table value
[print] = 111, -- function key, number value
["foo"] = 123, -- string key, number value
}
Only if you're using a string key which would work as a valid identifier (no spaces, contains only word characters, numbers, or underlines, and doesn't begin with a number) can you use the shortcut syntax. For the table above, that would be only 'foo':
t = {
-- [key] = value
[10] = "ten", -- number key, string value
["print function"] = print, -- string key, function value
["sub table"] = {}, -- string key, table value
[print] = 111, -- function key, number value
foo = 123, -- string key, number value
}

Resources