Индексы

В неймспейсах БД Reindexer предусмотрено 2 типа полей:

  • индексные,
  • кортежи неиндексных полей.

Каждый неймспейс может иметь до 256 одиночных индексов (1 системный псевдоиндекс tuple и 255 пользовательских индексов). Числов композитных индексов не ограничено.

Типы индексов в Reindexer

Типы индексов, предусмотренные в Reindexer, представлены в таблице:

Тип индекса Назначение Особенности
hash
Используется по умолчанию
Быстрый поиск по запросам EQ и SET Медленная и неэффективная сортировка результатов по полю
tree Быстрый поиск по запросам RANGE, GT, GE, LT и LE Быстрая сортировка результата по полю. По сравнению с типом hash работает немного медленнее для запросов EQ и SET
rtree Используется для выполнения запросов DWithin для определения расстояния между точками (Подробнее — в разделе «Геометрические типы данных») Поддерживается только геометрический (географический) тип данных point ([2] float64, reindexer.Point)
text Полнотекстовый поиск
ttl Для автоматического удаления документов/записей через заданное время Поле может содержать только данные с типом int64 для хранения времени в UNIX-формате
-
Колоночный индекс
Поиск полным перебором Не может обеспечить быстрый поиск по индексной структуре, однако при использовании компараторов работает намного эффективнее, чем поля без индексов. Характеризуется наименьшим потреблением памяти по сравнению с другими типами индексов

Также Reindexer поддерживает еще один тип индекса для полнотекстового поиска — fuzzytext. В этом случае используется полнотекстовый движок на триграммах. Функция экспериментальная. В большинстве случаев для организации полнотекстового поиска лучше использовать индексы типа text.

Допустимые типы значений индексных полей в Reindexer:

  • int,
  • int64,
  • double,
  • string,
  • bool,
  • composite,
  • point ([2]float64, reindexer.Point),
  • uuid.

Допустимые сочетания типов индексов и типов значений индексных полей в Reindexer

В Reindexer допускаются сочетания типов индексов и типов значений индексных полей согласно таблице (+ на пересечении означает допустимую комбинацию):

Тип индекса/тип значения индексного поля hash tree rtree text ttl -
int + + +
int64 + + + +
double + +
string + + + +
bool +
composite + + +
point +
uuid + +

Параметры индексных полей

Описание значений параметров индексных полей в Reindexer представлены в таблице:

Параметр Описание/значение Особенности
pk Поле — часть первичного индекса В каждом неймспейсе должно быть хотя бы одно поле с параметром pk
composite Создание композитного (составного) индекса. Перед созданием композитного индекса для всех полей из его состава требуется создать собственные индексы (любого типа, например -). Для полтонекстовых композитных индексов это условие не является обязательным, но позволит ускорить загрузку данных с диска и пересоздание индекса При взаимодействии с Reindexer через Go тип поля должен быть обозначен пустой структурой: struct{} (пример)
joined Joined-поле Поле с этим тэгом используется для хранения документов, полученных в результате join’a к текущему документу (SubitemType), и обращения к ним. При взаимодействии с Reindexer через Go поле должно иметь тип []*SubitemType (пример)
dense Уменьшение размера памяти, занимаемого индексным полем Параметр полезно использовать для индексных полей с высокой селективностью.
Экономия памяти составляет:
* для полей, помеченных индексом типа hash и tree — 8 байт для каждого уникального значения ключа,
* для полей, помеченных колоночным (-) индексом — 4-8 байт на каждый элемент.
Уменьшение объема памяти сопровождается снижением производительности. Для низкоселективных индексов типа hash и tree может существенно снизиться скорость обновления. Также из-за недостатков оптимизации кеша центрального процессора этот параметр замедляет поиск при полном сканировании в колоночных индексах -
sparse Строка (документ) содержит значение индекса Sparse только в том случае, если он задан специально - нет пустых (или по умолчанию) записей этого типа индексов в строке (документе) Позволяет экономить оперативную память, но в то же время может снизиться производительность по сравнению с использованием обычных индексов
collate_numeric Устанавливает режим сравнения строк как чисел (порядок при обычном сравнении строк: 1, 10, 2; порядок при сравнении строк как чисел: 1, 2, 10) Значение индексного поля должно быть задано в строковом формате
collate_ascii Создает нечувствительный к регистру строковый индекс в ASCII-кодировке Значение индексного поля должно быть задано в строковом формате
collate_utf8 Создает нечувствительный к регистру строковый индекс в кодировке UTF-8 Значение индексного поля должно быть задано в строковом формате
collate_custom=<ORDER> Пользовательский порядок сортировки (задается последовательностью символов <ORDER>). Сначала сортировка выполняется в соответствии с заданным правилом, затем — в обычном порядке (пример) Значение индексного поля должно быть задано в строковом формате
linear, quadratic, greene или rstar Задает алгоритм построения индекса типа rtree Значение по умолчанию — rstar
appendable Создает array-индекс из нескольких полей (в том числе из полей основной и вложенной структур) Параметр доступен только для Go-коннектора. appendable можно использовать для двух массивов: при поиске по индексу с этим параметром они будут рассматриваться как один массив

Добавление индексов

При добавлении некоторых типов индексов им можно задать несколько jsonpath. Массив jsonpath определяет, из каких JSON-полей документа будут взяты значения для индексирования. Если у индекса несколько jsonpath, то проиндексированы будут все указанные jsonpath, встретившиеся в документе. Возможность использования нескольких jsonpath предусмотрена для композитных и array-индексов. Если вам нужно создать одиночный индекс с несколькими jsonpath, используйте array-индекс ("is_array": true).

Ниже представлены примеры создания индексов при взаимодействии с Reindexer по HTTP и через Go:

Пример добавления индекса через AddIndex на Go:

db.AddIndex("items", reindexer.IndexDef{
		Name:      "id", // Имя индекса
		JSONPaths: []string{"id"}, // Имя индекса в JSON-структуре
		IndexType: "text", // Тип индекса 
		FieldType: "string", // Тип значения индексного поля
	})

Чтобы добавить индекс при объявлении структуры, нужно пометить индексное поле тегом reindex.

Тег reindex содержит имя индекса (name), его тип (type) и параметры/опции (opts):

reindex:"<name>[[,<type>],<opts>]"

Пример объявлении структуры тегами reindex через Go:

type Item struct {
	ID       int64  `reindex:"id,,pk"`   // Добавление индекса `id`, являющегося частью первичного ключа
	Name     string `reindex:"name"`     // Добавление индекса `name`
	Articles []int  `reindex:"articles"` // Добавление индекса `articles`
	Year     int    `reindex:"year,tree"` // Добавление индекса `year`. Параметр tree обеспечивает быстрый поиск по запросам RANGE, GT и LT
}

db := reindexer.NewReindex("builtin:///tmp/reindex/testdb") // Подключение к базе данных

db.OpenNamespace("items", reindexer.DefaultNamespaceOptions(), Item{}) // Создание нового неймспейса и добавление индексов

В теле запроса для добавления индекса по HTTP указывается его тип и параметры индексного поля.

curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/namespaces/items3/indexes' \
--header 'Content-Type: application/json' \
--data-raw '{
  "name": "id3",
  "json_paths": [
    "id3"
  ],
  "field_type": "string",
  "index_type": "hash",
  "expire_after": 0,
  "is_pk": false,
  "is_array": false,
  "is_dense": false,
  "is_sparse": false,
  "rtree_type": "linear",
  "is_simple_tag": false,
  "collate_mode": "none",
  "sort_order_letters": "",
  "config": {
    "enable_translit": true,
    "enable_numbers_search": false,
    "enable_warmup_on_ns_copy": false,
    "enable_kb_layout": true,
    "log_level": 0,
    "merge_limit": 20000,
    "extra_word_symbols": "-/+",
    "stop_words": [
      "string"
    ],
    "stemmers": [
      "en",
      "ru"
    ],
    "synonyms": [
      {
        "tokens": [
          "string"
        ],
        "alternatives": [
          "string"
        ]
      }
    ],
    "bm25_boost": 1,
    "bm25_weight": 0.1,
    "distance_boost": 1,
    "distance_weight": 0.5,
    "term_len_boost": 1,
    "term_len_weight": 0.3,
    "position_boost": 1,
    "position_weight": 0.1,
    "full_match_boost": 1.1,
    "partial_match_decrease": 15,
    "min_relevancy": 0.05,
    "max_typos": 2,
    "max_typo_len": 15,
    "max_rebuild_steps": 50,
    "max_step_size": 4000,
    "sum_ranks_by_fields_ratio": 0,
    "fields": [
      {
        "field_name": "string",
        "bm25_boost": 1,
        "bm25_weight": 0.1,
        "term_len_boost": 1,
        "term_len_weight": 0.3,
        "position_boost": 1,
        "position_weight": 0.1
      }
    ]
  }
}'

Изменение и удаление индексов

Изменение индекса

Изменение индекса происходит в два этапа: его удаление (Drop) и добавление в неймспейс с новыми значениями (Add). Поэтому при изменении индекса следует заполнять IndexDef целиком а не его отдельные поля. В противном случае опции индексного поля будут установлены в дефолтные значения.

Ниже представлены примеры создания неймспейсов при взаимодействии с базой данных через Go и по HTTP.

db.UpdateIndex("items", reindexer.IndexDef{
		Name:      "year",                // Поиск индекса по имени
		JSONPaths: []string{"Date_year"}, // Изменение имени индекса в JSON-структуре
		IndexType: "tree",                // Изменение типа индекса
		FieldType: "int",                 // Изменение типа значения индексного поля
	})
curl --location --request PUT 'http://127.0.0.1:9088/api/v1/db/testdb/namespaces/items3/indexes' \
--header 'Content-Type: text/plain' \
--data-raw '{
  "name": "id",
  "json_paths": [
    "New_id"
  ],
  "field_type": "int64",
  "index_type": "tree",
  "expire_after": 0,
  "is_pk": false,
  "is_array": false,
  "is_dense": false,
  "is_sparse": false,
  "rtree_type": "linear",
  "is_simple_tag": false,
  "collate_mode": "none",
  "sort_order_letters": "",
  "config": {
    "enable_translit": true,
    "enable_numbers_search": false,
    "enable_warmup_on_ns_copy": false,
    "enable_kb_layout": true,
    "log_level": 0,
    "merge_limit": 20000,
    "extra_word_symbols": "-/+",
    "stop_words": [
      "string"
    ],
    "stemmers": [
      "en",
      "ru"
    ],
    "synonyms": [
      {
        "tokens": [
          "string"
        ],
        "alternatives": [
          "string"
        ]
      }
    ],
    "bm25_boost": 1,
    "bm25_weight": 0.1,
    "distance_boost": 1,
    "distance_weight": 0.5,
    "term_len_boost": 1,
    "term_len_weight": 0.3,
    "position_boost": 1,
    "position_weight": 0.1,
    "full_match_boost": 1.1,
    "partial_match_decrease": 15,
    "min_relevancy": 0.05,
    "max_typos": 2,
    "max_typo_len": 15,
    "max_rebuild_steps": 50,
    "max_step_size": 4000,
    "sum_ranks_by_fields_ratio": 0,
    "fields": [
      {
        "field_name": "string",
        "bm25_boost": 1,
        "bm25_weight": 0.1,
        "term_len_boost": 1,
        "term_len_weight": 0.3,
        "position_boost": 1,
        "position_weight": 0.1
      }
    ]
  }
}'

Удаление индекса

Примеры команд для удаления индекса по HTTP и через Go:

db.DropIndex("items", "articles")
curl --location --request DELETE 'http://127.0.0.1:9088/api/v1/db/dbtest/namespaces/items/indexes/year'

Получение информации об индексах, доступных для неймспейса

Ниже представлен пример команды для получения списка индексов, доступных для неймспейса, по HTTP:

curl -X GET --header 'Accept: application/json' 'http://127.0.0.1:9088/api/v1/db/dbtest/namespaces/items/indexes'

Вложенные структуры

По умолчанию Reindexer сканирует все вложенные структуры и добавляет их поля (а также указатели) в неймспейс.

Пример:


type Actor struct {
	Name string `reindex:"actor_name"`
}

type BaseItem struct {
	ID int64 `reindex:"id,hash,pk"`
}

type ComplexItem struct {
	BaseItem         // Индексные поля BaseItem будут добавлены в текущую структуру
	Actor    []Actor // Индексные поля Actor будут добавлены в текущую структуру в виде массива
	Name     string  `reindex:"name"`      // Добавление индекса `name` 
	Year     int     `reindex:"year,tree"` // Добавление индекса `year`, обеспечивающего быстрый поиск по запросам RANGE, GT и LT
	parent   *Item   `reindex:"-"`         // Индексные поля parent не будут добавлены в структуру
}

Примеры объявления структур с индексными полями с указанием параметров на Go

Ниже представлены примеры объявления структур с индексными полями разных типов с указанием параметров на Go.

Пример 1

type Item struct {
	ID     int64 `reindex:"id,,pk"` // Добавление индекса `id`, являющегося частью первичного ключа (о чем говорит параметр `pk`)
	Rating int   `reindex:"rating"` // Добавление индекса `rating`
	Year   int   `reindex:"year"`   // Добавление индекса `year`

	_ struct{} `reindex:"rating+year,tree,composite"` // Композитный индекс с сортировкой по полям  `rating` и `year`
}

Пример 2

type Actor struct {
	ID        int    `reindex:"id"`           // Добавление индекса `id`
	Name      string `reindex:"name,tree"`    // Добавление tree-индекса `name`
	IsVisible bool   `reindex:"is_visible,-"` // Добавление колоночного индекса `is_visible`
	Metainfo int     `json:"-"`               // Поле "MetaInfo" не будет сохранено в reindexer

}

type ItemWithJoin struct {
	ID          int      `reindex:"id,tree,pk"`     // Добавление tree-индекса `id`, являющегося первичным ключом (о чем говорит параметр `pk`)
	Name        string   `reindex:"name"`           // Добавление индекса `name`
	ActorsIDs   []int    `reindex:"actors_ids"`     // Добавление индекса `actors_ids`
	ActorsNames []int    `reindex:"actors_names"`   // Добавление индекса `actors_names`
	Actors      []*Actor `reindex:"actors,,joined"` // Joined-поле с индексом `actors`
}

Пример 3

type SortModeCustomItem struct {
	ID      int    `reindex:"id,,pk"`                                    // Добавление hash-индекса `id`, являющегося первичным ключом
	InsItem string `reindex:"item_custom,hash,collate_custom=a-zA-Z0-9"` // Индекс типа `hash`, обеспечивающий быстрый поиск по запросам `EQ` и `SET`. Задан пользовательский порядок сортировки.
}