space_object:create_index() | Tarantool
Документация на русском языке
поддерживается сообществом
Вложенный модуль box.space space_object:create_index()

space_object:create_index()

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

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

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

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

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

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

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

index_object

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

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

Building or rebuilding a large index will cause occasional yields so that other requests will not be blocked. If the other requests cause an illegal situation such as a duplicate key in a unique index, building or rebuilding such index will fail.

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

Имя Описание Тип Значение по умолчанию
type тип индекса string („HASH“ or „TREE“ or „BITSET“ or „RTREE“). Note that vinyl only supports „TREE“. „TREE“
id уникальный идентификатор number last index’s id + 1
unique индекс уникален boolean true
if_not_exists ошибки нет, если имя дублируется boolean false
parts field numbers + types {field_no, „unsigned“ or „string“ or „integer“ or „number“ or „double“ or „decimal“ or „boolean“ or „varbinary“ or „uuid“ or „array“ or „scalar“, and optional collation or is_nullable value or path} {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) affects TREE only. true makes an index work faster, false – index size is reduced by half boolean true

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

Note on storage engine: vinyl has extra options which by default are based on configuration parameters vinyl_bloom_fpr, vinyl_page_size, vinyl_range_size, vinyl_run_count_per_level, and vinyl_run_size_ratio – see the description of those parameters. The current values can be seen by selecting from 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
...

Примечание

Другой способ объявления оператора parts

Если раньше (до версии 2.7.1) индекс состоял из одной части, содержал дополнительные параметры, например is_nullable или collation, и был описан так:

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

(с одинарными фигурными скобками), то Tarantool игнорировал эти параметры.

Начиная с версии 2.7.1, при описании индекса можно не указывать дополнительные фигурные скобки, но допускаются оба варианта:

-- с дополнительными фигурными скобками
my_space:create_index('one_part_idx', {parts = {{1, 'unsigned', is_nullable=true}}})

-- без дополнительных фигурных скобок
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, поэтому в ней нет сортировки

индексы типа TREE, HASH или BITSET в memtx (начиная с версии 2.7.1);

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)
datetime (since 2.10.0) object returned from a function in the datetime module memtx and vinyl TREE indexes datetime.new{year = 2002}
uuid (с версии 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) или идентификаторы UUID. Если используется несколько типов, порядок ключей следующий: null, логические значения, числа, строки, байтовые массивы, идентификаторы UUID.

индексы типа 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 для всех индексов и формата спейса.

Начиная с версии 2.8.2, при описании индекса можно указать параметр exclude_null, чтобы индекс пропускал кортежи со значением null в этой части.

Значение этого параметра по умолчанию — false. Если включить exclude_null, то is_nullable=true будет указано автоматически. Для первичного ключа использовать этот параметр нельзя. Параметр можно изменять динамически, и в этом случае индекс будет перестроен.

Такие индексы вообще не хранят отфильтрованные кортежи, поэтому индексирование будет выполняться быстрее.

exclude_null и is_nullable взаимосвязаны, поэтому ниже приводится таблица допустимых сочетаний значений этих параметров:

exclude_null/is_nullable false true
false допускается допускается
true не допускается допускается

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'}}]

Note regarding storage engine: vinyl supports only the TREE index type, and vinyl secondary indexes must be created before tuples are inserted.

Строка в параметре пути может содержать символ [*], который называется заменителем индекса массива. Описанные так индексы используются для 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 не индексируется)
  • В спейсе с индексами по массивам можно хранить не более ~8000 элементов, проиндексированных таким образом.

Функциональные индексы — это индексы, которые вызывают пользовательскую функцию для формирования ключа индекса, в отличие от других типов индексов, где ключ формирует сам 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).

To create a multikey function, the options of box.schema.func.create() must include is_multikey = true. The return value must be a table of tuples. If a multikey function returns N tuples, then N keys will be added to the index.

Пример:

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,
                         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')
-- Both tuples will be returned.
Нашли ответ на свой вопрос?
Обратная связь