Индексы
Каждый неймспейс может иметь до 256 одиночных индексов (1 системный псевдоиндекс tuple и 255 пользовательских индексов). Число композитных и sparse индексов не ограничено.
Типы индексов в 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-формате |
-Колоночный индекс |
Поиск полным перебором | Не может обеспечить быстрый поиск по индексной структуре, однако при использовании компараторов работает намного эффективнее, чем поля без индексов. Характеризуется наименьшим потреблением памяти по сравнению с другими типами индексов |
float_vector |
Векторные индексы |
Также Reindexer поддерживает еще один тип индекса для полнотекстового поиска — fuzzytext. В этом случае используется полнотекстовый движок на триграммах. Функция экспериментальная. В большинстве случаев для организации полнотекстового поиска лучше использовать индексы типа text.
Допустимые типы значений индексных полей в Reindexer:
int,int64,double,string,bool,composite,point([2]float64,reindexer.Point),uuid,float_vector.
Допустимые сочетания типов индексов и типов значений индексных полей в Reindexer
В Reindexer допускаются сочетания типов индексов и типов значений индексных полей согласно таблице (+ на пересечении означает допустимую комбинацию):
| Тип индекса/тип значения индексного поля | hash |
tree |
rtree |
text |
ttl |
- |
hnsw |
ivf |
brute-force |
|---|---|---|---|---|---|---|---|---|---|
int |
+ | + | + | ||||||
int64 |
+ | + | + | + | |||||
double |
+ | + | |||||||
string |
+ | + | + | + | |||||
bool |
+ | ||||||||
composite |
+ | + | + | ||||||
point |
+ | ||||||||
uuid |
+ | + | |||||||
float_vector(float32) |
+ | + | + |
Параметры индексных полей
Описание значений параметров индексных полей в Reindexer представлены в таблице:
| Параметр | Описание/значение | Особенности |
|---|---|---|
pk |
Поле — часть первичного индекса | В каждом неймспейсе должно быть хотя бы одно поле с параметром pk |
composite |
Создание композитного (составного) индекса. Перед созданием композитного индекса для всех полей из его состава требуется создать собственные индексы (любого типа, например -). Для полтонекстовых композитных индексов это условие не является обязательным, но позволит ускорить загрузку данных с диска и пересоздание индекса |
При взаимодействии с Reindexer через Go тип поля должен быть обозначен пустой структурой: struct{} (пример) |
joined |
Joined-поле | Поле с этим тегом используется для хранения документов, полученных в результате join’a к текущему документу (SubitemType), и обращения к ним. При взаимодействии с Reindexer через Go поле должно иметь тип []*SubitemType (пример) |
dense |
Уменьшение размера памяти, занимаемого индексным полем | Параметр полезно использовать для индексных полей с высокой селективностью. Экономия памяти составляет: * для полей, помеченных индексом типа hash и tree, ~8-32 байта для каждого уникального значения ключа (за счёт отсутствия оверхда на хранение дополнительного btree). Использование этой опции для низкоселективных (с большим количеством повторяющихся ключей) индексов типа hash и tree может существенно снизиться скорость обновления |
is_no_column |
Отключение колоночного саб-индекса | Если установлен в true, то отключает использование колоночного саб-индекса, позволяя сэкономить память (от 4 до 16 байт на каждый докумен в зависимости от типа поля). По умолчанию колоночный саб-индекс используется для скалярных hash, tree и - индексов (если они не sparse) для более эффективной реализации компараторов. Обычно колоночный саб-индекс не требуется для 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-индекс из нескольких полей (в том числе из полей основной и вложенной структур). Позволяет сгенерировать индекс с несколькими jsonpath |
Параметр доступен только для Go-коннектора. appendable можно использовать для двух массивов: при поиске по индексу с этим параметром они будут рассматриваться как один массив |
Добавление индексов
При добавлении некоторых типов индексов им можно задать несколько
jsonpath. Массивjsonpathопределяет, из каких JSON-полей документа будут взяты значения для индексирования. Если у индекса несколькоjsonpath, то проиндексированы будут все указанныеjsonpath, встретившиеся в документе. Возможность использования несколькихjsonpathпредусмотрена для композитных и array-индексов. Если вам нужно создать одиночный индекс с несколькимиjsonpath, используйте array-индекс ("is_array": true).
Ниже представлены примеры создания индексов:
Пример добавления индекса через AddIndex:
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:
Поля структуры, которые требуется положить в БД (а также joined-поля), должны быть публичными (экспортированными из модуля). Иначе модуль байндинга не сможет к ним обращаться. Исключением являются поля для композитных индексов - они могут быть приватными.
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{}) // Создание нового неймспейса и добавление индексов
db = RxConnector(
"cproto://127.0.0.1:6534/testdb",
enable_compression=True,
fetch_amount=500
)
index_definitions = [
{
"name": "id",
"json_paths": ["id"],
"field_type": "int",
'is_pk': True,
},
{
"name": "name",
"json_paths": ["name"],
"field_type": "string",
},
{
"name": "articles",
"json_paths": ["articles"],
"field_type": "int",
"is_array": True,
},
{
"name": "year",
"json_paths": ["year"],
"field_type": "int",
"index_type": "tree",
},
]
db.namespace_open("items")
for index in index_definitions:
db.index_add("items", index)
import java.util.List;
import ru.rt.restream.reindexer.NamespaceOptions;
import ru.rt.restream.reindexer.annotations.Reindex;
import static ru.rt.restream.reindexer.IndexType.TREE;
// Объявление структуры с индексными полями
public static class Item {
// Поле 'id' — первичный ключ
// Если для индекcа не указан тип, type = HASH
@Reindex(name = "id", isPrimaryKey = true)
private Integer id;
// Текстовое индексное поле.
@Reindex(name = "name")
private String name;
// Массив 'articles' — множество записей (документов, статей) в составе индекса
@Reindex(name = "articles")
private List<Integer> articles;
// Добавление сортируемого индекса по полю 'year'
@Reindex(name = "year", type = TREE)
private Integer year;
public Item() {
}
public Item(Integer id, String name, List<Integer> articles, Integer year) {
this.id = id;
this.name = name;
this.articles = articles;
this.year = year;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Integer> getArticles() {
return articles;
}
public void setArticles(List<Integer> articles) {
this.articles = articles;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
@Override
public String toString() {
return "Item{" +
"id=" + id +
", name='" + name + '\'' +
", articles=" + articles +
", year=" + year +
'}';
}
}
// Подключение к базе данных
Reindexer db = ReindexerConfiguration.builder()
.url("builtin:///tmp/reindex/testdb")
.getReindexer();
// Создание нового неймспейса
Namespace<Item> itemNamespace = db.openNamespace(
"items",
NamespaceOptions.defaultOptions(),
Item.class
);
В теле запроса для добавления индекса по HTTP указывается его тип и параметры индексного поля.
curl --location --request POST 'http://127.0.0.1:9088/api/v1/db/testdb/namespaces/items/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"
]
}
],
"terms_boost": [
{
"terms": [
"string"
],
"boost": 1
}
],
"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 целиком, а не его отдельные поля. В противном случае опции индексного поля будут установлены в дефолтные значения.
Ниже представлены примеры создания неймспейсов:
db.UpdateIndex("items", reindexer.IndexDef{
Name: "year", // Поиск индекса по имени
JSONPaths: []string{"Date_year"}, // Изменение имени индекса в JSON-структуре
IndexType: "tree", // Изменение типа индекса
FieldType: "int", // Изменение типа значения индексного поля
})
db.index_update(
"items",
{
"name": "year",
"json_paths": ["Date_year"],
"index_type": "tree",
"field_type": "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"
]
}
],
"terms_boost": [
{
"terms": [
"string"
],
"boost": 1
}
],
"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
}
]
}
}'
Удаление индекса
Примеры команд для удаления индекса:
db.DropIndex("items", "articles")
db.index_drop("items", "articles")
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 сканирует все вложенные структуры и указатели на них и добавляет объявленные в них индексы в неймспейс (см. комментарии в примерах ниже).
Примеры:
При сканировании есть ряд исключений:
- приватные (неэкспортируемые) вложенные поля пропускаются — ни их содержимое, ни объявленные в них индексы не будут добавлены в БД;
- вложенные поля, отмеченные тегом
reindex:"-"попадут в БД, но объявленные внутри них индексы будут проигнорированы; - вложенные поля, отмеченные тегом
json:"-"не попадут в БД, а объявленные внутри них индексы будут проигнорированы.
type Actor struct {
Name string `reindex:"actor_name"`
Age int `reindex:"age"`
}
type BaseItem struct {
ID int64 `reindex:"id,hash,pk"`
}
type ComplexItem struct {
BaseItem // Индексные поля из BaseItem будут добавлены в текущую структуру
Actor []Actor // Индексные поля из Actor ("name" and "age") будут добавлены в текущую структуру в виде отдельных индексов-массивов
Name string `reindex:"name"` // Hash-индекс для поля "name"
Year int `reindex:"year,tree"` // Tree-индекс для поля "year"
Value int `reindex:"value,-"` // Колоночный индекс для поля "value"
Metainfo int `json:"-"` // Содержимое поля MetaInfo НЕ будет сохраняться в reindexer
Parent *Item `reindex:"-"` // Индексные поля из Parent НЕ будут добавлены в текущую структуру. Содержимое полей Parent попадёт в reindexer в виде неиндексируемых данных
ParentHidden *Item `json:"-"` // Индексные поля и любые данные из ParentHidden НЕ попадут в reindexer
privateParent *Item // Индексные поля и любые данные из ParentHidden НЕ попадут в reindexer (поведение аналогично тегу `json:"-"`)
AnotherActor Actor `reindex:"actor"` // Индексные поля AnotherActor будут добавлены в текущую структуру с префиском "actor." (в этом конкретном примере это приведёт к добавлению двух индексов: "actor.actor_name" и "actor.age")
}
Добавления индексов в Python имеет следующие особенности:
- каждому индексу соответствует словарь, который содержит все его параметры;
- индекс имеет произвольное имя
name, которое не привязано к полям записи, и путьjson_paths, который должен соответствовать полю записи. При этом.означает вложенность поля, например:actor.name— это ключnameJSON-объекта в полеactorили ключ в массиве JSON-объектов; - индекс добавляется в неймспейс явно, вызовами команды
index_add().
Для записи со схемой:
{
"title": "complex_item",
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"actor": {
"type": "array",
"items": {
"type": "object",
"properties": {
"actor_name": {
"type": "string"
},
"age": {
"type": "integer"
}
}
}
},
"another_actor": {
"type": "object",
"properties": {
"actor_name": {
"type": "string"
},
"age": {
"type": "integer"
}
}
}
}
}
индексы будут выглядеть следующим образом:
nested_index_definitions = [
{
"name": "id",
"json_paths": ["id"],
"field_type": "int64",
"is_pk": True
},
# Индексные поля из actor ("name" and "age") добавляются в виде отдельных индексов-массивов ("is_array": True)
{
"name": "actor.name",
"json_paths": ["actor.name"],
"field_type": "string",
"is_array": True
},
{
"name": "actor.age",
"json_paths": ["actor.age"],
"field_type": "int",
"is_array": True
},
# Индексные поля из another_actor добавляются с префиском "another_actor."
{
"name": "another_actor.name",
"json_paths": ["another_actor.name"],
"field_type": "string"
},
{
"name": "another_actor.age",
"json_paths": ["another_actor.age"],
"field_type": "int"
}
]
// Индексные поля из BaseItem будут добавлены в текущую структуру
public static class BaseItem {
@Reindex(name = "id", isPrimaryKey = true)
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
// Индексные поля из Actor ("name" and "age") будут добавлены в текущую структуру в виде отдельных индексов-массивов
public class Actor {
@Reindex(name = "actor_name")
private String name;
@Reindex(name = "age")
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public static class ComplexItem extends BaseItem {
@Reindex(name = "actor")
private List<Actor> actor;
// Hash-индекс для поля "name"
@Reindex(name = "name")
private String name;
// Tree-индекс для поля "year"
@Reindex(name = "year", type = TREE)
private Integer year;
// Колоночный индекс для поля "value"
@Reindex(name = "value", isSparse = true, isNoColumn = true)
private Integer value;
// Содержимое поля MetaInfo НЕ будет
private Integer metainfo;
// Индексные поля из Parent НЕ будут добавлены в текущую структуру. Содержимое полей Parent попадёт в reindexer в виде неиндексируемых
@Reindex(name = "-")
private Item parent;
// Индексные поля и любые данные из ParentHidden НЕ попадут в reindexer
private Item parentHidden;
// Индексные поля и любые данные из ParentHidden НЕ попадут в reindexer (поведение аналогично тегу `json:"-"`)
private Item privateParent;
// Индексные поля AnotherActor будут добавлены в текущую структуру с префиском "actor." (в этом конкретном примере это приведёт к добавлению двух индексов: "actor.actor_name" и "actor.age")
@Reindex(name = "actor")
private Actor anotherActor;
public List<Actor> getActor() {
return actor;
}
public void setActor(List<Actor> actor) {
this.actor = actor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public Integer getMetainfo() {
return metainfo;
}
public void setMetainfo(Integer metainfo) {
this.metainfo = metainfo;
}
public Item getParent() {
return parent;
}
public void setParent(Item parent) {
this.parent = parent;
}
public Item getParentHidden() {
return parentHidden;
}
public void setParentHidden(Item parentHidden) {
this.parentHidden = parentHidden;
}
public Item getPrivateParent() {
return privateParent;
}
public void setPrivateParent(Item privateParent) {
this.privateParent = privateParent;
}
public Actor getAnotherActor() {
return anotherActor;
}
public void setAnotherActor(Actor anotherActor) {
this.anotherActor = anotherActor;
}
@Override
public String toString() {
return "ComplexItem{" +
"id=" + getId() +
", actor=" + actor +
", name='" + name + '\'' +
", year=" + year +
", value=" + value +
", metainfo=" + metainfo +
", parent=" + parent +
", parentHidden=" + parentHidden +
", privateParent=" + privateParent +
", anotherActor=" + anotherActor +
'}';
}
}
Примеры объявления структур с индексными полями с указанием параметров на 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`. Задан пользовательский порядок сортировки.
}
Особенности хранения данных для индексных полей с различными опциями в Reindexer
В отличие от многих классических СУБД, в Reindexer индексы могут влиять на логику хранения и, как следствие, на содержимое документа. Это сделано с целью оптимизации потребления памяти, а также для реализации более эффективной фильтрации по индексным полям и их модификации.
С точки зрения логики работы с данными индексы в Reindexer можно разделить на 3 основные группы:
- Обычные (regular);
- Sparse (разреженные);
- Композитные.
Обычные (regular) индексы
Обычные индексы напрямую связаны со слоем хранения данных. Если для поля создан такой индекс, то значение такого поля всегда попадает в плоский буфер PayloadValue по заранее известному смещению (для строк там будет храниться указатель, а для массивов - дополнительный уровень косвенности).
Такой подход позволяет:
- Реализовать очень быстрый доступ к значениям при фильтрации с использованием компараторов, при сортировках, агрегациях и модификациях значений - фактически доступ к значению эквивалентен переходу по одному или нескольким заведомо известным указателям.
- Дедуплицировать одинаковые строки - документы не владеют индексными строками, вместо этого владельцем строк выступает индекс. Для совпадающих строк индекс просто ведёт подсчитывает активные ссылки на строку, не создавая её копий.
- Не хранить вектора в документе - у документа нет своей копии вектора, вместо этого он ссылается на соответсвующий векторный индекс.
- Дедуплицировать документы с одинаковой структурой - JSON-структура хранится в системном индексе
-tupleи может быть дедуплицирована как обычная строка.
В то же время подобная модель хранения приводит к некоторым особенностям, которые необходимо учитывать:
- Обычные индексы не могут иметь значения
null. Несмотря на то, что в документе всё ещё допустимо указать"field": nullдля индексного поля (или вовсе пропустить его), вPayloadValueвсё равно будет создано значение по умолчанию для этого поля (дляstring- пустая строка, дляint/int64- 0, дляdouble- 0.0, дляbool- false, дляfloat_vector- пустой вектор, дляuuid- nil UUID , для массива иpoint- пустой массив). - Обычные индексы всегда выполняют конвертацию значиний при вставке и, если, например, в индекс с типом
intпопробовать вставить значение с плавающей точкой, то произойдёт его преобразование в целочисленный тип. Исходное значение при этом сохранено не будет. - Для каждого подобного поля резервируется фиксированное место в
PayloadValue, что делает невозможным исполнозование variadic-кодирования, применяемого для неиндексных и sparse-полей. То есть, в этом случаеint64всегда будет занимать 8 байт, даже если фактически в документе лежит значение5, которое можно было бы закодировать в 1 байт. - Таких индексов не может быть более 255.
Sparse индексы
Sparse индексы, в отличие от regular, никак не связаны со слоем хранения. Само значение индексируемого поля по-прежнему хранится внутри -tuple, как у неиндексных полей.
В отличие от обычных индексов, sparse индексы имеют следующие особенности:
- Могут иметь значение
null. - Могут эффективно использовать variadic-кодирование для целочисленных типов и не резервируют дополнительную память в случае отсутствующих и null-значений.
- При выборках избегают использования компараторов везде, где это возможно, используя
IdSet(набор упорядоченных ID). - Для извлечения значений (при точечной модификации, сортировке, агрегировании или в компараторах) полностью разбирают CJSON документа. Это может приводить к падению производительности в определённых сценариях.
- Не выполняют конвертацию значений в момент встаки.
- Нет явного ограничения на количество таких индексов.
Композитные индексы
Композитные индексы это индексы, построенные поверх нескольких полей в целях получения более высокого показателя селективности. В частных случаях могут строиться поверх одного поля - например, если требуется иметь несколько индексов с разными типами. Как и sparse, не имеют прямой связи со слоем хранения.
Особенности:
- Могут строиться только поверх других обычных (regular) индексов. Как следствие, не могут иметь null-полей в своём составе.
- Не резервируют дополнительного места под данные - хранят только саму индексную структуру.
- Не могут быть построены поверх массивов.
- Нет явного ограничения на количество таких индексов.