Spec-Zone.ru › RethinkDB javascript

Использование вторичных индексов в RethinkDB

Вторичные индексы — это структуры данных, которые повышают скорость многих запросов на чтение, но при этом увеличивают занимаемое место и снижают производительность операций записи.

RethinkDB поддерживает различные типы вторичных индексов:

  • Простые индексы, основанные на значении одного поля.
  • Составные индексы, основанные на нескольких полях.
  • Многозначные индексы, основанные на массивах значений.
  • Индексы, основанные на произвольных выражениях.
  • Простые индексы
  • Составные индексы
  • Многозначные индексы
  • Индексы на произвольных выражениях ReQL
  • Административные операции
  • Примечания
  • Подробнее

Простые индексы

Используйте простые индексы для эффективного извлечения и упорядочивания документов по значению одного поля.

Создание

// Create a secondary index on the last_name attribute
r.table("users").indexCreate("last_name").run(conn, callback)
// Wait for the index to be ready to use
r.table("users").indexWait("last_name").run(conn, callback)

Запросы

// Get all users whose last name is "Smith"
r.table("users").getAll("Smith", {index: "last_name"}).run(conn, callback)

// Get all users whose last names are "Smith" or "Lewis"
r.table("users").getAll("Smith", "Lewis", {index: "last_name"}).run(conn, callback)

// Get all users whose last names are between "Smith" and "Wade"
r.table("users").between("Smith", "Wade", {index: "last_name"}).run(conn, callback)

// Efficiently order users by last name using an index
r.table("users").orderBy({index: "last_name"}).run(conn, callback)

// For each blog post, return the post and its author using the last_name index
// (assume "author_full_name" is the name of a field in "posts")
r.table("posts").eqJoin("author_last_name", r.table("users"), {index: "last_name"}) \
    .zip().run(conn, callback)

Хотите узнать больше о объединениях в RethinkDB? Смотрите как использовать объединения для запросов один-ко-многим и многие-ко-многим отношениям.

Составные индексы

Составные индексы используют массивы для эффективного извлечения документов по нескольким полям.

Создание

// Create a compound secondary index based on the first_name and last_name attributes
r.table("users").indexCreate(
    "full_name", [r.row("last_name"), r.row("first_name")]
).run(conn, callback)
// Wait for the index to be ready to use
r.table("users").indexWait("full_name").run(conn, callback)

Запросы

// Get all users whose full name is John Smith.
r.table("users").getAll(["Smith", "John"], {index: "full_name"}).run(conn, callback)

// Get all users whose full name is between "John Smith" and "Wade Welles"
r.table("users").between(
    ["Smith", "John"], ["Welles", "Wade"], {index: "full_name"}
).run(conn, callback)

// Get all users whose last name is Smith.
r.table("users").between(
    ["Smith", r.minval], ["Smith", r.maxval], {index: "full_name"}
).run(conn, callback)

// Efficiently order users by first name and last name using an index
r.table("users").orderBy({index: "full_name"}).run(conn, callback)

// For each blog post, return the post and its author using the full_name index
r.table("posts").eqJoin(
    "author_full_name", r.table("users"), {index: "full_name"}
).run(conn, callback)

Внутренне, составные и простые индексы в RethinkDB — это один и тот же тип индекса; составные индексы — просто частный случай обычного индекса, который возвращает массив, а не одно значение. Обратите внимание, что это влияет на сортировку: значения составных индексов сортируются лексикографически, причём первые (левые) элементы составного значения имеют большее значение, чем последние (правые). Следовательно, используя индекс full_name, вышеприведенный пример «все пользователи с фамилией Smith» работает только для поля last_name. Поиск по first_name с запросом, подобным between([r.minval, "John"], [r.maxval, "John"], {index: "full_name"}) , фактически отобразит всех пользователей в таблице, за исключением (теоретически) пользователей, у которых фамилия r.minval и имя находится лексикографически раньше «John» (или наоборот для r.maxval).

Многозначные индексы

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

Ключи в многозначном индексе могут быть одиночными значениями, составными значениями или даже произвольными выражениями. (См. раздел ниже для получения более подробной информации об индексах, использующих функции.) Важно, что «многозначное» значение, которое индексируется, является массивом: документ будет упоминаться в индексе несколько раз, один раз для каждого элемента этого массива.

Создание

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

{
    title: "...",
    content: "...",
    tags: [ <tag1>, <tag2>, ... ]
}

// Create the multi index based on the field tags
r.table("posts").indexCreate("tags", {multi: true})

// Wait for the index to be ready to use
r.table("posts").indexWait("tags").run(conn, callback)

Запросы

// Get all posts with the tag "travel" (where the field tags contains "travel")
r.table("posts").getAll("travel", {index: "tags"}).run(conn, callback)

// For each tag, return the tag and the posts that have such tag
r.table("tags").eqJoin("tag", r.table("posts"), {index: "tags"}).run(conn, callback)

Обратите внимание, что запросы с getAll или between могут возвращать один и тот же документ несколько раз, если не использовать команду distinct.

Индексы на произвольных выражениях ReQL

Вы можете создать индекс на произвольном выражении, передав анонимную функцию в indexCreate.

// A different way to do a compound index
r.table("users").indexCreate("full_name2", function(user) {
    return r.add(user("last_name"), "_", user("first_name"))
}).run(conn, callback)

Функция, которую вы передаёте в indexCreate, должна быть детерминированной. На практике это означает, что вы не можете использовать функцию, содержащую подзапрос или команду r.js.

Если функция, переданная в indexCreate, возвращает ошибку для заданного документа, этот документ не будет индексирован. Ошибка не будет возвращена для этих документов.

Использование многозначных индексов и произвольных выражений вместе

Аналогичным образом можно создать многозначный индекс на произвольном выражении, передав параметр multi в качестве последнего параметра в indexCreate.

// Create a multi index on a ReQL expression
r.table("users").indexCreate("activities", r.row("hobbies").add(r.row("sports")),
    {multi: true}).run(conn, callback)

Это может быть полезно, если вы хотите искать документы по нескольким критериям одновременно, например, сообщения, опубликованные в форуме, которые должны быть идентифицируемы по:

  • автору
  • тегу (из многих)
  • времени публикации (должны быть возможны запросы по диапазонам)

Это пример документа, представляющего опубликованное сообщение:

{
  id: "XXYYZZ",
  author: "John"
  tags: ["art", "hobby", "fun"],
  time: 123,
  text: "Text goes here..."
}

Один из способов создания индекса:

r.db("db").table("posts").indexCreate(
  "myIndex",
  function(post) {
    return post("tags").map(function(tag) {
      return [tag, post("author"), post("time")];
    })
  },
  {multi: true}
)

Затем запрос, выбирающий сообщения о "fun", автором "John", и опубликованные между 120 и 130, будет:

r.db("db").table("posts").between(["fun", "John", 120], ["fun", "John", 130], {index:"myIndex"})

Обратите внимание, что из-за того, что индекс отсортирован лексикографически (и из-за того, что between возвращает один непрерывный интервал строк), можно было бы искать сообщения о "fun" любым автором, но при этом не было бы возможности ограничить диапазон времени (без создания другого индекса, конечно).

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

Использование многозначного индекса и отображающей функции для ускорения getAll/contains

Если ваша программа часто выполняет getAll, за которым следует contains, эту операцию можно сделать более эффективной, создав составной многозначный индекс с помощью отображающей функции для поля, содержащего список.

// Create the index
r.table("users").indexCreate("userEquipment", function(user) {
    return user("equipment").map(function(equipment) {
        return [ user("id"), equipment ];
    });
}, {multi: true}).run(conn, callback);

// Query equivalent to:
// r.table("users").getAll(1).filter(function (user) {
//     return user("equipment").contains("tent");
// });
r.table("users").getAll([1, "tent"], {index: "userEquipment"}).distinct().run(conn, callback);

Административные операции

// list indexes on table "users"
r.table("users").indexList().run(conn, callback)

// drop index "last_name" on table "users"
r.table("users").indexDrop("last_name").run(conn, callback)

// return the status of all indexes
r.table("users").indexStatus().run(conn, callback)

// return the status of the index "last_name"
r.table("users").indexStatus("last_name").run(conn, callback)

// return only when the index "last_name" is ready
r.table("users").indexWait("last_name").run(conn, callback)

Управление индексами с помощью веб-интерфейса

Веб-интерфейс поддерживает создание и удаление простых вторичных индексов. В списке таблиц нажмите на таблицу users. Вы можете управлять индексами через панель вторичных индексов в представлении таблицы.

Примечания

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

Индексы (как вторичные, так и первичные) гарантированно обновляются успешными операциями записи. Если операция insert, update или delete выполнена успешно, изменение будет правильно отражено в индексе. (Подробнее о гарантиях согласованности в RethinkDB Гарантии согласованности для операций записи.)

Часть ключа вторичного индекса, используемая для быстрых поисков, зависит от длины первичного ключа (который должен быть 127 байтов или меньше). Длина этой части составляет 238−PK, где PK — длина первичного ключа; если, например, длина первичного ключа — это 36-символьный GUID, это означает, что 202 символа во вторичном индексе будут значимыми. Если в таблице есть несколько записей, где первые 238−PK символов идентичны, производительность поиска резко снизится, так как RethinkDB будет вынуждена выполнить линейный поиск, чтобы найти правильные записи.

Вторичные индексы не будут хранить null значения или объекты. Таким образом, результаты команды, такой как:

r.table("users").indexCreate("group").run(conn, callback)
r.table("users").orderBy({index: "group"}).run(conn, callback)

могут отличаться от эквивалентной команды без индекса:

r.table("users").orderBy("group").run(conn, callback)

если поле, которое индексируется, имеет значения, которые нельзя индексировать. Это ограничение будет устранено в будущей версии RethinkDB. Следите за прогрессом в GitHub-вопросе #1032.

В настоящее время в RethinkDB нет оптимизатора. Например, следующий запрос не будет автоматически использовать индекс:

// This query does not use a secondary index! Use getAll instead.
r.table("users").filter({"last_name": "Smith" }).run(conn, callback)

Вы должны явно использовать команду getAll для использования вторичных индексов.

// This query uses a secondary index.
r.table("users").getAll("Smith", {index: "last_name"}).run(conn, callback)

Вы не можете объединять несколько команд getAll. Используйте составной индекс для эффективного извлечения документов по нескольким полям.

Команда orderBy, использующая вторичный индекс, не может быть объединена с getAll. Вы можете объединить её только после команды table. Однако вы можете объединить orderBy после команды between при условии, что она использует тот же индекс.

RethinkDB не поддерживает уникальные вторичные индексы даже для нефрагментированных таблиц.

Подробнее

Просмотрите справочник API, чтобы узнать больше о командах вторичных индексов:

  • Обработка индексов: indexCreate, indexDrop и indexList
  • Использование индексов: getAll, between, eqJoin и orderBy

© RethinkDB contributors
Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
https://rethinkdb.com/docs/secondary-indexes/javascript/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API