Как хранить реляционные данные в asticsearch

Какие есть варианты хранения реляционных данных в asticsearch. Я знаю следующие подходы

  1. Вложенный объект:- Я не хочу хранить данные во вложенном формате, потому что я хочу обновить один документ без изменения другого документа, и если я использую вложенный объект, то в родительских документах будут повторяться дочерние данные.

  2. Parent-child:- Я не хочу хранить данные в одном индексе, но для использования Parent-child данные должны присутствовать в одном индексе (разных типов). Я знаю, что это ограничение будет снято в будущем выпуске, как упомянуто в проблеме https://github.com/elastic/elasticsearch/issues/15613, но я хочу решение, которое должно работать с версией 5.5.

Есть ли какой-то другой подход, кроме как выше?

3 ответа

Вложенный объект является идеальным подходом для этого. Не будет повторения дочерних объектов в родительском документе, если вы обновите дочерние объекты правильно. Я использую тот же подход для одного из моих вариантов использования, где мне нужно поддерживать реляционные данные отношения Мастер-Ребенок один-ко-многим. Я написал сценарий Painless для Update API для добавления и обновления существующих вложенных дочерних объектов в родительском документе без создания дубликатов или повторяющихся записей.

Обновленный ответ:

Ниже приведена структура документа вложенного типа "родитель-потомок" со встроенными документами вложенного типа "потомки".

{
    "parent_id": 1,
    "parent_name": "ABC",
    "parent_number": 123,
    "parent_addr": "123 6th St. Melbourne, FL 32904"
    "childs": [
      {
        "child_id": 1,
        "child_name": "PQR",
        "child_number": 456,
        "child_age": 10
      },
      {
        "child_id": 2,
        "child_name": "XYZ",
        "child_number": 789,
        "child_age": 12
      },
      {
        "child_id": 3,
        "child_name": "QWE",
        "child_number": 234,
        "child_age": 16
      }

    ]   
}

Отображение будет как показано ниже:

PUT parent/
{
  "parent": {
    "mappings": {
      "parent": {
        "properties": {
          "parent_id": {
            "type": "long"
          },
          "parent_name": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "parent_number": {
            "type": "long"
          },
          "parent_addr": {
            "type": "text",
            "fields": {
              "keyword": {
                "type": "keyword",
                "ignore_above": 256
              }
            }
          },
          "child_tickets": {
            "type": "nested",
            "properties": {
              "child_id": {
                "type": "long"
              },
              "child_name": {
                "type": "text",
                "fields": {
                  "keyword": {
                    "type": "keyword",
                    "ignore_above": 256
                  }
                }
              },
              "child_number": {
                "type": "long"
              },
              "child_age": {
                "type": "long"
              }
            }
          }
        }
      }
    }
  }
}

В RDMS обе эти сущности (родительская, дочерняя) представляют собой две разные таблицы с отношением Один-ко-многим между Родителем -> Дочерним. Идентификатор родителя является внешним ключом для строки ребенка. (идентификатор должен для обеих таблиц)

Теперь в Elasticsearch, чтобы проиндексировать родительский документ, у нас должен быть идентификатор для его индексации, в данном случае это parent_id. Индекс Запрос родительского документа (parent_id - это идентификатор, о котором я говорил, и индексировать документ с идентификатором (_id) = 1):

POST parent/parent/1
{
    "parent_id": 1,
    "parent_name": "ABC",
    "parent_number": 123,
    "parent_addr": "123 6th St. Melbourne, FL 32904"
}

Теперь добавляем child(s) к родителю. Для этого вам потребуется дочерний документ, который должен иметь дочерний идентификатор и родительский идентификатор. Чтобы добавить ребенка, родительский идентификатор должен. Ниже приведен запрос на обновление для добавления новых дочерних элементов или обновления уже существующих дочерних элементов.

POST parent/parent/1/_update
{
    "script":{
    "lang":"painless",
    "inline":"if (!ctx._source.containsKey(\"childs\")) {
                ctx._source.childs = [];
                ctx._source.childs.add(params.child);
            } else {
                int flag=0;
                for(int i=0;i<ctx._source.childs.size();i++){
                    if(ctx._source.childs[i].child_id==params.child.child_id){
                        ctx._source.childs[i]=params.child;
                        flag++;
                    }
                }
                if(flag==0){
                    ctx._source.childs.add(params.child);
                }
            }",
    "params":{
        "child":{
                "child_id": 1,
                "child_name": "PQR",
                "child_number": 456,
                "child_age": 10
            }
        }
    }
}

Дать ему шанс. Ура!

Позвольте мне знать, если вам нужно что-нибудь еще.

Есть еще два подхода: денормализация и запуск нескольких запросов для объединений.

Денормализация потребляет больше места и увеличивает время записи, но вам просто нужно выполнить один запрос для извлечения ваших данных, следовательно, время чтения улучшится. Поскольку вы не хотите хранить данные в одном индексе, объединение может помочь вам.

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

Внутренний объект

  • Легко, быстро, производительно
  • Применимо только при сохранении отношений один к одному
  • Нет необходимости в специальных запросах

Вложенный

  • Вложенные документы хранятся в одном и том же блоке Lucene, что повышает производительность чтения/запроса. Чтение вложенного документа происходит быстрее, чем эквивалентного родителя/дочернего документа.
  • Обновление одного поля во вложенном документе (родительском или вложенном дочернем) заставляет ES переиндексировать весь вложенный документ. Это может быть очень дорого для больших вложенных документов.
  • "Перекрестные ссылки" вложенных документов невозможны
  • Лучше всего подходит для данных, которые не меняются часто

Родитель/Ребенок

  • Потомки хранятся отдельно от родителя, но перенаправляются в тот же шард. Таким образом, родительские/дочерние элементы немного менее производительны при чтении/запросе, чем вложенные.
  • Сопоставления родитель/потомок имеют дополнительные накладные расходы на память, поскольку ES поддерживает список соединений в памяти.
  • Обновление дочернего документа не влияет на родительский или любые другие дочерние документы, что потенциально может сэкономить много времени при индексировании больших документов.
  • Сортировка/оценка может быть затруднена с использованием родительского/дочернего элементов, поскольку
  • Операции Child/Has Parent иногда могут быть непрозрачными

Денормализация

  • Вы сами управляете всеми отношениями!
  • Самая гибкая, самые административные накладные расходы
  • Может быть более или менее производительным в зависимости от вашей настройки

Для получения дополнительной информации перейдите по ссылке: https://www.elastic.co/blog/managing-relations-inside-elasticsearch .

Другие вопросы по тегам