Elasticsearch yra paieškos variklis su json rest api, naudojant Lucene ir parašyta Java. Visų šio variklio pranašumų aprašymą rasite adresu . Toliau Elasticsearch vadinsime ES.
Panašūs varikliai naudojami sudėtingoms paieškoms dokumentų duomenų bazėje. Pavyzdžiui, ieškoti atsižvelgiant į kalbos morfologiją arba ieškoti pagal geografines koordinates.
Šiame straipsnyje kalbėsiu apie ES pagrindus, naudodamas tinklaraščio įrašų indeksavimo pavyzdį. Parodysiu, kaip filtruoti, rūšiuoti ir ieškoti dokumentų.
Kad nepriklausyčiau nuo operacinės sistemos, visas užklausas pateiksiu ES naudodamas CURL. Taip pat yra google chromo įskiepis, vadinamas .
Tekste pateikiamos nuorodos į dokumentus ir kitus šaltinius. Pabaigoje yra nuorodos, leidžiančios greitai pasiekti dokumentaciją. Nepažįstamų terminų apibrėžimus galite rasti .
ES diegimas
Norėdami tai padaryti, pirmiausia turime „Java“. Kūrėjai Įdiekite „Java“ versijas, naujesnes nei „Java 8“ naujinimas 20 arba „Java 7“ naujinimas 55.
ES platinimas pasiekiamas adresu . Išpakavus archyvą reikia paleisti bin/elasticsearch. Taip pat galima . Yra . .
Įdiegę ir paleidę patikrinkime funkcionalumą:
# для удобства запомним адрес в переменную
#export ES_URL=$(docker-machine ip dev):9200
export ES_URL=localhost:9200
curl -X GET $ES_URLGausime kažką panašaus:
{
"name" : "Heimdall",
"cluster_name" : "elasticsearch",
"version" : {
"number" : "2.2.1",
"build_hash" : "d045fc29d1932bce18b2e65ab8b297fbf6cd41a1",
"build_timestamp" : "2016-03-09T09:38:54Z",
"build_snapshot" : false,
"lucene_version" : "5.4.1"
},
"tagline" : "You Know, for Search"
}Indeksavimas
Pridėkime įrašą prie ES:
# Добавим документ c id 1 типа post в индекс blog.
# ?pretty указывает, что вывод должен быть человеко-читаемым.
curl -XPUT "$ES_URL/blog/post/1?pretty" -d'
{
"title": "Веселые котята",
"content": "<p>Смешная история про котят<p>",
"tags": [
"котята",
"смешная история"
],
"published_at": "2014-09-12T20:44:42+00:00"
}'
serverio atsakymas:
{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : false
}
ES sukurta automatiškai dienoraštį ir paštu. Galime nubrėžti sąlyginę analogiją: indeksas yra duomenų bazė, o tipas yra lentelė šioje duomenų bazėje. Kiekvienas tipas turi savo schemą - , kaip ir reliacinė lentelė. Atvaizdavimas sugeneruojamas automatiškai, kai dokumentas indeksuojamas:
# Получим mapping всех типов индекса blog
curl -XGET "$ES_URL/blog/_mapping?pretty"Serverio atsakyme komentaruose pridėjau indeksuoto dokumento laukų reikšmes:
{
"blog" : {
"mappings" : {
"post" : {
"properties" : {
/* "content": "<p>Смешная история про котят<p>", */
"content" : {
"type" : "string"
},
/* "published_at": "2014-09-12T20:44:42+00:00" */
"published_at" : {
"type" : "date",
"format" : "strict_date_optional_time||epoch_millis"
},
/* "tags": ["котята", "смешная история"] */
"tags" : {
"type" : "string"
},
/* "title": "Веселые котята" */
"title" : {
"type" : "string"
}
}
}
}
}
}Verta paminėti, kad ES neskiria vienos reikšmės ir verčių masyvo. Pavyzdžiui, pavadinimo lauke tiesiog yra pavadinimas, o žymų lauke yra eilučių masyvas, nors jos vaizduojamos vienodai atvaizduojant.
Daugiau apie žemėlapių sudarymą kalbėsime vėliau.
Paklausimai
Dokumento gavimas pagal jo ID:
# извлечем документ с id 1 типа post из индекса blog
curl -XGET "$ES_URL/blog/post/1?pretty"{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"title" : "Веселые котята",
"content" : "<p>Смешная история про котят<p>",
"tags" : [ "котята", "смешная история" ],
"published_at" : "2014-09-12T20:44:42+00:00"
}
}Atsakyme pasirodė nauji raktai: _version и _source. Apskritai, visi klavišai prasideda _ priskiriami oficialiems.
Raktas _version rodoma dokumento versija. Jis reikalingas, kad veiktų optimistinis užrakto mechanizmas. Pavyzdžiui, norime pakeisti dokumentą, kurio versija yra 1. Pateikiame pakeistą dokumentą ir nurodome, kad tai yra 1 versijos dokumento redagavimas. Jei kas nors kitas taip pat redagavo 1 versijos dokumentą ir pateikė pakeitimus prieš mus, tada ES nepriims mūsų pakeitimų, nes jame saugomas 2 versijos dokumentas.
Raktas _source yra dokumentas, kurį indeksavome. ES nenaudoja šios reikšmės paieškos operacijoms, nes Indeksai naudojami paieškai. Siekdama sutaupyti vietos, ES saugo suglaudintą šaltinio dokumentą. Jei mums reikia tik ID, o ne viso šaltinio dokumento, galime išjungti šaltinio saugyklą.
Jei mums nereikia papildomos informacijos, galime gauti tik _source turinį:
curl -XGET "$ES_URL/blog/post/1/_source?pretty"{
"title" : "Веселые котята",
"content" : "<p>Смешная история про котят<p>",
"tags" : [ "котята", "смешная история" ],
"published_at" : "2014-09-12T20:44:42+00:00"
}
Taip pat galite pasirinkti tik tam tikrus laukus:
# извлечем только поле title
curl -XGET "$ES_URL/blog/post/1?_source=title&pretty"{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"title" : "Веселые котята"
}
}Indeksuokime dar kelis įrašus ir vykdykime sudėtingesnes užklausas.
curl -XPUT "$ES_URL/blog/post/2" -d'
{
"title": "Веселые щенки",
"content": "<p>Смешная история про щенков<p>",
"tags": [
"щенки",
"смешная история"
],
"published_at": "2014-08-12T20:44:42+00:00"
}'curl -XPUT "$ES_URL/blog/post/3" -d'
{
"title": "Как у меня появился котенок",
"content": "<p>Душераздирающая история про бедного котенка с улицы<p>",
"tags": [
"котята"
],
"published_at": "2014-07-21T20:44:42+00:00"
}'Rūšiuoti
# найдем последний пост по дате публикации и извлечем поля title и published_at
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
"size": 1,
"_source": ["title", "published_at"],
"sort": [{"published_at": "desc"}]
}'{
"took" : 8,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 3,
"max_score" : null,
"hits" : [ {
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_score" : null,
"_source" : {
"title" : "Веселые котята",
"published_at" : "2014-09-12T20:44:42+00:00"
},
"sort" : [ 1410554682000 ]
} ]
}
}Mes pasirinkome paskutinį įrašą. size riboja išduodamų dokumentų skaičių. total rodo bendrą užklausą atitinkančių dokumentų skaičių. sort išvestyje yra sveikųjų skaičių masyvas, pagal kurį atliekamas rūšiavimas. Tie. data buvo konvertuota į sveikąjį skaičių. Daugiau informacijos apie rūšiavimą rasite .
Filtrai ir užklausos
ES nuo 2 versijos neskiria filtrų ir užklausų .
Užklausos kontekstas skiriasi nuo filtro konteksto tuo, kad užklausa generuoja _balą ir nėra saugoma talpykloje. Vėliau parodysiu, kas yra _balas.
Filtruoti pagal datą
Mes naudojame prašymą filtro kontekste:
# получим посты, опубликованные 1ого сентября или позже
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
"filter": {
"range": {
"published_at": { "gte": "2014-09-01" }
}
}
}'Filtruoti pagal žymas
Mes naudojame Norėdami ieškoti dokumentų ID, kuriuose yra nurodytas žodis:
# найдем все документы, в поле tags которых есть элемент 'котята'
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
"_source": [
"title",
"tags"
],
"filter": {
"term": {
"tags": "котята"
}
}
}'{
"took" : 9,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [ {
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"title" : "Веселые котята",
"tags" : [ "котята", "смешная история" ]
}
}, {
"_index" : "blog",
"_type" : "post",
"_id" : "3",
"_score" : 1.0,
"_source" : {
"title" : "Как у меня появился котенок",
"tags" : [ "котята" ]
}
} ]
}
}Pilno teksto paieška
Trijų mūsų dokumentų turinio lauke yra ši informacija:
<p>Смешная история про котят<p><p>Смешная история про щенков<p><p>Душераздирающая история про бедного котенка с улицы<p>
Mes naudojame Norėdami ieškoti dokumentų ID, kuriuose yra nurodytas žodis:
# source: false означает, что не нужно извлекать _source найденных документов
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
"_source": false,
"query": {
"match": {
"content": "история"
}
}
}'{
"took" : 13,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 3,
"max_score" : 0.11506981,
"hits" : [ {
"_index" : "blog",
"_type" : "post",
"_id" : "2",
"_score" : 0.11506981
}, {
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_score" : 0.11506981
}, {
"_index" : "blog",
"_type" : "post",
"_id" : "3",
"_score" : 0.095891505
} ]
}
}Tačiau jei turinio lauke ieškosime „istorijas“, nieko nerasime, nes Rodyklėje yra tik originalūs žodžiai, o ne jų kamienai. Norint atlikti kokybišką paiešką, reikia sukonfigūruoti analizatorių.
Laukas _score rodo . Jei užklausa vykdoma filtro kontekste, tada _score reikšmė visada bus lygi 1, o tai reiškia visišką filtro atitiktį.
Analizatoriai
reikalingi norint konvertuoti šaltinio tekstą į žetonų rinkinį.
Analizatoriai susideda iš vieno ir keli neprivalomi . Prieš tokenizatorių gali būti keli . Žetonai suskaido šaltinio eilutę į žetonus, tokius kaip tarpai ir skyrybos ženklai. TokenFilter gali keisti žetonus, ištrinti ar pridėti naujų, pavyzdžiui, palikti tik žodžio kamieną, pašalinti prielinksnius, pridėti sinonimų. CharFilter – pakeičia visą šaltinio eilutę, pavyzdžiui, išpjauna html žymas.
ES turi keletą . Pavyzdžiui, analizatorius .
Pasinaudokime ir pažiūrėkime, kaip standartiniai ir rusiški analizatoriai transformuoja eilutę „Juokingos istorijos apie kačiukus“:
# используем анализатор standard
# обязательно нужно перекодировать не ASCII символы
curl -XGET "$ES_URL/_analyze?pretty&analyzer=standard&text=%D0%92%D0%B5%D1%81%D0%B5%D0%BB%D1%8B%D0%B5%20%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D0%B8%20%D0%BF%D1%80%D0%BE%20%D0%BA%D0%BE%D1%82%D1%8F%D1%82"{
"tokens" : [ {
"token" : "веселые",
"start_offset" : 0,
"end_offset" : 7,
"type" : "<ALPHANUM>",
"position" : 0
}, {
"token" : "истории",
"start_offset" : 8,
"end_offset" : 15,
"type" : "<ALPHANUM>",
"position" : 1
}, {
"token" : "про",
"start_offset" : 16,
"end_offset" : 19,
"type" : "<ALPHANUM>",
"position" : 2
}, {
"token" : "котят",
"start_offset" : 20,
"end_offset" : 25,
"type" : "<ALPHANUM>",
"position" : 3
} ]
}# используем анализатор russian
curl -XGET "$ES_URL/_analyze?pretty&analyzer=russian&text=%D0%92%D0%B5%D1%81%D0%B5%D0%BB%D1%8B%D0%B5%20%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D0%B8%20%D0%BF%D1%80%D0%BE%20%D0%BA%D0%BE%D1%82%D1%8F%D1%82"{
"tokens" : [ {
"token" : "весел",
"start_offset" : 0,
"end_offset" : 7,
"type" : "<ALPHANUM>",
"position" : 0
}, {
"token" : "истор",
"start_offset" : 8,
"end_offset" : 15,
"type" : "<ALPHANUM>",
"position" : 1
}, {
"token" : "кот",
"start_offset" : 20,
"end_offset" : 25,
"type" : "<ALPHANUM>",
"position" : 3
} ]
}Standartinis analizatorius suskaidė eilutę į tarpus ir viską pavertė mažosiomis raidėmis, rusiškas analizatorius pašalino nesvarbius žodžius, pavertė mažosiomis raidėmis ir paliko žodžių kamieną.
Pažiūrėkime, kurį Tokenizer, TokenFilters, CharFilters naudoja rusiškas analizatorius:
{
"filter": {
"russian_stop": {
"type": "stop",
"stopwords": "_russian_"
},
"russian_keywords": {
"type": "keyword_marker",
"keywords": []
},
"russian_stemmer": {
"type": "stemmer",
"language": "russian"
}
},
"analyzer": {
"russian": {
"tokenizer": "standard",
/* TokenFilters */
"filter": [
"lowercase",
"russian_stop",
"russian_keywords",
"russian_stemmer"
]
/* CharFilters отсутствуют */
}
}
}Apibūdinkime savo analizatorių rusų kalba, kuris iškirps html žymas. Pavadinkime tai numatytuoju, nes pagal numatytuosius nustatymus bus naudojamas analizatorius tokiu pavadinimu.
{
"filter": {
"ru_stop": {
"type": "stop",
"stopwords": "_russian_"
},
"ru_stemmer": {
"type": "stemmer",
"language": "russian"
}
},
"analyzer": {
"default": {
/* добавляем удаление html тегов */
"char_filter": ["html_strip"],
"tokenizer": "standard",
"filter": [
"lowercase",
"ru_stop",
"ru_stemmer"
]
}
}
}Pirmiausia iš šaltinio eilutės bus pašalintos visos HTML žymės, tada žetonų standartas ją suskaidys į žetonus, gauti žetonai bus perkelti į mažąsias raides, nereikšmingi žodžiai bus pašalinti, o likę žetonai liks žodžio kamienu.
Indekso kūrimas
Aukščiau aprašėme numatytąjį analizatorių. Jis bus taikomas visiems eilutės laukams. Mūsų įraše yra daugybė žymų, todėl žymas taip pat apdoros analizatorius. Nes Ieškome įrašų pagal tikslią žymos atitiktį, tada turime išjungti žymų lauko analizę.
Sukurkime indekso dienoraštį2 su analizatoriumi ir atvaizdavimu, kuriame išjungta žymų lauko analizė:
curl -XPOST "$ES_URL/blog2" -d'
{
"settings": {
"analysis": {
"filter": {
"ru_stop": {
"type": "stop",
"stopwords": "_russian_"
},
"ru_stemmer": {
"type": "stemmer",
"language": "russian"
}
},
"analyzer": {
"default": {
"char_filter": [
"html_strip"
],
"tokenizer": "standard",
"filter": [
"lowercase",
"ru_stop",
"ru_stemmer"
]
}
}
}
},
"mappings": {
"post": {
"properties": {
"content": {
"type": "string"
},
"published_at": {
"type": "date"
},
"tags": {
"type": "string",
"index": "not_analyzed"
},
"title": {
"type": "string"
}
}
}
}
}'Pridėkime prie šios rodyklės (3 dienoraštis) tuos pačius 2 įrašus. Aš praleisiu šį procesą, nes... tai panašu į dokumentų įtraukimą į dienoraščio rodyklę.
Viso teksto paieška su išraiškos palaikymu
Pažvelkime į kito tipo užklausą:
# найдем документы, в которых встречается слово 'истории'
# query -> simple_query_string -> query содержит поисковый запрос
# поле title имеет приоритет 3
# поле tags имеет приоритет 2
# поле content имеет приоритет 1
# приоритет используется при ранжировании результатов
curl -XPOST "$ES_URL/blog2/post/_search?pretty" -d'
{
"query": {
"simple_query_string": {
"query": "истории",
"fields": [
"title^3",
"tags^2",
"content"
]
}
}
}'Nes Naudojame analizatorių su rusiška kamiene, tada ši užklausa grąžins visus dokumentus, nors juose yra tik žodis „istorija“.
Prašyme gali būti specialiųjų simbolių, pavyzdžiui:
""fried eggs" +(eggplant | potato) -frittata"Užklausos sintaksė:
+ signifies AND operation
| signifies OR operation
- negates a single token
" wraps a number of tokens to signify a phrase for searching
* at the end of a term signifies a prefix query
( and ) signify precedence
~N after a word signifies edit distance (fuzziness)
~N after a phrase signifies slop amount# найдем документы без слова 'щенки'
curl -XPOST "$ES_URL/blog2/post/_search?pretty" -d'
{
"query": {
"simple_query_string": {
"query": "-щенки",
"fields": [
"title^3",
"tags^2",
"content"
]
}
}
}'
# получим 2 поста про котиковNuorodos
PS
Jei jus domina tokios straipsnių pamokos, turite idėjų naujiems straipsniams ar pasiūlymų bendradarbiauti, mielai gausiu žinutę asmenine žinute arba el. paštu m.kuzmin+habr@darkleaf.ru.
Šaltinis: www.habr.com
