Top.Mail.Ru
space_object:create_index() | Tarantool
 
Вложенный модуль box.space / space_object:create_index()
Вложенный модуль box.space / space_object:create_index()

space_object:create_index()

space_object:create_index()

На этой странице:

object space_object
space_object:create_index(index-name[, options])

Создание индекса.

Обязательным условием является создание индекса для спейса до вставки в спейс кортежей или выборки кортежей из него. Первый созданный индекс, который будет использоваться в качестве первичного индекса, должен быть уникальным.

Параметры:
возвращает:

объект индекса

тип возвращаемого значения:
 

index_object

Возможные ошибки:

  • слишком много частей;
  • индекс „…“ уже существует;
  • первичный ключ должен быть уникальным.

Сборка или пересборка большого индекса будет периодически вызывать передачу управления, чтобы не блокировать другие запросы. А если другой запрос приведет к ошибке, например повторяющийся ключ в уникальном индексе, сборка или пересборка такого индекса не выполнится.

Параметры для space_object:create_index()

Имя Описание Тип Значение по умолчанию
type тип индекса string („HASH“, или „TREE“, или „BITSET“, или „RTREE“) Примечание про движок базы данных: vinyl поддерживает только „TREE“ „TREE“
id уникальный идентификатор number идентификатор последнего индекса +1
unique индекс уникален boolean true
if_not_exists ошибки нет, если имя дублируется boolean false
parts номера поля + типы {field_no, 'unsigned', или 'string', или 'integer', или 'number', или 'double', или 'decimal', или 'boolean', или 'varbinary', или 'uuid', или 'array', или 'scalar', и дополнительная сортировка, или значение is_nullable, или путь} {1, 'unsigned'}
dimension только для RTREE number 2
distance только для RTREE строка („euclid“, то есть Евклидова метрика; или „manhattan“, то есть расстояние городских кварталов) „euclid“
bloom_fpr только для vinyl number vinyl_bloom_fpr
page_size только для vinyl number vinyl_page_size
range_size только для vinyl number vinyl_range_size
run_count_per_level только для vinyl number vinyl_run_count_per_level
run_size_ratio только для vinyl number vinyl_run_size_ratio
sequence см. раздел об указании последовательности для create_index() строка или число отсутствует
func функциональный индекс string отсутствует
hint (с версии 2.6.1) только для TREE. true заставит индекс работать быстрее, false — размер индекса будет уменьшен наполовину boolean true

Параметры из вышеприведенной таблицы также могут использоваться в index_object:alter().

Примечание про движок базы данных: в vinyl есть дополнительные параметры, которые по умолчанию основаны на конфигурационных параметрах vinyl_bloom_fpr, vinyl_page_size, vinyl_range_size, vinyl_run_count_per_level и vinyl_run_size_ratio, см. описание этих параметров. Текущие значения можно увидеть, сделав выборку из box.space._index.

Пример:

tarantool> my_space = box.schema.space.create('tester')
---
...
tarantool> my_space:create_index('primary', {unique = true, parts = {
         > {field = 1, type = 'unsigned'},
         > {field = 2, type = 'string'}
         > }})
---
- unique: true
  parts:
  - type: unsigned
    is_nullable: false
    fieldno: 1
  - type: string
    is_nullable: false
    fieldno: 2
  id: 0
  space_id: 512
  type: TREE
  name: primary
...

Примечание

Alternative way to declare index parts

Before version 2.7.1, if an index consisted of a single part and had some options like is_nullable or collation and its definition was written as

my_space:create_index('one_part_idx', {parts = {1, 'unsigned', is_nullable=true}})

(with the only brackets) then options were ignored by Tarantool.

Since version 2.7.1 it is allowed to omit extra braces in an index definition and use both ways:

-- with extra braces
my_space:create_index('one_part_idx', {parts = {{1, 'unsigned', is_nullable=true}}})

-- without extra braces
my_space:create_index('one_part_idx', {parts = {1, 'unsigned', is_nullable=true}})

В зависимости от допустимых типов индексов и допустимых значений используются различные типы индексируемых полей.

Тип поля для индексирования Чем может быть Где использовать Примеры
unsigned беззнаковые целые числа от 0 до 18 446 744 073 709 551 615, то есть примерно 18 квинтиллионов. Также может называться „uint“ или „num“, но „num“ объявлен устаревшим

индексы типа TREE или HASH в memtx;

TREE-индексы в vinyl

123456
string любая последовательность октетов до максимальной длины. Также может называться „str“. В строке может быть сортировка

индексы типа TREE, HASH или BITSET в memtx;

TREE-индексы в vinyl

„A B C“

„\65 \66 \67“

varbinary любая последовательность октетов до максимальной длины. Последовательность байтов varbinary не содержит символы UTF-8, поэтому в ней нет сортировки

memtx TREE, HASH or BITSET (since version 2.7.1) indexes;

TREE-индексы в vinyl

„\65 \66 \67“
integer целые числа от -9 223 372 036 854 775 808 до 18 446 744 073 709 551 615. Может также называться „int“

индексы типа TREE или HASH в memtx;

TREE-индексы в vinyl

-2^63
number целые числа от -9 223 372 036 854 775 808 до 18 446 744 073 709 551 615, числа одинарной точности с плавающей запятой, числа двойной точности с плавающей запятой или точные числа

индексы типа TREE или HASH в memtx;

TREE-индексы в vinyl

1.234

-44

1.447e+44

double числа двойной точности с плавающей запятой

индексы типа TREE или HASH в memtx;

TREE-индексы в vinyl

1.234
boolean true или false

индексы типа TREE или HASH в memtx;

TREE-индексы в vinyl

false
decimal точное число, которая возвращает функция из модуля decimal

индексы типа TREE или HASH в memtx;

TREE-индексы в vinyl

decimal.new(1.2)
uuid (since 2.4.1) 128-разрядная последовательность шестнадцатеричных чисел нижнего регистра, которая представляет собой Универсальный уникальный идентификатор (UUID)

индексы типа TREE или HASH в memtx;

TREE-индексы в vinyl

uuid.fromstr(„64d22e4d-ac92-4a23-899a-e59f34af5479“)
array массив чисел индексы типа RTREE в memtx

{10, 11}

{3, 5, 9, 10}

scalar null (вводится с помощью msgpack.NULL, yaml.NULL или json.NULL), логические значения (true или false), целые числа от -9 223 372 036 854 775 808 до 18 446 744 073 709 551 615, числа одинарной точности с плавающей запятой, числа двойной точности с плавающей запятой, точные числа, строки или байтовые массивы (varbinary). При использовании нескольких типов, порядок ключей должен быть следующим: null, логические значения, затем числа, затем строки, затем байтовые массивы

индексы типа TREE или HASH в memtx;

TREE-индексы в vinyl

null

true

-1

1.234

„“

„ру“

nil Кроме того, допускается использовать nil с любым типом индексируемого поля, если указать is_nullable=true    

Если тип индекса — TREE, а индекс не является первичным, то в оператор parts={...} можно включить is_nullable=true или is_nullable=false (по умолчанию).

Если is_nullable=true, то можно вставить nil или его эквивалент, например msgpack.NULL. Если завершающие поля допускают null, в них можно вообще ничего не вставлять. В индексах такие значения null всегда считают равными другим значениям null и они всегда меньше, чем отличные от null значения. Null может встречаться несколько раз даже в уникальном индексе. Пример:

box.space.tester:create_index('I', {unique = true, parts = {{field = 2, type = 'number', is_nullable = true}}})

Предупреждение

Можно создать несколько индексов для одного и того же поля с различными значениями is_nullable или вызвать space_object:format() со значением is_nullable, отличным от значения, которое используется для индекса. При наличии несоответствий правило такое: запрещается использовать null, кроме случаев, когда is_nullable=true для всех индексов и формата спейса.

Since version 2.8.2 an index part definition may include option exclude_null, which allows an index to skip tuples with null at this part.

By default, the option is set to false. When exclude_null is turned on, the is_nullable=true option will be set automatically. It can’t be used for the primary key. This option can be changed dynamically: in this case the index is rebuilt.

Such indexes do not store filtered tuples at all, so indexing can be done faster.

exclude_null and is_nullable are connected, so this table describes the result of combining them:

exclude_null/is_nullable false true
false ok ok
true not allowed ok

create_index() может использовать имена полей и / или номера полей, которые описаны в необязательном операторе space_object:format().

В следующем примере мы покажем использование format() для спейса с двумя столбцами „x“ и „y“, а затем — пять вариантов использования оператора parts={} в create_index() сначала для столбца „x“, затем для столбцов „x“ и „y“. Эти варианты включают в себя пропуск типа, использование чисел и дополнительных скобок.

box.space.tester:format({{name = 'x', type = 'scalar'}, {name = 'y', type = 'integer'}})

box.space.tester:create_index('I2', {parts = {{'x', 'scalar'}}})
box.space.tester:create_index('I3', {parts = {{'x', 'scalar'}, {'y', 'integer'}}})

box.space.tester:create_index('I4', {parts = {{1, 'scalar'}}})
box.space.tester:create_index('I5', {parts = {{1, 'scalar'}, {2, 'integer'}}})

box.space.tester:create_index('I6', {parts = {1}})
box.space.tester:create_index('I7', {parts = {1, 2}})

box.space.tester:create_index('I8', {parts = {'x'}})
box.space.tester:create_index('I9', {parts = {'x', 'y'}})

box.space.tester:create_index('I10', {parts = {{'x'}}})
box.space.tester:create_index('I11', {parts = {{'x'}, {'y'}}})

Чтобы создать индекс для поля, которое представляет собой ассоциативный массив (строка с путем и скалярное значение), укажите строку c путем во время создания индекса:

parts = {field-number, 'data-type', path = 'path-name'}

Тип индекса должен быть TREE или HASH, а содержимое поля — всегда ассоциативный массив с одним и тем же путем.

Пример 1 — Простое использование пути:

tarantool> box.schema.space.create('T')
tarantool> box.space.T:create_index('I',{parts = {{field = 1, type = 'scalar', path = 'age'}}})
tarantool> box.space.T:insert({{age = 44}})
tarantool> box.space.T:select(44)
---
- [{'age': 44}]

Пример 2 — для большей наглядности используем path вместе с format() и JSON-синтаксисом:

tarantool> my_space = box.schema.space.create('T')
tarantool> format = {{'id', 'unsigned'}, {'data', 'map'}}
tarantool> my_space:format(format)
tarantool> parts = {{'data.FIO["firstname"]', 'str'}, {'data.FIO["surname"]', 'str'}}
tarantool> my_index = my_space:create_index('info', {parts = parts})
tarantool> my_space:insert({1, {FIO = {firstname = 'Ahmed', surname = 'Xi'}}})
---
- [1, {'FIO': {'surname': 'Xi', 'firstname': 'Ahmed'}}]

Примечание про движок базы данных: vinyl поддерживает только TREE-индексы, и следует создать в vinyl’е вторичные индексы до вставки кортежей.

Строка в параметре пути может содержать символ [*], который называется заменителем индекса массива. Описанные так индексы используются для JSON-документов, у которых одинаковая структура.

Например, при создании индекса по полю №2 для документа со строками, который будет начинаться с {'data': [{'name': '...'}, {'name': '...'}], раздел parts в запросе на создание индекса будет выглядеть так:

parts = {{field = 2, type = 'str', path = 'data[*].name'}}

Тогда кортежи с именами можно быстро получить с помощью index_object:select({key-value}).

На самом деле в одном поле может быть несколько ключей, как в примере ниже, который выводит один и тот же кортеж дважды, потому что оба ключа „A“ и „B“ соответствуют запросу:

my_space = box.schema.space.create('json_documents')
my_space:create_index('primary')
multikey_index = my_space:create_index('multikey', {parts = {{field = 2, type = 'str', path = 'data[*].name'}}})
my_space:insert({1,
         {data = {{name = 'A'},
                  {name = 'B'}},
          extra_field = 1}})
multikey_index:select({''}, {iterator = 'GE'})

Результат выборки будет выглядеть так:

tarantool> multikey_index:select({''},{iterator='GE'})
---
- - [1, {'data': [{'name': 'A'}, {'name': 'B'}], 'extra_field': 1}]
- [1, {'data': [{'name': 'A'}, {'name': 'B'}], 'extra_field': 1}]
...

Есть некоторые ограничения:

  • символ [*] должен стоять отдельно или в конце имени в пути
  • символ [*] не должен повторяться в пути
  • если в индексе есть путь с x[*], то в никаком другом индексе не может быть пути с x.компонентом
  • [*] нельзя указывать в пути первичного ключа
  • если индекс должен быть уникальным (unique=true), и в нем есть путь с символом [*], то запрещается использовать дублирующиеся ключи в разных кортежах, но в в одном кортеже можно использовать дублирующиеся ключи
  • структура значения поля должна соответствовать стуктуре, заданной в определении пути, или значение поля должно быть nil (nil не индексируется)

Функциональные индексы — это индексы, которые вызывают пользовательскую функцию для формирования ключа индекса, в отличие от других типов индексов, где ключ формирует сам Tarantool. Функциональные индексы используют для сжатия, усечения или реверсирования или любого другого изменения индекса по желанию пользователя.

Ниже приведены рекомендации по созданию функциональных индексов:

  • Функция должна принимать кортеж с содержимым полей на момент запроса на изменение данных и возвращать кортеж с содержимым, которое будет фактически помещено в индекс.
  • Описание create_index должно включать в себя определение всех частей ключа, а пользовательская функция должна возвращать таблицу, которая содержит то же количество частей ключа с теми же типами.
  • Спейс должен быть на движке memtx.
  • Функция должна быть персистентной и детерминированной (см. Создание функции с телом).
  • Части ключа не должны зависеть от JSON-путей.
  • Функция должна получать доступ к значениям частей ключа по индексу, а не по имени поля.
  • Функциональные индексы не должны быть по первичному ключу.
  • Нельзя изменить ни функциональные индексы, ни функцию, если она используется для индекса, то есть единственный способ изменить их — это удалить индекс и создать его заново.
  • Только функции, запущенные из песочницы, могут использоваться в функциональных индексах.

Пример:

Функция может создать ключ, используя только первую букву строкового поля.

  1. Создайте спейс. В спейсе должно быть поле с первичным ключом, которое не будет полем для функционального индекса:

    box.schema.space.create('tester', {engine = 'memtx'})
    box.space.tester:create_index('i',{parts={{field = 1, type = 'string'}}})
    
  2. Создайте функцию. Функция принимает кортеж. В этом примере она работает на кортеже tuple[2], поскольку источник ключа — поле номер 2, в которое мы вставляем данные. Используйте string.sub() из модуля string, чтобы получить первый символ:

    lua_code = [[function(tuple) return {string.sub(tuple[2],1,1)} end]]
    
  3. Сделайте функцию персистентной с помощью box.schema.create:

    box.schema.func.create('my_func',
        {body = lua_code, is_deterministic = true, is_sandboxed = true})
    
  4. Создайте функциональный индекс. Укажите поля, значения которых будут передаваться в функцию. Укажите функцию:

    box.space.tester:create_index('func_idx',{parts={{field = 1, type = 'string'}},func = 'my_func'})
    
  5. Проверьте, как это работает. Вставьте несколько кортежей. Сделайте выборку, используя только первую букву, — сработает, потому что это и есть ключ. Или выберите, используя ту же функцию, что была использована для вставки:

    box.space.tester:insert({'a', 'wombat'})
    box.space.tester:insert({'b', 'rabbit'})
    box.space.tester.index.func_idx:select('w')
    box.space.tester.index.func_idx:select(box.func.my_func:call({{'tester', 'wombat'}}));
    

    Результаты двух запросов select будут выглядеть так:

    tarantool> box.space.tester.index.func_idx:select('w')
    ---
    - - ['a', 'wombat']
    ...
    tarantool> box.space.tester.index.func_idx:select(box.func.my_func:call({{'tester','wombat'}}));
    ---
    - - ['a', 'wombat']
    ...
    

Вот пример кода полностью:

box.schema.space.create('tester', {engine = 'memtx'})
box.space.tester:create_index('i',{parts={{field = 1, type = 'string'}}})
lua_code = [[function(tuple) return {string.sub(tuple[2],1,1)} end]]
box.schema.func.create('my_func',
    {body = lua_code, is_deterministic = true, is_sandboxed = true})
box.space.tester:create_index('func_idx',{parts={{field = 1, type = 'string'}},func = 'my_func'})
box.space.tester:insert({'a', 'wombat'})
box.space.tester:insert({'b', 'rabbit'})
box.space.tester.index.func_idx:select('w')
box.space.tester.index.func_idx:select(box.func.my_func:call({{'tester', 'wombat'}}));

Функции для функциональных индексов могут возвращать множество ключей. Такие функции называют «мультиключевыми» (multikey).

В таком случае в box.func.create надо включить opts = {is_multikey = true}. Возвращается таблица с кортежами. Если мультиключевая функция возвращает N кортежей, то в индекс добавятся N ключей.

Пример:

s = box.schema.space.create('withdata')
s:format({{name = 'name', type = 'string'},
          {name = 'address', type = 'string'}})
pk = s:create_index('name', {parts = {{field = 1, type = 'string'}}})
lua_code = [[function(tuple)
               local address = string.split(tuple[2])
               local ret = {}
               for _, v in pairs(address) do
                 table.insert(ret, {utf8.upper(v)})
               end
               return ret
             end]]
box.schema.func.create('address',
                        {body = lua_code,
                         is_deterministic = true,
                         is_sandboxed = true,
                         opts = {is_multikey = true}})
idx = s:create_index('addr', {unique = false,
                              func = 'address',
                              parts = {{field = 1, type = 'string',
                                      collation = 'unicode_ci'}}})
s:insert({"James", "SIS Building Lambeth London UK"})
s:insert({"Sherlock", "221B Baker St Marylebone London NW1 6XE UK"})
idx:select('Uk')
-- Вернутся оба кортежа.