2014-12-16 12 views
78

Ich habe eine Eltern/Kind-Struktur in 3 Ebenen. Lassen Sie uns sagen:
ElasticSearch Multi-Level-Eltern-Kind-Aggregation

Unternehmen -> Mitarbeiter -> Verfügbarkeit

Seit Verfügbarkeit (und auch Mitarbeiter) hier häufig aktualisiert wird, wähle ich mit Eltern/Kind-Struktur gegen verschachtelt. Und Suchfunktion funktioniert gut (alle Dokumente in richtigen Shards).

Jetzt möchte ich diese Ergebnisse sortieren. Die Sortierung nach Metadaten aus der Firma (1. Ebene) ist einfach. Aber ich muss auch nach 3rd Level sortieren (Verfügbarkeit).

Ich möchte eine Liste von Unternehmen, die sind wie folgt sortiert:

  • Entfernung vom Standort gegeben ASC
  • Rating, absteigend
  • Soonest Verfügbarkeit ASC

Zum Beispiel:

Unternehmen A ist 5 Meilen entfernt, hat eine Bewertung von 4 und am schnellsten ist einer seiner Mitarbeiter in 20 Stunden verfügbar s Firma B ist auch 5 Meilen entfernt, hat auch Bewertung 4, aber bald ist einer ihrer Mitarbeiter in 5 Stunden zur Verfügung.

Daher ergeben Art Bedürfnisse B, A.

Ich möchte anfügen besonderes Gewicht auf jede dieser Daten sein, und so begann ich Aggregationen zu schreiben, die ich später in meinem custom_score Skript verwenden könnte.

Full gist for creating index, importing data and searching

Jetzt habe ich es geschafft, eine Abfrage zu schreiben, die tatsächlich Ergebnis zurückgibt zurück, aber die Verfügbarkeit Aggregation Eimer leer ist. Allerdings bekomme ich auch Ergebnisse zu strukturiert, ich würde sie gerne flacher machen.

Zur Zeit komme ich zurück:

Firma IDS -> Mitarbeiter IDS -> erste Verfügbarkeit

Ich möchte Aggregation wie haben:

Firma IDS -> erste Verfügbarkeit

So kann ich mein custom_score Skript zur Berechnung der Punkte und sortieren sie richtig.

Mehr vereinfachte Frage:
Wie kann eine Sorte/Aggregat durch Multi-Level (Enkel-) Kinder und möglicherweise das Ergebnis glätten.

+0

Könnten Sie fügen Sie Ihre Mapping und ein paar Beispieldokumente (mit Nachkommen) zum Kern? Es ist schwer zu sehen, wie man gefälschte Dokumente erfindet, die ein adäquates Testen Ihres Systems erlauben. –

+0

Hey Sloan - Ich habe Mapping und Sample-Ergebnisse hinzugefügt. Ich habe es zum besseren Verständnis entfernt. Full Stack hat viel mehr Daten drin :) Danke! –

+0

Ich hatte die gleiche Frage [hier] (http://elasticsearch-users.115913.n3.nabble.com/Help-Flattened-aggregations-with-limiting-and-sorting-td4065217.html). Obwohl wahrscheinlich weniger performant, fordere ich nur alle Ergebnisse an, die eine Standardart von DocCount haben. Ich machte dann meine eigene rekursive Abflachung, Sortierung und Begrenzung, was nicht ideal war. –

Antwort

3

Sie haben keine Aggregationen müssen dies tun:

Dies sind die Sortierkriterien:

  1. Entfernung ASC (company.location)
  2. Bewertung DESC (company.rating_value)
  3. Soonest Future Verfügbarkeit ASC (company.employee.availability.start)

Wenn Sie # 3 ignorieren, dann Sie laufen kann eine relativ einfache Firma Abfrage wie folgt: ist

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 heikel, weil Sie nach unten und finden, um die Verfügbarkeit erreichen müssen (Unternehmen> Mitarbeiter> Verfügbarkeit) für jedes Unternehmen am nächsten Zeitpunkt der Anfrage und verwenden Sie diese Dauer als dritte Sortierkriterium.

Wir werden eine function_score Abfrage auf der Enkel-Ebene verwenden, um den Zeitunterschied zwischen der Anfragezeit und jeder Verfügbarkeit in dem Treffer _score zu nehmen. (Dann verwenden wir das _score als drittes Sortierkriterium).

Um die Enkelkinder zu erreichen, müssen wir eine has_child Abfrage innerhalb einer has_child Abfrage verwenden.

Für jede Firma möchten wir den frühesten verfügbaren Mitarbeiter (und natürlich ihre nächste Verfügbarkeit). Elasticsearch 2.0 wird uns eine "score_mode": "min" für solche Fälle geben, aber jetzt, da wir auf "score_mode": "max" beschränkt sind, machen wir den Enkel _score den Kehrwert der Zeitdifferenz.

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

So, jetzt die _score für jedes Enkelkind (Verfügbarkeit) wird 1/number-of-hours-until-available sein (so, dass wir die maximale gegenseitige Zeit bis verfügbar pro Mitarbeiter verwenden können, und die maximale reziproke (ly?) verfügbar Mitarbeiter pro Firma).

setzen sie alle zusammen, wir weiterhin Firma aber verwenden Unternehmen> Mitarbeiter> Kalender zu erzeugen, um die _score zu verwenden, wie die # 3 Sortierkriterium abfragen:

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

Mit einer [linearen Zerfallsfunktion] (https://www.elastic.co/guide/en/elasticsearch/reference/1.6/query-dsl-function-score-query.html#function-decay) können Sie eine etwas bessere Leistung erzielen) anstatt eines Skripts, um '_score' von * time-to-available * zu generieren. –

+0

Elasticsearch deaktiviert standardmäßig dynamisches Scripting. Besser ist es, indexierte Skripte zu verwenden. Siehe hier: https://www.elastic.co/blog/running-groovy-scripts-without-dynamic-scripting – schellingerht

+0

Pete Minus: Konnten Sie das funktionieren? Ich weiß, dass dies eine ältere Frage ist, aber es gibt eine Menge Leute, die an Ihrer Lösung interessiert sind. –

Verwandte Themen