Elasticsearch: Die flexible Search Engine

Wer eine leistungsstarke Volltextsuche benötigt, wählt meist Apache Solr. Und dieses Projekt ist auch weiterhin eine gute Wahl – doch seit 2010 bietet der Markt eine interessante Alternative: Elasticsearch. Genau wie Solr basiert auch Elasticsearch auf Apache Lucene, wartet aber mit anderen Features auf. Wir erläutern die Merkmale des Suchservers und erklären Ihnen in unserem Elasticsearch-Tutorial, wie Sie die Volltextsuche für Ihr eigenes Projekt implementieren.

Inzwischen gehört Elasticsearch zu den wichtigsten Volltextsuchmaschinen im Internet. Auch große Unternehmen nutzen die Software: Facebook beispielsweise arbeitet seit mehreren Jahren erfolgreich mit Elasticsearch und auch GitHub, Netflix, SoundCloud und Zalando setzen auf die erfolgreiche Suchmaschine.

Was ist Elasticsearch?

Angesichts der Menge an Informationen auf manchen Websites lässt sich eine hohe Benutzerfreundlichkeit nur garantieren, wenn man eine funktionstüchtige Volltextsuche implementiert. Wer nicht auf Angebote von Google oder Bing zurückgreifen möchte, um seinen Besuchern eine Suchfunktion anzubieten, der muss stattdessen eine eigene Suchfunktion einbetten. Dies geht zum Beispiel mit Elasticsearch. Das Open-Source-Projekt basiert auf dem ebenfalls frei verfügbaren Apache Lucene.

Elasticsearch bietet die Vorteile des stabilen Vorgängers und baut diese mit weiteren Features aus. Genau wie bei Lucene funktioniert die Suche über einen Index: Statt bei einer Suchanfrage alle Dokumente zu untersuchen, überprüft das Programm einen zuvor erstellten Index der Dokumente, in dem alle Inhalte aufbereitet gespeichert sind. Dieser Prozess benötigt sehr viel weniger Zeit als die Suche in sämtlichen Dokumenten.

Während Sie mit Lucene vollkommen freie Hand haben, wo und wie Sie die Volltextsuche einsetzen, müssen Sie bei dieser Software allerdings vollkommen von Null anfangen. Elasticsearch ermöglicht Ihnen hingegen für den Einsatz im World Wide Web einen schnelleren Einstieg. So ist es mit Elasticsearch in kurzer Zeit möglich, einen stabilen Suchserver aufzubauen, der zudem noch leicht auf mehrere Maschinen verteilt werden kann.

Mehrere Knoten (verschiedene Server) schließen sich zusammen und bilden einen Cluster. Dabei kommt das sogenannte Sharding zum Einsatz: Elasticsearch bricht hierbei den Index auf und verteilt die einzelnen Teile (shards) auf mehrere Knoten. Dadurch wird auch die Rechenlast aufgeteilt – bei großen Projekten läuft die Volltextsuche so deutlich stabiler. Für mehr Sicherheit sorgt man, indem man die Shards zusätzlich auf mehrere Knoten kopiert.

Elaticsearch basiert – genau wie Lucene – auf der objektorientierten Programmiersprache Java. Die Suchmaschine gibt Suchergebnisse im JSON-Format aus und liefert diese über einen REST-Webservice aus. Das API macht es sehr leicht, die Suchfunktion in eine Website einzubauen.

Zudem bietet Elasticsearch mit Kibana, Beats und Logstash – zusammen als Elastic-Stack bekannt – praktische Zusatzdienste, mit denen Sie die Volltextsuche analysieren können. Die Firma Elastic, die hinter der Entwicklung von Elasticsearch steckt und vom Erfinder des Programms gegründet wurde, bietet zudem auch kostenpflichtige Dienste an – beispielsweise Cloud-Hosting.

Was bietet Elasticsearch, was Google & Co. nicht haben?

Es gibt doch bereits Google – und dessen populäre Suchfunktion können Sie problemlos in die eigene Website integrieren. Warum also sollte man sich für den mühsameren Schritt entscheiden und eine eigene Suchmaschine mit Elasticsearch bauen? Eine mögliche Antwort: Mit Google Custom Search (GCS) machen Sie sich abhängig vom Suchmaschinenriesen und lassen (zumindest in der kostenlosen Version) Werbung in den Suchergebnissen zu. Das können Sie mit Elasticsearch umgehen: Der Code ist Open Source, und wenn Sie eine Volltextsuchfunktion aufgesetzt haben, gehört diese auch Ihnen – Sie sind von niemandem abhängig.

Darüber hinaus können Sie Elasticsearch ganz nach Ihren Wünschen anpassen. Wenn Sie zum Beispiel eine eigene Online-Plattform oder einen E-Shop betreiben, können Sie die Suchfunktion so einrichten, dass Sie mithilfe von Elasticsearch auch die Profile der angemeldeten Nutzer durchsuchen können. Bei solchen Anwendungsbereichen stößt GCS an seine Grenzen.

Elasticsearch vs. Apache Solr: Was sind die wichtigsten Unterschiede?

Sowohl Elasticsearch als auch Apache Solr basieren auf Lucene, wurden aber zu eigenständigen Angeboten weiterentwickelt. Viele Nutzer fragen sich jedoch, für welches Projekt sie sich entscheiden sollten. Auch wenn Elasticsearch etwas jünger ist und im Gegensatz zu Solr nicht durch die erfahrene Apache-Community gestützt wird, hat man das Schwesterprojekt inzwischen in puncto Nutzerzahlen überholt. Grund dafür ist vor allem die einfachere Implementierung. Darüber hinaus ist Elasticsearch vor allem aufgrund seines Umgangs mit dynamischen Daten beliebt: Durch ein spezielles Caching-Verfahren schafft es Elasticsearch, dass Änderungen nicht im gesamten globalen Cache eingetragen werden müssen. Stattdessen reicht es, ein kleines Segment zu ändern. Das macht Elasticsearch flexibler.

Letzten Endes hat die Entscheidung aber oft lediglich mit den unterschiedlichen Ansätzen im Zuge von Open Source zu tun. Solr verpflichtet sich ganz dem Gedanken der Apache Software Foundation: Community over Code. Jeder Beitrag am Code wird ernstgenommen und die Gemeinschaft entscheidet gemeinsam, welche Ergänzungen und Weiterentwicklungen es in den finalen Code schaffen. Bei der Weiterentwicklung von Elasticsearch ist das anders: Auch dieses Projekt ist Open Source und wird unter einer freien Apache-Lizenz angeboten – allerdings bestimmt allein das Team von Elastic, welche Änderungen es in den Code schaffen. Gegen diese Gatekeeper-Mentalität wehren sich einige Entwickler und entscheiden sich daher für Solr.

Elasticsearch-Tutorial

Wenn man beginnt, mit Elasticsearch zu arbeiten, sollte man sich zunächst mit einigen Grundbegriffen auseinandersetzen. So ist zum Beispiel die Informationsstruktur erwähnenswert:

  • Index: Eine Suchanfrage bei Elasticsearch gilt nie den Inhalten selbst, sondern immer dem Index. In diesem sind alle Inhalte sämtlicher Dokumente gespeichert und bereits aufbereitet – dadurch erfordert die Suche nur wenig Zeit. Es handelt sich um einen sogenannten inverted index: Zu jedem Suchbegriff ist der Ort angegeben, an dem sich der Begriff finden lässt.
     
  • Document: Ausgang für den Index sind die Dokumente, in denen Daten vorkommen. Sie müssen nicht zwingend vollständige Texte sein (beispielsweise Blogartikel) – es reichen bloße Dateien mit Informationen.
     
  • Field: Ein Dokument wiederum besteht aus mehreren Feldern. Neben dem eigentlichen Inhaltsfeld gehören auch weitere Metadaten zu einem Dokument. So lässt sich mit Elasticsearch zum Beispiel gezielt nach Metadaten zum Autor oder zum Entstehungszeitpunkt suchen.

Übrigens: Wenn wir von Aufbereitung der Daten sprechen, geht es in erster Linie um das Tokenizing. Hierbei erstellt ein Algorithmus aus einem kompletten Text einzelne Begriffe. Für die Maschine gibt es erst mal so etwas wie Wörter nicht: ein Text besteht aus einer langer Zeichenfolge und ein Buchstabe hat für den Computer den gleichen Wert wie ein Leerzeichen. Damit ein Text also sinnvoll aufbereitet wird, muss dieser erst einmal in Tokens zerlegt werden. Dazu wird zum Beispiel Whitespace – also Leerzeichen und Leerzeilen – als Markierungen für Wörter angenommen. Darüber hinaus wird beim Aufbereiten auch normalisiert: Wörter werden einheitlich klein geschrieben und Satzzeichen ignoriert. Diese Methoden hat Elasticsearch alle von Apache Lucene übernommen.

Hinweis

Im folgenden Tutorial arbeiten wir mit der Version 6.3.0 von Elasticsearch. Nutzen Sie eine andere Version, könnten unter Umständen einige Code-Beispiele oder Anleitungsschritte anders funktionieren.

Installation

Die benötigten Dateien für Elasticsearch sind frei auf der offiziellen Website von Elastic verfügbar. Die Dateien werden dort als ZIP- oder tar.gz-Pakete angeboten, weshalb sie sich unter Linux und Mac auch ganz einfach per Konsole installieren lassen.

Hinweis

Elastic bietet Elasticsearch in zwei unterschiedlichen Paketen an. Die Standardversion beinhaltet auch kostenpflichtige Features, die Sie in einer Trial-Version für einige Zeit ausprobieren können. Solche Pakete hingegen, die mit OSS (Open-Source-Software) gekennzeichnet sind, enthalten ausschließlich freie Bestandteile, die unter der Lizenz Apache 2.0 veröffentlicht wurden.

Für ZIP:

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-oss-6.3.0.zip
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-oss-6.3.0.zip.sha512
shasum -a 512 -c elasticsearch-oss-6.3.0.zip.sha512 
unzip elasticsearch-oss-6.3.0.zip
cd elasticsearch-6.3.0

Für tar.gz:

wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-oss-6.3.0.tar.gz
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-oss-6.3.0.tar.gz.sha512
shasum -a 512 -c elasticsearch-oss-6.3.0.tar.gz.sha512 
tar -xzf elasticsearch-oss-6.3.0.tar.gz
cd elasticsearch-6.3.0

Laden Sie zunächst das Paket herunter und zudem noch die Hash-Prüfsumme (SHA512), die Sie im dritten Schritt auch überprüfen. Anschließend entpacken Sie das Paket und wechseln in den entsprechenden Ordner.

Das ZIP-Archiv lässt sich auch herunterladen und für die Installation unter Windows nutzen, denn in dem Paket ist eine Batch-Datei enthalten, die Sie ausführen können. Alternativ stellt Elastic inzwischen aber auch einen MSI-Installer zur Verfügung – dieser befindet sich allerdings noch in der Beta-Phase. Die Installationsdatei des letztgenannten enthält ein grafisches Interface, das Sie präzise durch die Installation führt.

Hinweis

Da Elasticsearch auf Java basiert, muss diese Programmiersprache auch auf Ihrem System installiert sein. Laden Sie sich dafür am besten das Java Development Kit (JDK) kostenlos von der offiziellen Website herunter.

Führen Sie Elasticsearch nun über die Konsole aus, indem Sie in den entsprechenden bin-Ordner navigieren und „elasticsearch eingeben – egal, ob unter Linux, Mac oder Windows. Öffnen Sie dann einen Browser Ihrer Wahl und rufen Sie folgenden Port des localhost auf: "http://localhost:9200/" Wenn Sie Elasticsearch korrekt installiert haben und auch Java richtig eingestellt ist, sollten Sie nun auf die Volltextsuche zugreifen können.

Mit Elasticsearch kommunizieren Sie über das REST-API, deshalb benötigen Sie zusätzlich einen entsprechenden Client. Es empfiehlt sich, hierfür Kibana einzusetzen (ebenfalls ein kostenfreies Open-Source-Angebot von Elastic). Mit diesem Programm können Sie Elasticsearch direkt im Browser verwenden. Dafür rufen Sie dort einfach http://localhost:5601/ auf und können dann auf eine grafische Benutzeroberfläche zugreifen. Wie Sie Kibana richtig installieren und einrichten, erfahren Sie in unserem Tutorial zu Kibana. In Kibana und jedem anderen Client können Sie mit den HTTP-Methoden PUT, GET, POST und DELETE Kommandos an Ihre Volltextsuche senden.

Index

In einem ersten Schritt müssen Sie zunächst Ihren Index erstellen und diesen mit Daten anfüttern. Dafür können Sie zwei verschiedenen HTTP-Methoden anwenden: POST und PUT. Sie benutzen PUT, wenn Sie für den Eintrag eine spezifische ID angeben möchten. Bei POST erstellt Elasticsearch selbst eine ID. In unserem Beispiel möchten wir eine Bibliografie einrichten. Jeder Eintrag soll den Namen des Autors, den Titel des Werks und das Erscheinungsjahr enthalten.

POST bibliography/novels
{
"author": "Isabel Allende",
"title": "La casa de los espíritus",
"year": "1982"
}

Wenn Sie die Eingabe so verwenden möchten, müssen Sie die Konsole von Kibana verwenden. Falls Sie aber nicht auf diese Software zurückgreifen wollen, können Sie alternativ auch cURL nutzen. Statt des vorangegangen Befehls müssen Sie dann folgendes in die Kommandozeile eingeben:

curl -XPOST http://localhost:9200/bibliography/novels -H "Content-Type: application/json" -d '{"author": "Isabel Allende", "title": "La casa de los espíritus", "year": "1982"}'

Nachfolgend zeigen wir Ihnen nur den Code für Kibana – dieser lässt sich aber problemlos in die Syntax von cURL übertragen.

Elasticsearch sollte, wenn Sie alles korrekt eingetragen haben, folgende Angaben zu Beginn der Meldung zurückgeben:

{
  "_index": "bibliography",
  "_type": "novels",
  "_id": "AKKKIWQBZat9Vd0ET6N1",
  "_version": 1,
  "result": "created",
}

An dieser Stelle wird deutlich, dass Elasticsearch nun einen Index mit dem Namen bibliography und dem Typ novels findet. Da wir die POST-Methode verwendet haben, generierte Elasticsearch automatisch eine einzigartige ID für unseren Eintrag. Der Eintrag befindet sich derzeit in der ersten Version und wurde kürzlich erstellt (created).

Hinweis

Bei einem Typ (_type) handelte es sich in Elasticsearch früher um eine Art Unterkategorie. Über sie war es möglich, mehrere Typen unter einem Index zu versammeln. Dies führte aber zu unterschiedlichen Problemen und so plant Elastic derzeit, solche Typen nicht mehr zu nutzen. In Version 6.x ist _type noch enthalten, es ist aber nicht mehr möglich, mehrere Typen unter einem Index zu versammeln. Ab Version 7.0 ist geplant, Typen komplett zu streichen, wie die Entwickler in ihrem Blog erklären.

Sie können mit PUT Ihrem Eintrag auch eine ganz bestimmte ID geben. Diese wird in der ersten Zeile des Codes festgelegt. PUT benötigen Sie auch dann, wenn Sie einen bestehenden Eintrag abändern möchten.

PUT bibliography/novels/1
{
"author": "William Gibson",
"title": "Neuromancer",
"year": "1984"
}

Der folgende Output ähnelt sehr dem, den wir auch mit der POST-Methode erhalten, allerdings gibt uns Elasticsearch die ID wieder, die wir dem Eintrag in der ersten Zeile gegeben haben. Die Reihenfolge der Angabe lautet immer _index/_type/_id.

{
  "_index": "bibliography",
  "_type": "novels",
  "_id": "1",
  "_version": 1,
  "result": "created",
}

Mit dem PUT-Befehl und der eindeutigen ID können wir Einträge auch abändern:

PUT bibliography/novels/1
{
"author": "William Gibson",
"title": "Count Zero",
"year": "1986"
}

Da der Eintrag mit der ID 1 bereits besteht, verändert Elasticsearch diesen nur, statt einen neuen zu erstellen. Das spiegelt sich auch im Output wider:

{
  "_index": "bibliography",
  "_type": "novels",
  "_id": "1",
  "_version": 2,
  "result": "updated",
}

Die Versionsnummer ist auf 2 gestiegen und als result erhalten wir updated statt created. Das gleiche können Sie selbstverständlich auch mit einer zufällig von Elasticsearch erstellten ID schaffen – durch die Länge und die Unordnung der Zeichen wäre die weitere Arbeit aber um einiges umständlicher. Das Verfahren bedeutet auch, dass Elasticsearch einen Eintrag einfach überschreibt, wenn Sie beispielsweise mit den ID-Nummern durcheinander kommen. Um versehentliche Überschreibungen zu vermeiden, können Sie den _create-Endpoint verwenden:

PUT bibliography/novels/1/_create
{
"author": "Mary Shelley",
"title": "Frankenstein; or, The Modern Prometheus",
"year": "1818"
}

Da im Index bereits ein Eintrag mit der ID 1 vorhanden ist, bekommen Sie eine Fehlermeldung angezeigt.

Wenn Sie Änderungen an einem Eintrag wie beschrieben vornehmen, erzeugen Sie strenggenommen einen komplett neuen Eintrag und müssen daher auch alle Angaben komplett eintragen. Stattdessen können Sie aber auch nur Veränderungen in den bestehenden Eintrag integrieren. Hierfür nutzen Sie den Endpoint _update:

POST bibliography/novels/1/_update
{
"doc": {
  "author": "Franz Kafka",
"genre": "Horror"
  }
}

Nun haben wir dem Eintrag ein zusätzliches Feld hinzugefügt und ein bestehendes Feld verändert, ohne die anderen dabei zu löschen – allerdings nur im Vordergrund. Im Hintergrund hat Elasticsearch dennoch den kompletten Eintrag neu angelegt, dafür aber die bereits bestehenden Inhalte selbstständig eingefügt.

Prinzipiell haben wir bisher einfach einen Eintrag in eine Datenbank geschrieben und können diesen daher auch direkt wieder abrufen. Dafür verwenden wir die GET-Methode.

GET bibliography/novels/1

Sie können sich den Eintrag aber auch ganz einfach im Browser anzeigen lassen:

http://localhost:9200/bibliography/novels/1

Im Output zeigt uns Elasticsearch alle Details unseres Eintrags:

{
  "_index": "bibliography",
  "_type": "novels",
  "_id": "1",
  "_version": 2,
  "found": true,
  "_source": {
    "author": "William Gibson",
    "title": “Count Zero",
    "year": "1984"
  }
}

Ergänzend zu den bereits bekannten Informationen finden Sie unter _source die Felder des Dokuments. Außerdem informiert uns Elasticsearch darüber, dass tatsächlich ein Eintrag gefunden wurde. Wenn Sie versuchen, einen nicht bestehenden Eintrag aufzurufen, würde keine Fehlermeldung auftauchen. Stattdessen meldet Elasticsearch "found": false und es gibt keine Einträge unter _source.

Sie haben darüber hinaus auch die Möglichkeit, nur bestimmte Informationen aus der Datenbank zu ziehen. Nehmen wir an, Sie haben nicht nur bibliografische Daten in Ihrem Index hinterlegt, sondern auch den kompletten Text jedes aufgenommenen Romans. Auch dieser würde bei einer einfachen GET-Anfrage mitangezeigt. Nehmen wir aber an, Sie interessieren sich momentan nur für den Namen des Autors und den Titel des Werkes – dann können Sie auch gezielt nur danach fragen:

GET bibliography/novels/1?_source=author,title

Wenn Sie die Metadaten eines Eintrags nicht interessieren, können Sie sich auch nur den Inhalt anzeigen lassen:

GET bibliography/novels/1/_source

Nehmen wir an, Sie möchten nicht nur einen einzelnen Eintrag in Ihrem Index aufrufen, sondern gleich mehrere. Elasticsearch hat hierfür den _mget-Endpoint (für multi-get) implementiert. Wenn Sie diesen nutzen, geben Sie ein Array aus mehreren IDs an:

GET bibliography/novels/_mget
{
  "ids": ["1", "2", "3"]
}

Auch wenn ein Eintrag noch nicht besteht, läuft nicht die komplette Anfrage fehl. Denn alle bestehenden Daten werden Ihnen angezeigt. Was die nicht vorhandenen Daten betrifft, gibt Elasticsearch die Rückmeldung, diese nicht finden zu können.

Ganz ähnlich wie der Aufruf eines Eintrags funktioniert auch dessen Löschung. Statt mit GET arbeiten Sie allerdings mit DELETE:

DELETE /bibliography/novels/1

Im folgenden Output gibt Elasticsearch Ihnen bekannt, dass es den Eintrag unter der angegebenen ID gefunden hat:

{
  "_index": "bibliography",
  "_type": "novels",
  "_id": "1",
  "_version": 5,
  "result": "deleted",
}

Außerdem erhöht das Programm die Versionsnummer um eins. Dies hat zwei Gründe:

  1. Elasticsearch markiert den Eintrag nur als gelöscht und entfernt diesen nicht direkt von der Festplatte. Erst im weiteren Verlauf der Indexierung wird der Eintrag verschwinden.
     
  2. Wenn man mit verteilten Indexen auf mehreren Knoten arbeitet, ist eine detaillierte Versionsverwaltung äußerst wichtig. Deshalb markiert Elasticsearch jede Änderung als neue Version – somit auch den Löschauftrag.

Sie können Änderungen auch nur an einer bestimmten, Ihnen bekannten Versionsnummer durchführen. Sollte es im Cluster bereits eine neuere als die von Ihnen angegebene Version geben, führt der Änderungsversuch zu einer Fehlermeldung.

PUT bibliography/novels/1?version=3
{
"author": "Marcel Proust",
"title": " À la recherche du temps perdu",
"year": "1927"
}

Sie können zudem nicht nur gleich mehrere Einträge auf einmal aufrufen, sondern mit _bulk-Aufträgen auch mehrere erstellen oder löschen. Hierfür verwendet Elasticsearch eine etwas abgewandelte Syntax.

POST bibliography/novels/_bulk
{"delete": {"_id": "1"}}
{"create": {"_id": "1"}}
{"author": "Johann Wolfgang von Goethe", "title": "Die Leiden des jungen Werther", "year": "1774"}
{"create": {"_id": "2"}}
{"author": "Umberto Eco", "title": "Il nome della rosa", "year": "1980"}
{"create": {"_id": "3"}}
{"author": "Margaret Atwood", "title": "The Handmaid’s Tale", "year": "1985"}

Jeder Auftrag bekommt eine eigene Zeile. Zunächst geben Sie an, welche Aktion durchgeführt werden soll (create, index, update, delete). Darüber hinaus geben Sie auch an, welchen Eintrag Sie erstellen wollen und an welchem Ort. Es ist mit einer solchen Bulk-Anweisung nämlich auch möglich, in mehreren Indexen zu arbeiten. Dafür würden Sie den Pfad nach POST leer lassen und jeder Aktion einen eigenen Pfad zuweisen. Beim Erstellen von Einträgen müssen Sie zudem – in einer neuen Zeile – einen request body angeben. In diesem steht der Inhalt des Eintrags. Die DELETE-Anweisung bedarf keines request bodys, da der komplette Eintrag gelöscht wird.

Bisher haben wir in den Beispielen die Inhalte stets gleich angegeben – egal, um welches Feld es sich handelte: Elasticsearch hat alle Informationen als zusammenhängende Zeichenfolge interpretiert. Doch das ist nicht immer für jedes Feld zielführend. Deshalb gibt es in Elasticsearch das Mapping. Dieses legt fest, wie die Algorithmen eine Eingabe zu interpretieren haben. Mit folgendem Code können Sie sich anzeigen lassen, welches Mapping derzeit in Ihrem Index eingesetzt wird:

GET bibliography/novels/_mapping

Alle Felder werden den Typen text und keyword zugeordnet. Elasticsearch kennt aber 6 Core Datatypes und noch mehr spezielle Felder. Die 6 Haupttypen sind teilweise in weitere Unterkategorien untergliedert:

  • string: Hierunter fallen sowohl text als auch keyword. Während Keywords als exakte Übereinstimmungen betrachtet werden, geht Elasticsearch bei einem Text davon aus, dass dieser analysiert werden muss, bevor man ihn benutzen kann.
     
  • numeric: Elasticsearch kennt verschiedene Zahlenwerte, die sich vor allem im Umfang unterscheiden. Während zum Beispiel der Typ byte Werte zwischen -128 und 127 einnehmen kann, hat man bei long einen Spielraum von -263 bis 263-1.
     
  • date: Ein Datum kann entweder tagesgenau oder mit einer Uhrzeit dazu angegeben werden. Außerdem hat man die Möglichkeit, ein Datum in Form der Unixzeit anzugeben: Sekunden oder Millisekunden seit dem 1. 1. 1970. (siehe hierzu auch ISO 8601 für Datumsformate und Zeitangaben)
     
  • boolean: Felder, die als boolean formatiert sind, können entweder einen wahren (true) oder einen falschen (false) Wert haben.
     
  • binary: In solchen Feldern können Sie Binärdaten unterbringen. Um diese zu übermitteln, verwenden Sie die Base64-Kodierung.
     
  • range: So geben Sie einen Bereich an. Dieser kann entweder zwischen zwei Zahlenwerten, zwei Daten oder sogar zwischen zwei IP-Adressen liegen.

Dies sind nur die Hauptkategorien, die Sie wahrscheinlich am häufigsten nutzen werden. Weitere Typen finden Sie in der Elasticsearch-Dokumentation. Die einzelnen Typen unterscheiden sich vor allem auch dahingehend, dass sie entweder exact-value oder full-text sind. Elasticsearch versteht den Inhalt des Feldes also entweder als eine genau Eingabe oder einen Inhalt, der erst verarbeitet werden muss. Im Zuge des Mappings ist nämlich auch das Analyzing wichtig: Das Analysieren eines Inhalts untergliedert sich wiederum in Tokenizing und Normalising:

  • Tokenizing: Aus einem Text werden einzelne Tokens gemacht. Diese können einzelne Wörter, aber auch feststehende Begriffe aus mehreren Wörtern umfassen.
     
  • Normalizing: Die Tokens werden normalisiert, indem sie alle kleingeschrieben und auf ihre Stammformen reduziert werden.

Um diesen Vorgang durchzuführen, nutzt Elasticsearch Analyzers. Wenn Sie ein Dokument in den Index aufnehmen und Ihr Mapping korrekt durchgeführt haben, werden alle Inhalte richtig in den invertierten Index aufgenommen. Damit Sie Mapping für sich nutzen können, müssen Sie einen komplett neuen Index erzeugen. Das Mapping von bereits existierenden Feldern ist nicht möglich.

PUT bibliography
{
  "mappings": {
    "novels": {
      "properties": {
        "author": {
          "type": "text",
          "analyzer": "simple"
        },
        "title": {
          "type": "text",
          "analyzer": "standard"
        },
        "year": {
          "type": "date",
          "format": "year"
        }
      }
    }
}
}

Die beiden Felder author und title haben wir jeweils als text und damit als full-text definiert. Daher benötigen diese noch einen passenden Analyzer. Während wir dem Feld für den Romantitel den Standard Analyzer zur Verfügung stellen, entscheiden wir uns beim Autorennamen für den weniger komplexen Simple Analyzer. Das Erscheinungsjahr hingegen legen wir als date – und somit als exact-value – fest. Da Elasticsearch als Standardformat eine Angabe von Jahr, Monat und Tag annimmt, ändern wir das noch, da wir uns bei der Angabe nur auf das Jahr beschränken möchten.

Suche

Im vorangegangenen Kapitel haben wir Elasticsearch und dessen Index vor allem als Datenbank genutzt. Der eigentliche Nutzen von Elasticsearch steckt allerdings in der Volltextsuche. Das heißt: Statt die ID eines Dokuments einzugeben und so den Eintrag aufzurufen, stellen wir Elasticsearch nun so ein, dass Sie konkret nach Inhalten suchen können. Zur Nutzung der Suchmaschine hat das Programm den _search-Endpoint vorgesehen. Mit diesem in Kombination mit der GET-Methode können Sie sich zum Beispiel alle Einträge anzeigen lassen:

GET bibliography/novels/_search
Fakt

Für komplexere Suchanfragen verwendet _search einen Body in geschweiften Klammern. Einige HTTP-Server sehen dies aber für die Methode GET nicht vor. Deshalb haben sich die Entwickler entschieden, dass solche Anfragen auch als POST funktionieren.

Sie können den Pfad auch frei lassen, um alle vorhandenen Indexe zu durchsuchen. Im Output finden Sie die interessanten Informationen unter hits:

"hits": {
  "total": 3,
  "max_score": 1,
  "hits": [
    {
      "_index": "bibliography",
      "_type": "novels",
      "_id": "2",
      "_score": 1,
      "_source": {
        "author": "Umberto Eco",
        "title": "Il nome della rosa",
        "year": "1980"
      }
    },
  ],
  }
}

Auch alle weiteren Einträge in unserem Index werden im Output aufgeführt (und nur der Übersicht halber an dieser Stelle ausgelassen). Die Rückmeldung von Elasticsearch liefert uns neben den eigentlichen Inhalten auch zwei weitere Informationen, die uns beim Verständnis der Volltextsuche helfen können:

  • hits: Jeder Eintrag, der den Suchkriterien entspricht, wird von Elasticsearch als Treffer gewertet. Das Programm zeigt zudem die Anzahl der hits an. Da sich in unserem Beispiel 3 Einträge im Index befinden und wir uns alle anzeigen lassen, gilt "total": 3.
     
  • score: In Form einer Punktzahl gibt Elasticsearch an, wie relevant der Eintrag in Bezug auf unsere Suchanfrage ist. Da wir in unserem Beispiel einfach nach allen Beiträgen gesucht habe, besitzen auch alle den gleichen score von 1. Die Einträge werden in den Suchergebnissen absteigend nach der Relevanz sortiert.

Darüber hinaus liefert Elasticsearch in der Rückmeldung noch Informationen, wie viele Shards in den Suchergebnissen involviert sind, wie viele Millisekunden die Suche gedauert hat und ob es zu einem Timeout gekommen ist.

Elasticsearch zeigt Ihnen standardmäßig nur die ersten 10 Suchergebnisse an. Sie können dies allerdings über die Einstellung von Parametern beeinflussen:

  • size: Wie viele Ergebnisse soll Elasticsearch anzeigen?
     
  • from: Wie viele Einträge soll das Programm überspringen, bevor es welche anzeigt?

Wenn Sie nun die ersten 10 Suchergebnisse gesehen hätten und sich nur noch die folgenden 15 anzeigen lassen möchten, müssen Sie eine Kombination aus beiden Parametern verwenden:

GET bibliography/novels/_search?size=15&from=10

Elasticsearch unterscheidet zwei verschiedene Sucharten. Zum einen nutzt es eine Lite-Version und zum anderen eine komplexere Variante, die mit der Query DSL arbeitet – einer spezifischen Suchsprache. Bei der Lite-Version geben Sie Ihre Suchanfrage als einfachen String direkt in die Suchanfrage ein:

GET bibliography/novels/_search?q=atwood

Sie können aber auch nur innerhalb eines bestimmten Feldes suchen:

GET bibliography/novels/_search?q=author:atwood
Fakt

Tatsächlich suchen Sie auch im ersten Beispiel in einem bestimmten Feld, ohne dieses angeben zu müssen: dem _all-Feld. Wenn Sie die Inhalte eines Dokuments in Felder sortiert in den Index einfügen, erzeugt Elasticsearch im Hintergrund ein zusätzliches Feld. In diesem sind alle Inhalte aus den anderen Feldern zusätzlich gespeichert, um eine solche Suche innerhalb aller Felder zu ermöglichen.

Wenn Sie mehrere Suchkriterien miteinander kombinieren möchten, nutzen Sie dafür +. Mit dem Zeichen - schließen Sie bestimmte Kriterien aus. Wenn Sie diese Operatoren verwenden, müssen Sie in der Suchanfrage allerdings eine Prozentkodierung anwenden:

GET bibliography/novels/_search?q=%2Bauthor%3Aatwood+%2Btitle%3Ahandmaid

Die Query-String-Syntax von Elasticsearch bietet noch mehr Feinheiten, mit denen Sie Ihre Suche anpassen können. In der Dokumentation der Software haben die Entwickler von Elastic alles Wesentliche zusammengefasst: Oder-Verknüpfungen, exakte Phrasen, leere Felder oder auch Platzhalter.

Für einfache Anfragen ist diese Suchvariante gut geeignet, aber bei komplexeren Aufgaben kann das Lite-Verfahren schnell versagen: Zu groß ist die Gefahr, einen Fehler in den langen String einzutragen. Deshalb bietet Elasticsearch mit Query DSL ein bequemeres Verfahren. Der Ausgangspunkt einer solchen Suche ist der query-Parameter in Kombination mit einem match query:

GET bibliography/novels/_search
{
  "query": {
    "match": {
"author": "allende"
}
  }
}

Das Ergebnis zeigt uns alle Einträge, die im author-Feld den Begriff „allende“ enthalten. An ihm erkennen wir auch, dass das Analyzing im Mapping funktioniert hat, denn Elasticsearch ignoriert Groß- und Kleinschreibung. Um sich alle Einträge anzeigen zu lassen, können Sie neben der einfachen, bereits vorgestellten Variante auch match_all verwenden:

GET bibliography/novels/_search
{
  "query": {
    "match_all": {}
  }
}

Das Gegenteil dieser Suche ist match_none. Elasticsearch gibt Ihnen allerdings auch die Möglichkeit, mit einem Suchbegriff in gleich mehreren Feldern zu suchen:

GET bibliography/novels/_search
{
  "query": {
    "multi_match": {
      "query": "la",
      "fields": ["author", "title"]
    }
  }
}

Um komplexe Suchaufträge zu ermöglichen, können Sie auch mehrere Suchbegriffe miteinander kombinieren und unterschiedlich bewerten. So stehen Ihnen drei Verbindlichkeiten zur Verfügung:

  • must: Der Begriff muss vorkommen.
  • must_not: Der Begriff darf nicht vorkommen.
  • should: Wenn dieser Begriff auftaucht, wird die Relevanz in den Suchergebnissen erhöht.

In der Praxis werden Sie diese mit einer booleschen Abfrage kombinieren:

GET bibliography/novels/search_
{
  "query": {
"bool": {
    "must": {
      "match": {
        "title": "la"
      }
},
    "must_not": {
      "match": {
        "title": "rabbit"
      }
    },
    "should": {
      "match": {
        "author": "allende"
      }
    }
  }
}
}

Erweitern können Sie Ihre Suche zudem noch, indem Sie einen Filter einbauen. Dadurch können Sie Kriterien festlegen, die die Suchergebnisse einschränken:

 GET bibliography/novels/search_
{
  "query": {
"bool": {
    "must": {
      "match": {
        "title": "la"
      }
},
    "filter": {
      "range": {
        "year": {
          "gte": "1950",
          "lt": "2000"
        }
      }
    }
  }
}
}

Wir haben den Filter im vorangegangen Beispiel mit einem Spektrum verknüpft: Es sollen nur die Einträge angezeigt werden, die zwischen 1950 und 2000 erschienen sind.

Fazit

Mit diesem Rüstzeug haben Sie alles zur Verfügung, um die Volltextsuche für Ihr Projekt zu implementieren. Elasticsearch bietet allerdings noch weitere Methoden, mit denen Sie Ihre Suche verfeinern und komplexer gestalten können. Mehr dazu finden Sie auf der offiziellen Website von Elastic. Wenn Sie die Volltextsuche stärker erweitern möchten, können Sie auch eigene Skripte mit anderen Sprachen wie Groovy und Clojure erstellen.

Vor- und Nachteile von Elasticsearch

Elasticsearch kann eine kraftvolle Volltextsuche sein. Man kann Elasticsearch eigentlich nur die schlechte Umsetzung des Open-Source-Gedankens vorwerfen. Ansonsten bietet die Volltextsuche zahlreiche Vorteile – auch gegenüber dem direkten Konkurrenten Apache Solr.

Vorteile

Nachteile

Open Source

Elastic als Gatekeeper

schnell & stabil

skalierbar

viele vorgefertigte Programm-Module (Analyzer, Volltextsuche …)

einfache Umsetzung dank Java, JSON und REST-API

flexibel & dynamisch