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

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

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

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

Есть ли какой-либо другой подход, отличный от указанного выше.


person mkalsi    schedule 07.09.2017    source источник


Ответы (2)


Вложенный объект — идеальный подход для этого. Если вы правильно обновите дочерние объекты, в родительском документе не будет повторения дочерних объектов. Я использую тот же подход для одного из моих вариантов использования, когда мне нужно поддерживать реляционные данные отношений Master-Child One-to-Many. Я написал безболезненный скрипт для 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 эти оба объекта (родительский, дочерний) представляют собой две разные таблицы с отношением «один ко многим» между «родительский» -> «дочерний». Идентификатор родителя является внешним ключом для дочерней строки. (id обязателен для обеих таблиц)

Теперь в 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"
}

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

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
            }
        }
    }
}

Дать ему шанс. Ваше здоровье!

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

person Hatim Stovewala    schedule 08.09.2017
comment
Спасибо, Хатим. Можете ли вы поделиться своим скриптом для хранения вложенных объектов без повторения. - person mkalsi; 12.09.2017
comment
@mkalsi Я обновил свой ответ примерами и сценариями. Проверь это. - person Hatim Stovewala; 13.09.2017
comment
Какова будет производительность встроенного скрипта, если наш набор данных очень большой, поскольку мы планируем проиндексировать 20 миллионов документов. - person mkalsi; 19.09.2017
comment
Я не проверял это с таким огромным количеством записей. Но, насколько я знаю, это не будет проблемой. Время, необходимое для индексации конкретного документа, будет почти таким же, как и для его индексации. И индексация 20 миллионов документов будет одноразовой работой, поскольку вы переходите из какой-то другой БД. Для этой цели можно использовать пакетную обработку. - person Hatim Stovewala; 19.09.2017
comment
Спасибо @Hatim Stovewala - person mkalsi; 19.09.2017

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

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

person rndus2r    schedule 08.09.2017