Elasticsearch pilno teksto paieška: indeksavimas ir užklausos

Kas yra Elasticsearch ir kodėl jis tapo tokiu populiariu

Elasticsearch – tai ne tik dar vienas duomenų bazės sprendimas. Tai pilnavertė paieškos ir analitikos platforma, pastatyta ant Apache Lucene bibliotekos. Jei kada nors stebėjotės, kaip greitai Amazon suranda produktus pagal jūsų įvestą užklausą arba kaip GitHub akimirksniu randa kodo fragmentus milijonuose repozitorijų, greičiausiai už kulisų dirba būtent Elasticsearch ar panašus sprendimas.

Pagrindinis Elasticsearch privalumas – gebėjimas žaibiškai ieškoti informacijos milžiniškuose duomenų kiekiuose. Tradicinės reliacinės duomenų bazės su LIKE '%tekstas%' užklausomis čia tiesiog neprilygsta. Elasticsearch naudoja invertintus indeksus, kurie veikia panašiai kaip knygos gale esantis dalykinė rodyklė – vietoj to, kad skaitytumėte visą knygą ieškodami žodžio „algoritmas”, tiesiog pažvelgiate į rodyklę ir iš karto žinote, kuriuose puslapiuose jis minimas.

Šiandien Elasticsearch naudojamas ne tik paieškos funkcionalumui. Jis tapo neatsiejama log’ų analizės, metrikų rinkimo, saugumo monitoringo ir net mašininio mokymosi dalimi. ELK stack’as (Elasticsearch, Logstash, Kibana) tapo standartu daugelyje organizacijų.

Kaip veikia indeksavimas ir kodėl jis toks svarbus

Indeksavimas Elasticsearch – tai procesas, kurio metu jūsų dokumentai transformuojami į paieškos sistemai suprantamą formatą. Įsivaizduokite, kad turite tūkstančius straipsnių ir norite, kad vartotojai galėtų juos rasti per sekundes dalį. Tiesiog įkėlus tekstą į duomenų bazę nepakanka – reikia sukurti efektyvią struktūrą paieškai.

Kai indeksuojate dokumentą, Elasticsearch atlieka keletą svarbių operacijų. Pirma, tekstas padalijamas į atskirus žodžius (tokenization). Antra, šie žodžiai normalizuojami – pavyzdžiui, „Bėgimas”, „bėgimo” ir „bėga” gali būti suvesti į tą pačią šaknį „bėg”. Trečia, sukuriamas invertintas indeksas, kuris leidžia greitai rasti, kuriuose dokumentuose pasitaiko konkretus žodis.

Štai paprastas dokumento indeksavimo pavyzdys:

PUT /straipsniai/_doc/1
{
"pavadinimas": "Įvadas į Elasticsearch",
"autorius": "Jonas Jonaitis",
"turinys": "Elasticsearch yra galingas paieškos variklis, leidžiantis greitai ieškoti didelių duomenų kiekių.",
"data": "2024-01-15",
"kategorija": "technologijos"
}

Svarbu suprasti, kad Elasticsearch nėra schemos nebuvimas (schemaless) – jis turi dinamišką schemą. Tai reiškia, kad jei nenurodėte mapping’o (lauko tipo aprašymo), Elasticsearch pats pamėgins atspėti, kokio tipo yra kiekvienas laukas. Tačiau gamybinėse sistemose visada rekomenduoju aiškiai apibrėžti mapping’ą, nes automatinis atpažinimas ne visada veikia taip, kaip tikitės.

Mapping’as – jūsų indekso architektūros pagrindas

Mapping’as Elasticsearch yra tai, kas schema reliacinėse duomenų bazėse. Jis apibrėžia, kaip dokumentai ir jų laukai turėtų būti saugomi ir indeksuojami. Teisingai sukonfigūruotas mapping’as gali būti skirtumas tarp greitai veikiančios sistemos ir tos, kuri lėtai šliaužia.

Yra keletas pagrindinių lauko tipų, kuriuos turėtumėte žinoti. text tipas naudojamas pilno teksto paieškai – jis analizuojamas, tokenizuojamas ir indeksuojamas paieškai. keyword tipas saugo tikslią reikšmę ir naudojamas filtravimui, rūšiavimui ir agregacijoms. date, integer, boolean ir kiti tipai veikia intuityviai.

Štai kaip galėtų atrodyti gerai apgalvotas mapping’as:

PUT /straipsniai
{
"mappings": {
"properties": {
"pavadinimas": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"autorius": {
"type": "keyword"
},
"turinys": {
"type": "text",
"analyzer": "lithuanian"
},
"data": {
"type": "date"
},
"perziuros": {
"type": "integer"
}
}
}
}

Atkreipkite dėmesį į multi-field konfigūraciją pavadinimo lauke – tai leidžia naudoti tą patį lauką ir pilno teksto paieškai (text), ir tiksliam atitikimui ar rūšiavimui (keyword). Tai labai dažnas ir naudingas pattern’as.

Analizatoriai – paslaptis už efektyvios paieškos

Analizatoriai yra Elasticsearch širdis, kai kalbame apie teksto apdorojimą. Jie nusprendžia, kaip tekstas bus padalintas į žodžius, kaip šie žodžiai bus normalizuojami ir kaip bus vykdoma paieška. Standartinis analizatorius veikia gerai anglų kalbai, bet lietuviškam tekstui reikia specialių sprendimų.

Analizatorius susideda iš trijų komponentų: character filters (simbolių filtrai), tokenizer (žodžių skaidytojas) ir token filters (žodžių filtrai). Character filters pirmiausia apdoroja tekstą – pavyzdžiui, gali pašalinti HTML tagus. Tokenizer paskirsto tekstą į atskirus žodžius. Token filters atlieka galutinį apdorojimą – mažina raides, šalina stop words, atlieka stemming’ą.

Lietuvių kalbai rekomenduoju naudoti specialius analizatorius arba sukurti savo:

PUT /straipsniai
{
"settings": {
"analysis": {
"analyzer": {
"lietuviu_analizatorius": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"lietuviu_stop",
"lietuviu_stemmer"
]
}
},
"filter": {
"lietuviu_stop": {
"type": "stop",
"stopwords": ["ir", "bei", "ar", "kad", "yra"]
},
"lietuviu_stemmer": {
"type": "stemmer",
"language": "lithuanian"
}
}
}
}
}

Testuoti analizatorius galite naudodami _analyze API – tai neįtikėtinai naudinga funkcija, kuri parodo, kaip tiksliai jūsų tekstas bus apdorotas:

POST /straipsniai/_analyze
{
"analyzer": "lietuviu_analizatorius",
"text": "Bėgantys žmonės greitai pavargo"
}

Užklausų tipai ir kada juos naudoti

Elasticsearch turi daugybę užklausų tipų, ir pradedantiesiems tai gali atrodyti pribloškiančiai. Tačiau praktikoje dažniausiai naudojami keli pagrindiniai tipai, kuriuos gerai išmokus galima išspręsti 90% situacijų.

match užklausa – tai jūsų pagrindinis įrankis pilno teksto paieškai. Ji analizuoja paieškos tekstą tuo pačiu analizatoriumi, kuris buvo naudotas indeksuojant, ir ieško atitikimų:

GET /straipsniai/_search
{
"query": {
"match": {
"turinys": "elasticsearch paieška"
}
}
}

term užklausa ieško tikslaus atitikmens ir nenaudoja analizatoriaus. Ji puikiai tinka keyword tipo laukams:

GET /straipsniai/_search
{
"query": {
"term": {
"autorius": "Jonas Jonaitis"
}
}
}

bool užklausa leidžia kombinuoti kelias užklausas su loginiais operatoriais. Ji turi keturis kontekstus: must (turi atitikti, įtakoja relevance score), filter (turi atitikti, neįtakoja score), should (gali atitikti, pagerina score) ir must_not (neturi atitikti):

GET /straipsniai/_search
{
"query": {
"bool": {
"must": [
{ "match": { "turinys": "elasticsearch" }}
],
"filter": [
{ "term": { "kategorija": "technologijos" }},
{ "range": { "data": { "gte": "2024-01-01" }}}
],
"should": [
{ "match": { "pavadinimas": "įvadas" }}
]
}
}
}

Svarbi detalė – filter kontekstas yra greitesnis nei must, nes rezultatai kešuojami ir nereikia skaičiuoti relevance score. Todėl visada naudokite filter tiksliems kriterijams (datos, kategorijos, boolean reikšmės), o must – teksto paieškai.

Relevance scoring – kaip Elasticsearch nusprendžia, kas svarbiau

Vienas didžiausių Elasticsearch privalumų – gebėjimas ne tik rasti atitinkančius dokumentus, bet ir juos surūšiuoti pagal relevance (atitikimo) balą. Tai tas mechanizmas, kuris lemia, kodėl vieni rezultatai rodomi viršuje, o kiti apačioje.

Elasticsearch naudoja TF-IDF (Term Frequency-Inverse Document Frequency) algoritmą kartu su kitais faktoriais. Paprastai tariant, dokumentas gauna aukštesnį balą, jei:
– Paieškos terminas pasitaiko dokumente dažnai (TF)
– Paieškos terminas yra retas visame indekse (IDF)
– Dokumentas yra trumpesnis (field length norm)

Galite paveikti relevance scoring naudodami boost parametrą. Pavyzdžiui, jei pavadinime rastas žodis yra svarbesnis nei turinyje:

GET /straipsniai/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"pavadinimas": {
"query": "elasticsearch",
"boost": 2.0
}
}
},
{
"match": {
"turinys": "elasticsearch"
}
}
]
}
}
}

Jei norite suprasti, kodėl konkretus dokumentas gavo tam tikrą balą, naudokite explain parametrą:

GET /straipsniai/_search
{
"explain": true,
"query": {
"match": { "turinys": "elasticsearch" }
}
}

Tai grąžins detalų scoring’o paaiškinimą, kuris gali būti sudėtingas, bet labai informatyvus debugging’ui.

Agregacijos – daugiau nei paieška

Agregacijos – tai viena galingiausių Elasticsearch funkcijų, kuri dažnai lieka nepakankamai išnaudota. Jos leidžia ne tik rasti dokumentus, bet ir analizuoti duomenis, skaičiuoti statistiką, grupuoti rezultatus.

Yra trys pagrindiniai agregacijų tipai: metric (metrikų), bucket (grupavimo) ir pipeline (konvejerio) agregacijos. Metric agregacijos skaičiuoja reikšmes – vidurkius, sumas, minimumą, maksimumą. Bucket agregacijos grupuoja dokumentus pagal kriterijus. Pipeline agregacijos dirba su kitų agregacijų rezultatais.

Pavyzdys – skaičiuojame straipsnių kiekį pagal kategoriją ir vidutinį peržiūrų skaičių:

GET /straipsniai/_search
{
"size": 0,
"aggs": {
"pagal_kategorija": {
"terms": {
"field": "kategorija"
},
"aggs": {
"vidutines_perziuros": {
"avg": {
"field": "perziuros"
}
}
}
}
}
}

Agregacijos gali būti įdėtos viena į kitą (nested), sukuriant sudėtingas analitines užklausas. Pavyzdžiui, galite grupuoti straipsnius pagal mėnesį, tada pagal kategoriją, ir kiekvienai kategorijai skaičiuoti statistiką.

Svarbus patarimas – jei naudojate agregacijas dideliems duomenų kiekiams, atkreipkite dėmesį į atminties vartojimą. terms agregacija pagal nutylėjimą grąžina tik 10 populiariausių reikšmių, bet galite padidinti size parametrą. Tačiau būkite atsargūs – didelis size gali sukelti atminties problemų.

Praktiniai patarimai ir dažniausios klaidos

Dirbant su Elasticsearch, yra keletas dalykų, kuriuos išmokau per sunkųjį kelią, ir noriu jais pasidalinti.

Pirma, niekada nenaudokite wildcard užklausų pradžioje (*tekstas). Tai viena lėčiausių operacijų, nes Elasticsearch negali panaudoti indekso efektyviai. Jei tikrai reikia tokios funkcionalumos, apsvarstykite n-gram analizatorių.

Antra, būkite atsargūs su script užklausomis. Jos yra labai lankstūs, bet lėtos, nes vykdomos kiekvienam dokumentui atskirai. Jei galite pasiekti tą patį rezultatą standartinėmis užklausomis – darykite tai.

Trečia, indeksų skaičius turi būti protingas. Turint tūkstančius mažų indeksų, cluster’io valdymas tampa košmaru. Geriau naudoti mažiau, bet didesnius indeksus su gerai apgalvota struktūra.

Ketvirta, monitorinkite savo cluster’io būseną. Elasticsearch turi puikų _cluster/health API, kuris parodo, ar viskas gerai. Yellow statusas reiškia, kad kai kurios replikos nepasiekiamos – tai priimtina development aplinkoje, bet ne production’e.

Penkta, naudokite aliases indeksams. Tai leidžia keisti indeksų struktūrą be aplikacijos kodo pakeitimų. Pavyzdžiui, galite sukurti naują indeksą su patobulinta schema, reindeksuoti duomenis, ir tada tiesiog perjungti alias’ą:

POST /_aliases
{
"actions": [
{ "remove": { "index": "straipsniai_v1", "alias": "straipsniai" }},
{ "add": { "index": "straipsniai_v2", "alias": "straipsniai" }}
]
}

Šešta, optimizuokite savo mapping’us. Jei laukas niekada nebus naudojamas paieškai, nustatykite "index": false. Jei nereikia saugoti originalios reikšmės (tik indeksuoti paieškai), nustatykite "store": false. Kiekvienas sutaupytas baitas dauginasi iš milijonų dokumentų.

Kai viskas susideda į vieną paveikslą

Elasticsearch – tai įrankis, kuris atveria duris į greitą ir efektyvų duomenų pasiekiamumą. Tačiau kaip ir su bet kokia galinga technologija, reikia laiko ir praktikos, kad išmoktumėte ją naudoti efektyviai.

Pradėkite nuo paprastų dalykų – sukurkite indeksą, apibrėžkite mapping’ą, indeksuokite kelis dokumentus, išbandykite pagrindines užklausas. Eksperimentuokite su analizatoriais, stebėkite, kaip skirtingi nustatymai paveikia paieškos rezultatus. Naudokite Kibana Dev Tools – tai neįkainojamas įrankis mokantis ir debuginant.

Nepamirškite, kad Elasticsearch nėra sprendimas visiems atvejams. Jis puikiai tinka paieškai, log’ų analizei, real-time analitikai. Bet jis nėra transakcinis duomenų bazė ir neturėtų būti naudojamas kaip pagrindinis duomenų saugykla kritinei informacijai. Dažniausiai Elasticsearch naudojamas kartu su kita duomenų baze – pavyzdžiui, PostgreSQL saugo pagrindinius duomenis, o Elasticsearch indeksuoja juos paieškai.

Svarbiausias patarimas – skaitykite oficialią dokumentaciją. Elasticsearch dokumentacija yra viena geriausių, kokias esu matęs – išsami, su pavyzdžiais, nuolat atnaujinama. Ir prisijunkite prie bendruomenės – Elasticsearch forume ir Stack Overflow rasite atsakymus į daugumą klausimų.

Technologijos pasaulis nuolat keičiasi, ir Elasticsearch nėra išimtis. Naujos versijos atneša naujų funkcijų, pagerinimų, kartais breaking changes. Bet pagrindinės koncepcijos – indeksavimas, mapping’as, užklausos, agregacijos – išlieka stabilios. Išmokus šiuos pagrindus, galėsite adaptuotis prie bet kokių pokyčių ir panaudoti Elasticsearch visą potencialą savo projektuose.

Daugiau

Service mesh saugumas: mTLS autentifikacija