© istockphoto.com/ akindo
Big Data in Elasticsearch und Laravel 5 mit einer Repository-Schicht

Suchen für Profis


Das Suchformular ist in modernen und großen Webprojekten wie Shops und Magazinen das wohl wichtigste Element der Seite geworden. Schaut man sich gut funktionierende Shops wie eBay und Amazon an, liegt der Fokus beim Einstieg auf die Seite ganz klar auf der Suche. Elasticsearch bietet Ihnen dafür die nötigen Werkzeuge und (REST-)Schnittstellen.

An Suchfunktionen werden heute hohe Ansprüche gestellt. Der Nutzer soll möglichst schnell und einfach über (Volltext-)Suchphrasen, die eventuell noch über eine Combo-Box auswählbar sind, zu einem Ergebnis gelangen. Schaut man sich Onlineshops wie Amazon, eBay und Co. an, fällt auf, dass der Fokus des Nutzers direkt auf die Suche gelenkt wird. Sonst würde er bei der schier unendlichen Produktpalette nämlich wohl nie zu dem gewünschten Ergebnis kommen.

Elasticsearch: ein kleiner Überblick

Elasticsearch ist eine auf Apache Lucene basierende Volltextsuchmaschine. Die aktuelle Version 1.5.2 bietet Ihnen eine Vielzahl an Features, zum Beispiel:

  • HTTP-JSON-REST-API zum Indexieren und Auslesen von Dokumenten

  • Google-ähnliche Suggestions für Suchvorschläge

  • Spellchecking: „Brmsbelg“ findet auch einen Bremsbelag

  • Die Facettensuche ist seit Version 1.0 über Aggregationen realisiert, was auch verschachtelte und zusammengesetzte Filter ermöglicht. Eine Facettensuche schränkt die verfügbaren Filter anhand der gesetzten Filter ein

  • Volltextsuche über mehrere Felder

  • Eigene Indexierungs- und Such-Analyzer

  • Clustering

Im Gegensatz zu einer relationalen Datenbank wie zum Beispiel MySQL, die über Schlüssel auf Dokumente verweist, werden in Elasticsearch Dokumente in einem invertierten Index gespeichert, der die Texte eines Dokuments in Terme trennt. Somit wird im Index nur noch die Anzahl der Dokumente mit dem entsprechenden Term sowie Informationen, welches Dokument diesen Term enthält und wo er sich im Dokument befindet, gespeichert.

Gegenüber einer Volltextsuche in MySQL bietet das folgende Vorgehen beim Indexieren einen deutlichen Geschwindigkeitsvorteil:

select title, body, created, author from article where body like '%foobar%';

Dort wird MySQL-intern ähnlich wie mittels grep auf der Kommandozeile das Feld body nach dem Vorkommen von foobar durchsucht. Dieses Vorgehen ist relativ aufwändig, weil die Suche über alle Dokumente hinweg durchgeführt werden muss, was gerade bei großen Datenmengen viel Zeit in Anspruch nehmen kann. Elasticsearch sucht für die gleiche Anfrage nach allen Dokumenten, die im Feld body den Term foobar beinhalten und diese Dokumente zurückgeben.

Aus Onlineshops kennen Sie sicher auch die Möglichkeit, Ihre Suchergebnisse mit Filtern einschränken zu können. Auch für diesen Zweck bietet Elasticsearch standardmäßig Funktionalitäten, zum Beispiel die Aggregationen. Über einen „Filtered“-Filter in Elasticsearch werden sie direkt eingeschränkt und nur noch Filter zurückgeliefert, die zur aktuellen Ergebnismenge passen – und das, ohne weitere Queries abfeuern zu müssen.

Installation von Elasticsearch und Ihr erstes Laravel-Projekt

Für die erste Installation von Elasticsearch empfehle ich Ihnen, eine Vagrant-Box [1] über PuPHPet [2] zu erstellen und dort unter „Search Servers“ bei Elasticsearch das Häkchen zu setzen. Sollten Sie schon einen funktionierenden Entwicklungsserver haben, schauen Sie auf der offiziellen Elasticsearch-Webseite [3] unter Downloads [4] nach einem für Sie geeigneten Installationskandidaten.

Achtung Windows-User

Sollten Sie Windows-User sein, empfehle ich Ihnen unbedingt den Einsatz eines *nix-Entwicklungsservers. Am schnellsten geht das via Vagrant und PuPHPet. Im weiteren Verlauf des Artikels werde ich einige Kommandozeilenprogramme verwenden, die eventuell unter Windows anders gehandhabt werden müssen.

Laravel ist aktuell in Version 5.x verfügbar. Die Installation eines neuen Projekts ist ziemlich einfach, es müssen nur einige Voraussetzungen erfüllt werden:

  • PHP 5.5+: MCrypt, OpenSSL, mbstring, curl (für das Elasticsearch-API)

  • Apache oder NGINX

  • Composer

Ich empfehle Ihnen den Laravel-Installer, weil dieser am simpelsten zu bedienen ist. Mit dem folgenden Befehl kann der Laravel-Installer installiert und ein neues Laravel-Projekt angelegt werden:

$ composer global require "laravel/installer=~1.1" $ cd /var/www $ laravel new laravel5-elasticsearch $ cd laravel5-elasticsearch $ php artisan app:name Laravel5Elasticsearch

In Zeile 1 wird der Laravel-Installer global in Ihrem Home-Verzeichnis unter ~/.composer/vendor installiert. Danach haben Sie über die Kommandozeile Zugriff auf das Kommando laravel. Nach dem Wechseln in das Webserver-Root-Verzeichnis erstellen Sie das neue Laravel-5-Projekt mit dem Namen laravel5-elasticsearch im gleichnamigen Verzeichnis. Der besseren Übersicht halber vergeben Sie noch in der letzten Zeile einen sprechenden Root Namespace. Wenn Sie jetzt http://localhost/laravel5-elasticsearch/public aufrufen, sollten Sie eine Standardseite von Laravel zu sehen bekommen.

Achtung

Um das Kommando laravel direkt in der Kommandozeile verwenden zu können, muss das Verzeichnis ~/.composer/vendor/bin/ in der $PATH-Variable vorhanden sein.

Sollten Sie Composer noch nicht installiert haben, können Sie dies mit dem folgenden Befehl tun:

$ sudo curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

Das Laravel-Projekt vorbereiten

Um das kommende Beispiel nicht zu komplex zu machen, verwenden Sie erst einmal nur ein Artikelmodell. Mit dem folgenden Befehl lässt sich das Modell inklusive eines Migrationsskripts mit Artisan sehr einfach anlegen:

$ php artisan make:model Article

Welche Felder in dem neuen Modell zur Verfügung stehen sollten, zeigt Tabelle 1.

Feldname

Beschreibung

id

Primärschlüssel | unsigned integer(11) | autoincrement

title

Artikeltitel | varchar(255)

short_desc

Kurzbeschreibung | varchar(255)

long_desc

Lange Beschreibung des Artikels | medium_text

price

Artikelpreis | float(8,2)

vat

Steuern | float(8,2)

created_at

Datum und Zeit der Erstellung des Artikels | datetime(iso8601)

updated_at

Datum und Zeit der letzten Änderung des Artikels | datetime(iso8601)

Tabelle 1: Felder im neuen Migrationsmodell

Listing 1

<?php use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateArticlesTable extends Migration {  /**  * Run the migrations.  *  * @return void  */ public function up() { Schema::create('articles', function(Blueprint $table) { $table->increments('id'); $table->string('title'); $table->string('short_desc'); $table->string('long_desc', 2048); $table->float('price'); $table->float('vat'); $table->timestamps(); }); } /**  * Reverse the migrations.  *  * @return void  */ public function down() { Schema::drop('articles'); } }

Achtung

Sollten Sie einmal händisch ein Modell mit Migration anlegen, achten Sie darauf, dass Laravel das Modell – in Ihrem Fall Article – im Singular und die Tabelle, in Ihrem Fall articles, im Plural erwartet.

Damit Sie Ihre Migration nun auch migrieren können, legen Sie noch eine MySQL-Datenbank in Ihrem Entwicklungsstack an. Die Konfiguration der Datenbank kann über die mitgelieferte .env-Datei erfolgen. Diese Dateien können seit Laravel 5 für verschiedene Stacks, zum Beispiel Staging, Development und Production, genutzt werden. Der Datenbank-Part der .env-Datei sollte folgendermaßen aussehen:

DB_HOST=localhost DB_DATABASE=elasticsearch DB_USERNAME=elasticsearch DB_PASSWORD=123

Die Parameter müssen Sie natürlich Ihren Einstellungen entsprechend anpassen.

Achtung

Eventuell müssen Sie noch die Datei .env.example in .env umbenennen.

Nachdem Sie die Datenbankeinstellungen vorgenommen haben, können Sie jetzt über $ php artisan migrate die Migration in die Datenbank durchführen. Lassen Sie sich nicht davon stören, dass dort noch eine users- und eine password_resets-Tabelle mit angelegt wird. Dabei handelt es sich um Laravel-Standardtabellen, die vom Auth Package in Laravel verwendet werden.

Um das Eloquent-Article-Modell nun auch mit Mass-Assignment-Methoden wie Article::create() verwenden zu können, müssen Sie dem Modell mit Protected Property: $fillable noch mitteilen, welche Felder befüllt werden dürfen. Alternativ können Sie mit Protected Property: $guarded natürlich auch definieren, welche Felder nicht über Mass-Assignment-Methoden befüllt werden dürfen. In Ihrem Fall ist Letzteres sicher etwas weniger Code.

Achtung

Vorsicht mit der $guarded-Property! Damit wären alle anderen Felder durchaus, falls die Applikation dies über Formulare oder andere Schnittstellen hergibt, befüllbar – auch, wenn sie nicht explizit sichtbar sind.

Beide Möglichkeiten habe ich in Listing 2 einmal beschrieben. Um die Änderungen vorzunehmen...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang