Facettierte Filter: wie man kocht und womit man serviert

Worum geht es 



Wie führe ich eine facettierte Suche in einem Online-Shop durch? Wie werden Werte in facettierten Suchfiltern generiert? Wie wirkt sich die Auswahl eines Werts in einem Filter auf Werte in benachbarten Filtern aus? Auf der Suche nach Antworten habe ich die fünfte Seite der Google-Suchergebnisse erreicht. Ich fand keine erschöpfenden Informationen, ich musste es selbst herausfinden. Der Artikel beschreibt:



  1. Wie reagiert die Benutzeroberfläche, wenn der Benutzer Filter verwendet?
  2. Algorithmus zum Erzeugen von Filterwerten; 
  3. ElasticSearch-Abfragevorlagen und Indexstrukturen mit Erläuterungen.


Hier gibt es keine vorgefertigten Lösungen. Sie können nicht kopieren und einfügen. Um Ihr eigenes Problem zu lösen, müssen Sie sich damit befassen.







Konzepte, um es klar zu machen 



Volltextsuche - Suche nach Produkten nach Wort oder Satz. Für den Benutzer ist dies ein Feld zur Texteingabe mit der Schaltfläche "Suchen", das auf jeder Seite der Site verfügbar ist.



Facettierte Suche - Suche nach einem Produkt anhand verschiedener Merkmale: Farbe, Größe, Speichergröße, Preis usw. Für den Benutzer ist es eine Reihe von Filtern. Jedem Filter ist nur ein Merkmal zugeordnet und umgekehrt. Filterwerte sind alle möglichen Werte für ein Merkmal. Der Benutzer sieht Filter auf der Abschnittsseite, Kategorie, auf der Seite mit Volltextsuchergebnissen. Wenn der Benutzer einen Wert auswählt, wird der Filter als aktiv betrachtet.



Facettiertes Filterverhalten 



Kurz gesagt, ein Filter filtert Produkte und Filterauswahloptionen in anderen Filtern. 



Filtert Produkte



Damit ist es einfach. Der ausgewählte Benutzer:



  1. Ein Wert sieht Produkte, die dem Wert entsprechen.
  2. Mehrere Werte in einem Filter sehen Produkte, die mit mindestens einem übereinstimmen.
  3. Werte in mehreren Filtern sehen Produkte, die mit dem Wert jedes Filters übereinstimmen.


In Bezug auf die Boolesche Algebra: Es gibt ein logisches "UND" zwischen den Filtern, ein logisches "ODER" zwischen den Werten im Filter . Einfache Logik. 



Filterauswahl in anderen Filtern



"Nun ... welche Optionen gibt es - angezeigt, welche nicht - versteckt" - so beschreibt das Unternehmen das Verhalten von Filtern. Klingt logisch. In der Praxis funktioniert es so:



  1. Gehen Sie zum Abschnitt Telefone und sehen Sie Filter nach Merkmalen: Marke, Diagonale, Speicher. Jeder Filter enthält Werte. 
  2. . . 1.
  3. . , . , 2. 
  4. . . , 3.
  5. «» . 3 ..


Die Anzahl der Filterwerte hängt von der Anzahl der Produkte ab: Je mehr Produkte mit unterschiedlichen Kennwerten, desto mehr Werte enthält der Filter. Der Benutzer hat die Anzahl der Produkte in der Auswahl für die verbleibenden Filter reduziert, als er eine Marke ausgewählt hat. Dies führte zu einer Aktualisierung der Wertelisten.



Dies impliziert eine universelle Regel: Filterwerte werden aus einer Auswahl von Produkten abgerufen, die von den übrigen aktiven Filtern gebildet werden.



Jeder aktive Filter hat seine eigene Produktauswahl.



Wenn wir N Filter haben und:



  • nicht aktiv sind, dann ist die Stichprobe allgemein. Es ist für alle Filter gleich und stimmt mit den Suchergebnissen überein.
  • M ist aktiv und M <N, dann ist die Anzahl der Abtastwerte M + 1, wobei 1 die Abtastung ist, auf die alle aktiven Filter angewendet werden. Dies ist für alle inaktiven Filter gleich und stimmt mit den Suchergebnissen überein.
  • aktives M und N = M, dann die Anzahl der Abtastwerte N. Jeder Filter hat seine eigene Abtastung.


Schließlich , wenn der Benutzer eine Facette Filterwert auswählt, geschieht folgendes: 



  1. eine Suchauswahl von Waren wird gebildet; 
  2. Die Werte für inaktive Filter werden aus der Suchauswahl abgerufen.
  3. Für jeden aktiven Filter wird eine neue Probe gebildet und neue Werte von aktiven Filtern werden daraus extrahiert.


Es stellt sich die Frage, wie dies in die Praxis umgesetzt werden kann.



Implementierung von Elasticsearch (ES)



Die Produkteigenschaften sind nicht universell, daher finden Sie hier keine vorgefertigte Indexstruktur zum Speichern von Produkten oder vorgefertigten Abfragen. Stattdessen finden Sie Links zur Dokumentation, in denen erläutert wird, wie Sie die "richtigen" Indizes und Abfragen selbst erstellen. "Richtig" - basierend auf meiner Erfahrung und meinem Wissen. 



"Richtige" Arten von Textfeldern



In ES interessieren uns 2 Datentypen: 



  • Text für die Volltextsuche. Felder dieses Typs können nicht zum genauen Vergleichen, Sortieren und Aggregieren verwendet werden.
  • Schlüsselwort für Zeichenfolgen, die an Operationen zum genauen Vergleichen, Sortieren und Aggregieren beteiligt sind.


ES analysiert die Werte im Textfeld und erstellt ein Wörterbuch für die Volltextsuche. Die Werte im Schlüsselwortfeld werden als empfangen indiziert. Aggregation und Sortierung ist nur für Keyword-Felder verfügbar.



Der Benutzer verwendet in beiden Fällen Merkmale: bei der Volltextsuche und durch Filter. Mit ES können Sie einem einzelnen Feld keine zwei Typen zuweisen, bietet jedoch andere Lösungen:



Felder 

PUT my_index
{
  «mappings»: {
    «properties»: {
      «some_property»: { 
        «type»: «text», // 1
        «fields»: { // 2
          «raw»: { 
            «type»: «keyword»
          }
        }
      }
    }
  }
}


  1. Wir deklarieren Produkteigenschaften als  Textfeld
  2. Erstellen Sie mit dem Parameter fields ein untergeordnetes virtuelles Feld vom Typ keyword . Virtuell, weil es im Index und nicht in der Produktbeschreibung vorhanden ist. ES speichert die Daten beim Empfang automatisch im untergeordneten Feld.


Also für jedes Merkmal.  



Bei Abfragen für genaue Vergleichs-, Sortier- und Aggregationsvorgänge müssen Sie ein untergeordnetes virtuelles Feld vom Typ Schlüsselwort verwenden . Im Beispiel ist dies some_property.raw . Für die Textsuche - Eltern.



copy_to .

PUT my_index
{
  «mappings»: {
    «properties»: {
      «all_properties»: { // 1
        «type»: «text»
      },      «some_property_1»: {
        «type»: «keyword»,
        «copy_to»: «all_properties» // 2 
      },
      «some_property_2»: {
        «type»: «keyword»,
        «copy_to»: «all_properties»
      }
    }
  }


  1. Erstellen Sie ein virtuelles Feld mit dem Texttyp im Index .    
  2. Deklarieren Sie jedes Merkmal als  Schlüsselwort mit dem Parameter copy_to . Geben Sie das virtuelle Feld mit dem Parameterwert an. ES kopiert den Wert aller Merkmale in das virtuelle Feld, wenn das Dokument gespeichert wird. 


Für den genauen Vergleich, die Sortierung und die Aggregation müssen Sie das Merkmalsfeld für die Textsuche verwenden - ein Feld mit den Werten aller Merkmale.



Beide Ansätze erstellen zusätzliche Felder im Index, die in der ursprünglichen Dokumentstruktur nicht vorhanden sind. Um eine Abfrage zu erstellen, müssen Sie daher die Struktur des Index kennen.



Ich bevorzuge die Option copy_to . Um eine Volltextsuchabfrage zu erstellen, reicht es aus, ein Feld mit einer Kopie der Werte aller Merkmale zu kennen. 



Anfragen 



Nach Produkten suchen 



Wir gehen davon aus, dass die Indexstruktur dieselbe ist wie in der copy_to- Variante . Für die Volltextsuche in ES wird das Übereinstimmungskonstrukt zum Vergleich mit den Werten von Facettenfiltern verwendet - BegriffsabfrageDie boolesche Abfrage kombiniert Konstrukte zu einer Abfrage. Es wird ungefähr so ​​sein:



{
  «query» : { 
    «bool»: {
      «must»: {
        «match»: { 
          «virtual_field_for_fulltext_searching»: {
            «query»: «some text»
          }
        }
      },
      «filter»: { 
        «must»: [
           {«property_1»: [ «value_1_1», …, «value_1_n»]},
          … 
           {«property_n»: [ «value_n_1», …, «value_n_m»]}
        ]
      }
    }
  }
}


query.bool.must.match Haupt-Volltextsuchabfrage 

query.bool.filter filtert, um die Hauptabfrage zu verfeinern. must inside bedeutet logisches "und" zwischen Filtern. Das Wertearray in jedem Filter ist ein Boolescher Wert oder.   



Für Filterwerte



Die Begriffe Aggregationsklausel gruppieren Produkte nach charakteristischem Wert und berechnen die Menge in jeder Gruppe. Diese Operation wird als Aggregation bezeichnet. Die Schwierigkeit besteht darin, dass für jeden aktiven Filter die  Begriffsaggregation für eine Auswahl von Waren durchgeführt werden muss, die von anderen aktiven Filtern gebildet werden. Für inaktive Filter - für eine Auswahl, die den Suchergebnissen entspricht. Mit dem Filteraggregationskonstrukt können Sie eine separate Auswahl für jede Aggregations- und "Pack" -Operation in einer Abfrage erstellen.



Die Anforderungsstruktur sieht folgendermaßen aus: 

{
  «size»: 0,
  «query» : { 
    «bool»: {
      «must»: {
        «match»: {
          «field_for_fulltext_searching»: {
            «fuzziness»: 2,
            «query»: «some text»
          }
        }
      },
      «filter»: {
      
      }
    }
  },
  «aggs» : {
    «inavtive_filter_agg» : {
      «filter» : {        … 
      },
      «aggs»: {
        «some_inavtive_filter_subagg»: { 
          «terms» : {
            «field» : «some_property»
          }
        },
        ... 
        «some_other_inavtive_filter_subagg»: {
          «terms» : {
            «field» : «some_other_property»
          }
        }
      }
      
    }, 
    «active_filter_1_agg» : {
       «filter»: {
         …        },
       «aggs»: {
          «active_filter_1_subagg»: {
             «terms» : {
                «field»: «property_1»
             }
          }
       }
    },
    …,  
    «active_filter_N_agg» : {
       «filter»: {
         …        
      },
       «aggs»: {
          «active_filter_N_subagg»: {
             «terms» : {
                «field»: «property_N»
             }
          }
       }
    }
  }
}


query.bool - Hauptabfrage, Filteroperationen werden in ihrem Kontext ausgeführt. Es besteht aus: 

  • match - Anfrage für Volltextsuche;
  • Filter - Filter nach Merkmalen, die nicht mit Facettenfiltern verknüpft sind und in einer Teilmenge vorhanden sein müssen. Dies kann ein Filter von in_stock, is_visible sein, wenn Sie immer nur Produkte auf Lager oder nur sichtbare anzeigen möchten.


aggs.inavtive_filter_agg - Aggregation für inaktive Facettenfilter besteht aus:

  • Filter -   Bedingungen durch Eigenschaften, die durch aktive Facettenfilter gebildet werden. Zusammen mit der Hauptabfrage wird eine Auswahl von Waren gebildet, für die die untergeordneten Aggregationen dieses Abschnitts ausgeführt werden. 
  • aggs ist ein benanntes Aggregationsobjekt für jeden inaktiven Filter. 


aggs.active_filter_1_agg - Aggregation zum Abrufen der Werte des ersten aktiven Facettenfilters. Jedes Design ist einem Facettenfilter zugeordnet. Besteht aus: 

  • Filter - Bedingungen nach Eigenschaften, die von aktiven Facettenfiltern mit Ausnahme des aktuellen gebildet werden. Zusammen mit der Hauptabfrage bildet es eine Auswahl von Waren, für die die untergeordnete Aggregation dieses Abschnitts durchgeführt wird.
  • aggs - ein Objekt aus einer Aggregation gemäß der Eigenschaft des aktuell aktiven Facettenfilters. 


Es ist wichtig, "Größe" anzugeben : 0 , andernfalls erhalten Sie eine Liste der Produkte, die der Hauptabfrage ohne Aggregationen entsprechen. 



Zusammenfassend 



Zwei Anfragen erhalten:



  1. Gibt für Suchergebnisse Produkte zurück, die dem Benutzer angezeigt werden sollen.
  2. Führt für Filterwerte eine Aggregation durch, gibt Filterwerte und die Anzahl der Produkte mit diesem Wert zurück.


Jede Anforderung ist in sich geschlossen, daher ist es am besten, sie asynchron auszuführen.   



PS Ich gebe zu, dass es mehr "richtige" Ansätze und Werkzeuge gibt, um das Problem der facettierten Suche zu lösen. Für zusätzliche Informationen und Beispiele in den Kommentaren wäre ich dankbar.



All Articles