2013-01-06 17 views
14

Ho bisogno di aggregare (group-by) utilizzando 3 campi in ES.Multiple group-by in Elasticsearch

Posso farlo in 1 query o che ho bisogno di usare un facet + iterate per ogni colonna?

Grazie

+0

https://github.com/ elasticsearch/elasticsearch/issues/256 – ehsanul

risposta

7

Puoi farlo da 2 modi:

1) con campi multipli in un unico risultato sfaccettatura:

esempio per i singoli campi Facet:

curl -X GET "http://localhost:9200/sales/order/_search?pretty=true" -d '{ 
    "query": { 
    "query_string": { 
     "query": "shohi*", 
     "fields": [ 
     "billing_name" 
     ] 
    } 
    }, 
    "facets": { 
    "facet_result": { 
     "terms": { 
     "fields": [ 
      "status" 
     ], 
     "order": "term", 
     "size": 15 
     } 
    } 
    } 
}' 

esempio per più campi in un risultato a singola faccia:

curl -X GET "http://localhost:9200/sales/order/_search?pretty=true" -d '{ 
    "query": { 
    "query_string": { 
     "query": "shohi*", 
     "fields": [ 
     "billing_name" 
     ] 
    } 
    }, 
    "facets": { 
    "facet_result": { 
     "terms": { 
     "fields": [ 
      "status", 
      "customer_gender", 
      "state" 
     ], 
     "order": "term", 
     "size": 15 
     } 
    } 
    } 
}' 

2) uso multiplo sfaccettatura set di risultati:

curl -X GET "http://localhost:9200/sales/order/_search?pretty=true" -d '{ 
    "query": { 
    "query_string": { 
     "query": "*", 
     "fields": [ 
     "increment_id" 
     ] 
    } 
    }, 
    "facets": { 
    "status_facets": { 
     "terms": { 
     "fields": [ 
      "status" 
     ], 
     "size": 50, 
     "order": "term" 
     } 
    }, 
    "gender_facets": { 
     "terms": { 
     "fields": [ 
      "customer_gender" 
     ] 
     } 
    }, 
    "state_facets": { 
     "terms": { 
     "fields": [ 
      "state" 
     ], 
     , 
     "order": "term" 
     } 
    } 
    } 
}' 

Riferimento Link: http://www.elasticsearch.org/guide/reference/api/search/facets/terms-facet.html

+6

Questa è una semplice faccetta di domanda, che dire dei gruppi? Penso che venga chiesto all'OP di raggruppare i risultati della ricerca. (Anche io ho bisogno di raggrupparmi in ES) –

28

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 impostano 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