Spec-Zone.ru › RethinkDB python

Объединение таблиц в RethinkDB

Хотите узнать, как моделировать данные? Прочитайте о моделировании данных в RethinkDB.

Как и многие традиционные системы баз данных, RethinkDB поддерживает JOIN команды для объединения данных из нескольких таблиц. В RethinkDB объединения автоматически распределяются — команда объединения автоматически отправляется на соответствующие узлы по всему кластеру, соответствующие данные объединяются, а конечный результат представляется пользователю.

Давайте посмотрим, как мы можем использовать объединения в RethinkDB для запроса данных на основе отношений «один ко многим» и «многие ко многим».

  • Отношения «один ко многим»
  • Отношения «многие ко многим»
  • Разрешение конфликтов имен полей
  • Подробнее

Table Join Illustration

Отношения «один ко многим»

Использование первичных ключей

Предположим, что мы создали две таблицы: 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. Для получения подробной информации ознакомьтесь с документацией по командам объединения:

  • eq_join
  • inner_join
  • outer_join
  • zip

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

Spec-Zone.ru

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