Эластичный поиск — Как объединить данные между двумя типами?

#elasticsearch

#elasticsearch

Вопрос:

Я пытаюсь изучить эластичный поиск для проекта, над которым я работаю, но застрял на том, как объединить два типа документов.

Например, если бы у меня было 10 документов, в которых указаны тарифы на проживание в отелях, и 10 документов, в которых указаны все рейсы в тот пункт назначения, в котором находится отель.

Обычно в MySQL я бы сделал объединение на основе даты и продолжительности проживания в отеле, рейса и т.д.

Как мне вернуть один документ об отеле с самым дешевым рейсом из 10 доступных?

Комментарии:

1. Я думаю, это будет более понятно, если вы разделите сопоставления и запрос

2. Сегодня я попытаюсь кое-чем поделиться.

Ответ №1:

Самое близкое, что я могу придумать, чтобы сделать то, что вы хотите, — это составные агрегации. Это не настоящее объединение, но оно МОЖЕТ приблизить вас к тому, что вы хотите.

Условия:

  • Поля должны иметь одинаковые имена между индексами
  • Вам придется выровнять результирующую агрегацию
  • Все поля результата (те, которые вас интересуют) будут представлять собой некоторого рода агрегации

Вот минимальный пример (вырезанный в консоли Kibana):

С помощью документов:

 POST my-test1/_doc/_bulk
{"index": {}}
{"entityID":"entity1", "value": 12}
{"index": {}}
{"entityID":"entity1", "value": 22}
{"index": {}}
{"entityID":"entity2", "value": 2}
{"index": {}}
{"entityID":"entity2", "value": 12}


POST my-test2/_doc/_bulk
{"index": {}}
{"entityID":"entity1", "otherValue": 5}
{"index": {}}
{"entityID":"entity1", "otherValue": 1}
{"index": {}}
{"entityID":"entity2", "otherValue": 3}
{"index": {}}
{"entityID":"entity2", "otherValue": 7}
  

Мы будем агрегировать данные вокруг общего поля сущности entityID

 GET my-test*/_search
{
  "size": 0,
  "aggs": {
    "by-entity": {
      "composite": {
        "sources": [
          {
            "entityID": {
              "terms": {
                "field": "entityID.keyword"
              }
            }
          }
        ]
      },
      "aggs": {
        "value": {
          "avg": {
            "field": "value"
          }
        },
        "otherValue": {
          "avg": {
            "field": "otherValue"
          }
        }
      }
    }
  }
}
  

Это приведет к ответу:

 {
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 10,
    "successful" : 10,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 8,
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "by-entity" : {
      "after_key" : {
        "entityID" : "entity2"
      },
      "buckets" : [
        {
          "key" : {
            "entityID" : "entity1"
          },
          "doc_count" : 4,
          "otherValue" : {
            "value" : 3.0
          },
          "value" : {
            "value" : 17.0
          }
        },
        {
          "key" : {
            "entityID" : "entity2"
          },
          "doc_count" : 4,
          "otherValue" : {
            "value" : 5.0
          },
          "value" : {
            "value" : 7.0
          }
        }
      ]
    }
  }
}
  

Вы могли бы создать составную агрегацию для множества разных полей и разных групповых агрегатов. Итак, вы могли бы создать terms агрегацию для вашего hotel_id и объединить ее с date_histogram вокруг вашего timestamp .

Ответ №2:

В Elasticsearch нет межиндексных соединений (как в большинстве баз данных документов). Если вам нужно сделать это в ES, вы обычно делаете это путем денормализации данных во время индексации. Если вы не можете этого сделать, тогда вам придется выполнить несколько запросов.

Если вам нужно выполнить реляционный запрос, вам лучше использовать реляционную базу данных, такую как MySQL или Postgres.

Комментарии:

1. Да, я понимаю это, но могу ли я использовать вложенный документ или дочерний и родительский?