2017-05-11 4 views
0

Ich bin neu bei ElasticSearch. Zuvor habe ich es nur mit Django-Haystack benutzt, in einer sehr begrenzten Art und Weise und habe nie direkt mit ES gesprochen.Elasticsearch Bestelltermine aggregieren nach Punktzahl

Momentan habe ich einen ElasticSearch Index (5.x, wenn es darauf ankommt) mit ein paar Dokumenten. Ich benutze Python + elasticsearch-dsl + django-elasticsearch-dsl, also indexiere ich Datenbankmodelle, aber es sollte nicht wirklich wichtig sein. Ich werde versuchen, diese Frage Bibliothek-Agnostiker zu verlassen.

Konzeptionell speichern ich Benutzer und ihre Beiträge alle im selben Index. Die Dokumente für Benutzer und für Posts haben eines gemeinsam - ein Feld user_id.

Benutzer wie folgt aussehen:

{ 
    "_id": 1, 
    "_type": "user_document", 
    "username": "jdoe", 
    "user_id": 1, 
    "title": "Test user" 
} 

und Beiträge wie diese sind:

{ 
    "_id": 1, 
    "_doc": "post_document", 
    "user_id": 1, 
    "title": "Hello world!", 
    "text": "Lorem ipsum test test test..." 
} 

Was will ich meine App ist ein Feld, Single-Input-Suche zu implementieren, die Volltextsuche funktioniert über sowohl die Benutzer als auch ihre Posts (in der realen Welt gibt es mehr Dokument- "Typen" - ich vereinfache die Dinge hier, nur zum Beispiel Zwecke). Und ich möchte nach user_id aggregieren, um nur eine Liste der verschiedenen Benutzer anzuzeigen, die übereinstimmten.

Derzeit mache ich Abfrage wie folgt:

{ 
    "query": { 
     "multi_match": { 
      "query": "test", 
      "fields": ["username^3", "title^2", "text"] 
     } 
    }, 
    "aggs": { 
     "user_ids": {"terms": {"field": "user_id"}} 
    } 
} 

Dann Antwort des mit aggregations.user_ids.buckets.key eine Liste passender Benutzer zu erhalten.

Diese Liste scheint jedoch einfach nach der Anzahl der Dokumente sortiert zu sein (wenn der Benutzer also ein Paar Posts mit dem Wort "test" hat, scheint er den Benutzer "test" zu gewinnen), mit dem ich experimentieren möchte Bestellung. Meine derzeitige Idee ist es, einen durchschnittlichen (oder einen Medianwert) Dokument mit _score zu verwenden.

Hinweis: In der realen Situation gibt es mehr als nur zwei Dokumenttypen, so dass eine Shortcut und Abfrage nur über eine bestimmte _type wird nicht funktionieren.

Wie kann ich das tun? Ich lese das "Sorting by a Metric" Kapitel, aber die Ideen dort sind etwas verloren auf mir. Ich machte ein paar Versuche, aber sie waren im Grunde Unsinn. Kann jemand bitte ein konkretes Abfrage-Beispiel zeigen (sehr bevorzugt, mit Erklärung wie es aufgebaut wurde), damit ich daraus lernen kann?

Hier ist die Gist with an example dataset, die Suchabfrage oben gezeigt, und die genauen Ergebnisse, die ich bekomme. Was ich will (in test_query_01_results.json) werden user_id 1 mehr als 2, mit der Logik Priorität zu haben ist, dass 2,0794415> (0,78306973 + 0,45315093)/2.

Eine andere Sache, die ich fühle ich falsch mache ist, dass ich don‘ t verwende hits überhaupt - ich brauche sie nur nicht - nur die aggregierten user_id Werte. Wenn das in Ordnung ist - gibt es eine Möglichkeit, sie zu "deaktivieren" und nur Aggregationen zurückzugeben?

Antwort

3

Verwenden folgende Abfrage

{ 
"size": 0 ,     ==> to return no hits 
"query": {      ==> query similar to yours 
    "multi_match": { 
     "query": "test", 
     "fields": ["username^3", "title^2", "text"] 
    } 
}, 
"aggs": { 
    "user_ids": { 
     "terms": { 
      "field": "user_id", 
      "order": {"avg_score": "desc"} 
     }, 
     "aggs": { 
      "avg_score": { 
       "avg": {"script": "_score"} 
       } 
      } 
     } 
    } 
    } 
+0

Dank. Das ist meine Zeit gerettet. – Arafath

0

Ich denke, ich habe eine Lösung für das Sortieren der Aggregation gefunden. Ich musste eine Sub-Aggregation erstellen, dann hat alles funktioniert. Ich habe mich geirrt und versucht, "order": {"_score: "desc"} und ähnlichen Unsinn zu verwenden, wenn es dort keine _score gab (das ist eine Sammlung von Dokumenten, kein Dokument, also keine Punkte dort).

{ 
    "query": { 
     "multi_match": { 
      "query": "test", 
      "fields": ["username^3", "title^2", "text"] 
     } 
    }, 
    "aggs": { 
     "user_ids": { 
      "terms": { 
       "field": "user_id", 
       "order": {"avg_score": "desc"} 
      }, 
      "aggs": { 
       "avg_score": { 
        "avg": {"script": "_score"} 
       } 
      } 
     } 
    } 
} 

Mit dieser meiner aggregations sieht genauso genau wie ich wollte:

... 
"aggregations": { 
    "user_ids": { 
     "buckets": [ 
      { 
       "avg_score": {"value": 2.079441547393799}, 
       "doc_count": 1, 
       "key": 1 
      }, 
      { 
       "avg_score": {"value": 0.618110328912735}, 
       "doc_count": 2, 
       "key": 2 
      } 
     ], 
     "doc_count_error_upper_bound": 0, 
     "sum_other_doc_count": 0 
    } 
}, 
... 

jedoch die Frage nach hits mit (die ich nicht verwenden) hält immer noch.

Verwandte Themen