space_object:create_index()
На этой странице:
- Описание create_index()
- Описание типов индексируемых полей
- Использование null для индексируемого ключа
- Создание индекса с использованием имен полей вместо номеров полей
- Создание индекса с использованием пути для полей с ассоциативными массивами (индексы по пути JSON)
- Creating a multikey index using the path option with [*]
- Создание функционального индекса
-
object
space_object
¶ -
space_object:
create_index
(index-name[, options])¶ Создание индекса.
Обязательным условием является создание индекса для спейса до вставки в спейс кортежей или выборки кортежей из него. Первый созданный индекс, который будет использоваться в качестве первичного индекса, должен быть уникальным.
Параметры: - space_object (
space_object
) – ссылка на объект - index_name (
string
) – имя индекса, которое должно соответствовать правилам именования объектов - options (
table
) – см. «Параметры для space_object:create_index()» ниже
возвращает: объект индекса
тип возвращаемого значения: Возможные ошибки:
- слишком много частей;
- индекс „…“ уже существует;
- первичный ключ должен быть уникальным.
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 halfboolean 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}})
- space_object (
-
В зависимости от допустимых типов индексов и допустимых значений используются различные типы индексируемых полей.
Тип поля для индексирования | Чем может быть | Где использовать | Примеры |
---|---|---|---|
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-путей.
- Функция должна получать доступ к значениям частей ключа по индексу, а не по имени поля.
- Функциональные индексы не должны быть по первичному ключу.
- Нельзя изменить ни функциональные индексы, ни функцию, если она используется для индекса, то есть единственный способ изменить их — это удалить индекс и создать его заново.
- Только функции, запущенные из песочницы, могут использоваться в функциональных индексах.
Пример:
Функция может создать ключ, используя только первую букву строкового поля.
Создайте спейс. В спейсе должно быть поле с первичным ключом, которое не будет полем для функционального индекса:
box.schema.space.create('tester', {engine = 'memtx'}) box.space.tester:create_index('i',{parts={{field = 1, type = 'string'}}})
Создайте функцию. Функция принимает кортеж. В этом примере она работает на кортеже tuple[2], поскольку источник ключа — поле номер 2, в которое мы вставляем данные. Используйте
string.sub()
из модуляstring
, чтобы получить первый символ:lua_code = [[function(tuple) return {string.sub(tuple[2],1,1)} end]]
Сделайте функцию персистентной с помощью
box.schema.create
: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'}}));
Результаты двух запросов
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.