2013-08-26 18 views
23

L'unica cosa vicino che ho trovato è stato: Multiple group-by in Elasticsearchgruppo elasticsearch da molteplici campi

Fondamentalmente sto cercando di ottenere l'equivalente ES del seguente query mysql:

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

L'età e genere da loro stessi erano facili da ottenere:

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

che dà:

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

Ma ora ho bisogno di qualcosa che assomiglia a questo:

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

    ) 

prega di notare che 0,1,2,3,4,5,6 sono "mappature" per l'età varia in modo che in realtà significano qualcosa :) e non solo numeri. per esempio. Gender [1] (che è "maschio") suddivide in fascia di età [0] (che è "under 18") con un conteggio di 246.

risposta

15

Come hai solo 2 campi un modo semplice sta facendo due query con singole sfaccettature. Per maschile:

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

E per femminile:

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

Oppure si può fare in una singola query con un filtro sfaccettatura (vedi this link per ulteriori informazioni)

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

Aggiornamento:

Come sfaccettature stanno per essere rimosse. Questa è la soluzione con aggregazioni:

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

Perfetto! Grazie! – Pavel

+1

Come per mercoledì 28 ottobre 2015, il sito web ufficiale di elasticsearch recita "Le faccette sono deprecate e verranno rimosse in una versione futura.Sei incoraggiato a migrare alle aggregazioni invece. " – Ankur

+0

posso avere data_histogram come una aggregazione? – Clindo

48

A partire dalla versione 1.0 del ElasticSearch, il nuovo aggregations API permette raggruppamento per più campi, utilizzando sotto-aggregazioni. Si supponga di voler gruppo da campi field1, field2 e field3:

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

Naturalmente questo può andare avanti per il maggior numero di campi come vuoi.

Aggiornamento:
Per completezza, ecco come l'output della query sopra appare. Di seguito è riportato anche il codice Python per generare la query di aggregazione e appiattire il risultato in un elenco di dizionari.

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

Il seguente codice python esegue il gruppo dato l'elenco di campi. Mi si specifica include_missing=True, include anche combinazioni di valori in cui alcuni dei campi sono mancanti (non è necessario se si ha la versione 2.0 del elasticsearch grazie alla 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

Ricevo un errore come token non riconosciuto" valore dei miei campi ". Come posso risolvere questo? –

+0

utilizzando le sotto-aggregazioni per i dati di grandi dimensioni e la modifica il formato della risposta a una tabella a due colonne con una semplice codifica può richiedere un tempo piuttosto lungo. C'è un altro modo per farlo? –

+0

@HappyCoder: puoi aggiungere ulteriori dettagli sul problema che stai riscontrando? è la query che stai utilizzando? – Joe