Laravel: Performante Suche

31. Juli 2018, von Daniel
vom Suchen und Finden

Bei unseren großen Projekten haben wir hauptsächlich Symfony- und Laravel Komponenten im Einsatz, erweitern den Kern um die spezifischen Anforderungen abzudecken und bauen dann ein Modul-Management darüber, um Erweiterbarkeit zu gewährleisten.

Beim Aufsetzen des Kerns bin ich immer wieder auf den Use-Case gestoßen, dass Daten aus unterschiedlichsten Tabellen irgendwo im System zentral benötigt werden. Das einfachste Beispiel dafür ist eine zentrale Suche, welche verschiedenste Inhaltstypen durchsucht. In diesem Beitrag werde ich euch erklären, wie einfach es ist, so eine Suche in Laravel zu integrieren.

Als Entwickler sucht man gewöhnlich immer nach fertigen Packages, bevor man selbst einen Finger rührt. Das habe ich in diesem Fall natürlich auch gemacht, und bin auf “Laravel Scout” — die hauseigene Suche von Laravel — gestoßen. Für unser Projekt ist das Package aber nicht gut geeignet, da unsere Datenstruktur & unser Ranking zu komplex ist. Keine Sorge: in diesem Beitrag konzentriere ich mich auf die Basics, um den Rahmen nicht zu sprengen 😉

Nach kurzem Grübeln war schnell mal klar:
Um die Suche performant zu halten, muss eine neue Datenbanktabelle angelegt werden, welche bei Suchen abgefragt wird.

Sollte es diese nämlich nicht geben, müssten alle Tabellen, welche durchsuchbar sind, einzeln abgefragt werden!! Das wäre für die Performance natürlich ein No-Go!

Im einfachsten Fall reicht eine Tabelle mit folgenden Spalten:

  • Suchtext: nach dem Text wird bei Abfragen gesucht
  • Inhaltstyp-Klasse: damit beim Ergebnis der richtige Inhaltstyp geladen werden kann
  • Inhalts-ID: die ID des Inhaltselements

Diese Tabelle soll beim Erstellen, Updaten und Löschen von durchsuchbaren Inhalten automatisch geupdatet werden.

Laravel erweitern & Models vorbereiten

Für diesen Schritt wird ein “Manager” benötigt, welcher alle durchsuchbaren Inhalte überwacht, und bei bestimmten Events die Suchtabelle updatet. Ich werde den Manager in den nächsten Zeilen einfach “Search-Engine” oder “Suchmaschine” nennen.

Laravel bietet die einfache Möglichkeit, das System mit sogenannten Service-Providern zu erweitern. Ein solcher muss erstellt werden, um darin die Search-Engine zu instanzieren. Dann braucht die Suchmaschine noch die Information, welche Models durchsuchbar sind. Wie sie zu dieser Information kommt ist eigentlich egal. In kleinen Projekten löse ich das über ein config-Array, welches alle Klassen enthält. Bei großen Projekten überlasse ich diesen Task dem Modul-Management, um auch Inhalte aus Modulen für die Suche bereitzustellen.

Um sicherzustellen, dass die Inhalte korrekte Daten für die Suchtabelle zur Verfügung stellen, und bei einem Such-Request die richtigen Daten zurückgeben, verpflichte ich diese, ein Searchable Interface implementieren.

Ein einfaches Beispiel dafür wäre:

interface Searchable {
    public function searchFields(): array;
    public function searchResult(); array;
}

Damit ist sichergestellt, dass alle durchsuchbaren Models angeben, welche Felder bei der Suche berücksichtigt werden. Außerdem muss das Model auch definieren, welche Daten als Suchergebnis zurückgegeben werden.

Somit sind die Models für die Suche vorbereitet.

Suchtabelle aktuell halten

Hier kommt Laravel’s Event-System zum Einsatz. Ein mächtiges Instrument, welches einem die Arbeit um ein Vielfaches erleichtert! Eloquent (Laravels ORM — Object-relational mapping) bietet bereits viele vordefinierte Events für Models. Wenn man auf mehrere Events eines Models reagieren will, kann man sogenannte “Event-Observer” registrieren. Wenn ein Model-Event auftritt, wird dann die gleichnamige Methode des Observers aufgerufen.

Das bedeutet, wenn auf das “created” Event reagiert werden soll, muss im Observer stets eine Methode namens “created” implementiert werden. Alle Event-Methoden bekommen das jeweilige Model als Parameter übergeben. Da alle Models das Searchable Interface implementieren, kann als Argumenttyp-Deklaration “Searchable” angegeben werden.

Um die Inhalte der Suchtabelle aktuell zu halten, muss für alle Searchables auf die Events “saved” und “deleted” reagiert werden. Der Observer muss sich dann nur darum kümmern, dass beim “saved”-Event ein Eintrag erstellt beziehungsweise upgedatet wird, und beim “deleted” die jeweiligen Einträge aus der Suchtabelle gelöscht werden.

Um das zu arrangieren, muss die Search-Engine über alle registrierten Searchable-Klassen iterieren und jeweils die statische Methode “observe” ausführen, um für das Model einen Event-Observer zu registrieren:

foreach ($searchableClasses as $searchableClass) {
    // hier prüfen, ob $searchableClass das Interface implementiert!
    $searchableClass::observe(SearchObserver::class);
}

Ergebnisse Finden

Um nun zu Suchergebnissen zu kommen, muss jetzt einfach nur die Suchtabelle nach einem bestimmten Text abgefragt werden:

SELECT model_class, model_id
FROM search_table
WHERE search_text
LIKE “%danach wird gesucht%”;

Diese Abfrage gibt alle Ergebnisse von allen durchsuchbaren Inhaltstypen zurück. Dann muss nur noch die model_id von der model_class laden.

$content = $modelClass::find($model_id);

Der Inhalt sollte dann existieren, da beim Löschen ja auch der Inhalt aus der Suchtabelle gelöscht wird; aber um sicherzugehen, kann geprüft werden, ob $content eine Instanz von $modelClass ist.

Andere Use-Cases

Der Einsatz von Event-Observer in Kombination mit einem “DataManager” erweist sich auch in zahlreichen anderen Anwendungsgebieten als äußerst hilfreich. Wir haben beispielsweise ein Reporting-System, welches diverse Events speichert und Statistiken auswertet, auf Basis dieses Konzepts aufgebaut. In diesem Fall waren jedoch einige Custom-Events im Einsatz (nicht nur die default Eloquent Events). Ein weiterer Anwendungsfall, welcher mir spontan dazu einfällt, wäre ein Notification-Center.

Daniel

Meine Rolle bei Liechtenecker: Backend Development🤓 Wenn es weder IT noch Digitalisierung gäbe, wäre mein Beruf: Musiker Mein Herz schlägt für: PHP Development, Progressive Metal, Kaffee
Keine Kommentare vorhanden.
Kommentar verfassen
Name
Mail
Web
Captcha
Erfolgreich!
Fehler!
Arbeitskultur – Blogbeitrag

Wie man den Design Sprint für sich selbst anwendet und mehr Zeit gewinnt.

20. Februar 2020, von Blumi

Dauerstress? Vollgestopfter Terminkalender? Andauernd beschäftigt und doch ständig abgelenkt? Alles Oasch und für nix mehr Zeit? Lies weiter mein Kind, ich zeig dir was.

Jetzt lesen
Liechtenecker Leseliste mit Günter Lischka
Arbeitskultur – Podcasts

Folge #56 mit Günter Lischka

20. Dezember 2019

In Folge 56 erlaubt Günter Lischka, verantwortlich für das Privatkundengeschäft beim Telekomanbieter Drei Österreich, Einblicke in die Einführung eines neuen Managementstils anhand von OKRs (Objectives and Key Results). Das Prinzip der OKRs [...]

Jetzt anhören
Close