Объедините результаты нескольких агрегатов - PullRequest
1 голос
/ 05 мая 2020

У меня есть индекс movies, в котором каждый документ имеет такую ​​структуру:

Документ:

{
                    "color": "Color",
                    "director_name": "Sam Raimi",
                    "actor_2_name": "James Franco",
                    "movie_title": "Spider-Man 2",
                    "actor_3_name" : "Brad Pitt",
                    "actor_1_name": "J.K. Simmons"
}

Мне нужно подсчитать количество фильмов, соответствующих каждому актеру (актер может быть как в поле имя_ актера, так и имя_ актера_2 или имя_ актера)

Отображение этих 3 полей:

Отображение

"mappings": {
            "properties": {
                "actor_1_name": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                "actor_2_name": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword",
                            "ignore_above": 256
                        }
                    }
                },
                 "actor_3_name": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                }
            }
       }
}

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

В настоящее время я создаю отдельную агрегацию для каждого поля актера и с помощью моего JAVA кода объединяю эти разные агрегации в одну.

Поисковый запрос, создавая разные агрегации:

Поиск Запрос:

{
    "aggs" : {
        "actor1_count" : {
            "terms" : {
                "field" : "actor_1_name.keyword"
            }
        },
        "actor2_count" : {
            "terms" : {
                "field" : "actor_2_name.keyword"
            }
        },
        "actor3_count" : {
            "terms" : {
                "field" : "actor_3_name.keyword"
            }
        }
    }
}

Результат

Пример результата:

"aggregations": {
"actor1_count": {

            "buckets": [

                {
                    "key": "Johnny Depp",
                    "doc_count": 2
                }
            ]
},

"actor2_count": {

            "buckets": [
                {
                    "key": "Johnny Depp",
                    "doc_count": 1                   }
      ]
},
"actor3_count": {

            "buckets": [

                {
                    "key": "Johnny Depp",
                    "doc_count": 3
                }

           ]
    }
 }

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

В основном это я хочу:

"aggregations": {
    "actor_count": {

                "buckets": [

                    {
                        "key": "Johnny Depp",
                        "doc_count": 6
                    }
                ]
    }
}

(Johnny Depp doc_count должен показывать сумму из всех 3 полей актер_1_name, актер_2_name, актер_3_name везде, где он присутствует)

Я пробовал использовать сценарий, но он не работал.

Запрос сценария:

{
    "aggregations": {
        "name": {
            "terms": {
                "script": "doc['actor_1_name.keyword'].value + ' ' +  doc['actor_2_name.keyword'].value + ' ' + doc['actor_2_name.keyword'].value"
            }
        }
    }
}

Он объединяет имена актеров и затем дает результат.

Результат:

"buckets": [

                {
                    "key": "Steve Buscemi Adam Sandler Adam Sandler",
                    "doc_count": 6
                },
                {
                    "key": "Leonard Nimoy Nichelle Nichols Nichelle Nichols",
                    "doc_count": 4
                }

            ]

1 Ответ

1 голос
/ 05 мая 2020

Это не будет работать с terms. Приходится прибегать к scripted_metric, я думаю:

GET actors/_search
{
  "size": 0,
  "aggs": {
    "merged_actors": {
      "scripted_metric": {
        "init_script": "state.actors_map=[:]",
        "map_script": """
          def actor_keys = ['actor_1_name', 'actor_2_name', 'actor_3_name'];

          for (def key : actor_keys) {

            def actor_name = doc[key + '.keyword'].value;

            if (state.actors_map.containsKey(actor_name)) {
              state.actors_map[actor_name] += 1;
            } else {
              state.actors_map[actor_name] = 1; 
            }
          }
        """,
        "combine_script": "return state",
        "reduce_script": "return states"
      }
    }
  }
}

дает

...
"aggregations" : {
    "merged_actors" : {
      "value" : [
        {
          "actors_map" : {
            "Brad Pitt" : 5,
            "J.K. Simmons" : 1,
            "James Franco" : 3
          }
        }
      ]
    }
  }
...