2013-08-26 26 views
23

Jedyną rzeczą, która blisko Znalazłem to: Multiple group-by in ElasticsearchElasticSearch grupa przez wielu dziedzinach

Zasadniczo próbuję dostać równowartość ES z poniższym zapytaniu MySQL:

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

wiek i Płeć same były łatwo dostać:

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

co daje:

{ 
    "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 
     } 
    ] 
    } 
} 

Ale teraz muszę coś, co wygląda tak:

[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 
      ) 

    ) 

Należy pamiętać, że 0,1,2,3,4,5,6 są „mapowania” na wiek waha się więc rzeczywiście coś znaczy :) i nie tylko liczby. na przykład Płeć [1] (która jest "męska") rozkłada się na przedział wiekowy [0] (który jest "poniżej 18") z liczbą 246.

Odpowiedz

15

Ponieważ masz tylko 2 pola, w prosty sposób możesz wykonać dwie kwerendy z pojedynczymi fasetami. Dla mężczyzn:

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

A dla kobiety:

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

Można to zrobić w jednym zapytaniu z filtrem facet (patrz this link celu uzyskania dalszych informacji)

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

Update:

Ponieważ fasety mają zostać usunięte. Jest to rozwiązanie z agregacji:

{ 
    "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" 
      } 
     } 
     } 
    } 
    } 
} 
+0

Perfect! Dziękuję Ci! – Pavel

+1

W środę, 28 października 2015 r. Oficjalna strona elasticsearch stwierdza: "Aspekty są przestarzałe i zostaną usunięte w przyszłej wersji.Jesteś zachęcany do migracji do agregacji zamiast ". – Ankur

+0

Czy mogę mieć date_histogram jako jedną agregację? – Clindo

48

Począwszy od wersji 1.0 ElasticSearch nowa aggregations API umożliwia grupowanie według wielu pól przy użyciu podrzędne agregacje. Załóżmy, że chcesz do grupy pól field1, field2 i field3:

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

Oczywiście może to trwać tak wielu dziedzinach, jak chcesz.

Aktualizacja:
Dla kompletności, tutaj jest jak wyjście z powyższym zapytaniu wygląda. Poniżej znajduje się również kod Pythona do generowania kwerendy agregacji i spłaszczenia wyniku do listy słowników.

{ 
    "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> 
       }, ... 
       ] 
      }, ... 
      ] 
     }, ... 
     ] 
    } 
    } 
} 

Następujący kod Pythona wykonuje grupę przez podanie listy pól. I podać include_missing=True, zawiera również kombinacje wartości gdzie niektóre pola są brakujące (nie trzeba go, jeśli masz wersję 2.0 Elasticsearch dzięki this)

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 
+0

Otrzymuję błąd jak Nierozpoznany tokenu" moje wartości pól ".Jak mogę to naprawić? –

+0

za pomocą sub-agregacji dla dużych danych i zmian format jego odpowiedzi na tabelę z dwiema kolumnami z prostym kodowaniem może trwać dość długo. Czy istnieje inny sposób, aby to zrobić? –

+0

@HappyCoder - czy możesz dodać więcej szczegółów na temat problemu? jest kwerenda, której używasz? – Joe