Вопрос по group-by, faceted-search, elasticsearch, facet – Группа ElasticSearch по нескольким полям

32

Единственная близкая вещь, которую янайдено было:Несколько в группе в Elasticsearch

В основном яm пытается получить эквивалент ES следующего запроса mysql:

select gender, age_range, count(distinct profile_id) as count FROM TABLE group by age_range, gender

Возраст и пол сами по себе было легко получить:

{
  "query": {
    "match_all": {}
  },
  "facets": {
    "ages": {
      "terms": {
        "field": "age_range",
        "size": 20
      }
    },
    "gender_by_age": {
      "terms": {
        "fields": [
          "age_range",
          "gender"
        ]
      }
    }
  },
  "size": 0
}

который дает:

{
  "ages": {
    "_type": "terms",
    "missing": 0,
    "total": 193961,
    "other": 0,
    "terms": [
      {
        "term": 0,
        "count": 162643
      },
      {
        "term": 3,
        "count": 10683
      },
      {
        "term": 4,
        "count": 8931
      },
      {
        "term": 5,
        "count": 4690
      },
      {
        "term": 6,
        "count": 3647
      },
      {
        "term": 2,
        "count": 3247
      },
      {
        "term": 1,
        "count": 120
      }
    ]
  },
  "total_gender": {
    "_type": "terms",
    "missing": 0,
    "total": 193961,
    "other": 0,
    "terms": [
      {
        "term": 1,
        "count": 94799
      },
      {
        "term": 2,
        "count": 62645
      },
      {
        "term": 0,
        "count": 36517
      }
    ]
  }
}

Но теперь мне нужно что-то похожее на это:

[breakdown_gender] => Array
    (
        [1] => Array
            (
                [0] => 264
                [1] => 1
                [2] => 6
                [3] => 67
                [4] => 72
                [5] => 40
                [6] => 23
            )

        [2] => Array
            (
                [0] => 153
                [2] => 2
                [3] => 21
                [4] => 35
                [5] => 22
                [6] => 11
            )

    )

Обратите внимание, что0,1,2,3,4,5,6 являются "отображения» для возрастных диапазонов, так что они на самом деле что-то значат :), а не только цифры. например Пол [1] (который является "мужчина") разбивается на возрастной диапазон [0]до 18") со счетом 246.I '

мне нравится, когда я звоню, используя curl 3 {"ошибка" : {"первопричина" : [{"тип" : "parsing_exception ","причина ":"Неизвестный ключ для START_OBJECT в [facets]. ","линия" : 6 "Col» : 13}], "тип" : "parsing_exception ","причина ":"Неизвестный ключ для START_OBJECT в [facets]. ","линия" : 6 "Col» : 13}, "статус" : 400} Yagnesh bhalala

Ваш Ответ

2   ответа
63

Начиная с версии 1.0ElasticSearch, новыйагрегирование API позволяет группировать по нескольким полям, используясуб-агрегирование, Предположим, вы хотите сгруппировать по полям,field1field2 а также :field3

{
  "aggs": {
    "agg1": {
      "terms": {
        "field": "field1"
      },
      "aggs": {
        "agg2": {
          "terms": {
            "field": "field2"
          },
          "aggs": {
            "agg3": {
              "terms": {
                "field": "field3"
              }
            }
          }          
        }
      }
    }
  }
}

Конечно, это может длиться столько полей, сколько вымне нравится

Обновить:

Для полноты, вот как выглядит результат вышеприведенного запроса. Также ниже приведен код Python для генерации запроса агрегации и сведения результата в список словарей.

{
  "aggregations": {
    "agg1": {
      "buckets": [{
        "doc_count": <count>,
        "key": <value of="" field1="">,
        "agg2": {
          "buckets": [{
            "doc_count": <count>,
            "key": <value of="" field2="">,
            "agg3": {
              "buckets": [{
                "doc_count": <count>,
                "key": <value of="" field3="">
              },
              {
                "doc_count": <count>,
                "key": <value of="" field3="">
              }, ...
              ]
            },
            {
            "doc_count": <count>,
            "key": <value of="" field2="">,
            "agg3": {
              "buckets": [{
                "doc_count": <count>,
                "key": <value of="" field3="">
              },
              {
                "doc_count": <count>,
                "key": <value of="" field3="">
              }, ...
              ]
            }, ...
          ]
        },
        {
        "doc_count": <count>,
        "key": <value of="" field1="">,
        "agg2": {
          "buckets": [{
            "doc_count": <count>,
            "key": <value of="" field2="">,
            "agg3": {
              "buckets": [{
                "doc_count": <count>,
                "key": <value of="" field3="">
              },
              {
                "doc_count": <count>,
                "key": <value of="" field3="">
              }, ...
              ]
            },
            {
            "doc_count": <count>,
            "key": <value of="" field2="">,
            "agg3": {
              "buckets": [{
                "doc_count": <count>,
                "key": <value of="" field3="">
              },
              {
                "doc_count": <count>,
                "key": <value of="" field3="">
              }, ...
              ]
            }, ...
          ]
        }, ...
      ]
    }
  }
}
</value></count></value></count></value></count></value></count></value></count></value></count></value></count></value></count></value></count></value></count></value></count></value></count></value></count></value></count>

Следующий код Python выполняет группирование по заданному списку полей. Я тебе уточняюinclude_missing=Trueтакже включает в себя комбинации значений, в которых некоторые поля отсутствуютэто нужно, если у вас есть версия 2.0 Elasticsearch благодаряэтот)

def group_by(es, fields, include_missing):
    current_level_terms = {'terms': {'field': fields[0]}}
    agg_spec = {fields[0]: current_level_terms}

    if include_missing:
        current_level_missing = {'missing': {'field': fields[0]}}
        agg_spec[fields[0] + '_missing'] = current_level_missing

    for field in fields[1:]:
        next_level_terms = {'terms': {'field': field}}
        current_level_terms['aggs'] = {
            field: next_level_terms,
        }

        if include_missing:
            next_level_missing = {'missing': {'field': field}}
            current_level_terms['aggs'][field + '_missing'] = next_level_missing
            current_level_missing['aggs'] = {
                field: next_level_terms,
                field + '_missing': next_level_missing,
            }
            current_level_missing = next_level_missing

        current_level_terms = next_level_terms

    agg_result = es.search(body={'aggs': agg_spec})['aggregations']
    return get_docs_from_agg_result(agg_result, fields, include_missing)


def get_docs_from_agg_result(agg_result, fields, include_missing):
    current_field = fields[0]
    buckets = agg_result[current_field]['buckets']
    if include_missing:
        buckets.append(agg_result[(current_field + '_missing')])

    if len(fields) == 1:
        return [
            {
                current_field: bucket.get('key'),
                'doc_count': bucket['doc_count'],
            }
            for bucket in buckets if bucket['doc_count'] > 0
        ]

    result = []
    for bucket in buckets:
        records = get_docs_from_agg_result(bucket, fields[1:], include_missing)
        value = bucket.get('key')
        for record in records:
            record[current_field] = value
        result.extend(records)

    return result
Ну, я был назначен на другой проект в настоящее время. но все равно спасибо Makan Tayebi
Я кодирую с PHP. Я мог бы справиться с этой конкретной задачей с помощью модуля C, но, конечно, яЯ бы предпочел, чтобы эластичный поиск делал это самостоятельно. Makan Tayebi
@HappyCoder - можете ли вы добавить более подробную информацию о проблеме, которую вы 'есть? Например - какой запрос выповторно используете? Joe
Я получаю сообщение об ошибке типа нераспознанного токена "мои поля значения " , Как я могу это исправить ? Happy Coder
17

простой способ - выполнить два запроса с одним фасетом. Для мужчины:

{
    "query" : {
      "term" : { "gender" : "Male" }
    },
    "facets" : {
        "age_range" : {
            "terms" : {
                "field" : "age_range"
            }
        }
    }
}

И для женщин:

{
    "query" : {
      "term" : { "gender" : "Female" }
    },
    "facets" : {
        "age_range" : {
            "terms" : {
                "field" : "age_range"
            }
        }
    }
}

Или вы можете сделать это в одном запросе с фасетным фильтром (см.эта ссылка для дополнительной информации)

{
    "query" : {
       "match_all": {}
    },
    "facets" : {
        "age_range_male" : {
            "terms" : {
                "field" : "age_range"
            },
            "facet_filter":{
                "term": {
                    "gender": "Male"
                }
            }
        },
        "age_range_female" : {
            "terms" : {
                "field" : "age_range"
            },
            "facet_filter":{
                "term": {
                    "gender": "Female"
                }
            }
        }
    }
}

Обновить:

Поскольку грани вот-вот будут удалены. Это решение с агрегатами:

{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "male": {
      "filter": {
        "term": {
          "gender": "Male"
        }
      },
      "aggs": {
        "age_range": {
          "terms": {
            "field": "age_range"
          }
        }
      }
    },
    "female": {
      "filter": {
        "term": {
          "gender": "Female"
        }
      },
      "aggs": {
        "age_range": {
          "terms": {
            "field": "age_range"
          }
        }
      }
    }
  }
}
Отлично! Спасибо! Pavel
По состоянию на среду, 28 октября 2015 года, на официальном веб-сайтеasticsearch говорится "Аспекты устарели и будут удалены в следующем выпуске. Вместо этого вам предлагается перейти на агрегаты ". Ankur
я могу иметь date_histogram как одну агрегацию? Clindo

Похожие вопросы