Niet alleen copy-pasten van Stack Overflow, maar begrijpen wat je copy-past.
Elasticsearch is tegenwoordig de zoekmachine bij uitstek, maar de Query DSL heeft wel een steile leercurve. Toen ik begon met het schrijven van Elasticsearch-query’s, kon ik iets in elkaar zetten dat werkte door een combinatie van de Elastic.co docs en Stack Overflow, maar ik begreep de onderliggende concepten achter de syntaxis niet volledig.
Deze tutorial is bedoeld om te zijn wat ik wou dat ik eerst had gelezen. Het gaat ervan uit dat de lezer bekend is met de basis Elasticsearch concepten, eenvoudige queries kan schrijven, en booleaanse logica begrijpt. Het is bedoeld om de lezer een stevige conceptuele basis van Elasticsearch te geven voordat hij probeert de syntax te begrijpen.
Filtering Exact Values vs Full Text Analyzed Search
Een overkoepelend thema in Elasticsearch is dat alle queries in drie typen kunnen worden ingedeeld:
1. Filteren op exacte waarden
2. Zoeken op geanalyseerde tekst
3. Een combinatie van de twee
Elk documentveld kan worden geclassificeerd als een exacte waarden of geanalyseerde tekst (ook wel volledige tekst genoemd). Exacte waarden zijn velden als user_id
date
email_addresses
, enz. Geanalyseerde tekst is tekstgegevens zoals product_description
of email_body
. Zoals de naam al aangeeft, zijn deze tekstgegevens geanalyseerd (meer hierover later). Het is vaak in een menselijke natuurlijke taal, maar dat hoeft niet.
Het bevragen van documenten kan worden gedaan door filters op te geven over exacte waarden. In deze gevallen is de vraag of het document wordt geretourneerd een binair ja of nee. Bijvoorbeeld, is de user_id
van het document gelijk aan 174517
? Ligt de datum van het document created_at
binnen het bereik van de laatste maand?
Aan de andere kant levert het doorzoeken van documenten op basis van geanalyseerde tekst resultaten op die op relevantie zijn gebaseerd. Een document wordt niet teruggegeven op basis van een ja/nee criterium, maar op basis van de relevantie ervan. Als de geanalyseerde tekst van een document bijvoorbeeld Johnny Depp bevat, moet het ook worden teruggegeven bij zoekopdrachten naar John Depp of Johnnie Depp. Een zoekactie naar kok zou ook resultaten moeten opleveren voor koken en gekookt.
Uit dit gedrag kunnen we afleiden dat zoeken op geanalyseerde tekst een zeer complexe operatie is en dat er verschillende analysepakketten bij betrokken zijn, afhankelijk van het type tekstgegevens. Sommige analysepakketten zijn bijvoorbeeld taalspecifiek en worden gebruikt om tekst in een bepaalde taal te analyseren. Het standaard analyzer pakket is de standaard analyzer die tekst splitst op woordgrenzen, kleine letters en interpunctie verwijdert. Omdat zoeken op geanalyseerde tekst zo veel ingewikkelder is dan filteren op exacte waarden, is het veel minder performant dan gewoon filteren op exacte waarden. Opmerking: we noemen zoeken op geanalyseerde tekst kortweg geanalyseerd zoeken.
De Query DSL
Elasticsearch queries bestaan uit een of meer query-clausules. Query clausules kunnen gecombineerd worden om andere query clausules te maken, samengestelde query clausules genoemd. Alle query-clausules hebben een van deze twee indelingen:
{
QUERY_CLAUSE: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}{
QUERY_CLAUSE: {
FIELD_NAME: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
}
De syntax-regel is dat query-clausules herhaaldelijk genest kunnen worden binnen andere query-clausules
{
QUERY_CLAUSE {
QUERY_CLAUSE: {
QUERY_CLAUSE: {
QUERY_CLAUSE: {
ARGUMENT: VALUE,
ARGUMENT: VALUE,...
}
}
}
}
}
Hieronder staan enkele veel voorkomende query-clausules.
Match Query Clause
De match query clause is de meest algemene en veelgebruikte query clause. Het is vrij slim in de zin dat wanneer het wordt uitgevoerd op een geanalyseerd tekstveld, het een geanalyseerde zoekopdracht uitvoert op de tekst.
In het onderstaande voorbeeld zal de eerste query een geanalyseerde zoekopdracht uitvoeren omdat description
een geanalyseerd tekstveld is. Terwijl de tweede 2 query’s filters zijn over exacte waarde velden.
{ "match": { "description": "Fourier analysis signals processing" }}
{ "match": { "date": "2014-09-01" }}
{ "match": { "visible": true }}
De match all query clausule
De match all query clausule retourneert alle documenten. Het is analoog aan SELECT * FROM table
in SQL.
{ "match_all": {} }
Term/Terms Query Clause
De term en terms query clausules worden gebruikt om te filteren op een exacte waarde velden op enkele of meerdere waarden, respectievelijk. In het geval van meerdere waarden, is de logische verbinding OR
.
Voorbeeld, de eerste query vindt alle documenten met de tag “math”. De tweede query vindt alle documenten met de tags “math” of “statistics”.
{ "term": { "tag": "math" }}
{ "terms": { "tag": }}
Multi Match Query Clause
De multi match query clausule is een match query die over meerdere velden wordt uitgevoerd in plaats van slechts één.
{
"multi_match": {
"query": "probability theory",
"fields":
}
}
Exists en Missing Filters Query Clause
De exists filter controleert of documenten een waarde hebben op een gespecificeerd veld. De missing filter controleert of documenten geen waarde hebben in een gespecificeerd veld. Ze zijn analoog aan SQL’s IS NULL
en IS NOT NULL
clausules.
{
"exists" : {
"field" : "title"
}
}
en
{
"missing" : {
"field" : "title"
}
}
Range Filter Query Clause
De range filter query clause wordt gebruikt om getal en datum velden in ranges te filteren, met behulp van de operatoren gt
gte
lt
lte
kort voor greater_than
greater_than_or_equal
less_than
en less_than_or_equal
, respectievelijk.
{ "range" : { "age" : { "gt" : 30 } } }{
"range": {
"born" : {
"gte": "01/01/2012",
"lte": "2013",
"format": "dd/MM/yyyy||yyyy"
}
}
}
Samengestelde queryclausule
Queryclausules die zijn opgebouwd uit andere queryclausules worden samengestelde queryclausules genoemd. Merk op dat samengestelde query-clausules ook kunnen worden samengesteld uit andere samengestelde query-clausules, waardoor meerlaagse nesting mogelijk wordt.
De bool query-clausule is een voorbeeld van een samengestelde query-clausule, omdat deze wordt gebruikt om meerdere query-clausules te combineren met behulp van booleaanse operatoren. De drie ondersteunde booleaanse operatoren zijn must
must_not
en should
, die overeenkomen met respectievelijk AND
NOT
, en OR
,.
Voorbeeld, stel dat we een index hebben op de posts
van een populaire social media site. Hier is een query om alle posts
over wiskunde te vinden die niet waarschijnlijk is, waar het ofwel ongelezen is of is gefavoriet.
{
"bool": {
"must": { "term": { "tag": "math" }},
"must_not": { "term": { "tag": "probability" }},
"should":
}
}
Combinatie van geanalyseerd zoeken met filters
We hebben het gehad over exacte veldfilters en geanalyseerd zoeken in afzonderlijke contexten, maar in echte wereldtoepassingen willen we vaak de twee combineren. We combineren geanalyseerd zoeken en exacte veldfilters met behulp van de filterclausule.
Voorbeeld, stel we hebben een index op de posts
van een populair webforum over wiskunde. Hier is een query om alle posts te vinden door een geanalyseerde zoekopdracht uit te voeren voor “Probability Theory” maar we willen alleen posts
met 20 of meer upvotes en niet die met die tag “frequentist”.
{
"filtered": {
"query": { "match": { "body": "Probability Theory" }},
"filter": {
"bool": {
"must": {
"range": { "upvotes" : { "gt" : 20 } }
},
"must_not": { "term": { "tag": "frequentist" } }
}
}
}
}
Summary
De conceptuele achtergrond van de Elasticsearch query DSL is deze dichotomie van het filteren van documenten vs. het zoeken door geanalyseerde tekst. Ik hoop dat je dit nuttig vond.