Кросс-индексный запрос Elasticsearch с агрегированием

Я использую: Elasticsearch 7.7, Kibana 7.7

Например, возьмем два индекса:

Пользовательский индекс с простым отображением:

PUT /user_index
{
  "mappings": {
    "properties": {
      "user_id":    { "type": "text" },
      "user_phone":    { "type": "text" },
      "name":   { "type": "text"  }     
    }
  }
}

Проверьте с помощью простого сопоставления:

PUT /check_index
{
  "mappings": {
    "properties": {
      "user_id":    { "type": "text" },  
      "price":   { "type": "integer"  },
      "goods_count":  {"type": "integer"}
    }
  }
}

Я хочу построить визуализацию таблицы следующим образом:

________________________________________________________________________
  user_id  |   user_phone  | average_price       |    sum_goods_count  |
___________|_______________|_____________________|______________________
     1     |       123     |       512           |         64          |
___________|_______________|_____________________|______________________
     2     |       456     |       256           |         16          | 
___________|_______________|_____________________|______________________

Итак, мои вопросы:

  1. Это реально?

  2. Я правильно понимаю, что мне нужно запросить эти два индекса, получить список пользователей, а потом в цикле создать корзины с чеками?


person Артем Савельев    schedule 16.09.2020    source источник
comment
какая польза от user_index для получения ожидаемого результата? Поскольку поля, которые содержит ваш ожидаемый вывод, также присутствуют в check_index.   -  person ESCoder    schedule 17.09.2020
comment
@Bhavya Gupta, это всего лишь пример, реальная задача намного сложнее, и мне нужно построить таблицу с полями из обоих индексов. Я надеялся, что кто-то сможет дать мне представление о том, как это реализовать или как подойти к этой проблеме.   -  person Артем Савельев    schedule 17.09.2020


Ответы (1)


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

Сопоставление индекса

{
    "mappings": {
        "properties": {
            "user_id": {
                "type": "text",
                "fielddata": "true"
            },
            "price": {
                "type": "integer"
            },
            "goods_count": {
                "type": "integer"
            }
        }
    }
}

Данные индекса:

С помощью сопоставления индекса, определенного выше, проиндексируйте эти три документа, где один документ имеет "user_id":"1", а 2 документа имеют "user_id":"2".

{
    "user_id":"1",
    "price":500,
    "goods_count":100
}
{
    "user_id":"2",
    "price":500,
    "goods_count":100
}
{
    "user_id":"2",
    "price":100,
    "goods_count":200
}

Поисковый запрос:

См. официальную документацию ES по адресу Агрегация терминов, Агрегация лучших результатов, Суммирование и Среднее агрегирование, чтобы получить подробное объяснение.

{
  "size": 0,
  "aggs": {
    "user": {
      "terms": {
        "field": "user_id"
      },
      "aggs": {
        "top_user_hits": {
          "top_hits": {
            "_source": {
              "includes": [
                "user_id"
              ]
            }
          }
        },
        "avg_price": {
          "avg": {
            "field": "price"
          }
        },
        "goods_count": {
          "sum": {
            "field": "goods_count"
          }
        }
      }
    }
  }
}

Результат поиска:

{
  "took": 10,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 3,
      "relation": "eq"
    },
    "max_score": null,
    "hits": [
      
    ]
  },
  "aggregations": {
    "user": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "2",
          "doc_count": 2,
          "top_user_hits": {
            "hits": {
              "total": {
                "value": 2,
                "relation": "eq"
              },
              "max_score": 1.0,
              "hits": [
                {
                  "_index": "stof_63925596",
                  "_type": "_doc",
                  "_id": "2",
                  "_score": 1.0,
                  "_source": {
                    "user_id": "2"
                  }
                },
                {
                  "_index": "stof_63925596",
                  "_type": "_doc",
                  "_id": "3",
                  "_score": 1.0,
                  "_source": {
                    "user_id": "2"
                  }
                }
              ]
            }
          },
          "avg_price": {
            "value": 300.0
          },
          "goods_count": {
            "value": 300.0
          }
        },
        {
          "key": "1",
          "doc_count": 1,
          "top_user_hits": {
            "hits": {
              "total": {
                "value": 1,
                "relation": "eq"
              },
              "max_score": 1.0,
              "hits": [
                {
                  "_index": "stof_63925596",
                  "_type": "_doc",
                  "_id": "1",
                  "_score": 1.0,
                  "_source": {
                    "user_id": "1"
                  }
                }
              ]
            }
          },
          "avg_price": {
            "value": 500.0
          },
          "goods_count": {
            "value": 100.0
          }
        }
      ]
    }
  }
}

Как видно из результатов поиска выше, для "user_id":"2" средняя цена составляет (500+100)/2 = 300, а сумма goods_count равна 100+200 = 300.

Точно так же для "user_id":"1" средняя цена равна 500/1 = 500, а сумма goods_count равна 100.

person user156327    schedule 19.09.2020
comment
Elasticsearch, спасибо за ответ. Концепцию я понял, но в реальной задаче мне придется объединять несколько больших индексов, использовать вложенные объекты, количество которых может достигать тысяч, и строить таблицы с 10-15 колонками в кибане. Можете ли вы сказать мне, подходит ли для этой цели elasticsearch + kibana или мне следует рассмотреть другое решение? - person Артем Савельев; 19.09.2020
comment
@АртемСавельев, спасибо за более подробное объяснение вашего варианта использования, но все же я не знаю полного варианта использования вашего приложения, но это кажется неправильным, сначала ограниченное представление и информация, как агрегация, так и вложенность документы очень дороги в ES, и о них есть несколько статей, по агрегации, пожалуйста, обратитесь к моему последнему ответу на SO stackoverflow.com/a/ 63965634/4039431, а для вложенных ссылок — средний блог Гойека blog.gojekenengineering.com/, чтобы получить некоторое представление о проблемах с производительностью. - person user156327; 19.09.2020
comment
@АртемСавельев, я бы посоветовал вам задать новый вопрос по дизайну с функциональными и нефункциональными требованиями, т.к. сообщество может помочь вам лучше. также, пожалуйста, не забудьте проголосовать и принять мой ответ, если он был полезен, так как это мотивирует меня помогать другим :) - person user156327; 19.09.2020