Объединение таблиц в RethinkDB
Хотите узнать, как моделировать данные? Прочитайте о моделировании данных в RethinkDB.
Как и многие традиционные системы баз данных, RethinkDB поддерживает JOIN команды для объединения данных из нескольких таблиц. В RethinkDB объединения автоматически распределяются — команда объединения автоматически отправляется на соответствующие узлы кластера, необходимые данные объединяются, и окончательный результат представляется пользователю.
Давайте посмотрим, как мы можем использовать объединения в RethinkDB для запроса данных на основе отношений один ко многим и многие ко многим.
Отношения один ко многим
Использование первичных ключей
Предположим, что мы создали две таблицы: employees и companies. Мы будем использовать эти таблицы для моделирования понятия людей, работающих в организациях (каждая организация имеет несколько человек, работающих в ней, но каждый человек работает только в одной организации). Вот пример документа в таблице employees:
{
"id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"name": "Jean-Luc Picard",
"company_id": "064058b6-cea9-4117-b92d-c911027a725a",
"rank": "captain"
}
И вот пример документа в таблице companies:
{
"id": "064058b6-cea9-4117-b92d-c911027a725a",
"company": "Starfleet",
"type": "paramilitary"
}
Мы можем объединить две таблицы следующим образом:
r.table("employees").eq_join("company_id", r.table("companies")).run()
Этот запрос объединяет company_id таблицы сотрудников с первичным ключом таблицы компаний. Он возвращает последовательность документов, где каждый документ содержит два поля — информацию об сотруднике и информацию о компании:
{
"left": {
"id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"name": "Jean-Luc Picard",
"company_id": "064058b6-cea9-4117-b92d-c911027a725a",
"rank": "captain"
},
"right": {
"id": "064058b6-cea9-4117-b92d-c911027a725a",
"company": "Starfleet",
"type": "paramilitary"
}
}
- Поле
leftсодержит информацию из левой таблицы в запросе (в данном случае, информацию об сотруднике) - Поле
rightсодержит информацию из правой таблицы в запросе (в данном случае, информацию о компании)
Мы можем применить команду zip в конце запроса, чтобы объединить два поля в один документ. Например, следующий запрос:
r.table("employees").eq_join("company_id", r.table("companies")).zip().run()
Возвращает следующий результат:
{
"id": "064058b6-cea9-4117-b92d-c911027a725a",
"name": "Jean-Luc Picard",
"company_id": "064058b6-cea9-4117-b92d-c911027a725a",
"rank": "captain",
"company": "Starfleet",
"type": "paramilitary"
}
Использование подзапросов
Частая задача доступа к данным — извлечение одного документа с ассоциированными «дочерними» документами. (Это часто встречается в отношениях один ко многим, как показано здесь, но может быть отношениями многие ко многим или один к одному.) В нашем примере набора данных мы можем получить информацию о компании и всех ее сотрудниках. Мы можем сделать это одной командой ReQL, используя merge и подзапрос в его лямбда-функции.
id = "064058b6-cea9-4117-b92d-c911027a725a"
r.table("companies").get(id).merge(lambda company:
{ 'employees': r.table('employees').get_all(company['id'],
index='company_id').coerce_to('array') }
).run()
Это вернет результат, похожий на:
{
"id": "064058b6-cea9-4117-b92d-c911027a725a",
"company": "Starfleet",
"type": "paramilitary",
"employees": [
{
"id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"name": "Jean-Luc Picard",
"company_id": "064058b6-cea9-4117-b92d-c911027a725a",
"rank": "captain"
},
...
]
}
Где eq_join создает табличный результат (приблизительный эквивалент SQL SELECT * FROM companies, employees WHERE companies.id = employees.company_id), использование подзапроса создает вложенный документ, где объекты сотрудников возвращаются в списке в поле employees.
Использование вторичных индексов
Предположим, что наша модель данных для сотрудников хранит название компании вместо идентификатора компании:
{
"id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"name": "Jean-Luc Picard",
"company_name": "Starfleet",
"rank": "captain"
}
Мы можем создать вторичный индекс на поле company таблицы companies и выполнить наш запрос, используя вторичный индекс:
r.table("companies").index_create("company").run()
Запрос будет выглядеть так:
r.table("employees").eq_join("company_name",
r.table("companies"), index="company").run()
Хотите узнать больше об индексах?: Прочитайте об использовании вторичных индексов в RethinkDB.
Примечание: вы также можете объединять таблицы по произвольным полям без создания индекса, используя команду inner_join. Однако произвольные внутренние объединения менее эффективны, чем объединения по равенству.
Отношения многие ко многим
Вы также можете использовать RethinkDB для запроса отношений многие ко многим. Предположим, у нас есть платформа для совместного ведения блогов, где авторы сотрудничают над публикациями (несколько авторов могут работать над каждой публикацией и публиковать несколько публикаций).
Для моделирования этих данных мы создадим три таблицы — authors, posts и authors_posts, аналогично тому, как мы это делаем в реляционной системе. Вот пример данных для таблицы authors:
{
"id": "7644aaf2-9928-4231-aa68-4e65e31bf219",
"name": "William Adama",
"tv_show": "Battlestar Galactica"
}
{
"id": "064058b6-cea9-4117-b92d-c911027a725a",
"name": "Laura Roslin",
"tv_show": "Battlestar Galactica"
}
Вот пример данных для таблицы posts:
{
"id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"title": "Decommissioning speech",
"content": "The Cylon War is long over..."
}
И вот пример данных для таблицы authors_posts:
{
"author_id": "7644aaf2-9928-4231-aa68-4e65e31bf219",
"post_id": "543ad9c8-1744-4001-bb5e-450b2565d02c"
}
{
"author_id": "064058b6-cea9-4117-b92d-c911027a725a",
"post_id": "543ad9c8-1744-4001-bb5e-450b2565d02c"
}
В отношениях многие ко многим мы можем использовать несколько eq_join команд для объединения данных из всех трех таблиц:
r.table("authors_posts").eq_join("author_id", r.table("authors")).zip().
eq_join("post_id", r.table("posts")).zip().run()
Результатом этого запроса является поток документов, включающих каждую статью, написанную каждым автором в нашей базе данных:
{
"tv_show": "Battlestar Galactica",
"title": "Decommissioning speech",
"post_id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"name": "William Adama",
"id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"content": "The Cylon War is long over...",
"author_id": "7644aaf2-9928-4231-aa68-4e65e31bf219"
}
{
"tv_show": "Battlestar Galactica",
"title": "Decommissioning speech",
"post_id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"name": "Laura Roslin",
"id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"content": "The Cylon War is long over...",
"author_id": "064058b6-cea9-4117-b92d-c911027a725a"
}
Решение конфликтов имен полей
Если вы используете команду zip после join, документ из правой таблицы будет объединен с левым.
Рассмотрим следующий запрос:
r.table("employees").eq_join("company_id", r.table("companies"))
Предположим, его вывод выглядит следующим образом:
{
# Employee
"left": {
"id": "543ad9c8-1744-4001-bb5e-450b2565d02c",
"name": "Jean-Luc Picard",
"company_id": "064058b6-cea9-4117-b92d-c911027a725a",
"rank": "captain"
},
# Company
"right": {
"id": "064058b6-cea9-4117-b92d-c911027a725a",
"company": "Starfleet",
"type": "paramilitary"
}
}
Конфликтное поле — id. Если вы напрямую используете команду zip, поле id результата будет взято из компании. Есть три способа разрешить потенциальные конфликты полей.
Удаление конфликтующих полей
Предположим, что вы хотите сохранить поле id сотрудника, но не поле компании. Вы можете сделать это, удалив поле right.id, а затем вызвав команду zip.
r.table("employees").eq_join("company_id", r.table("companies"))
.without({"right": {"id": True}}) # Remove the field right.id
.zip()
.run()
Переименование полей
Если вам нужно сохранить оба поля, вы можете переименовать их с помощью map и without перед использованием команды zip.
r.table("employees").eq_join("company_id", r.table("companies"))
# Copy the field right.id into right.c_id
.map( r.row.merge({
"right": {
"c_id": r.row["right"]["id"]
}
}))
# Remove the field right.id
.without({"right": {"id": True}})
.zip()
.run()
Ручное объединение левого и правого полей
Вы можете вручную объединить поля left и right без использования команды zip. Предположим, что вы хотите сохранить имя сотрудника и имя его компании. Вы можете сделать:
r.table("employees").eq_join("company_id", r.table("companies"))
.map({
"name": r.row["left"]["name"],
"company": r.row["right"]["company"]
}).run()
Подробнее
Для получения дополнительной информации, ознакомьтесь с моделированием данных в RethinkDB. Для получения подробной информации ознакомьтесь с документацией API для команд объединения:
© RethinkDB contributors
Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
https://rethinkdb.com/docs/table-joins/