2014-12-16 19 views
78

Ho una struttura padre/figlio in 3 livelli. Diciamo:
ElasticSearch aggregazione padre-figlio multi livello

Company -> dipendenti -> Disponibilità

Dal disponibilità (e anche dei dipendenti) è aggiornato frequentemente qui, ho scelto usando la struttura genitore/bambino contro annidati. E la funzione di ricerca funziona bene (tutti i documenti in frammenti corretti).

Ora voglio ordinare quei risultati. Ordinarli per meta data dalla compagnia (1 ° livello) è facile. Ma ho bisogno di ordinare anche per il 3 ° livello (disponibilità).

Voglio elenco di aziende che sono allineati secondo:

  • Distanza dalla posizione indicata ASC
  • Valutazione DECR
  • Soonest disponibilità ASC

Ad esempio:

società A è a 5 miglia di distanza, ha 4 e il più presto uno dei loro dipendenti è disponibile in 20 ore s La compagnia B si trova a 5 miglia di distanza, ha anche una valutazione 4 ma il più presto uno dei suoi dipendenti è disponibile in 5 ore.

Pertanto sort Risultato necessità di essere B, A.

Vorrei aggiungere un peso particolare a ciascuno di questi dati, così ho iniziato a scrivere aggregazioni che ho potuto poi utilizzare nel mio script custom_score.

Full gist for creating index, importing data and searching

Ora, sono riuscito a scrivere una query che restituisce in realtà di nuovo risultato, ma la disponibilità di aggregazione secchio è vuoto. Tuttavia, sto anche ottenendo risultati troppo strutturati, vorrei appiattirli.

Attualmente torno:

società IDS -> Employee IDS -> primo disponibilità

mi piacerebbe avere l'aggregazione come:

società IDS -> prima disponibilità

In questo modo sono in grado di eseguire lo script custom_score per calcolare il punteggio e ordinarli correttamente.

Ulteriori domande semplificate:
Come è possibile ordinare/aggregare per più livelli (grandi) bambini e eventualmente appiattire il risultato.

+0

Potrebbe aggiungere il mappatura e alcuni documenti di esempio (con i discendenti) per la sintesi? È difficile vedere come inventare documenti falsi che consentano test adeguati del tuo sistema. –

+0

Hey Sloan - Ho aggiunto mappatura e risultati campione. L'ho spogliato per una comprensione più semplice. Lo stack completo ha molti più dati in esso :) Grazie! –

+0

Ho avuto la stessa domanda [qui] (http://elasticsearch-users.115913.n3.nabble.com/Help-Flattened-aggregations-with-limiting-and-sorting-td4065217.html). Anche se probabilmente meno performante, richiedo solo tutti i risultati che hanno un tipo predefinito di DocCount. Poi ho fatto il mio appiattimento ricorsivo, lo smistamento e la limitazione, che non era l'ideale. –

risposta

3

Non è necessario aggregazioni di fare questo:

Questi sono i criteri di ordinamento:

  1. Distanza ASC (company.location)
  2. Valutazione DECR (company.rating_value)
  3. Soonest Future Availability ASC (company.employee.availability.start)

Se si ignora il numero 3, allora si può essere eseguito relativamente semplice azienda query come questa:

GET /companies/company/_search 
{ 
"query": { "match_all" : {} }, 
"sort": { 
    "_script": { 
     "params": { 
      "lat": 51.5186, 
      "lon": -0.1347 
     }, 
     "lang": "groovy", 
     "type": "number", 
     "order": "asc", 
     "script": "doc['location'].distanceInMiles(lat,lon)" 
    }, 
    "rating_value": { "order": "desc" } 
    } 
} 

# 3 è difficile perché è necessario per raggiungere il basso e trovare la disponibilità (azienda> dipendente> disponibilità) per ciascuna società più vicina al tempo della richiesta e utilizzare tale durata come terzo criterio di ordinamento.

Utilizzeremo una query function_score a livello di nipote per misurare la differenza di orario tra il tempo di richiesta e ogni disponibilità nell'attacco _score. (Quindi utilizzeremo lo _score come terzo criterio di ordinamento).

Per raggiungere i nipoti è necessario utilizzare una query has_child all'interno di una query has_child.

Per ciascuna azienda desideriamo il Dipendente più presto disponibile (e ovviamente la loro disponibilità più vicina). Elasticsearch 2.0 ci darà un "score_mode": "min" per casi come questo, ma per ora, dal momento che siamo limitati a "score_mode": "max" faremo il nipote _score il reciproco della differenza di tempo.

  "function_score": { 
      "filter": { 
       "range": { 
       "start": { 
        "gt": "2014-12-22T10:34:18+01:00" 
       } 
       } 
      }, 
      "functions": [ 
       { 
       "script_score": { 
        "lang": "groovy", 
        "params": { 
         "requested": "2014-12-22T10:34:18+01:00", 
         "millisPerHour": 3600000 
        }, 
        "script": "1/((doc['availability.start'].value - new DateTime(requested).getMillis())/millisPerHour)" 
       } 
       } 
      ] 
      } 

Così ora la _score per ogni nipote (disponibilità) sarà 1/number-of-hours-until-available (in modo da poter utilizzare il massima tempo reciproca fino disponibili per dipendente, e la massima reciproca (ly?) disponibili Dipendente per azienda).

Mettere tutto insieme, continuiamo ad interrogare società ma utilizzare azienda> dipendente> Disponibilità per generare il _score da utilizzare come # 3 Criterio di ordinamento:

GET /companies/company/_search 
{ 
"query": { 
    "has_child" : { 
     "type" : "employee", 
     "score_mode" : "max", 
     "query": { 
      "has_child" : { 
      "type" : "availability", 
      "score_mode" : "max", 
      "query": { 
       "function_score": { 
       "filter": { 
        "range": { 
        "start": { 
         "gt": "2014-12-22T10:34:18+01:00" 
        } 
        } 
       }, 
       "functions": [ 
        { 
        "script_score": { 
         "lang": "groovy", 
         "params": { 
          "requested": "2014-12-22T10:34:18+01:00", 
          "millisPerHour": 3600000 
         }, 
         "script": "1/((doc['availability.start'].value - new DateTime(requested).getMillis())/millisPerHour)" 
        } 
        } 
       ] 
       } 
      } 
      } 
     } 
    } 
}, 
"sort": { 
    "_script": { 
    "params": { 
     "lat": 51.5186, 
     "lon": -0.1347 
    }, 
    "lang": "groovy", 
    "type": "number", 
    "order": "asc", 
    "script": "doc['location'].distanceInMiles(lat,lon)" 
    }, 
    "rating_value": { "order": "desc" }, 
    "_score": { "order": "asc" } 
} 
} 
+0

È possibile ottenere prestazioni leggermente migliori utilizzando una [funzione di decadimento lineare] (https://www.elastic.co/guide/en/elasticsearch/reference/1.6/query-dsl-function-score-query.html#function-decay) piuttosto che uno script per generare '_score' da * time-until-available *. –

+0

Elasticsearch ha disattivato lo scripting dinamico per impostazione predefinita. Meglio usare gli script indicizzati. Vedi qui: https://www.elastic.co/blog/running-groovy-scripts-without-dynamic-scripting – schellingerht

+0

Pete Minus: Sei riuscito a farlo funzionare? So che questa è una domanda più vecchia, tuttavia ci sono molte persone interessate alla tua soluzione. –

Problemi correlati