Apr 22, 2020
Zuvor haben wir über die Geburt und die Qualen der Erweiterung komplexer ES-Abfragen gesprochen, wenn die benutzerdefinierte Generierung mit all ihren Nachteilen angewendet wird.
In diesem Beitrag zeigen wir anhand von Beispielen, wie einfach der elastic-builder die Generierung handhaben kann und welches die positiven Ergebnisse seiner Integration in das Framework sind.
Anstelle der benutzerdefinierten Generierung für den Must-Teilbaum, die wir in unserem vorherigen Blogbeitrag vorgestellt haben, haben wir die Hilfsmethoden des elastic-builders verwendet. Fast das Gleiche kann mit den Klassenkonstruktoren erreicht werden. Dies ist unser Ergebnis:
let esb = require('elastic-builder'); let tasksSearchObject = esb.requestBodySearch(); let tasksSearchBoolQuery = esb.boolQuery().must(esb.matchPhraseQuery('country', country)); if (!!statusList.length) { tasksSearchBoolQuery = tasksSearchBoolQuery .must(esb.queryStringQuery(statusList.map(status => "(" + status + ")").join(' OR ')) .defaultField('status')); } if (prediction) { tasksSearchBoolQuery = tasksSearchBoolQuery .must(esb.matchPhraseQuery('prediction', prediction)); } let postObject = tasksSearchObject.query(tasksSearchBoolQuery).toJSON();
Kurz und kompakt, nicht wahr? Und die Ausgabe ist fast dieselbe wie die benutzerdefinierte, nur dass sie für das Land und die Vorhersage optimiert ist . Anstatt eine Übereinstimmung mit dem Feld phrase zu verwenden, setzen wir nur die Klausel match_phrase, wodurch die Größe des JSON reduziert wird, aber immer noch das gleiche Ziel erreicht wird:
"must": [ { "match_phrase": { "country": country } }, { "query_string": { "query": "(" + status_1 + ") OR (" + status_2 + ")", "default_field": "status" } }, { "match_phrase": { "prediction": vorhersage } } ]
Denken Sie daran, dass wir vor der Verwendung des elastic-builders nicht einmal wussten, dass die match_phrase-Klausel existiert.
Wenn es darum geht, Filter unter bestimmten Bedingungen anzuwenden, wird die erwartete Ausgabe für jede Bedingung unterschiedlich sein. Wenn Sie mehrere Bedingungen auf verschiedene Filter anwenden, müssen Sie entweder für jede Bedingung eine eigene Generation erstellen oder Sie müssen zumindest für die betroffenen Klauseln Bedingungen einbeziehen.
Der zweite Ansatz ist weitaus effizienter, aber Sie müssen mit dem benutzerdefinierten Code im Zusammenhang mit der Bedingung äußerst vorsichtig sein. Schauen wir uns das folgende Beispiel an, in dem Sie zwei verschiedene Statuswerte haben, für die Sie zwei verschiedene Abfragen generieren müssen. Die boolsche Verbundklausel für die erste Abfrage sieht wie folgt aus:
"should": [ { "bool": { "must_not": { "term": { "task_type": task_type } } }, { "bool": { "must": [ { "term": { "task_type": taskType } }, { "range": { "details.update_date": { "gte": date } } } ] } } ]
Der zweite Statuswert führt nur zu einer anderen Sortierung:
"sort": [ { { "close_date": sortOrder } ]
Obwohl die zweite Variante einfacher ist, kostet sie uns immer noch mehr als 40 Zeilen benutzerdefinierten Code für die Erzeugung von nur 3 + 1 Blattklauseln. Um Fehler so weit wie möglich zu vermeiden, haben wir dieses große Codesegment in ähnlicher Weise wie bei der match_phrase-Klausel durch die Hilfsmethoden des elastic-builders ersetzt:
case status_1: tasksSearchBoolQuery = tasksSearchBoolQuery .filter(esb.boolQuery() .should(esb.boolQuery() .mustNot(esb.termQuery('task_type', taskType))) .should(esb.boolQuery() .must(esb.termQuery('task_type', taskType)) .must(esb.rangeQuery('details.update_date') .gte(date))); break; case status_2: tasksSearchObject = tasksSearchObject.sort(esb.sort('close_date', sortOrder)); break;
Es ist offensichtlich, dass jede Bedingung immer noch unterschiedliche Teile des Post-Objekts betrifft. Der Fall status_1 fügt 3 Blätter in der zusammengesetzten Klausel bool hinzu und status_2 ändert die Sortierklausel. Wir haben es jedoch vermieden, eine neue Abfrage zu erstellen, indem wir eine "Kette" von Hilfsmethoden über zwei Variablen*(tasksSearchObject* und tasksSearchBoolQuery, die an die erste Variable angehängt wird) verwendet haben.
Eine weitere gute Erfahrung mit der kompakten Art und Weise, Abfragen mit elastic-builder zu generieren, ist die mehrfache Filterung mit mehreren Werten pro Filter. Obwohl wir mit weitaus komplizierteren Filtern konfrontiert waren, zeigen wir Ihnen hier ein Beispiel, bei dem nur nach 2 - user und source_name - mit 3 bzw. 2 Werten gefiltert wird. Im Folgenden sehen Sie die Abfrage, die mit unserem benutzerdefinierten Ansatz erstellt wurde:
"must": [ { "bool": { "filter": { "terms": { "user": [ user_id_1, user_id_2, user_id_3 ] } } }, { "bool": { "filter": { "terms": { "details.source_name": [ quell_name_1, quell_name_2 ] } } } } ]
Die gleiche Abfrage kann in zwei Zeilen mit nur einigen Hilfsmethoden generiert werden, im Gegensatz zu dem, was wir vorher mit dem benutzerdefinierten Code hatten:
tasksSearchBoolQuery = tasksSearchBoolQuery .must(esb.boolQuery() .filter(esb.termsQuery('user', [user_id_1, user_id_2, user_id_3]))); tasksSearchBoolQuery = tasksSearchBoolQuery .must(esb.boolQuery() .filter(esb.termsQuery('details.source_name', [source_name_1, source_name_2])));
Alle oben genannten nützlichen Codezeilen wurden unter der Annahme geschrieben, dass Sie die genaue Abfrage kennen, die Sie generieren möchten. Was aber, wenn Sie aufgrund mangelnder Kenntnisse auf dem Gebiet der ES-Abfragen nicht sicher sind, wie unsere Abfrage aussehen wird?
Die gewünschte Ausgabe hängt von der korrekt angewandten Abfrage ab, und mit elastic-builder müssen Sie nicht zu tief in die DSL (domänenspezifische Sprache) eindringen, um die Abfragen zu erzeugen. Natürlich ist jede Art von Wissen über die Abfrage-DSL von Vorteil, aber es ist definitiv kein Muss.
Stattdessen genügt es, den Zweck der elastic-builder Klassen und Hilfsmethoden zu kennen. Wenn Sie zum Beispiel eine effiziente benutzerdefinierte Suche nach mehreren Feldern (wie title, task_type) durchführen wollen, egal ob sie verschachtelt sind (wie d_etails.retailer_) oder nicht, können Sie einfach den folgenden queryStringQuery-Helfer verwenden, nachdem Sie die offizielle Dokumentation schnell durchgelesen haben:
let tasksSearchBoolQuery = tasksSearchBoolQuery.must( esb.queryStringQuery(searchString)) .fields(['title', 'task_type', 'details.retailer']) .defaultOperator('AND');
Es ist jedoch ratsam, mit der Verwendung von Platzhaltern und logischen Operatoren (wie AND) vertraut zu sein, da Sie sonst am Ende einen falschen Operator angeben oder ihn gar nicht definieren können (standardmäßig wird OR verwendet). Die entsprechende Blattklausel (im Teilbaum must ) sieht wie folgt aus:
"query_string": { "query": searchString, "fields": [ "title", "task_type", "details.retailer" ], "default_operator": "AND"
Durch die Angabe von AND als Standardoperator behandelt der ES den Wert des Suchstrings als Ganzes, während er standardmäßig als Array von Wörtern betrachtet worden wäre. Daher hat es eine völlig andere Bedeutung, wenn Sie stattdessen einen anderen logischen Operator verwenden. Ein ähnliches Beispiel ist der Keyword-Analyzer. Wir verwenden ihn häufig aufgrund seiner Effektivität, zum Beispiel wenn der Sortierwert mehrere Wörter enthalten kann, wie beim Sortieren nach details.retailer und nach details.source_type:
let tasksSearchObject = tasksSearchObject .sort(esb.sort('details.retailer.keyword', orderRetailer)) .sort(esb.sort('details.source_type.keyword', orderSourceType));
Beachten Sie, dass keyword überhaupt kein Feld ist, obwohl es im Wert des Feldparameters angegeben ist. Als einfache Schlussfolgerung aus der Beantwortung dieser Frage möchten wir darauf hinweisen, dass elastic-builder Ihnen nur helfen kann, die Abfragen zu verstehen und einfach zu erstellen. Er ist nicht dafür verantwortlich, was Sie als Parameterwert angeben.
Wenn Sie in Erwägung ziehen, elastic-builder oder eine andere ähnliche Bibliothek zur Erstellung von ES-Abfragen einzusetzen, empfehlen wir Ihnen, dies von Anfang an zu tun, bevor die Abfragen zu komplex werden. Nachfolgend finden Sie die Ergebnisse, die wir mit elastic builder in unserem Framework erzielt haben:


Velimir Graorkoski
Software Engineer - AI
Vertrauen bei führenden Unternehmen weltweit




Intertec


Aneta Pejchinoska


Intertec