Версия:

Вложенный модуль box.index

Вложенный модуль box.index

Общие сведения

Вложенный модуль box.index обеспечивает доступ к схемам индекса и ключам индекса в режиме только для чтения. Индексы хранятся в массиве box.space.имя-спейса.index в каждом спейсе. Они предоставляют API для упорядоченной итерации по кортежам. Этот API представляет собой прямую привязку к соответствующим методам объектов типа``box.index`` в движке базы данных.

Индекс

Ниже приведен перечень всех функций и элементов модуля box.index.

Имя Использование
index_object.unique Флаг, если индекс уникальный – true
index_object.type Тип индекса
index_object.parts Массив полей с ключами индекса
index_object:pairs() Подготовка к итерации
index_object:select() Выбор одного или более кортежей по индексу
index_object:get() Выбор кортежа по индексу
index_object:min() Поиск минимального значения в индексе
index_object:max() Поиск максимального значения в индексе
index_object:random() Поиск случайного значения в индексе
index_object:count() Подсчет кортежей с совпадающим значением ключа
index_object:update() Обновление кортежа
index_object:delete() Удаление кортежа по ключу
index_object:alter() Изменение индекса
index_object:drop() Удаление индекса
index_object:rename() Переименование индекса
index_object:bsize() Подсчет байтов для индекса
index_object:stat() Get statistics for an index
index_object:compact() Remove unused index space
index_object:user_defined() Any function / method that any user wants to add
object index_object
index_object.unique

Если индекс уникальный – true, если индекс не уникален – false.

тип возвращаемого значения:
 boolean (логический)
index_object.type

Тип индекса: „TREE“ или „HASH“ или „BITSET“ или „RTREE“.

index_object.parts

Массив, описывающий поля индекса. Чтобы узнать больше о типах полей индекса, обращайтесь к этой таблице.

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

Пример:

tarantool> box.space.tester.index.primary
            ---
            - unique: true
              parts:
              - type: unsigned
                is_nullable: false
                fieldno: 1
              id: 0
              space_id: 513
              name: primary
              type: TREE
            ...
index_object:pairs([key[, iterator-type]])

Поиск кортежа или набора кортежей по заданному индексу и итерация по одному кортежу за раз.

Параметр key (ключ) задает, что именно должно совпадать в индексе.

Примечание

key используется в поиске только первого совпадения. Не стоит ожидать, что все подобранные кортежи будут содержать этот ключ.

Параметр iterator (итератор) задает правило для совпадений и упорядочивания. Различные типы индексов поддерживают различные итераторы. Например, TREE-индекс поддерживает строгий порядок ключей и может вернуть все кортежи в порядке по возрастанию или по убыванию, начиная с указанного ключа. Однако другие типы индексов не поддерживают упорядочивание.

Чтобы понять логику возврата кортежей с помощью итератора, важно знать принципы работы подсистемы обработки транзакций в Tarantool’е. В итераторе Tarantool’а нет собственного постоянного вида просмотра. Наоборот, каждая процедура получает эксклюзивный доступ ко всем кортежам и спейсам до тех пор, пока не «переключится контекст», что может произойти по причине неявной передачи управления или в результате явного вызова функции fiber.yield. Когда поток выполнения возвращается к процедуре, передавшей управление, набор данных может уже значительно измениться. Итерация возобновляется после стадии передачи управления и не сохраняет вид просмотра, а продолжает работу с новым содержимым базы данных. В практическом задании «Индексированный поиск по шаблонам» демонстрируется один из способов одновременного использования итераторов и передачи управления.

Параметры:
  • index_object (index_object) – ссылка на объект.
  • key (scalar/table) – значение должно совпасть с индексным ключом, который может быть составным
  • iterator – как определено в таблицах ниже. По умолчанию используется итератор „EQ“
возвращается:

итератор, который может использовать в цикле for/end или с функцией totable()

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

  • спейс отсутствует; неправильный тип;
  • выбранный тип итерации не поддерживается для данного типа индекса;
  • ключ не поддерживается для данного типа итерации.

Факторы сложности Размер индекса, тип индекса; количество кортежей, к которым получен доступ.

Значение искомого ключа может представлять собой число (например, 1234), строку (например, 'abcd') или таблицу из чисел и строк (например, {1234, 'abcd'}). Каждая часть ключа будет сопоставляться с каждой частью ключа в индексе.

Найденные кортежи будут упорядочены по значению ключа в индексе или по хешу значения ключа, если тип индекса – „hash“. Если индекс не уникален, то дубликаты будут упорядочены во вторую очередь по первичному значению ключа. Порядок будет обратным, если тип итератора – „LT“, „LE“ или „REQ“.

Типы итераторов для TREE-индексов

Type Аргументы Описание
box.index.EQ или „EQ“ искомое значение Оператором сравнения будет „==“ (равный). Если ключ индекса равен искомому значению, получим совпадение. Найденные кортежи упорядочены по возрастанию по ключу индекса. Этот тип используется по умолчанию.
box.index.REQ или „REQ“ искомое значение Совпадения находятся таким же образом, что и для box.index.EQ. Найденные кортежи упорядочены по убыванию по ключу индекса.
box.index.GT или „GT“ искомое значение Оператором сравнения будет „>“ (больше чем). Если ключ индекса больше, чем искомое значение, получим совпадение. Найденные кортежи упорядочены по возрастанию по ключу индекса.
box.index.GE или „GE“ искомое значение Оператором сравнения будет „>=“ (больше или равен). Если ключ индекса больше искомого значения или равен ему, получим совпадение. Найденные кортежи упорядочены по возрастанию по ключу индекса.
box.index.ALL или „ALL“ искомое значение Как для box.index.GE.
box.index.LT или „LT“ искомое значение Оператором сравнения будет „<“ (меньше чем). Если ключ индекса меньше искомого значения, получим совпадение. Найденные кортежи упорядочены по убыванию по ключу индекса.
box.index.LE или „LE“ искомое значение Оператором сравнения будет „<=“ (меньше или равен). Если ключ индекса меньше искомого значения или равен ему, получим совпадение. Найденные кортежи упорядочены по убыванию по ключу индекса.

Неофициально можно сказать, что поиск с помощью TREE-индексов пользователи обычно считают интуитивно понятным при условии, что нет нулевых значений и отсутствующих частей. Формально же логика заключается в следующем. Ключ поиска состоит из нуля или более частей, например, {}, {1,2,3},{1,nil,3}. Ключ индекса состоит из одной или более частей, например, {1}, {1,2,3},{1,2,3}. Ключ поиска может содержать нулевое значение nil (но не msgpack.NULL, этот тип не будет правильным). Ключ индекса не может содержать nil или msgpack.NULL, хотя в последующих версиях правила работы Tarantool’а будут другие – поведение поиска с nil может измениться. Возможные итераторы: LT, LE, EQ, REQ, GE, GT. Считается, что ключ поиска соответствует ключу индекса, если следующие операторы, которые представляют собой псевдокод для операции сопоставления, возвращают TRUE.

If (number-of-search-key-parts > number-of-index-key-parts) return ERROR
If (number-of-search-key-parts == 0) return TRUE
for (i = 1; ; ++i)
{
  if (i > number-of-search-key-parts) OR (search-key-part[i] is nil)
  {
    if (iterator is LT or GT) return FALSE
    return TRUE
  }
  if (type of search-key-part[i] is not compatible with type of index-key-part[i])
  {
    return ERROR
  }
  if (search-key-part[i] == index-key-part[i])
  {
    if (iterator is LT or GT) return FALSE
    continue
  }
  if (search-key-part[i] > index-key-part[i])
  {
    if (iterator is EQ or REQ or LE or LT) return FALSE
    return TRUE
  }
  if (search-key-part[i] < index-key-part[i])
  {
    if (iterator is EQ or REQ or GE or GT) return FALSE
    return TRUE
  }
}

Типы итераторов для HASH-индексов

Type Аргументы Описание
box.index.ALL нет Все ключи индекса являются совпадениями. Найденные кортежи упорядочены по возрастанию по хешу ключа индекса, который будет выглядеть случайным.
box.index.EQ или „EQ“ искомое значение Оператором сравнения будет „==“ (равный). Если ключ индекса равен искомому значению, получим совпадение. Количество найденных кортежей будет 0 или 1. Этот тип используется по умолчанию.
box.index.GT или „GT“ искомое значение Оператором сравнения будет „>“ (больше чем). Если хеш ключа индекса больше, чем хеш искомого значения, получим совпадение. Найденные кортежи упорядочены по возрастанию по хешу ключа индекса, который будет выглядеть случайным. При условии, что спейс не обновляется, можно получить все кортежи в спейсе, N кортежей за раз, используя {iterator=“GT“, limit=N} в каждом поиске и последнее найденное значение из предыдущего результата поиска в качестве начального значения для следующего поиска.

Типы итераторов для BITSET-индексов

Type Аргументы Описание
box.index.ALL или „ALL“ нет Все ключи индекса являются совпадениями. Найденные кортежи упорядочены по положению в спейсе.
box.index.EQ или „EQ“ значение bitset (битовое множество) Если ключ индекса равен искомому значению, получим совпадение. Найденные кортежи упорядочены по положению в спейсе. Этот тип используется по умолчанию.
box.index.BITS_ALL_SET значение bitset (битовое множество) Если все биты, которые равны 1 в битовом множестве, также равны 1 в ключе индекса, получим совпадение. Найденные кортежи упорядочены по положению в спейсе.
box.index.BITS_ANY_SET значение bitset (битовое множество) Если один из битов, которые равны 1 в битовом множестве, также равен 1 в ключе индекса, получим совпадение. Найденные кортежи упорядочены по положению в спейсе.
box.index.BITS_ALL_NOT_SET значение bitset (битовое множество) Если все биты, которые равны 1 в битовом множестве, равны 0 в ключе индекса, получим совпадение. Найденные кортежи упорядочены по положению в спейсе.

Типы итераторов для RTREE-индексов

Type Аргументы Описание
box.index.ALL или „ALL“ нет Все ключи являются совпадениями. Найденные кортежи упорядочены по положению в спейсе.
box.index.EQ или „EQ“ искомое значение Если все точки прямоугольника-или-параллелепипеда, определенные искомым значением, совпадают с точками прямоугольника-или-параллелепипеда, определенного ключом индекса, получим совпадение. Найденные кортежи упорядочены по положению в спейсе. «Прямоугольник-или-параллелепипед» означает «прямоугольник-или-параллелепипед, как описано в разделе о RTREE». Этот тип используется по умолчанию.
box.index.GT или „GT“ искомое значение Если все точки прямоугольника-или-параллелепипеда, определенные искомым значением, находятся в пределах прямоугольника-или-параллелепипеда, определенного ключом индекса, получим совпадение. Найденные кортежи упорядочены по положению в спейсе.
box.index.GE или „GE“ искомое значение Если все точки прямоугольника-или-параллелепипеда, определенные искомым значением, находятся в пределах прямоугольника-или-параллелепипеда, определенного ключом индекса, или рядом с ним, получим совпадение. Найденные кортежи упорядочены по положению в спейсе.
box.index.LT или „LT“ искомое значение Если все точки прямоугольника-или-параллелепипеда, определенные ключом индекса, находятся в пределах прямоугольника-или-параллелепипеда, определенного искомым значением, получим совпадение. Найденные кортежи упорядочены по положению в спейсе.
box.index.LE или „LE“ искомое значение Если все точки прямоугольника-или-параллелепипеда, определенные ключом индекса, находятся в пределах прямоугольника-или-параллелепипеда, определенного искомым значением, или рядом с ним, получим совпадение. Найденные кортежи упорядочены по положению в спейсе.
box.index.OVERLAPS или „OVERLAPS“ искомые значения Если некоторые точки прямоугольника-или-параллелепипеда, определенные искомым значением, находятся в пределах прямоугольника-или-параллелепипеда, определенного ключом индекса, получим совпадение. Найденные кортежи упорядочены по положению в спейсе.
box.index.NEIGHBOR или „NEIGHBOR“ искомое значение Если некоторые точки прямоугольника-или-параллелепипеда, определенные ключом, находятся в пределах, определенных ключом индекса, или рядом с ним, получим совпадение. Найденные кортежи упорядочены следующим образом: сначала ближайший сосед.

Первый пример pairs():

„TREE“-индекс, используемый по умолчанию, и функция pairs():

tarantool> s = box.schema.space.create('space17')
 ---
 ...
 tarantool> s:create_index('primary', {
          >   parts = {1, 'string', 2, 'string'}
          > })
 ---
 ...
 tarantool> s:insert{'C', 'C'}
 ---
 - ['C', 'C']
 ...
 tarantool> s:insert{'B', 'A'}
 ---
 - ['B', 'A']
 ...
 tarantool> s:insert{'C', '!'}
 ---
 - ['C', '!']
 ...
 tarantool> s:insert{'A', 'C'}
 ---
 - ['A', 'C']
 ...
 tarantool> function example()
          >   for _, tuple in
          >     s.index.primary:pairs(nil, {
          >         iterator = box.index.ALL}) do
          >       print(tuple)
          >   end
          > end
 ---
 ...
 tarantool> example()
 ['A', 'C']
 ['B', 'A']
 ['C', '!']
 ['C', 'C']
 ---
 ...
 tarantool> s:drop()
 ---
 ...

Второй пример pairs():

Данный код на Lua найдет все кортежи, значения первичного ключа в которых начинаются с „XY“. Рабочие предположения заключаются в следующем: есть однокомпонентный первичный TREE-индекс по первому полю, которое должно представлять собой строку. Цикл с итератором обеспечивает поиск кортежей, в которых первое значение больше или равно „XY“. Условный оператор в цикле служит для того, чтобы цикл останавливался, если первые две буквы не „XY“.

for _, tuple in
 box.space.t.index.primary:pairs("XY",{iterator = "GE"}) do
   if (string.sub(tuple[1], 1, 2) ~= "XY") then break end
   print(tuple)
 end

Третий пример pairs():

Данный код на Lua найдет все кортежи, значения первичного ключа которых равны или больше 1000 и меньше или равны 1999 (такой тип запроса иногда называют поиском по диапазону или поиском в заданных пределах). Рабочие предположения заключаются в следующем: есть однокомпонентный первичный TREE-индекс по первому полю, которое должно представлять собой число. Цикл с итератором обеспечивает поиск кортежей, в которых первое значение больше или равно 1000. Условный оператор в цикле служит для того, чтобы цикл останавливался, если первое значение больше 1999.

for _, tuple in
 box.space.t2.index.primary:pairs(1000,{iterator = "GE"}) do
   if (tuple[1] > 1999) then break end
   print(tuple)
 end
index_object:select(search-key, options)

Это может быть альтернативой для функции box.space…select(), которая проходит по определенному индексу и может использовать дополнительные параметры, которые определяют тип итератора и пределы (то есть максимальное количество возвращаемых кортежей) и смещение (то есть с какого кортежа в списке начинать).

Параметры:
  • index_object (index_object) – ссылка на объект.
  • key (scalar/table) – значения для сопоставления с ключом индекса
  • options (table/nil) – ни один, любой или все следующие параметры
  • options.iterator – тип итератора
  • options.limit (number) – максимальное количество кортежей
  • options.offset (number) – номер начального кортежа
возвращается:

кортеж или кортежи, которые совпадают со значениями поля.

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

массив кортежей

Пример:

-- Создать спейс под названием tester.
 tarantool> sp = box.schema.space.create('tester')
 -- Создать уникальный индекс 'primary'
 -- который не будет нужен для данного примера..
 tarantool> sp:create_index('primary', {parts = {1, 'unsigned' }})
 -- Создать неуникальный индекс 'secondary'
 -- по второму полю.
 tarantool> sp:create_index('secondary', {
          >   type = 'tree',
          >   unique = false,
          >   parts = {2, 'string'}
          > })
 -- Вставить три кортежа, значения в поле2 field[2]
 -- равны 'X', 'Y' и 'Z'.
 tarantool> sp:insert{1, 'X', 'Row with field[2]=X'}
 tarantool> sp:insert{2, 'Y', 'Row with field[2]=Y'}
 tarantool> sp:insert{3, 'Z', 'Row with field[2]=Z'}
 -- Выбрать все кортежи, где вторичные ключи
 -- больше, чем 'X'.`
 tarantool> sp.index.secondary:select({'X'}, {
          >   iterator = 'GT',
          >   limit = 1000
          > })

Результатом будет следующая таблица кортежа:

---
     - - [2, 'Y', 'Row with field[2]=Y']
       - [3, 'Z', 'Row with field[2]=Z']
     ...

Примечание

Параметр index.имя-индекса необязателен. Если он пропущен, то подразумевается первый индекс (первичный ключ). Таким образом, для примера выше, box.space.tester:select({1}, {iterator = 'GT'}) вернет две одинаковых строки по первичному индексу „primary“.

Примечание

Параметр типа итератора iterator = тип-итератора необязателен. Если он пропущен, то подразумевается, что iterator = 'EQ'.

Примечание

Параметр field-value [, значение поля ] необязателен. Если он пропущен, то каждый ключ в индексе будет считаться совпадением независимо от типа итератора. Таким образом, для примера выше, box.space.tester:select{} выберет каждый кортеж в спейсе tester по первому индексу (первичный ключ).

Примечание

box.space.имя-спейса.index.имя-индекса:select(...)[1]` можно заменить box.space.имя-спейса.index.имя-индекса:get(...). А именно, get можно использовать в качестве удобного сокращения для получения первого кортежа в наборе кортежей, который был бы выведен по запросу select. Однако, если в наборе кортежей больше одного кортежа, get вернет ошибку.

Пример с индексом BITSET:

Следующий скрипт показывает создание BITSET-индекса и поиск по нему. Обратите внимание, что битовое множество BITSET не может быть уникальным, поэтому сначала создается первичный индекс. Обратите внимание, что битовые значения вводятся как шестнадцатеричные литералы для удобства чтения.

tarantool> s = box.schema.space.create('space_with_bitset')
 tarantool> s:create_index('primary_index', {
          >   parts = {1, 'string'},
          >   unique = true,
          >   type = 'TREE'
          > })
 tarantool> s:create_index('bitset_index', {
          >   parts = {2, 'unsigned'},
          >   unique = false,
          >   type = 'BITSET'
          > })
 tarantool> s:insert{'Tuple with bit value = 01', 0x01}
 tarantool> s:insert{'Tuple with bit value = 10', 0x02}
 tarantool> s:insert{'Tuple with bit value = 11', 0x03}
 tarantool> s.index.bitset_index:select(0x02, {
          >   iterator = box.index.EQ
          > })
 ---
 - - ['Tuple with bit value = 10', 2]
 ...
 tarantool> s.index.bitset_index:select(0x02, {
          >   iterator = box.index.BITS_ANY_SET
          > })
 ---
 - - ['Tuple with bit value = 10', 2]
   - ['Tuple with bit value = 11', 3]
 ...
 tarantool> s.index.bitset_index:select(0x02, {
          >   iterator = box.index.BITS_ALL_SET
          > })
 ---
 - - ['Tuple with bit value = 10', 2]
   - ['Tuple with bit value = 11', 3]
 ...
 tarantool> s.index.bitset_index:select(0x02, {
          >   iterator = box.index.BITS_ALL_NOT_SET
          > })
 ---
 - - ['Tuple with bit value = 01', 1]
 ...
index_object:get(key)

Поиск кортежа по заданному индексу, как описано выше.

Параметры:
  • index_object (index_object) – ссылка на объект.
  • key (scalar/table) – значения для сопоставления с ключом индекса
возвращается:

кортеж, в котором поля ключа в индексе равны переданным значениям ключа.

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

кортеж

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

  • отсутствие такого индекса;
  • неправильный тип;
  • больше одного кортежа подходят.

Факторы сложности: Размер индекса, тип индекса. См. также space_object:get().

Пример:

tarantool> box.space.tester.index.primary:get(2)
     ---
     - [2, 'Music']
     ...
index_object:min([key])

Поиск минимального значения в указанном индексе.

Параметры:
  • index_object (index_object) – ссылка на объект.
  • key (scalar/table) – значения для сопоставления с ключом индекса
возвращается:

the tuple for the first key in the index. If optional key value is supplied, returns the first key which is greater than or equal to key value. In a future version of Tarantool, index:min(key value) will return nothing if key value is not equal to a value in the index.

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

кортеж

Возможные ошибки: тип индекса не „TREE“.

Факторы сложности: Размер индекса, тип индекса.

Пример:

tarantool> box.space.tester.index.primary:min()
     ---
     - ['Alpha!', 55, 'This is the first tuple!']
     ...
index_object:max([key])

Поиск максимального значения в указанном индексе.

Параметры:
  • index_object (index_object) – ссылка на объект.
  • key (scalar/table) – значения для сопоставления с ключом индекса
возвращается:

the tuple for the last key in the index. If optional key value is supplied, returns the last key which is less than or equal to key value. In a future version of Tarantool, index:max(key value) will return nothing if key value is not equal to a value in the index.

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

кортеж

Возможные ошибки: тип индекса не „TREE“.

Факторы сложности: Размер индекса, тип индекса.

Пример:

tarantool> box.space.tester.index.primary:max()
     ---
     - ['Gamma!', 55, 'This is the third tuple!']
     ...
index_object:random(seed)

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

Параметры:
  • index_object (index_object) – ссылка на объект.
  • seed (number) – произвольное неотрицательное целое число
возвращается:

кортеж для случайного ключа в индексе.

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

кортеж

Факторы сложности: Размер индекса, тип индекса.

Примечание про движок базы данных: vinyl не поддерживает random().

Пример:

tarantool> box.space.tester.index.secondary:random(1)
     ---
     - ['Beta!', 66, 'This is the second tuple!']
     ...
index_object:count([key][, iterator])

Итерация по индексу с подсчетом количества кортежей, которые соответствуют паре ключ-значение.

Параметры:
  • index_object (index_object) – ссылка на объект.
  • key (scalar/table) – значения для сопоставления с ключом индекса
  • iterator – метод сопоставления
возвращается:

количество совпадающих ключей индекса.

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

число

Пример:

tarantool> box.space.tester.index.primary:count(999)
 ---
 - 0
 ...
 tarantool> box.space.tester.index.primary:count('Alpha!', { iterator = 'LE' })
 ---
 - 1
 ...
index_object:update(key, {{operator, field_no, value}, ...})

Обновление кортежа.

То же, что и box.space…update(), но поиск ключа происходит в этом индексе, вместо первичного. Данный индекс должен быть уникальным.

Параметры:
  • index_object (index_object) – ссылка на объект.
  • key (scalar/table) – значения для сопоставления с ключом индекса
  • operator (string) – тип операции, представленный строкой
  • field_no (number) – к какому полю применяется операция. Номер поля может быть отрицательным, что означает, что позиция рассчитывается с конца кортежа. (#кортеж + отрицательный номер поля + 1)
  • value (lua_value) – какое значение применяется
возвращается:

обновленный кортеж.

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

кортеж

index_object:delete(key)

Удаление кортежа по ключу.

То же, что и box.space…delete(), но поиск ключа происходит в этом индексе, вместо первичного. Данный индекс должен быть уникальным.

Параметры:
  • index_object (index_object) – ссылка на объект.
  • key (scalar/table) – значения для сопоставления с ключом индекса
возвращается:

удаленный кортеж.

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

кортеж

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

index_object:alter({options})

Alter an index. It is legal in some circumstances to change an index’s parts and/or change the type and the is_nullable flag for a part. However, this usually causes rebuilding of the space, except for the simple case where the is_nullable flag is changed from false to true.

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

nil

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

  • индекс не существует,
  • the first index cannot be changed to {unique = false}.

Note re storage engine: vinyl supports alter() for non-empty spaces. Primary index definition cannot be altered.

Пример:

tarantool> box.space.space55.index.primary:alter({type = 'HASH'})
     ---
     ...

     tarantool> box.space.vinyl_space.index.i:alter({page_size=4096})
     ---
     ...
index_object:drop()

Удаление индекса. Побочный эффект удаления первичного индекса – все кортежи удалятся.

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

nil.

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

  • индекс не существует,
  • первичный индекс невозможно удалить, если существует вторичный индекс.

Пример:

tarantool> box.space.space55.index.primary:drop()
     ---
     ...
index_object:rename(index-name)

Переименование индекса.

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

nil

Возможные ошибки: index_object не существует.

Пример:

tarantool> box.space.space55.index.primary:rename('secondary')
     ---
     ...

Факторы сложности: Размер индекса, тип индекса, количество кортежей, к которым получен доступ.

index_object:bsize()

Возврат общего количества байтов, занятых индексом.

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

количество байтов

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

число

index_object:stat()

Return statistics about actions taken that affect the index, including details such as a count of cache evictions, number of accesses, and latency. This is for use with the vinyl engine.

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

statistics

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

таблица

index_object:compact()

Remove unused index space. For the memtx storage engine this method does nothing; index_object:compact() is only for the vinyl storage engine. For example, with vinyl, if a tuple is deleted, the space is not immediately reclaimed. There is a scheduler for reclaiming space automatically based on the timeout configuration parameter, so calling index_object:compact() manually is not always necessary.

возвращается:nil (Tarantool returns without waiting for compaction to complete)
index_object:user_defined()

Users can define any functions they want, and associate them with indexes: in effect they can make their own index methods. They do this by:

  1. creating a Lua function,
  2. adding the function name to a predefined global variable which has type = table, and
  3. invoking the function any time thereafter, as long as the server is up, by saying index_object:function-name([parameters]).

There are three predefined global variables:

  • Adding to box_schema.index_mt makes the method available for all indexes.
  • Adding to box_schema.memtx_index_mt makes the method available for all memtx indexes.
  • Adding to box_schema.vinyl_index_mt makes the method available for all vinyl indexes.

Alternatively, user-defined methods can be made available for only one index, by calling getmetatable(index_object) and then adding the function name to the meta table.

Параметры:

Пример:

-- Visible to any index of a memtx space, no parameters.
-- After these requests, the value of global_variable will be 6.
box.schema.space.create('t', {engine='memtx'})
box.space.t:create_index('i')
global_variable = 5
function f() global_variable = global_variable + 1 end
box.schema.memtx_index_mt.counter = f
box.space.t.index.i:counter()

Пример:

-- Visible to index box.space.t.index.i only, 1 parameter.
-- After these requests, the value of X will be 1005.
box.schema.space.create('t', {engine='memtx', id = 1000})
box.space.t:create_index('i')
X = 0
i = box.space.t.index.i
function f(i_arg, param) X = X + param + i_arg.space_id end
box.schema.memtx_index_mt.counter = f
meta = getmetatable(i)
meta.counter = f
i:counter(5)

Пример использования функций box

Данный пример сработает на конфигурации из песочницы, описанной в предисловии, то есть создан спейс под названием tester с первичным числовым ключом. Функция в примере выполнит следующие действия:

  • выбрать кортеж, значение ключа в котором равно 1000;
  • вернуть ошибку, если такой кортеж уже существует и содержит 3 поля;
  • вставить или заменить кортеж следующими данными:
    • поле [1] = 1000
    • поле [2] = UUID
    • поле [3] = количество секунд с 01.01.1970;
  • получить поле [3] из того, что заменили;
  • преобразовать значение из поля [3] в формат yyyy-mm-dd hh:mm:ss.ffff (год-месяц-день час:минута:секунда.десятитысячные доли секунды);
  • вернуть преобразованное значение.

Данная функция использует функции box в Tarantool’е: box.space…select, box.space…replace, fiber.time, uuid.str. Данная функция использует Lua-функции os.date() и string.sub().

function example()
   local a, b, c, table_of_selected_tuples, d
   local replaced_tuple, time_field
   local formatted_time_field
   local fiber = require('fiber')
   table_of_selected_tuples = box.space.tester:select{1000}
   if table_of_selected_tuples ~= nil then
     if table_of_selected_tuples[1] ~= nil then
       if #table_of_selected_tuples[1] == 3 then
         box.error({code=1, reason='This tuple already has 3 fields'})
       end
     end
   end
   replaced_tuple = box.space.tester:replace
     {1000,  require('uuid').str(), tostring(fiber.time())}
   time_field = tonumber(replaced_tuple[3])
   formatted_time_field = os.date("%Y-%m-%d %H:%M:%S", time_field)
   c = time_field % 1
   d = string.sub(c, 3, 6)
   formatted_time_field = formatted_time_field .. '.' .. d
   return formatted_time_field
 end

… А вот что происходит, когда вызывается функция:

tarantool> box.space.tester:delete(1000)
 ---
 - [1000, '264ee2da03634f24972be76c43808254', '1391037015.6809']
 ...
 tarantool> example(1000)
 ---
 - 2014-01-29 16:11:51.1582
 ...
 tarantool> example(1000)
 ---
 - error: 'This tuple already has 3 fields'
 ...

Пример с заданным пользователем итератором

Здесь приведен пример того, как создать свой собственный итератор. Функция paged_iter представляет собой «функцию с итератором», что поймут только разработчики, которые ознакомились с разделом руководства по Lua Итераторы и замыкания. Она делает постраничную выборку, то есть возвращает 10 кортежей одновременно из таблицы под названием «t», первичный ключ которой определен с помощью create_index('primary',{parts={1,'string'}}).

function paged_iter(search_key, tuples_per_page)
   local iterator_string = "GE"
   return function ()
   local page = box.space.t.index[0]:select(search_key,
     {iterator = iterator_string, limit=tuples_per_page})
   if #page == 0 then return nil end
   search_key = page[#page][1]
   iterator_string = "GT"
   return page
   end
 end

Programmers who use paged_iter do not need to know why it works, they only need to know that, if they call it within a loop, they will get 10 tuples at a time until there are no more tuples.

In this example the tuples are merely printed, a page at a time. But it should be simple to change the functionality, for example by yielding after each retrieval, or by breaking when the tuples fail to match some additional criteria.

for page in paged_iter("X", 10) do
   print("New Page. Number Of Tuples = " .. #page)
   for i = 1, #page, 1 do
     print(page[i])
   end
 end

Вложенный модуль box.index с типом индекса RTREE для поиска в пространственных данных

Вложенный модуль box.index может использоваться для поиска в пространственных данных, если тип индекса – RTREE. Существуют операции для поиска прямоугольников (геометрические фигуры с 4 углами и 4 сторонами) и параллелепипедов (геометрические фигуры с количеством углов более 4 и количеством сторон более 4, которые иногда называются гиперпрямоугольниками). В данном руководстве используется термин прямоугольник-или-параллелепипед для всего класса объектов, который включает в себя прямоугольники и параллелепипеды. Примерами иллюстрируются только прямоугольники.

Прямоугольники описаны в соответствии с координатами по оси X (горизонтальной оси) и оси Y (вертикальной оси) на сетке произвольного размера. Ниже представлен рисунок четырех прямоугольников на сетке с 11 горизонтальными точками и 11 вертикальными точками:

      X AXIS
           1   2   3   4   5   6   7   8   9   10  11
        1
        2  #-------+                                           <-Прямоугольник №1
Y AXIS  3  |       |
        4  +-------#
        5          #-----------------------+                   <-Прямоугольник №2
        6          |                       |
        7          |   #---+               |                   <-Прямоугольник №3
        8          |   |   |               |
        9          |   +---#               |
        10         +-----------------------#
        11                                     #               <-Прямоугольник №4

Прямоугольники определяются в соответствии со следующей схемой: {верхняя левая координата по оси X, верхняя левая координата по оси Y, нижняя правая координата по оси X, нижняя правая координата по оси Y} – или коротко: {x1,y1,x2,y2}. Таким образом, на рисунке … Прямоугольник № 1 начинается в точке 1 по оси X и точке 2 по оси Y, а заканчивается в точке 3 по оси X и точке 4 по оси Y, поэтому его координаты будут следующие: {1,2,3,4}. Координаты Прямоугольника № 2: {3,5,9,10}. Координаты Прямоугольника № 3: {4,7,5,9}. И наконец, координаты Прямоугольника № 4: {10,11,10,11}. Прямоугольник № 4, на самом деле, является точкой, поскольку у него нулевая ширина и нулевая высота, так что его можно описать всего двумя числами: {10,11}.

Некоторые отношения между прямоугольниками могут быть описаны так: «Прямоугольник №1 является ближайшим соседом Прямоугольника №2», а «Прямоугольник №3 полностью находится внутри Прямоугольника №2».

Сейчас создадим спейс и добавим RTREE-индекс.

tarantool> s = box.schema.space.create('rectangles')
     tarantool> i = s:create_index('primary', {
              >   type = 'HASH',
              >   parts = {1, 'unsigned'}
              > })
     tarantool> r = s:create_index('rtree', {
              >   type = 'RTREE',
              >   unique = false,
              >   parts = {2, 'ARRAY'}
              > })

Поле №1 не имеет значения, мы создаем его лишь потому, что необходим первичный индекс. (RTREE-индексы не могут быть уникальными, поэтому не могут быть первичными индексами.) Второе поле должно быть массивом («array»), что означает, что его значения должны представлять собой точки {x,y} или прямоугольники {x1,y1,x2,y2}. Заполним таблицу, вставив два кортежа с координатами Прямоугольника №2 и Прямоугольника №4.

tarantool> s:insert{1, {3, 5, 9, 10}}
     tarantool> s:insert{2, {10, 11}}

Затем, после описания типов RTREE-итераторов (RTREE iterator types), можно произвести поиск прямоугольников с помощью данных запросов:

tarantool> r:select({10, 11, 10, 11}, {iterator = 'EQ'})
 ---
 - - [2, [10, 11]]
 ...
 tarantool> r:select({4, 7, 5, 9}, {iterator = 'GT'})
 ---
 - - [1, [3, 5, 9, 10]]
 ...
 tarantool> r:select({1, 2, 3, 4}, {iterator = 'NEIGHBOR'})
 ---
 - - [1, [3, 5, 9, 10]]
   - [2, [10, 11]]
 ...

Запрос №1 возвращает 1 кортеж, потому что точка {10,11} представляет собой то же, что и прямоугольник {10,11,10,11} («Прямоугольник №4» на рисунке). Запрос № 2 возвращает 1 кортеж, потому что прямоугольник {4,7,5,9}, который был «Прямоугольником №3» на рисунке находится полностью внутри {3,5,9,10}, что представляет собой Прямоугольник № 2. Запрос № 3 возвращает 2 кортежа, потому что итератор NEIGHBOR (сосед) всегда возвращает все кортежи, а первым найденным кортежем будет {3,5,9,10} («Прямоугольник №2» на рисунке), потому что он является ближайшим соседом {1,2,3,4} («Прямоугольник №1» на рисунке).

Теперь создадим спейс и индекс для кубоидов, которые представляют собой прямоугольники-или-параллелепипеды, у которых 6 углов и 6 сторон.

tarantool> s = box.schema.space.create('R')
     tarantool> i = s:create_index('primary', {parts = {1, 'unsigned'}})
     tarantool> r = s:create_index('S', {
              >   type = 'RTREE',
              >   unique = false,
              >   dimension = 3,
              >   parts = {2, 'ARRAY'}
              > })

Здесь задается дополнительный параметр``dimension=3``. По умолчанию, измерений 2, поэтому не было необходимости указывать данный параметр в примерах для прямоугольника. Максимальное количество измерений – 20. Что касается вставки и выборки, здесь будет 6 координат. Например:

tarantool> s:insert{1, {0, 3, 0, 3, 0, 3}}
     tarantool> r:select({1, 2, 1, 2, 1, 2}, {iterator = box.index.GT})

Теперь создадим спейс и индекс для пространственных объектов с метрикой расстояния городских кварталов (метрика Манхэттена), которые представляют собой прямоугольники-или-параллелепипеды; соседи для них рассчитываются иным образом.

tarantool> s = box.schema.space.create('R')
     tarantool> i = s:create_index('primary', {parts = {1, 'unsigned'}})
     tarantool> r = s:create_index('S', {
              >   type = 'RTREE',
              >   unique = false,
              >   distance = 'manhattan',
              >   parts = {2, 'ARRAY'}
              > })

Здесь задается дополнительный параметр distance='manhattan'. По умолчанию, расстояние измеряется по Евклидовой метрике, что лучше всего подходит для измерений по прямой линии. Другой способ расчета расстояния по метрике Манхэттена („manhattan“), который больше подходит, если необходимо следовать линиям сетки, а не по прямой.

tarantool> s:insert{1, {0, 3, 0, 3}}
     tarantool> r:select({1, 2, 1, 2}, {iterator = box.index.NEIGHBOR})

Другие примеры поиска в пространственных данных см. по ссылке R tree index quick start and usage.