Einführung in die automatische Filterung von Abfragen
Query Autofiltering ist die automatische Kennzeichnung der eingehenden Anfrage, wobei die Wissensquelle der Suchindex selbst ist. Was bedeutet das und…
Query Autofiltering ist die automatische Kennzeichnung der eingehenden Anfrage, wobei die Wissensquelle der Suchindex selbst ist. Was bedeutet das und warum sollte uns das interessieren?
Die Kennzeichnung von Inhalten erfolgt traditionell zum Zeitpunkt der Indexierung entweder manuell oder automatisch durch maschinelles Lernen oder wissensbasierte (Taxonomie/Ontologie) Ansätze. Einen Inhalt zu ‚taggen‘ bedeutet, ein Stück Metadaten anzuhängen, das ein Attribut dieses Inhalts definiert (z.B. Produkttyp, Farbe, Preis, Datum usw.). Wir verwenden dies nun für die facettierte Suche – wenn ich nach ‚Hemden‘ suche, bringt die Suchmaschine alle Datensätze zurück, die das Token ‚Hemden‘ oder die Singularform ‚Hemd‘ enthalten (mit einer Technik, die Stemming genannt wird). Gleichzeitig werden alle Werte der verschiedenen Tags, die wir dem Inhalt zur Indexierungszeit hinzugefügt haben, unter dem Feldnamen oder der „Kategorie“ dieser Tags angezeigt. Wir nennen diese Dinge Facetten. Wenn der Benutzer auf einen Facetten-Link klickt, z.B. Farbe = Rot, generieren wir eine Solr-Filterabfrage mit dem Namen/Wert-Paar <Feldname> = <Facettenwert> und fügen dies der ursprünglichen Abfrage hinzu. Dadurch wird die Suchergebnismenge auf alle Datensätze eingegrenzt, die „Hemd“ oder „Hemden“ und den Facettenwert „Farbe“ von „rot“ enthalten.
Ein weiterer Vorteil der Facettierung ist, dass der Benutzer alle Farben sehen kann, in denen es Hemden gibt, so dass er auch blaue Hemden auf die gleiche Weise finden kann. Was aber, wenn er ungeduldig ist und „blaue Hemden“ in das Suchfeld eingibt? So wie die Dinge jetzt funktionieren, wird die Suchmaschine Datensätze zurückgeben, die das Wort „Hemd“ oder „Hemden“ ODER das Wort „blau“ enthalten. Dies ist insofern teilweise erfolgreich, als blaue Hemden in der Ergebnismenge enthalten sind, aber auch rote, grüne, orange und gelbe Hemden, da sie alle den Begriff ‚Hemd‘ gemeinsam haben. (Dies ist der Fall, wenn die Produktbeschreibung wie folgt lautet: „Dies ist ein wirklich schönes Hemd. Es gibt es in rot, blau, grün, orange und gelb.“) Schlimmer noch, wir bekommen auch andere blaue Dinge wie Pullover, Hosen, Socken, Mützen usw., weil sie alle das Wort ‚blau‘ in ihrer Beschreibung haben.
Ah, sagen Sie, aber dann können Sie die Facettierung nutzen, um das zu bekommen, was Sie wirklich wollen. Klicken Sie auf die Facette für den Hemdentyp und die Farbfacette für Blau. Aber warum sollten wir den Benutzer dazu zwingen? Es ist ärgerlich, wenn er zuerst einen Haufen Dinge sieht, nach denen er nicht gefragt hat, und dann auf Dinge klicken muss, um das zu bekommen, was er will. Sie wollten es uns leichter machen, indem sie im Voraus angeben, welche Farbe sie haben möchten, und wir haben es ihnen schwerer gemacht. Aber das müssen wir nicht – wie Dorothy im Zauberer von Oz haben wir bereits die Informationen, die wir brauchen, um „das Richtige zu tun“, wir nutzen sie nur nicht. Daher die automatische Filterung von Abfragen.
Es gibt hier noch eine weitere Besonderheit, die wir berücksichtigen sollten. Traditionell werden Markierungsvorgänge, ob manuell oder automatisiert, zum Zeitpunkt der Indexierung zur „Anreicherung von Inhalten“ angewendet. Im Grunde genommen fügen wir dem Inhalt Wissen hinzu – ein Tag teilt der Suchmaschine mit, dass dieser Inhalt ein bestimmtes Attribut oder eine bestimmte Eigenschaft besitzt. Was aber, wenn wir dies mit der eingehenden Suchanfrage machen? Das können wir, denn wie der Inhalt sind auch die Abfragen nur Text, und nichts hindert uns daran, die gleichen Techniken auf sie anzuwenden (hier muss es allerdings ein Autotagging sein, wenn wir die Ergebnisse in weniger als einer Sekunde haben wollen). In der Regel tun wir dies jedoch nicht, weil wir die Abfrage nicht so verändern wollen, dass sie die Suchmaschine in die Irre führt und die ‚falschen‘ Ergebnisse liefert – d.h. wir wollen es nicht vermasseln, also lassen wir es bleiben. Wir wissen, dass, wenn wir standardmäßig ODER verwenden, die richtigen Ergebnisse „irgendwo“ enthalten sein sollten und der Benutzer dann Facetten verwenden kann, um das Gesuchte zu finden.
Wir verwenden auch standardmäßig ODER, weil wir Angst haben, UND zu verwenden – was zu den gefürchteten NULL-Ergebnissen führen kann. Das kommt uns wie ein Misserfolg vor, denn wir haben NICHTS, NICHTS, NICHTS gefunden – was ist los mit uns? Aber ist das richtig? Was ist, wenn die Sache, die der Benutzer eigentlich finden möchte, in der Suchkollektion nicht vorhanden ist? Sollten wir NULL Ergebnisse zurückgeben oder nicht? Im Moment tun wir das nicht. Wir geben einen Haufen Ergebnisse zurück, und wenn der Benutzer versucht, die Suche zu vertiefen, stößt er immer wieder auf eine Sackgasse. Nehmen wir an, dass wir keine lila Socken führen. Wenn ein Benutzer danach sucht, geben wir Socken zurück, die nicht lila sind, und lila Dinge, die keine Socken sind. Wenn es schon frustrierend ist, dass man erst nachbohren muss, um das Gewünschte zu finden, nachdem man uns das in der Anfrage gesagt hat, dann ist es SEHR frustrierend, wenn man immer wieder mit leeren Händen dasteht. Warum können wir nicht von vornherein zugeben, dass wir dieses Produkt nicht verkaufen? Wir werden den Artikel sowieso nicht kaufen, weil wir ihn nicht verkaufen, aber warum sollten wir sie verärgern, wenn wir ihn nicht finden?
Das tun wir nicht, weil wir nicht von der Genauigkeit unserer Suchergebnisse überzeugt sind, d.h. wir wissen es auch nicht. Wir wollen also keine Kunden verlieren, indem wir ihnen sagen, dass wir etwas nicht führen, obwohl wir es in Wirklichkeit führen. Also benutzen wir die Facettierung als Netz, um die verirrten Fische zu fangen. Aber manchmal sind wir zuversichtlich genug, dass wir wissen, was der Nutzer will, so dass wir die Suchmaschine außer Kraft setzen und ihm die richtige Antwort geben können. Eine Möglichkeit sind die besten Wetten, auch bekannt als Landing Pages oder Spotlighting. Dies erfordert, dass ein Mensch bestimmt, was für eine bestimmte Gruppe von Suchbegriffen angezeigt werden soll – eine sehr arbeitsintensive Methode, aber die einzige, die wir kennen. Wir vertrauen darauf, denn ein Mensch hat sich die Suchanfragen angesehen und herausgefunden, wonach gesucht wird.
Ein weiteres tolles Beispiel dafür ist die Abfrage der lokalen Wettervorhersage bei Google oder Bing. Ich bezeichne diese Technik als „inferentielle Suche“ – der Prozess, bei dem der Nutzer auf das, was er sucht, schließt und dann genau das zurückgibt (oder es an den Anfang stellt). Dies funktioniert, indem die Suchanfrage analysiert wird und dann die Suchausgabe auf der Grundlage dieser Vorabanalyse umgelenkt oder erweitert wird. Um auf das Autotagging zurückzukommen, das ich vorhin angesprochen habe: Warum machen wir das nicht zur Abfragezeit? Wir haben diese Wissensbasis, die wir unseren Suchindex nennen, bereits durch (manuelle und automatisierte) Markierungsprozesse zur Indexzeit aufgebaut – warum nutzen wir dieses Wissen nicht einfach zur Abfragezeit? Wir wissen bereits, wie wir es mit Facetten nutzen können, um den Benutzer zur richtigen Antwort zu führen, aber warum sollten wir den Benutzer warten lassen oder ihm mehr Arbeit aufbürden, wenn er das nicht muss? Verstehen Sie jetzt, wozu die automatische Filterung von Abfragen gut ist? Wir können damit diesen frustrierenden Prozess abkürzen, so dass wir dem Benutzer bei der Suche nach „blaue Hemden“ nur blaue Hemden anzeigen! Wir können dies tun, weil wir der Suchmaschine gesagt haben, dass „blau“ eine „Farbe“ ist, indem wir dies als einen der Werte der Facette „Farbe“ hinzugefügt haben – d.h. der Suchindex „kennt“ diese linguistische Wahrheit bereits, weil wir ihr das gesagt haben, als wir die Dinge getaggt haben. Das Vertrauen, das wir in unsere Facettierungsmaschine eingebaut haben, kann also zum Zeitpunkt der Abfrage genutzt werden, um dasselbe zu tun – wenn wir ‚blau‘ in der Abfrage sehen, ziehen wir es heraus und machen es zu einer Filterabfrage, als ob der Benutzer zuerst nach ‚Hemden‘ gesucht und dann auf die Facette ‚blaue Farbe‘ geklickt hätte.
Es stellt sich heraus, dass Lucene-Solr dies wirklich einfach macht. Der Lucene-Index enthält eine Datenstruktur namens FieldCache, die alle Werte enthält, die für ein bestimmtes Feld indiziert wurden. Dieser wird in Lucene 5.0 in „UninvertedIndex“ oder so ähnlich umbenannt – eine Art suchtechnischer Ausdruck dafür, dass es sich um einen Vorwärtsindex handelt – und nicht um den normalen „invertierten Index“, den Suchmaschinen verwenden. Mit einem invertierten Index können wir fragen – gib mir alle Dokumente, die diesen Begriff in diesem Feld enthalten. Mit einem nicht invertierten Index können wir fragen: „Was sind alle Begriffswerte, die in diesem Feld indexiert wurden? Sobald wir also Dinge getaggt haben, können wir zur Abfragezeit feststellen, ob einer dieser Tag-Werte in der Abfrage vorkommt, und dank Lucene können wir dies sehr schnell tun (bei anderen Suchmaschinen können wir dies möglicherweise gar nicht tun, da Facettierungen zur Indexzeit berechnet werden und es möglicherweise kein Äquivalent zum FieldCache gibt – aber Sie können trotzdem White Lists verwenden). Aber zurück zu den NULL-Ergebnissen: Wir können jetzt getrost sagen: „Nein, wir führen keine lila Socken“ oder orangefarbene Teppiche, weil wir jetzt wissen, dass es keine Socken oder Teppiche gibt, die wir jemals auf diese Weise getaggt haben. Wir sollten auch die Meldung „Keine Ergebnisse“ freundlicher gestalten, anstatt dem Benutzer zu suggerieren, dass er etwas falsch gemacht hat, wie wir es jetzt tun. (Beschämenderweise haben wir nie zugegeben, dass wir der wahre Übeltäter gewesen sein könnten – d.h. wir verweigern die Aussage.)
Die automatische Filterung von Suchanfragen ist daher eine sehr leistungsstarke Technik, denn sie nutzt das gesamte Wissen, das wir in unseren Suchindex eingegeben haben, und gibt uns ein viel größeres Vertrauen, dass wir „das Richtige“ tun, wenn wir an der ursprünglichen Anfrage herumspielen. Eine einfache Implementierung dieser Idee steht auf github zum Download bereit. Einer der Vorteile dieses Ansatzes ist, dass er denselben Index verwendet, auf den die Abfrage über den FieldCache abzielt – und er ist sehr schnell. Ein Nachteil ist, dass er eine ‚exact-match‘-Semantik voraussetzt, so dass er nicht mit Dingen wie Synonymen umgehen kann – z.B. was wäre, wenn der Benutzer nach „roten Sofas“ statt nach „roten Sofas“ fragt und wir automatisch nach dem Produkttyp filtern wollen – oder nach „marineblauen Hemden“? (Die Lösung wäre hier ein mehrwertiges Facettenfeld, das wir für die automatische Filterung verwenden würden). Aufgrund der Semantik der exakten Übereinstimmung müssen wir auch sicherstellen, dass Dinge wie Groß- und Kleinschreibung oder Stemming keine Auswirkungen auf uns haben. Aus diesem Grund müssen wir die Abfrage eventuell ein wenig vorverarbeiten, damit sie mit dem übereinstimmt, was wir indiziert haben. Dabei müssen wir darauf achten, dass die Teile der ursprünglichen Abfrage, die wir in unveränderter Form an Solr weitergeben, nicht verzerrt werden.
Ein weiterer Nachteil ist, dass die Suchkomponente für die automatische Filterung von Abfragen jeweils nur mit einem Feld arbeitet, so dass wir für mehrere Felder mehrere Komponentenstufen benötigen. Ein guter Anwendungsfall sind Oldtimer, bei denen Sie Jahr, Marke und Modell wie in „1967 Ford Mustang Cabrio“ ermitteln möchten.
Ein anderer Ansatz, der von Erik Hatcher vorgeschlagen wurde, besteht darin, eine separate Sammlung zu haben, die als Wissensspeicher spezialisiert ist, und diese abzufragen, um die Kategorien zu erhalten, mit denen die Inhaltssammlung automatisch gefiltert wird. Dies ist eine weniger „brutale“ Methode, bei der eine Sammlung verwendet wird, die „Facettenwissen“ enthält. Wir können diese Sammlung vorab abfragen, um etwas über Synonyme und Kategorien usw. zu erfahren und dabei all die coolen Tricks verwenden, die wir in die Suche eingebaut haben – unscharfe Suche zur Behandlung von Rechtschreibfehlern, Annäherung, Multi-Term-Fixes wie Autophrasing und mehr (Pivot-Facetten und Statistiken – Oh My!) – die Möglichkeiten sind hier buchstäblich endlos. Die Ergebnisse dieser Pre-Query-Phase können dann die automatische Filterung oder eine weniger aggressive Strategie wie Spotlighting oder Abfragevorschläge (wie bei der Rechtschreibkorrektur) steuern. Im Gegensatz zum FieldCache-Ansatz kann dieser Ansatz in eine Query Pipeline-Phase ausgelagert werden, wie sie unser Produkt Lucidworks Fusion bietet. Der Schlüssel ist, dass wir in beiden Fällen den Suchindex selbst als Wissensquelle nutzen, die wir für eine intelligente Abfrageintrospektion und damit für eine leistungsstarke inferentielle Suche verwenden können!!
Nochmals vielen Dank an Erik Hatcher für die Anregung und die Zusammenarbeit mit mir bei dieser Idee. Das Problem des „roten Sofas“ wurde in meinem Beitrag „Die wohltemperierte Suchanwendung – Fugue“ diskutiert. Ich hatte ursprünglich daran gedacht, eine Whitelist zu verwenden, um dieses Problem zu lösen. Erik schlug vor, die Feldwerte zu verwenden, die im Index enthalten sind, und dieser Vorschlag hat den Kreis für mich „geschlossen“ – also ein großer Lichtblick. Erik ist ein Genie. Er ist bekannt für seine Bücher über Ant und Lucene und ist außerdem ein extrem netter Kerl. Eine meiner größten Freuden, als ich zu Lucidworks kam, war die Möglichkeit, mit Leuten wie Erik, Grant Ingersoll, Tim Potter und Chris Hostetter – besser bekannt als Hossman oder einfach Hoss – zusammenzuarbeiten. Diese Jungs sind Koryphäen in der Welt von Lucene-Solr, aber die Liste meiner außergewöhnlichen Kollegen bei Lucidworks ist noch viel länger.