Grundlagen der Suche

Nachdem Sie nun einen Elasticsearch-Index erstellt und einige Dokumente hineingeladen haben, können Sie die Volltextsuche implementieren.

So funktioniert die Suche

Werfen wir einen kurzen Blick darauf, wie die Suchfunktion in der Tutorial-Anwendung funktionieren wird. Wenn die Flask-Anwendung läuft, können Sie unter http://localhost:5001 auf die Hauptseite zugreifen, die folgendermaßen aussieht:

Der Code, der diese Seite rendert, ist in der Datei app.py implementiert:

Dies ist ein sehr einfacher Endpunkt, der eine HTML-Vorlage rendert. In Flask-Anwendungen befinden sich die Templates in einem Unterverzeichnis templates . Dort finden Sie dieses und weitere Templates, die zur Anwendung gehören.

Schauen wir uns die Implementierung des Suchfelds in der Datei templates/index.html an. Hier ist der relevante Teil dieser Vorlage:

Hier sehen Sie, dass es sich um ein HTML-Formular mit einem einzigen Feld vom Typ text mit dem Namen query handelt. Das Attribut method des Formulars ist auf POST gesetzt, was dem Browser signalisiert, dieses Formular per POST-Anfrage zu senden. Das Attribut action wird auf die URL gesetzt, die dem handle_search -Endpunkt der Flask-Anwendung entspricht. Wenn das Formular abgeschickt wird, wird die Funktion handle_search() ausgeführt.

Die aktuelle Implementierung von handle_search() ist unten dargestellt:

Die Funktion liest den vom Benutzer im Textfeld eingegebenen Text aus dem request.form -Wörterbuch von Flask aus und speichert ihn in der lokalen Variable query . Die Funktion rendert dann die index.html- Vorlage, übergibt in diesem Fall jedoch einige zusätzliche Argumente, damit die Seite die Suchergebnisse anzeigen kann. Die vier Argumente, die die Vorlage empfängt, sind:

  • query: der vom Benutzer im Formular eingegebene Suchanfragetext.
  • results: eine Liste der Suchergebnisse
  • from_: der nullbasierte Index des ersten Ergebnisses
  • total: die Gesamtzahl der Ergebnisse

Da die Suchfunktion noch nicht implementiert ist, geben die an die Funktion render_template() übergebenen Argumente vorerst an, dass keine Ergebnisse gefunden wurden.

Die Aufgabe besteht nun darin, eine Volltextabfrage zu implementieren und die tatsächlichen Ergebnisse so zu übergeben, dass die index.html -Seite sie anzeigen kann.

Die Elasticsearch-Dienste verwenden eine Query-DSL (Domain Specific Language) auf Basis des JSON-Formats zur Definition von Abfragen.

Der Elasticsearch -Client für Python verfügt über eine search() -Methode, mit der eine Suchanfrage übermittelt werden kann. Fügen wir in search.py eine search() Hilfsmethode hinzu, die diese Methode verwendet:

Diese Methode ruft die search() -Methode des Elasticsearch-Clients mit dem Indexnamen auf. Das Argument query_args erfasst alle Schlüsselwortargumente, die der Methode übergeben werden, und leitet sie dann an die Methode es.search() weiter. Mithilfe dieser Argumente gibt der Aufrufer an, wonach gesucht werden soll.

Suchabfragen

Die Elasticsearch Query DSL bietet viele verschiedene Möglichkeiten, einen Index abzufragen. Beim Durchlesen der Unterabschnitte in der Dokumentation werden Sie mit den verschiedenen möglichen Abfragetypen vertraut gemacht. Die sehr häufige Aufgabe der Textsuche wird im Abschnitt „Volltextabfragen“ behandelt.

Für die erste Suchimplementierung verwenden wir die Match-Abfrage. Nachfolgend sehen Sie ein Beispiel, das diese Abfrage verwendet:

Das obige Beispiel ist in einem Format angegeben, das einer rohen HTTP-Anfrage ähnelt. Es ist hilfreich, mit diesem Format vertraut zu sein, da es in der Elasticsearch-Dokumentation und in der Elasticsearch API Console häufig verwendet wird. Zum Glück lässt sich dieses Format mithilfe der Python-Clientbibliothek sehr einfach in einen Aufruf übersetzen. Nachfolgend sehen Sie den entsprechenden Python-Code für das obige Beispiel:

Bei der Konvertierung von API Console-Beispielen nach Python ist zu beachten, dass die Schlüssel der obersten Ebene im Abfragetext in Schlüsselwortargumente im Python-Aufruf umgewandelt werden müssen. Die Beispiele enthalten auch keine Angabe zu einem Index, der für den Python-Aufruf erforderlich wäre.

Anhand der Abfragestruktur lässt sich wahrscheinlich ableiten, um welche Art von Suche es sich handelt. Der Aufruf fordert eine match -Abfrage auf einem Feld namens name an, und der zu suchende Text ist search text here.

Diese Art von Abfrage lässt sich relativ einfach in die Tutorial-Anwendungen integrieren. Öffnen Sie app.py und suchen Sie die Methode handle_search() . Ersetzen Sie die aktuelle Version durch diese neue:

Der Aufruf von es.search() in der zweiten Zeile dieser neuen Version des Endpunkts ruft die oben in search.py hinzugefügte Methode search() auf. was wiederum die search() -Methode des Elasticsearch-Clients aufruft.

Können Sie herausfinden, was die Abfrage bewirken wird? Dies ist eine match -Abfrage, ähnlich dem obigen Beispiel. Das zu durchsuchende Feld ist name, welches die Titel der Dokumente im my_documents -Index enthält, den Sie im vorherigen Abschnitt erstellt haben. Der zu suchende Text ist das, was der Benutzer im Suchfeld auf der Webseite eingegeben hat und was in der lokalen Variable query gespeichert wird.

Der Teil der Suchergebnisse, der die Ergebnisse enthält, ist response['hits']. Es handelt sich um ein Objekt mit einigen Schlüsseln, von denen zwei für diese Implementierung von Interesse sind:

  • response['hits']['hits']: die Liste der Suchergebnisse.
  • response['hits']['total']: die Gesamtzahl der verfügbaren Ergebnisse. Die Anzahl der Ergebnisse wird in einem Unterschlüssel value angegeben, daher lautet der Ausdruck zur Ermittlung der Gesamtzahl der Ergebnisse in der Praxis results['hits']['total']['value']. Beachten Sie, dass die Gesamtzahl der Ergebnisse bei einer großen Anzahl von Ergebnissen eine Näherung sein kann. Weitere Einzelheiten finden Sie in der Dokumentation zum Antworttext .

Der Aufruf von render_template() in dieser neuen Version des Endpunkts übergibt die Liste der Ergebnisse im Template-Argument results und die Gesamtzahl der Ergebnisse in total. Das Argument query empfängt wie zuvor die Abfragezeichenfolge, und from_ ist noch fest auf 0 codiert, da es erst später implementiert wird, wenn die Paginierung hinzugefügt wird.

Damit verfügt die Anwendung über eine erste Implementierung der Volltextsuche. Kehren Sie zu Ihrem Webbrowser zurück und navigieren Sie zu http://localhost:5001, um die Anwendung zu öffnen. Falls Ihre Flask-Anwendung aus irgendeinem Grund nicht läuft, starten Sie die Anwendung bitte erneut, bevor Sie fortfahren. Geben Sie einen Suchbegriff wie policy oder work from home ein, und Sie erhalten relevante Ergebnisse. Nachfolgend sehen Sie die Ergebnisse der Suche nach work from home:

Die mit der Starter-Anwendung heruntergeladene Vorlage index.html enthält die gesamte Logik zum Rendern von Suchergebnissen. Falls Sie daran interessiert sind, finden Sie hier den Abschnitt dieser Vorlage, der die Ergebnisliste rendert:

Aus diesem Code geht hervor, dass die zu einem zurückgegebenen Ergebnis verknüpften Daten unter dem Schlüssel _source verfügbar sind. Außerdem gibt es ein Feld _id , das die dem Ergebnis zugewiesene eindeutige Kennung enthält.

Aus _score kann eine jedem Ergebnis zugeordnete Punktzahl ermittelt werden. Die Punktzahl dient als Maß für die Relevanz; höhere Punktzahlen deuten auf eine engere Übereinstimmung mit dem Suchanfragetext hin. Standardmäßig werden die Ergebnisse in der Reihenfolge ihrer Punktzahl zurückgegeben, von der höchsten zur niedrigsten. Die Scores in Elasticsearch werden mithilfe des Okapi BM25 -Algorithmus berechnet.

Wenn Sie die in diesem Abschnitt behandelten Themen genauer erkunden möchten, nutzen Sie die folgenden Links:

Abrufen individueller Ergebnisse

Möglicherweise ist Ihnen aufgefallen, dass die Vorlage index.html den Titel jedes Suchergebnisses als Link darstellt. Der Link verweist auf den dritten und letzten Endpunkt, der in der Flask-Starteranwendung implementiert wurde und get_document heißt. Die bereitgestellte Implementierung gibt den fest codierten Text „Dokument nicht gefunden“ zurück. Dies ist also das Ergebnis, das Sie sehen werden, wenn Sie beim Ausprobieren der Anwendung auf eines der Ergebnisse klicken.

Um einzelne Dokumente korrekt darzustellen, fügen wir in search.py eine retrieve_document() -Hilfsmethode hinzu. Verwendung der get() -Methode des Elasticsearch-Clients:

Hier können Sie sehen, wie nützlich diese eindeutigen Kennungen sind, die jedem Dokument zugewiesen wurden, denn die Anwendung kann sie verwenden, um auf einzelne Dokumente zu verweisen.

Hier ist die aktuelle Implementierung des get_document() -Endpunkts:

Sie können sehen, dass die URL, die diesem Endpunkt zugeordnet ist, das Dokument id enthält, und dass die Links, die für jedes Suchergebnis gerendert werden, die ID ebenfalls in den jeweiligen URLs enthalten. Es fehlt also nur noch, diese einfache Implementierung durch eine zu ersetzen, die das Dokument abruft und rendert. Ersetzen Sie den Endpunkt durch diese aktualisierte Version:

Hier wird die retrieve_document() -Methode aus search.py verwendet, um das angeforderte Dokument zu erhalten. Anschließend wird die Datei document.html gerendert, wobei der Titel aus dem Feld name und die Absätze aus dem Feld content stammen.

Versuchen Sie, weitere Abfragen auszuführen und auf die Ergebnisse zu klicken. Dadurch sollte Ihnen nun der vollständige Inhalt angezeigt werden.

Suche in mehreren Feldern

Nachdem Sie die Anwendung eine Weile ausprobiert haben, ist Ihnen vielleicht aufgefallen, dass viele Abfragen keine Ergebnisse liefern. Wie Sie sich erinnern, wird die Suche derzeit im Feld name jedes Dokuments durchgeführt, in dem die Dokumenttitel gespeichert sind. Dokumente verfügen außerdem über die Felder summary und content , die längere Texte enthalten, die ebenfalls durchsucht werden könnten, diese werden jedoch derzeit ignoriert.

In diesem Abschnitt lernen Sie eine weitere häufig verwendete Volltextsuchanfrage kennen, die Mehrfachübereinstimmung, mit der eine Suche in mehreren Feldern eines Index durchgeführt werden soll.

Hier ist das Beispiel einer Mehrfachabfrage aus der Dokumentation:

Nehmen wir dieses Beispiel als Grundlage, um den handle_search() -Endpunkt so zu erweitern, dass Mehrfachabfragen für die kombinierten Felder name, summary und content ausgeführt werden können. Hier ist der aktualisierte Endpunktcode:

Durch diese Änderung gibt es wesentlich mehr Text zu durchsuchen, so viel, dass manche Suchanfragen mehr als die standardmäßig maximal 10 zurückgegebenen Ergebnisse liefern können. Im nächsten Kapitel erfahren Sie, wie Sie mit langen Ergebnislisten durch Paginierung umgehen.

Sind Sie bereit, hochmoderne Sucherlebnisse zu schaffen?

Eine ausreichend fortgeschrittene Suche kann nicht durch die Bemühungen einer einzelnen Person erreicht werden. Elasticsearch wird von Datenwissenschaftlern, ML-Ops-Experten, Ingenieuren und vielen anderen unterstützt, die genauso leidenschaftlich an der Suche interessiert sind wie Sie. Lasst uns in Kontakt treten und zusammenarbeiten, um das magische Sucherlebnis zu schaffen, das Ihnen die gewünschten Ergebnisse liefert.

Probieren Sie es selbst aus