Einführung zu Elasticsearch

Zum Autor

Andreas Stotzer ist Senior Consultant bei der Diso AG und konnte in verschiedenen Projekten Erfahrung mit Elasticsearch sammeln. Er hält sich ständig rund um neue Technologien auf dem Laufenden, und beschäftigt sich aktuell mit Node.JS und Angular.JS.

Einleitung

Die Suchfunktion ist aus vielen mobilen, Web- und Desktopanwendungen nicht mehr wegzudenken und wird immer mehr zur zentralen Komponente in unserem digitalen Alltag. Ein Benutzer eines mobilen Endgerätes etwa benutzt die Suchfunktionalität, um schnellen Zugriff auf Kontakte, Applikationen oder Musiktitel zu erhalten. Oder er beginnt seine Navigation im Internet durch das Eingeben eines Suchbegriffes in der Navigationsleiste des Webbrowsers. Kombiniert mit den Geolocation Features unseres Smartphones benutzen wir die standortbasierte Suche, um Points of Interest in unserer nächsten Umgebung zu suchen.

Elasticsearch ist eine verteilte Volltext-Suchengine, die sich immer grösserer Beliebtheit erfreut. Bestimmt sind auch Sie schon indirekt mit Elasticsearch in Berührung gekommen. Zahlreiche namhafte Unternehmen wie LinkedIn, Xing oder GitHub setzen auf Elasticsearch, um ihre Anforderungen an eine hochverfügbare, skalierbare und performante Such-Engine abdecken zu können. Mit Elasticsearch können auch Sie Ihre Anwendung mit geringem Aufwand um eine vielseitige Suchkomponente erweitern. Zusammen mit Logstash und Kibana formt Elasticsearch den ELK-Stack, eine offene Plattform für die Verarbeitung, Speicherung, Auswertung und Visualisierung von Zeit- und Event-basierten Daten.

Dieser Artikel gibt einen Überblick über Elasticsearch, beantwortet einige grundlegende Fragen und zeigt praktische Beispiele für den Einsatz dieser Technologie auf. Zudem zeigt er anhand eines Beispiels aus der Praxis wie die Schweizerische Post AG basierend auf Elasticsearch erfolgreich eine Adress-Suche umgesetzt hat und welche Performanceverbesserungen der Einsatz von Elasticsearch eingebracht hat.

Was ist Elasticsearch?

Basierend auf Apache Lucene – einer bewährten Such-Engine für Java – stellt Elasticsearch eine Vielzahl von Möglichkeiten für Freitextabfragen, für die Indexierung von Daten, sowie für die Administration über eine HTTP / RESTful Schnittstelle zur Verfügung. Darüber hinaus erweitert Elasticsearch Lucene mit zusätzlichen Features wie Clustering und Monitoring. Auch erweiterte Werkzeuge für die Auswertung und Analyse der indexierten Daten (z.B. Aggregationen) werden zur Verfügung gestellt. Eines der mächtigsten Features ist die Cluster-Fähigkeit von Elasticsearch. Dabei werden die Such-Indizes auf mehrere Elasticsearch-Knoten verteilt, um eine bessere Suchperformance bei grosser Abfragelast sowie bessere Verfügbarkeit und Ausfallsicherheit zu erreichen. Trotz des grossen Funktionsumfanges von Elasticsearch legen die Entwickler viel Wert auf einfache Bedienung. So lässt sich beispielsweise mittels Autodiscovery automatisch ein Such-Cluster formieren: Sobald neue Knoten im Netzwerk vorhanden sind, werden diese automatisch in den Cluster-Verbund aufgenommen.

Als Dokument-Format setzt Elasticsearch auf JSON. Da ein Index nicht zwingend auf einem Schema basieren muss, ist die Art der Daten die dort abgelegt werden irrelevant, seien dies strukturierte, unstrukturierte oder zeitbasierte Daten. Elasticsearch erlaubt es aber auch, mittels Schema Mappings festzulegen, wie die Daten in einem Index aussehen sollen. Dazu werden sämtliche gängigen Datentypen unterstützt, aber auch komplexe Typen wie Arrays, Objekte, Geo Point / Geo Shape und Attachements. Es ist auch möglich Relationen mit Parent – Child Mappings zu modellieren.

Die Abfrage von Daten aus Elasticsearch erfolgt über eine eigene DSL, welche ebenfalls auf JSON basiert. Sie ist sehr mächtig, da sich Suchabfragen beliebig kombinieren lassen, und Features wie unscharfe (Fuzzy) oder phonetische Suche verwendet werden können. Die Such-API erlaubt es auch die Relevanz von Suchresultaten zu beeinflussen (Scoring), relevante Stellen in Suchresultaten zu markieren oder Suchresultate seitenweise abzufragen. Die Aggregations API bietet darüber hinaus die Möglichkeit Ihren Datenbestand aus einer „High-Level“-Perspektive zu betrachten, und dient somit als Basis für Auswertungen und Dashboards.

Logstash und Kibana

Zur Vervollständigung des Themas wird nun aufgezeigt, wie Logstash und Kibana zusammen mit Elasticsearch genutzt werden können.

Logstash

Logstash ist ein Werkzeug welches es ermöglicht Daten oder Events aus beliebigen Quellsystemen zu lesen, zu manipulieren, zu transformieren und in einen persistenten Speicher (z.B. Elasticsearch) zu schreiben. Dabei steht nicht nur die Verarbeitung von Logdaten im Vordergrund, es können beliebige Zeit- oder Event-basierte Daten prozessiert werden. Aufgrund der Plugin-Architektur ist Logstash sehr flexibel und unterstützt eine Vielzahl möglicher Quell- und Zielsysteme. Sollte Logstash die Anforderungen nicht „out of the box“ unterstützen, besteht auch die Möglichkeit, eigene Input- oder Output-Plugins mittels Java zu implementieren. Logstash ist nur eine von vielen Möglichkeiten wie Daten mittels Elasticsearch indexiert werden können. Weitere Informationen dazu erhalten Sie später in diesem Artikel.

Kibana

Kibana ist eine Browser-basierte Platform für die Auswertung und Visualisierung von Daten aus Elasticsearch. Mit Kibana können grosse Datenbestände auf einfache Art und Weise in einem grafischen Dashboard dargestellt werden. Dazu bietet Kibana die Möglichkeit von interaktiven Drilldowns und eine grosse Anzahl von Diagrammen, die für die Visualisierung der Daten verwendet werden können. Kombiniert mit Elasticsearch können mit wenigen Handgriffen individuelle Dashboards erstellt werden, welche eine völlig neue Sicht auf die Daten ermöglichen.

Folgende Grafik zeigt eine mögliche Anwendungsarchitektur unter der Verwendung von Logstash, Elasticsearch und Kibana und wie die einzelnen Komponenten dabei zusammenspielen:

Was sind mögliche Anwendungsfälle für Elasticsearch?

Der klassische Anwendungsfall für Elasticsearch ist die Freitextsuche. Daneben kann es aber auch im Analytics-Bereich eingesetzt werden, um grosse Datenbestände in “near real time” auszuwerten. Hier einige Beispiele für den Einsatz von Elasticsearch:

  • Sie möchten Ihre Web- oder Mobile-Anwendung mit einer Suchfunktion erweitern, die dem Benutzer Features wie Autocomplete oder die Sortierung von Suchresultaten nach ihrer Relevanz bietet? Indexieren Sie Ihre Produkt- oder Kundendaten mit Elasticsearch und setzen Sie so die Grundlage für eine performante und vielseitige Freitextsuche in Ihrer Applikation.

  • Sie möchten eine Mobile App entwickeln die anhand des aktuellen Standortes des Benutzers die nächstgelegenen Mikrobrauereien lokalisiert, diese auf einer Landkarte anzeigt oder anhand der Laufdistanz sortiert? Elasticsearch bietet vielfältige Möglichkeiten, um eine standortbasierte Suche umzusetzen. So können Daten bei der Indexierung beispielsweise mit Koordinaten angereichert und Suchresultate nach der Distanz zu einem bestimmten Standort gefiltert werden.

  • Sie möchten die Logdaten Ihrer Enterprise-Anwendungen zentralisiert speichern und Auswertungen auf diesen Daten machen (z.B. wie oft wird die Funktionalität ‚x‘ genutzt)? Nutzen Sie Logstash um die Daten von mehreren Quellsystemen zu sammeln und für die Suche und Auswertung mittels Elasticsearch zu indexieren. Erstellen Sie mittels Elasticsearch Query DSL Auswertungen, die es Ihnen ermöglichen, rasch auf unvorhergesehene Situationen zu reagieren und so Ihre Systeme zu optimieren.

  • Sie möchten Transaktionsdaten auswerten und analysieren, um gewisse Trends und Abweichungen sichtbar zu machen, oder Sie möchten Statistiken und ad-hoc Abfragen auf grossen Datenbeständen durchführen, um Ihre Anforderungen im Bereich Business Intelligence / Advanced Analytics zu erfüllen? Nutzen Sie auch hierfür Logstash als Werkzeug für die Transformation und Bereitstellung Ihrer Daten in Elasticsearch. Setzen Sie zudem auf Kibana um die Daten in einer interaktiven, grafischen Oberfläche zu visualisieren und auszuwerten, sowie auf Elasticsearch Aggregations, die es Ihnen ermöglichen komplexe BI-Abfragen durchzuführen.

Welchen Nutzen bringt Elasticsearch in Unternehmen oder Anwendungen?

Open Source und Verwendung gängiger Protokolle

Sämtliche Schnittstellen von Elasticsearch basieren auf standardisierten Protokollen (HTTP und REST) und verwenden JSON als Datenformat. Dies hat den Vorteil, dass für die Indexierung oder für die Abfrage von Daten keine proprietäre Sprache oder Syntax erlernt oder spezielle Software eingesetzt werden muss. Benutzer die bereits mit REST-Schnittstellen und JSON vertraut sind, erlernen die API’s relativ schnell. Um Suchabfragen auszuführen und zu testen kann zum Beispiel ein Webbrowser verwendet werden.

Elasticsearch basiert auf Java und ist Open Source Software (Apache 2 Lizenz). Sollten Sie Sich also für den Einsatz von Elasticsearch für Ihr Unternehmen entscheiden, fallen keine zusätzlichen Lizenzkosten an. Die User-Community von Elasticsearch ist sehr aktiv, auf Blogs wie beispielsweise http://www.elastic.co/blog finden Sie zahlreiche Artikel, Tutorials, etc. zum Thema. Sollte für den Einsatz von Elasticsearch kommerzieller Support benötigt werden, bietet elastic – die Firma hinter Elasticsearch – entsprechende Pakete mit Web-, Mail- und Telefonsupport sowie Emergency Patches an.

Ready for the Cloud

Ihr Unternehmen setzt auf eine Cloud-Infrastruktur und Sie möchten Elasticsearch ebenfalls in der Cloud einsetzen? Elasticsearch bietet Discovery Module für Amazon EC2 oder Google Compute an, die den Betrieb eines Elasticsearch Clusters auf diesen Plattformen ermöglichen. Dazu gibt es mehrere Anbieter für hosted Elasticsearch Lösungen wie zum Beispiel found (http://www.found.no). Der Einsatz von Elasticsearch in der Cloud bietet Ihnen zusätzliche Vorteile wie:

  • Kostenersparnis, da Sie keine eigene Infrastruktur benötigen und nur die Ressourcen bezahlen welche Sie auch wirklich benötigen

  • „Scale as needed“: Der Elasticsearch Cluster kann rasch durch zusätzliche Instanzen erweitert werden um Performance-Engpässe oder temporär erhöhte Last zu bewältigen

Integration mit bestehenden Systemen

Elasticsearch kann sehr gut in bestehende Systeme integriert werden. Daten können aus verschiedenen Quellsystemen (Fachapplikation, Data Warehouse, ERP, etc.) zusammengetragen und in einer Elasticsearch-Instanz gespeichert werden. So kann ein zentraler „Hub“ für die Suche und Analyse von Geschäftsdaten geschaffen werden.

Neben den erwähnten HTTP / REST-Schnittstellen bietet Elasticsearch zudem eine grosse Menge alternativer Clients beispielsweise für Java, JavaScript, Python oder .NET an. Diese ermöglichen es, Elasticsearch problemlos in eine vorhandene Systemlandschaft zu integrieren.

Skalierbarkeit

Elasticsearch setzt auf die vertikale Skalierbarkeit (scale out). Bei schnell wachsenden Datenmengen kann ein Such-Cluster problemlos durch das Hinzufügen neuer Hardware-Systeme oder Cloud-Instanzen erweitert werden. Diese Anforderung ist insbesondere im Big Data Umfeld sehr wichtig, wo rasch wachsende Datenmengen ein enormes Potential für ein Unternehmen bieten, aber deren Verarbeitung, Auswertung etc. eben auch eine grosse Herausforderung darstellen kann.

Die Cluster-Features sind sehr robust und einfach zu verwenden. Neue Knoten werden im Netzwerk automatisch erkannt, zu dem Cluster hinzugefügt und die Such-Indizes automatisch neu verteilt, damit der neue Knoten genutzt werden kann.

Ausfallsicherheit und Verfügbarkeit

Durch die Cluster-Fähigkeit von Elasticsearch wird auch eine bessere Ausfallsicherheit und Verfügbarkeit erreicht. Elasticsearch nutzt Sharding (horizontale Partitionierung), das heisst Partitionen der Daten werden auf mehrere Knoten repliziert. Durch diese Redundanz wird beim Ausfall eines oder mehrerer Knoten sichergestellt, dass immer alle Daten verfügbar sind und kein Datenverlust entsteht. Für den Benutzer ist das Sharding bei der Indexierung und bei der Suche völlig transparent. Ein Dokument wird beispielsweise immer zuerst auf einem primären Shard indexiert und danach automatisch auf die Replikate kopiert.

Fällt ein Knoten des Clusters durch einen Hardwaredefekt aus, kann dessen Arbeit von einem Standby-Knoten übernommen werden. Durch das automatische „rebalancing“ des Clusters passiert dies im besten Fall so, dass Endbenutzer gar nichts von dem Systemausfall merken.

Suchperformance

Elasticsearch ist von Grund auf dazu ausgelegt, enormen Durchsatz bei Suchabfragen zu erreichen, auch bei massiven Datenmengen. Hierfür tragen einerseits der Inverted Index von Lucene, andererseits aber auch die verteilte Architektur von Elasticsearch bei. Durch die Verteilung auf mehrere Knoten können die Ressourcen mehrerer Systeme optimal genutzt werden, um eine bessere Performance zu erreichen. Und auch hier gilt: Treten Performance-Engpässe auf, können diese durch die Erweiterung des Such-Clusters um weitere Knoten problemlos entschärft werden, da Elasticsearch linear skaliert.

Index, Typen, Dokumente und Mappings

Ein Elasticsearch Index kann aus einem oder mehreren Dokument-Typen bestehen. Jeder Typ hat sein eigenes Mapping und beschreibt eine Sammlung ähnlicher Dokumente. Ähnlich wie bei einer relationalen Datenbank beschreibt das Mapping die Attribute die ein Dokument haben kann, und welche Datentypen diese verwenden. Elasticsearch unterstützt die folgenden Datentypen:

  • Core Types (String, Number, Boolean, Date)

  • Arrays

  • Objekte

  • IP

  • Geo Point

  • Geo Shape

  • Attachments (Binäre Dokumente wie MS Office, PDF, usw.)

Daten können in Elasticsearch schemafrei abgelegt werden. Dieser Ansatz ist vor allem für Anwendungsfälle im Big Data Umfeld interessant. Elasticsearch verwendet dazu dynamische Schema Mappings: Wird ein Dokument eines noch unbekannten Typs indexiert, wird basierend auf dessen Inhalt ein neuer Dokument-Typ angelegt (dasselbe gilt für Indizes). Bei bestehenden Typen können zudem dynamisch neue Attribute hinzugefügt werden, wobei Elasticsearch automatisch den korrekten Datentyp ermittelt.

Elasticsearch verwendet per default dynamisches Mapping. Je nach Use Case ist es aber empfehlenswert, explizite Mappings zu definieren und so genau festzulegen, aus welchen Typen ein Index besteht und welche Attribute ein bestimmter Typ hat. Denn grundsätzlich gilt: Je genauer die Daten sind, desto genauer sind später die Suchresultate bei Abfragen und Auswertungen. Durch das Schema Mapping wird auch festgelegt, welche Attribute für die Freitextsuche indexiert und welche Algorithmen für die Textanalyse und Tokenization angewandt werden sollen.

Hier ein einfaches Beispiel eines Schema Mappings, das später auch für die Demonstration der Indexierungs- und Abfragefunktionalitäten von Elasticsearch verwenden wird:

Dieses Beispiel definiert den Typ „brewery“ mit einer Liste von Attributen. Dabei ist folgendes interessant:

  • Die Datentypen String, Integer, Date, Float und Array werden verwendet

  • Durch die Option „dynamic“: „strict“ wird verhindert dass der Typ „brewery“ dynamisch erweitert werden kann (dies würde bei der Indexierung zu einem Fehler führen)

  • Das Attribut „phone“ verwendet die Option „index“: „no“. Dadurch wird festgelegt dass dieses Attribut nicht indexiert wird und später keine Abfragen auf diesem Feld gemacht werden können

  • Die Attribute „zip“ und „type“ verwenden die Option „index“: „not_analyzed“. Dies bedeutet dass diese zwar indexiert werden, aber ohne die Verwendung von String Tokenization oder Analyzern

  • Alle anderen Attribute verwenden für die Indexierung den globalen Default Analyzer

  • Das Attribut „registered“ verwendet das Datumsformat „YYYY-MM-dd“

  • Das Attribut „specialities“ ist ein Array von Objekten

Sobald ein JSON-Schema definiert wurde, kann ein leerer Index erstellt werden und “brewery” als Typ für diesen Index hinzugefügt werden. Dafür kann ein beliebiger HTTP / REST Client verwendet werden, in diesem Fall curl:

# Erstellt einen leeren Index mit dem Namen ‚breweries‘
$ curl -XPUT ‚http://localhost:9200/breweries/‘
{„acknowledged“:true}
# Fügt den Typ ‚brewery‘ zu dem leeren Index hinzu
$ curl -XPUT ‚http://localhost:9200/breweries/_mapping/brewery‘ –data-binary @brewery.json
{„acknowledged“:true}

Tipp: Dynamische Mappings

Dynamische Mappings eignen sich hervorragend um – basierend auf einem JSON-Dokument – einen Index zu erstellen, welcher beispielsweise als Prototyp verwendet werden kann. Dazu kann einfach ein beliebiges JSON-Dokument indexiert werden, und Elasticsearch erstellt – basierend auf dessen Inhalt – ein dynamisches Mapping. Dieses kann später optimiert und den individuellen Anforderungen angepasst werden.

Tokenization und Wortverarbeitung

Elasticsearch resp. Lucene basiert auf einem Inverted Index, dem Grund-Konzept das die performante Feitextsuche ermöglicht. Ein Inverted Index besteht aus einer eindeutigen Liste der Worte (Terms oder Tokens), die in allen Dokumenten innerhalb eines Indexes vorkommen, dem Term-Dictionary. Jeder Term im Dictionary referenziert zudem die Dokumente in welchem er vorkommt, ein Beispiel. Die Ausgangslage besteht aus folgenden Dokumenten:

  • Doc 1: „it is what it is“

  • Doc 2: „what is it“

  • Doc 3: „it is a microbrewery“

Ein Inverted Index wird erstellt, indem die drei Dokumente in einzelne Terme aufgeteilt (Tokenizing), in einer sortierten Liste abgelegt und mit den Dokumenten verknüpft werden:

 

Werden nun bestimmte Begriffe abgefragt, braucht Elasticsearch diese nur noch im Term-Dictionary aufzusuchen um die Referenzen zu den Dokumenten zu erhalten, die diese enthalten. Diese Strategie ist gerade beim Durchsuchen grosser Datenmengen sehr effizient. Bei der Indexierung wird also immer der Prozess der Tokenization und Wortverarbeitung durchlaufen. Die Möglichkeiten, so die Indexierung und später auch die Suche zu beeinflussen und zu optimieren sind sehr vielfältig. Anwendungsbeispiele:

  • Text in einzelne Terme segmentieren

  • Sonderzeichen und Whitespace entfernen

  • Terme in Lowercase umwandeln

  • Terme welche den selben Wortstamm haben, so indexieren, dass in obigem Beispiel sowohl „microbrewery“ wie auch „microbreweries“ zu einem Treffer führen würden

  • Synonyme indexieren, beispielsweise „brewhouse“ für „microbrewery“

  • Entfernen von Stopwords welche für die Suche nicht relevant sind (z.B. der, die, das)

  • Input für die phonetische Suche aufbereiten

Es gibt Fälle in denen es aber auch Sinn machen kann, einen Term ohne Manipulation abzulegen. Etwa wurde im Beispiel im letzten Kapitel das Attribut „zip“ mit der Option „index“: „not_analyzed“ definiert, da dieses nur Postleitzahlen enthalten kann. Durch diese Option werden Werte in diesem Feld als Keyword unsegmentiert im Index abgelegt und können dennoch für die Suche verwendet werden. Dies kann beispielsweise auch für die Optimierung eines Indexes für Autocompletion von Nutzen sein.

Defaultmässig verwendet Elasticsearch einen Tokenizer, der die Unicode Text Segmentierung verwendet, um Phrasen in einzelne Tokens zu zerlegen, und einen Analyzer, welcher keine erweiterte Wortverarbeitung wie das Entfernen von Stopwords vornimmt. Dieses Verhalten kann pro Dokument-Typ im Index überschrieben und beispielsweise mit eigenen Tokenizern oder Analyzern erweitert werden.

Sehr wichtig ist in diesem Zusammenhang zu erwähnen, dass für die Indexierung und für die Suche immer die gleichen Analyzer und Tokenizer verwendet werden sollten, um optimale Suchergebnisse zu erzielen.

Input und Output von Daten

CRUD-Operationen mit Elasticsearch

Das Hinzufügen, Modifizieren und Löschen von Dokumenten in einem Elasticsearch Index erfolgt über die Document API. Um ein Dokument im „breweries“ Index abzulegen, wird folgender Befehl verwendet:

Elasticsearch Beispiel 2

In der URL wird der Index spezifiziert in welchem das Dokument abgelegt werden soll („breweries“), der Typ welcher verwendet werden soll (“brewery“) sowie die ID des Dokumentes (1). Jedes Dokument in Elasticsearch hat eine eindeutige ID, die entweder wie in unserem Beispiel manuell vergeben oder generiert werden kann (dafür muss POST anstelle von PUT verwendet werden). Die Response des Index-Requests zeigt, dass Elasticsearch das Dokument automatisch mit einer Version versehen hat. Diese kann für optimistisches Locking verwendet werden, um bei Updates Schreib-Konsistenz zu gewährleisten. Ähnlich kann auch ein Dokument von Elasticsearch anhand der ID abgefragt werden:

Elasticsearch Beispiel 3

Dieser Request gibt das Dokument zurück, welches wir vorher mit der ID „1“ indexiert haben. Das Dokument wurde von Elasticsearch mit gewissen Metadaten (Index, Typ, etc.) erweitert. Interessant ist das Attribut „_source“, die Speicherung der Daten in Elasticsearch geschieht in zwei Schritten:

  • 1) Der Input wird für die Suche indexiert, also Tokenized und in einem Lucene Index abgelegt. Pro Attribut kann festgelegt werden ob es indexiert werden soll.

  • 2) Das Quell-Dokument wird in einem speziellen Feld im Index abgelegt, damit dieses später bei Suchabfragen wieder verfügbar ist. Dafür verwendet Elasticsearch das Attribut „_source“, dieses wird per Default automatisch für jedes indexierte Dokument erstellt.

Um ein Dokument zu aktualisieren wird die POST-Methode verwendet, es müssen nur die Attribute des Dokumentes mitgegeben werden, die aktualisiert werden sollen:

Ein Dokument kann wie folgt gelöscht werden:

Bulk-API

Werden grosse Datenmengen indexiert, ist es wenig effizient, für jedes Dokument einen API-Call zu machen, da dies zu einen massiven Overhead und zu einer schlechten Performance bei der Indexierung führen würde. Um Dokumente in Stapeln zu indexieren, bietet Elasticsearch die Bulk-API. Damit lassen sich CRUD-Operationen beliebig kombinieren und in einem einzelnen Aufruf ausführen:

 Mit diesem Request werden drei Operationen ausgeführt:

  • Die Dokumente mit den ID’s „2“ und „3“ werden indexiert

  • Das Dokument mit der ID „1“ wird gelöscht

Bei Bulk Operationen ist die spezielle Struktur zu beachten, da es sich nicht um valides JSON handelt. Ein Bulk-Request besteht immer aus einer Zeile Metadaten mit welcher festgelegt wird, ob ein Dokument indexiert, aktualisiert oder gelöscht werden soll, sowie weiteren Metadaten wie Index, Dokument-Typ und ID. In der zweiten Zeile werden dann die eigentlichen Daten geschickt. Bei der Indexierung ist dies das komplette Dokument, bei Updates sind es nur die Attribute, die aktualisiert werden sollen. Einzige Ausnahme ist das Löschen von Dokumenten, dort muss bloss die ID des zu löschenden Dokumentes definiert werden.

Aggregations API

Im Gegenteil zu der normalen Suche, bei welcher die Resultate immer möglichst präzis eingeschränkt werden und eine Abfrage im besten Fall nur wenige Resultate liefern soll, bietet die Aggregations API eine „High Level“ Übersicht über eine Datensammlung. Beispielsweise können Aggregationen Fragen wie die folgenden beantworten:

Alternativen zu der REST-API

Durch die grosse Anzahl vorhandener Clients für Elasticsearch bietet sich auch eine Vielzahl möglicher Szenarien um Daten zu indexieren. Soll Elasticsearch in einer Java / Java EE Applikation integriert werden, kann der Java Client verwendet werden, welcher sämtliche Features der REST-API ebenfalls unterstützt. Daneben gibt es offizielle von Elasticsearch unterhaltene Clients, beispielsweise für JavaScript, Python oder .NET, oder Sie nutzen Logstash für die Konfiguration einer flexiblen Index-Pipeline.

Tipp: Generieren von Elasticsearch Bulks aus JSON

Um JSON-Input in Elasticsearch Index-Bulks zu konvertieren und in Elasticsearch zu laden, kann z.B. jq verwendet werden (ein Command Line Processor für JSON). Folgender Befehl liest eine JSON-Datei, fügt für jeden Eintrag im Array „data“ die Metadaten für die Indexierung hinzu und schickt die Daten anschliessend zur Bulk-Indexierung an Elasticsearch. In diesem Fall werden die ID’s von Elasticsearch automatisch generiert da sie nicht explizit definiert werden:

cat breweries.json | jq -c ‚.data[] | {„index“: {„_index“: „breweries“, „_type“: „brewery“}}, .‘ |
curl -XPOST http://localhost:9200/_bulk –data-binary @-

Daten abfragen

Elasticsearch Query DSL

Dokumente werden bei der Speicherung entweder manuell oder automatisch mit einer ID versehen. Anhand dieser ID können Dokumente wieder geladen und Elasticsearch so als Key- / Value-Store verwendet werden. Dokumente werden aber nicht bloss abgespeichert, sondern deren Inhalt wird für die Freitextsuche auch indexiert. Elasticsearch bietet sehr vielfältige Möglichkeiten für die Abfrage von Daten und für das Filtern von Suchresultaten.

Die Abfragesprache von Elasticsearch (Elasticsearch Query DSL) bietet eine Vielzahl verschiedener Typen von Queries und Filtern. Mit ihr lassen sich sehr komplexe Abfragen formulieren, welche einerseits für die Freitextsuche, andererseits auch für die strukturierte Suche verwendet werden können. Einige Key-Features der Elasticsearch Query DSL:

  • Abfrage von exakten Werten mittels Term(s) Query oder von Ranges mittels Range Query. Dies ist insbesondere für die Suche von strukturierten Daten nützlich

  • Freitextabfragen mittels Match Query. Bei diesen Abfragen wird der Input analog der Indexierung tokenized, analysiert und so die Volltextsuche ermöglicht. Bei Match-Abfragen kann unter anderem konfiguriert werden wie die einzelnen Terme verknüpft werden sollen (AND / OR)

  • Kombinierte Freitextabfragen auf mehrere Attribute mittels Multi Match Query

  • Beliebig komplexe Kombination mehrerer Abfragen mittels boolschen Ausdrücken (Bool Query). Dabei kann definiert werden welche Teile einer Abfrage obligatorisch und welche optional sind

  • Matching von Phrasen mittels Match Phrase Query, wobei nicht nur einzelne Terme, sondern auch deren Reihenfolge berücksichtigt werden

  • Partielles Matching mittels Prefix Query oder Wildcard Query. Dies ist z.B. nützlich für Autocompletion

  • Suche anhand von regulären Ausdrücken mittels Regexp Query

  • Unscharfe Suche mittels Fuzzy Query unter Verwendung der Levenshtein Editierdistanz

  • Steuerung der Relevanz von Suchresultaten mittels Boost, um Teile einer Abfrage höher zu gewichten

Zusätzlich zu den erwähnten Queries bietet Elasticsearch auch das Konzept von Filtern an. Filter unterscheiden sich insofern von Queries, dass diese grundsätzlich nur prüfen ob ein Dokument eine gewisse Bedingung erfüllt oder nicht (true / false). Sie werden vor der eigentlichen Suche ausgeführt um die Liste zu durchsuchender Dokumente einzuschränken. Für die Freitextsuche sind sie daher irrelevant. Analog der erwähnten Abfragetypen gibt es unter anderem auch Bool Filter, Term(s) Filter und Range Filter. Richtig eingesetzt sind Filter wesentlich performanter als Queries, da diese von Elasticsearch gecached werden können. Ein weiterer Unterschied von Filtern ist, dass diese die Relevanz (Score) der Suchresultate nicht beeinflussen. Ein einfaches Beispiel einer Elasticsearch Abfrage:

Folgendes ist bei diesem Beispiel interessant:

  • Es werden zwei Match-Abfragen mittels einer Boolschen Abfrage verknüpft

  • Der zweite Teil der Abfrage ist optional (should), diese Suche würde also z.B. auch „glenns brewhouse“ in New York als Resultat zurückgeben

  • Bei der ersten Abfrage werden die einzelnen Terme mittels AND verknüpft, d.h. gültige Dokumente müssen die Terme „glenns“ und „brewhouse“ enthalten

  • Da die Felder „name“ und „state“ den Standard Analyzer verwenden ist die Gross- / Kleinschreibung irrelevant

  • Durch die (optionale) Angabe von „size“: 5 beschränken wir die Anzahl der Suchresultate auf fünf Elemente (analog funktioniert auch das Paging mittels der Attribute „from“ / „to“)

  • Sowohl die Top-Level Abfrage als auch die Sub-Abfragen können beliebig mit weiteren Queries und Filtern erweitert werden

  • Als Endpoint für die Suche wurde breweries/_search verwendet. Abfragen können auf einzelne oder mehrere Indizes und Typen oder sogar auf alle Indizes einer Elasticsearch Instanz angewendet werden

Die Suchresultate werden von Elasticsearch als Array von JSON-Objekten zurückgegeben. Das Resultat enthält Metadaten wie Dauer der Ausführung der Abfrage oder Anzahl gefundener Suchresultate. Zudem werden für jedes Suchresultat Relevanz, Index und Typ sowie das ursprünglich indexierte JSON Dokument (_source) zurückgegeben:

Aggregations API

Im Gegenteil zu der normalen Suche, bei welcher die Resultate immer möglichst präzis eingeschränkt werden und eine Abfrage im besten Fall nur wenige Resultate liefern soll, bietet die Aggregations API eine „High Level“ Übersicht über eine Datensammlung. Beispielsweise können Aggregationen Fragen wie die folgenden beantworten:

  • Welches ist die am meisten produzierte Biersorte aller indexierten Mikrobrauereien?

  • Was ist der durchschnittliche Alkoholgehalt des produzierten Bieres?

  • In welcher Stadt gibt es die meisten Mikrobrauereien?

Mit Aggregations können also sehr spezifische Auswertungen gemacht werden, diese Funktionalität ist gerade für Dashboards und Reporting sehr interessant. Aggregationen können mit normalen Such-Requests kombiniert werden, dies erlaubt es auch Auswertungen auf einem bestimmten Sub-Set von Daten auszuführen. Auch hier nutzt Elasticsearch das Konzept des Inverted Index. Aggregationen sind daher sehr performant und werden wie Such-Abfragen in „Near Real Time“ ausgeführt. Elasticsearch stellt eine ganze Reihe verschiedener Typen von Aggregationen zur Verfügung, z.B min, max, sum, avg oder stats für die Berechnung von Metriken, oder Terms für die Aggregation von Dokumenten anhand eines bestimmten Termes.

Ein einfaches Beispiel einer metrischen Aggregation:

Dieses Beispiel berechnet den durchschnittlichen Alkoholgehalt aller Biersorten im index „breweries“:

Während metrische Aggregationen verwendet werden um gewisse Kennzahlen zu berechnen, können mittels Bucketing-Aggregations Dokumente gruppiert werden. Dabei wird die Aggregation auf jedes Dokument angewendet und das Dokument basierend auf einem Kriterium einer Gruppe (Bucket) zugewiesen. Ein Beispiel dazu:

In diesem Beispiel wurde eine Terms Aggregation verwendet um zu ermitteln, welches die am meisten produzierte Biersorte ist. Für jeden gefundenen Typ (Stout, Porter, etc.) wird eine Gruppe erstellt und die Dokumente, welche diesen Typ haben, werden der entsprechenden Gruppe zugeordnet. Mit 478 gefundenen Dokumenten ist also “Red Ale” der am häufigsten produzierte Typ:

Die grundlegenden Konzepte von Aggregationen in Elasticsearch sind also „Metrics“ und „Bucketing“. Da Aggregationen ähnlich wie Suchabfragen beliebig verschachtelt werden können, sind sehr komplexe Auswertungen möglich. Dank der vielfältigen Möglichkeiten der Aggregations-API kann Elasticsearch auch als Analytics- oder Reporting-Engine eingesetzt werden. Auf die Freitextsuche kann komplett verzichtet werden, falls diese nicht benötigt wird.

Tipp: Tools für Abfragen und Administration

Es gibt eine ganze Reihe nützlicher Tools für die Administration eines Elasticsearch-Cluster. Zwei davon sind Marvel und elasticsearch-kopf. Beide bieten eine REST-Konsole, Features für die Administration und Überwachung eines Such-Clusters sowie für die Abfrage von Daten. Marvel ist für den kommerziellen Einsatz kostenpflichtig, während elasticsearch kopf Open Source und frei erhältlich ist.

Bild: Die Rest-Konsole von elasticsearch-kopf

Bild: Die Cluster-Übersicht von Marvel

Geolocation

Elasticsearch bietet out of the box verschiedene Features für die standortbasierte Suche an. Darunter die Indexierung von Koordinaten mittels Geo Point Type, die Indexierung von geografischen Umrissen (z.B. Polygon oder Kreis) mittels Geo Shape Type, sowie verschiedene Filter für lokationsbasierte Abfragen wie beispielsweise Geo Distance Filter oder Geo Bounding Box Filter. Um das vorherige Beispiel mit einem Geo Point Attribut zu erweitern, kann der Typ „brewery“ wie folgt mit dem Attribut „location“ erweitert werden:

Bei der Indexierung können die Koordinaten in verschiedenen Formaten angegeben werden, beispielsweise als Objekt mit den Eigenschaften „lat“ / „lon“:

Analog dazu kann mittels Geo Distance Filter, basierend auf der Entfernung zu einem geografischen Punkt, gesucht werden. Die folgende Abfrage würde z.B. alle Kleinbrauereien im Umkreis von 1.5 Kilometer zum Stadtzentrum von Bern zurückgeben, welche Bier einer bestimmten Sorte produzieren:

 

Referenz Schweizerische Post AG

Die Diso AG hat an folgendem Projekt bei der Post in den Bereichen Elasticsearch, Java und Oracle unterstützend mitgewirkt. Die Schweizerische Post AG hat im Rahmen des Projektes „Konsolidierung Adressdaten Post“ historische und aktuelle Adressdaten aus verschiedenen Quellen in einem zentralen System zusammengeführt, um die bestehenden veralteten Systeme abzulösen. Teil dieser Ablösung war auch die Migration einer bestehenden, auf SQL und PL/SQL basierenden Adress-Suche auf eine moderne Plattform. Aufgrund der sehr hohen Anforderungen an die Performance entschied man sich für den Einsatz von Elasticsearch. Die neue Suchlösung wurde vor etwas mehr als einem Jahr erfolgreich eingeführt. Durch den Einsatz von Elasticsearch konnten die Anforderungen an die Performance problemlos erfüllt werden. Bei Lasttests wurde – verglichen mit dem alten System – eine massive Verbesserung der Antwortzeiten festgestellt. Zudem war das System auch unter extremer Last sehr robust und Suchabfragen wurden innerhalb der vorgegebenen Toleranz beantwortet.

Die indexierten Adressen sind personenbezogen und wurden aus Performancegründen auf zwei Indizes verteilt:

  • Current: Enthält alle aktuellen, gültigen Adressen der Schweiz sowie Auslandadressen (ca. 15 Mio. Adressen)

  • Historic: Enthält aktuelle und historische Adressen der letzten ca. 40 Jahre der Schweiz sowie Auslandadressen (ca. 80 Mio. Adressen)

Zudem wurden mehre kleinere Indizes umgesetzt, welche beispielsweise nur Ortschaften oder Strassenbezeichnungen enthalten, oder beispielsweise für Bulk-Abfragen optimiert wurden.

Die Lasttests haben ergeben dass die Suchabfragen auf den historischen Index durchschnittlich in 25ms beantwortet werden, Suchabfragen mit Wildcard Query auf den kleineren Index (Current) in durchschnittlich 50ms. Bei den verwendeten Abfragen handelt es sich um kombinierte Abfragen auf den Attributen Name, Strasse, PLZ oder Ort.

Folgendes Diagramm zeigt den Vergleich des alten Systems zum neuen, wobei für beide Systeme die selben Test-Szenarien verwendet wurden:

Legende:

V3 Durchschnitt: Durchschnittliche Response-Zeit des abgelösten Systems

V4 Durchschnitt: Durchschnittliche Response-Zeit des neuen Systems

Nebst dem Erreichen der Vorgaben bezüglich Performance konnten durch den Einsatz von Elasticsearch auch fachliche Anforderungen an die Suche erfolgreich umgesetzt werden. So werden für die Suche und Indexierung beispielsweise spezielle Analyzer eingesetzt, welche die Bedürfnisse der Post abdecken, z.B für die Suche nach alternativen Ortschafts- oder Strassenbezeichnungen mittels Synonymen oder für die phonetische Suche.

Rückfragen und Diso AG Unterstützung

Für Fragen zum Artikel wenden Sie sich bitte direkt an Andreas Stotzer, astotzer@diso.ch

Daniel Meienberg beantwortet gerne Fragen zu unseren weiteren Dienstleistungen und Services dmeienberg@diso.ch

Diso AG – Der Daten- und Cloud-Experte

Die Diso AG ist ein renommierter IT-Dienstleister und langjähriger Oracle-Vertriebspartner in der Schweiz mit Schwerpunkten in den Bereichen Datenbanken und Cloud-Lösungen. Diso bietet ihren Kunden beispielsweise die Oracle Plattform as a Service-Lösung und die dazugehörige Datenmigration an. Kunden profitieren des Weiteren vom Komplettlösungsangebot im Sinne von Planung, Integration, Support inklusive Betrieb und Überwachung von IT-Infrastrukturen und Datenbanksystemen.

Im Bereich Software-Engineering entwickelt Diso massgeschneiderte IT und Software-Lösungen für unternehmensspezifische Anwendungen, wann immer sinnvoll mit einem mobile-first Ansatz. Zudem ist Diso Spezialist wenn es um die Software-basierte Optimierung von Performance geht. Auf die Kompetenz des traditionsreichen IT-Dienstleisters und Mittelständlers vertrauen bereits namhafte Kunden aus den Schwerpunktbranchen Banken, Versicherungen Detailhandel und öffentliche Verwaltung.

Die Diso designt wandlungsfähige IT-Systeme, entwickelt massgeschneiderte Software und ermöglicht die performante Verwendung und Auswertung von Informationen.