Beherrschung des Elasticsearch Query DSL: Wesentliche Befehle für die Datenabfrage
Entfesseln Sie die Leistungsfähigkeit der Elasticsearch-Abfrage, indem Sie das Query DSL meistern. Dieser Leitfaden erläutert wesentliche JSON-Abfragestrukturen und konzentriert sich auf die praktische Verwendung von `match`-, `term`- und Bereichsabfragen. Lernen Sie den entscheidenden Unterschied zwischen `must` (Bewertung) und `filter` (Caching)-Klauseln innerhalb der grundlegenden `bool`-Abfrage kennen, um komplexe, leistungsstarke Datensuchen effizient zu erstellen.
Beherrschung des Elasticsearch Query DSL: Wesentliche Befehle für die Datenabfrage
Elasticsearch Query DSL ist die JSON-Sprache, die Sie verwenden, wenn ein einfaches Suchfeld nicht ausreicht. Es ermöglicht Ihnen, Volltextsuche, exakte Filter, Datumsbereiche, Sortierung, Paginierung und Aggregationen in einer einzigen Anfrage zu kombinieren. Diese Flexibilität ist nützlich, aber sie macht es auch leicht, eine Abfrage zu schreiben, die die falschen Dokumente zurückgibt oder im Test gut funktioniert und in der Produktion langsamer wird.
Der beste Weg, Query DSL zu lernen, ist, sich zwei Fragen zu merken: "Suche ich nach Text mit Relevanz?" und "Filtere ich nach exakten Werten?" Die meisten Abfrageentscheidungen folgen aus dieser Trennung.
Die Anatomie einer Elasticsearch-Suchanfrage
Alle Elasticsearch-Suchen werden gegen den _search-Endpunkt eines bestimmten Index (oder mehrerer Indizes) durchgeführt. Eine grundlegende Suchanfrage ist eine POST-Anfrage, die einen JSON-Body enthält, der die Abfrageparameter definiert. Der wichtigste Teil dieses Bodys ist das query-Objekt.
Grundlegende Struktur:
POST /your_index_name/_search
{
"query": { ... Definieren Sie hier Ihre Abfragestruktur ... },
"size": 10,
"from": 0
}
Kernabfragetypen: Präzision und Relevanz
Das Query DSL bietet eine breite Palette von Abfragen, die auf verschiedene Datentypen und Matching-Anforderungen zugeschnitten sind. Die Wahl der Abfrage hat erhebliche Auswirkungen auf die Relevanzbewertung und die Leistung.
1. Volltextsuche: Die match-Abfrage
Die match-Abfrage ist der Standard für die Volltextsuche über analysierte Felder. Sie tokenisiert den Suchbegriff und prüft auf übereinstimmende Token in den angegebenen Feldern.
Anwendungsfall: Suche nach natürlichsprachlichem Text, bei dem die Relevanzbewertung wichtig ist.
Beispiel: Finden von Dokumenten, in denen das Feld 'description' die Wörter 'cloud' oder 'computing' enthält.
GET /products/_search
{
"query": {
"match": {
"description": "cloud computing"
}
}
}
2. Exakte Wertübereinstimmung: Die term-Abfrage
Die term-Abfrage sucht nach Dokumenten, die den exakten angegebenen Begriff enthalten. Im Gegensatz zu match führt sie keine Analyse des Suchstrings durch, was sie ideal für exakte Übereinstimmungen bei Schlüsselwörtern, IDs oder numerisch indizierten Feldern macht.
Anwendungsfall: Filtern nach exakten Werten in nicht analysierten Feldern (wie keyword-Feldern oder Zahlen).
Beispiel: Abrufen eines Produkts mit der genauen ID SKU10021.
GET /products/_search
{
"query": {
"term": {
"product_id": "SKU10021"
}
}
}
3. Bereichsabfragen
Bereichsabfragen ermöglichen es Ihnen, Dokumente zu filtern, bei denen der Wert eines Feldes innerhalb eines bestimmten Bereichs liegt (numerisch, Datum oder Zeichenfolge).
Syntax: Verwendet gt (größer als), gte (größer oder gleich), lt (kleiner als) und lte (kleiner oder gleich).
Beispiel: Finden von Bestellungen, die nach dem 1. Januar 2024 aufgegeben wurden.
GET /orders/_search
{
"query": {
"range": {
"order_date": {
"gte": "2024-01-01",
"lt": "2025-01-01"
}
}
}
}
4. Filtern nach Vorhandensein: Die exists-Abfrage
Die exists-Abfrage identifiziert Dokumente, in denen ein bestimmtes Feld vorhanden ist (d. h. nicht null oder fehlend).
Beispiel: Finden aller Benutzer, die eine E-Mail-Adresse angegeben haben.
GET /users/_search
{
"query": {
"exists": {
"field": "email_address"
}
}
}
Erstellen komplexer Logik mit der bool-Abfrage
Für praktisch alle realen Suchanwendungen müssen Sie mehrere Kriterien kombinieren. Die bool-Abfrage ist das wesentliche Werkzeug dafür, da sie es Ihnen ermöglicht, andere Abfrageklauseln mit boolescher Logik zu kombinieren.
Klauseln innerhalb von bool
Die bool-Abfrage akzeptiert vier primäre Klauseln:
must: Alle Klauseln in diesem Array müssen übereinstimmen. Klauseln inmusttragen zum Relevanz-Score bei.filter: Alle Klauseln in diesem Array müssen übereinstimmen, werden aber in einem nicht bewertenden Kontext ausgeführt. Dies macht sie viel schneller für strenge Einschluss-/Ausschlusskriterien.should: Mindestens eine Klausel in diesem Array sollte übereinstimmen. Diese Klauseln beeinflussen den Relevanz-Score, sind aber für die Übereinstimmung optional.must_not: Keine der Klauseln in diesem Array darf übereinstimmen (das Äquivalent zu einem logischen NICHT).
Praktisches bool-Abfragebeispiel
Kombinieren wir mehrere Konzepte, um hochpriorisierte Dokumente zu finden, die 'security' erwähnen, aber Entwürfe ausschließen und in der Region 'US' verfügbar sind.
GET /logs/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"content": "security breach"
}
}
],
"filter": [
{
"term": {
"region.keyword": "US"
}
}
],
"should": [
{
"term": {
"priority": 5
}
}
],
"must_not": [
{
"term": {
"status.keyword": "DRAFT"
}
}
]
}
}
}
Erklärung des Beispiels:
- Must: Das Dokument muss den Ausdruck "security breach" im analysierten Inhaltsfeld enthalten.
- Filter: Das Dokument muss für die Region 'US' gekennzeichnet sein (eine schnelle, exakte Übereinstimmung).
- Should: Dokumente, die
priority: 5entsprechen, erhalten eine Erhöhung ihres Relevanz-Scores, aber Dokumente mit niedrigeren Prioritäten, die diemust- undfilter-Klauseln erfüllen, werden trotzdem zurückgegeben. - Must Not: Als 'DRAFT' gekennzeichnete Dokumente werden strikt ausgeschlossen.
Best Practices für die Abfrageerstellung
Um sicherzustellen, dass Ihre Suchen sowohl genau als auch leistungsstark sind, befolgen Sie diese Richtlinien:
- Bevorzugen Sie
filtergegenübermustfür nicht bewertende Kriterien. Wenn Sie nur auf Einschluss/Ausschluss prüfen (z. B. Filtern nach ID, exaktem Datum oder Status), verwenden Sie immer diefilter-Klausel innerhalb einerbool-Abfrage. Dies nutzt Caching und vermeidet teure Bewertungsberechnungen. - Verwenden Sie exakte Abfragen mit Bedacht: Verwenden Sie für Felder, die als
text(analysiert) zugeordnet sind,match. Verwenden Sie für Felder, die alskeyword(nicht analysiert) zugeordnet sind,term- oder Bereichsabfragen. - Vermeiden Sie tiefe Verschachtelung: Obwohl möglich, können tief verschachtelte
bool-Abfragen schwer zu lesen und zu debuggen sein und manchmal zu Leistungseinbußen führen. - Nutzen Sie
minimum_should_match: Fürshould-Klauseln erzwingt das Setzen vonminimum_should_match(z. B. auf1oder2), dass eine bestimmte Anzahl dieser optionalen Kriterien erfüllt sein muss, was sie effektiv in erforderliche Kriterien verwandelt, während sie dennoch zur Bewertung beitragen können.
Das Mapping entscheidet, welche Abfrage sinnvoll ist
Die meisten Query-DSL-Fehler beginnen mit dem Mapping. Eine Abfrage kann korrekt aussehen und dennoch verwirrende Ergebnisse liefern, wenn das Feld anders zugeordnet ist, als Sie denken.
Ein häufiges Muster ist ein Textfeld mit einem Keyword-Unterfeld:
{
"mappings": {
"properties": {
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"status": { "type": "keyword" },
"created_at": { "type": "date" },
"price": { "type": "double" }
}
}
}
Verwenden Sie match für title, wenn Sie analysiertes Volltextverhalten wünschen. Verwenden Sie term für title.keyword, wenn Sie den genauen Titelwert benötigen. Verwenden Sie term für status, da es bereits ein Keyword ist. Verwenden Sie range für created_at oder price, da diese Felder Datums- und Zahlenwerte sind.
Wenn eine term-Abfrage für ein Textfeld nicht wie erwartet funktioniert, liegt das Problem oft an der Analyse. Die gespeicherten Token können kleingeschrieben, geteilt, gestemmt oder anderweitig geändert worden sein. Überprüfen Sie das Mapping, bevor Sie die Abfrage ändern.
GET /products/_mapping
Bei Textanalyseproblemen ist _analyze nützlich:
GET /products/_analyze
{
"field": "description",
"text": "Cloud Computing"
}
Das zeigt, welche Token Elasticsearch durchsuchen wird.
match, match_phrase und multi_match
match ist die alltägliche Volltextabfrage, aber nicht die einzige, die Sie verwenden werden.
Verwenden Sie match_phrase, wenn die Wortreihenfolge wichtig ist:
GET /products/_search
{
"query": {
"match_phrase": {
"description": "wireless charging stand"
}
}
}
Dies ist nützlich für Produktnamen, Protokollmeldungen, Dokumenttitel und Phrasen, bei denen die genaue Reihenfolge eine Bedeutung hat. Es ist strenger als match, kann also weniger Dokumente zurückgeben.
Verwenden Sie multi_match, wenn dieselbe Benutzereingabe mehrere Felder durchsuchen soll:
GET /products/_search
{
"query": {
"multi_match": {
"query": "noise cancelling headphones",
"fields": ["title^3", "description", "brand^2"]
}
}
}
Die Boosts ^3 und ^2 teilen Elasticsearch mit, dass Übereinstimmungen in title und brand mehr zählen sollen als Übereinstimmungen in description. Boosting ist keine Garantie dafür, dass ein Dokument an erster Stelle steht; es ist ein Bewertungshinweis. Testen Sie mit echten Abfragen, bevor Sie Boosts zu aggressiv einstellen.
Paginierung ohne den Cluster zu belasten
Die grundlegenden Parameter from und size sind für flache Paginierung in Ordnung:
GET /products/_search
{
"from": 20,
"size": 10,
"query": {
"match": {
"description": "laptop sleeve"
}
}
}
Tiefe Paginierung ist anders. Die Anforderung von Seite 1.000 zwingt Elasticsearch, viele Ergebnisse zu sortieren und zu überspringen. Vermeiden Sie für benutzerseitige Suchen unbegrenztes tiefes Blättern. Verwenden Sie für Exporte oder Hintergrundscans search_after mit einer stabilen Sortierung:
GET /products/_search
{
"size": 100,
"sort": [
{ "created_at": "asc" },
{ "_id": "asc" }
],
"search_after": ["2025-01-10T12:00:00Z", "abc123"],
"query": {
"term": {
"status": "active"
}
}
}
Die Werte in search_after stammen aus dem sort-Array des letzten Treffers in der vorherigen Antwort. Dieser Ansatz ist stabiler für das Durchlaufen großer Ergebnismengen.
Quellfilterung hält Antworten nützlich
Die Suchleistung hängt nicht nur von der Abfrageausführung ab. Die Rückgabe großer Dokumente kann den Client, das Netzwerk und den koordinierenden Knoten verlangsamen. Wenn die Benutzeroberfläche nur wenige Felder benötigt, fragen Sie nach diesen Feldern:
GET /orders/_search
{
"_source": ["order_id", "customer_id", "total", "created_at", "status"],
"query": {
"bool": {
"filter": [
{ "term": { "status": "paid" } },
{ "range": { "created_at": { "gte": "now-7d/d" } } }
]
}
}
}
Dies macht die Antwort leichter lesbar und kann die Nutzlastgröße reduzieren. Es ersetzt kein gutes Indexdesign, hilft aber, wenn Dokumente große Beschreibungen, Metadaten-Blobs oder verschachtelte Arrays enthalten, die die aktuelle Seite nicht benötigt.
Sortierung und Aggregationen benötigen die richtigen Felder
Das Sortieren nach analysiertem Text ist normalerweise ein Fehler. Sortieren Sie nach Keyword-, numerischen oder Datumsfeldern:
GET /products/_search
{
"sort": [
{ "price": "asc" },
{ "title.keyword": "asc" }
],
"query": {
"term": {
"status": "active"
}
}
}
Das Gleiche gilt für viele Aggregationen. Wenn Sie Zählungen nach Status wünschen, aggregieren Sie auf einem Keyword-Feld:
GET /orders/_search
{
"size": 0,
"aggs": {
"orders_by_status": {
"terms": {
"field": "status"
}
}
},
"query": {
"range": {
"created_at": {
"gte": "now-30d/d"
}
}
}
}
size: 0 teilt Elasticsearch mit, dass Sie nur Aggregationsergebnisse wünschen, keine übereinstimmenden Dokumente. Das ist eine kleine Gewohnheit, die Antworten sauberer hält.
Debuggen von Abfragen mit explain und profile
Wenn ein Ergebnis seltsam eingestuft wird, verwenden Sie explain für ein einzelnes Dokument:
GET /products/_explain/SKU10021
{
"query": {
"match": {
"description": "cloud computing"
}
}
}
Wenn eine Abfrage langsam ist, verwenden Sie profile in einem Nicht-Produktions- oder sorgfältig kontrollierten Produktionstest:
GET /products/_search
{
"profile": true,
"query": {
"bool": {
"must": [
{ "match": { "description": "cloud computing" } }
],
"filter": [
{ "term": { "status": "active" } }
]
}
}
}
Die Profilausgabe ist ausführlich, kann aber zeigen, ob Zeit in einer Textabfrage, einem Filter, einem Skript oder einem anderen Teil der Anfrage verbracht wird. Lassen Sie die Profilerstellung nicht im Anwendungscode aktiviert; verwenden Sie sie als Debugging-Werkzeug.
Eine sinnvolle Gewohnheit beim Erstellen von Abfragen
Erstellen Sie für die meisten Anwendungssuchen die Anfrage in dieser Reihenfolge:
- Setzen Sie exakte Einschränkungen in
filter: Mandanten-ID, Status, Region, Zeitfenster, Berechtigungen. - Setzen Sie vom Benutzer eingegebenen Text in
mustmitmatch,match_phraseodermulti_match. - Verwenden Sie
shouldfür Ranking-Präferenzen, nicht für harte Anforderungen, es sei denn, Sie setzenminimum_should_match. - Beschränken Sie
_sourceauf Felder, die der Aufrufer benötigt. - Fügen Sie eine stabile Sortierung hinzu, wenn Paginierung oder Exporte wichtig sind.
- Überprüfen Sie das Mapping, bevor Sie Elasticsearch die Schuld geben.
Das Query DSL ist leistungsstark, weil es Filtern, Bewerten, Sortieren und Antwortgestaltung trennt. Sobald Sie diese Aufgaben getrennt halten, werden Abfragen leichter lesbar, leichter abstimmbar und in der Produktion weniger überraschend.
Ein kleines Fehlerbehebungsbeispiel
Angenommen, ein Benutzer sucht nach ACME-1000 und erhält kein Ergebnis, obwohl das Produkt existiert. Fügen Sie nicht sofort Platzhalter hinzu. Überprüfen Sie zuerst das Mapping. Wenn sku ein keyword ist, sollte dies funktionieren:
GET /products/_search
{
"query": {
"term": {
"sku": "ACME-1000"
}
}
}
Wenn sku versehentlich als text zugeordnet wurde, hat die Analyse den Wert möglicherweise geteilt oder geändert. Sie können es in einigen Fällen trotzdem abfragen, aber die bessere Lösung ist normalerweise eine Mapping-Änderung für zukünftige Indizes. Exakte Identifikatoren, Status, Regionen und Mandanten-IDs sollten keyword-ähnliche Felder sein. Von Menschen geschriebene Beschreibungen und Titel sollten Textfelder sein. Das Query DSL wird viel einfacher, wenn das Mapping der Art und Weise entspricht, wie die Leute die Daten tatsächlich abrufen.