Elasticsearch, Lucene-dən istifadə edən və Java-da yazılmış JSON REST API ilə axtarış motorudur. Bu mühərrikin bütün üstünlüklərinin təsviri burada mövcuddur Bundan sonra biz Elasticsearch-a ES kimi müraciət edəcəyik.
Bu cür mühərriklər dil morfologiyası əsasında axtarış və ya coğrafi koordinatlar üzrə axtarış kimi mürəkkəb sənəd verilənlər bazası axtarışları üçün istifadə olunur.
Bu yazıda mən blog yazılarının indeksləşdirilməsi nümunəsindən istifadə edərək ES-nin əsaslarını əhatə edəcəyəm. Mən sizə sənədləri necə süzgəcdən keçirməyi, çeşidləməyi və axtarmağı göstərəcəyəm.
Əməliyyat sistemindən müstəqil olmaq üçün mən bütün ES sorğularımı CURL istifadə edərək edəcəm. Google Chrome üçün plagin də var .
Sənədlərə və digər mənbələrə keçidlər mətn boyu verilir. Sənədlərə sürətli giriş bağlantıları sonunda təqdim olunur. Tanış olmayan terminlərin təriflərinə burada rast gəlmək olar .
ES quraşdırılması
Bunun üçün bizə ilk olaraq Java lazımdır. Tərtibatçılar Java 8 yeniləmə 20 və ya Java 7 yeniləmə 55-dən daha yeni Java versiyalarını quraşdırın.
ES paylanması mövcuddur Arxivi açdıqdan sonra qaçmaq lazımdır bin/elasticsearch. Həmçinin mövcuddur . Var . .
Quraşdırıldıqdan və işə salındıqdan sonra funksionallığı yoxlayaq:
# для удобства запомним адрес в переменную
#export ES_URL=$(docker-machine ip dev):9200
export ES_URL=localhost:9200
curl -X GET $ES_URLBu kimi bir cavab alacağıq:
{
"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"
}İndeksləmə
ES-ə yazı əlavə edək:
# Добавим документ 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"
}'
server cavabı:
{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : false
}
ES avtomatik yaradılmışdır blog və post. Təxmini bir bənzətmə çəkmək olar: indeks verilənlər bazasıdır, tip isə həmin verilənlər bazası daxilindəki cədvəldir. Hər növün öz sxemi var - , eynilə əlaqə cədvəli kimi. Sənəd indeksləşdirildikdə xəritə avtomatik olaraq yaradılır:
# Получим mapping всех типов индекса blog
curl -XGET "$ES_URL/blog/_mapping?pretty"Server cavabında indekslənmiş sənədin sahə dəyərlərini şərhlərə əlavə etdim:
{
"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"
}
}
}
}
}
}Qeyd etmək lazımdır ki, ES tək dəyər və dəyərlər massivi arasında fərq qoymur. Məsələn, başlıq sahəsində sadəcə başlıq var, teqlər sahəsində isə xəritədə eyni şəkildə təmsil olunsalar da, bir sıra sətirlər var.
Xəritəçəkmə haqqında daha sonra daha ətraflı danışacağıq.
İstək
Sənədin şəxsiyyət vəsiqəsi ilə çıxarılması:
# извлечем документ с 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"
}
}Cavabda yeni açarlar göründü: _version и _sourceÜmumiyyətlə, ilə başlayan bütün düymələr _ xidmət obyektləri kimi təsnif edilir.
Açar _version Sənədin versiyasını göstərir. Bu, optimist kilidləmə mexanizminin işləməsi üçün lazımdır. Məsələn, biz 1-ci versiya ilə sənədi dəyişmək istəyirik. Biz dəyişdirilmiş sənədi təqdim edirik və bunun 1-ci versiyaya malik sənədə yenidən baxılması olduğunu bildiririk. Əgər başqası da 1-ci versiyaya malik sənədi redaktə edibsə və dəyişiklikləri bizdən əvvəl təqdim edibsə, ES sənədi 2-ci versiya ilə saxladığı üçün dəyişikliklərimizi qəbul etməyəcək.
Açar _source İndekslədiyimiz sənədi ehtiva edir. ES bu dəyəri axtarış əməliyyatları üçün istifadə etmir, çünki indekslər axtarış üçün istifadə olunur. Yerə qənaət etmək üçün ES orijinal sənədin sıxılmış versiyasını saxlayır. Əgər bizə bütün orijinal sənəd yox, yalnız şəxsiyyət vəsiqəsi lazımdırsa, orijinalın saxlanmasını söndürə bilərik.
Əgər bizə əlavə məlumat lazım deyilsə, biz yalnız _source məzmununu əldə edə bilərik:
curl -XGET "$ES_URL/blog/post/1/_source?pretty"{
"title" : "Веселые котята",
"content" : "<p>Смешная история про котят<p>",
"tags" : [ "котята", "смешная история" ],
"published_at" : "2014-09-12T20:44:42+00:00"
}
Siz həmçinin yalnız müəyyən sahələri seçə bilərsiniz:
# извлечем только поле title
curl -XGET "$ES_URL/blog/post/1?_source=title&pretty"{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"title" : "Веселые котята"
}
}Gəlin daha bir neçə postu indeksləşdirək və daha mürəkkəb sorğular aparaq.
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"
}'Sortlaşdırma
# найдем последний пост по дате публикации и извлечем поля 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 ]
} ]
}
}Sonuncu postu seçdik. size verilən sənədlərin sayını məhdudlaşdırır. total sorğuya uyğun gələn sənədlərin ümumi sayını göstərir. sort Çıxış çeşidləmənin həyata keçirildiyi tam ədədlər massivindən ibarətdir. Yəni tarix tam ədədə çevrilmişdir. Çeşidləmə haqqında daha çox oxuya bilərsiniz .
Filtrlər və sorğular
Versiya 2-dən bəri ES filtrlər və sorğular arasında fərq qoymur .
Sorğu konteksti filtr kontekstindən ona görə fərqlənir ki, sorğu _score yaradır və keşlənmir. Hesabın nə olduğunu sonra izah edəcəyəm.
Tarixə görə süzün
Sorğunun istifadəsi filtr kontekstində:
# получим посты, опубликованные 1ого сентября или позже
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
"filter": {
"range": {
"published_at": { "gte": "2014-09-01" }
}
}
}'Teqlərə görə filtrləmə
Biz istifadə edirik Verilmiş sözü ehtiva edən sənəd identifikatorlarını axtarmaq üçün:
# найдем все документы, в поле 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" : [ "котята" ]
}
} ]
}
}Tam mətn axtarışı
Üç sənədimiz məzmun sahəsində aşağıdakıları ehtiva edir:
<p>Смешная история про котят<p><p>Смешная история про щенков<p><p>Душераздирающая история про бедного котенка с улицы<p>
Biz istifadə edirik Verilmiş sözü ehtiva edən sənəd identifikatorlarını axtarmaq üçün:
# 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
} ]
}
}Bununla belə, məzmun sahəsində "hekayələri" axtarsaq, heç nə tapa bilmərik, çünki indeksdə yalnız orijinal sözlər var, onların kökləri deyil. Keyfiyyətli axtarış aparmaq üçün analizatoru konfiqurasiya etməliyik.
Sahə _score göstərir Sorğu filtr kontekstində yerinə yetirilirsə, _score dəyəri həmişə 1 olacaq, yəni filtr tam uyğundur.
Analizatorlar
mənbə mətni işarələr toplusuna çevirmək üçün lazımdır.
Analizatorlar birdən ibarətdir və bir neçə isteğe bağlı Tokenizer bir neçədən əvvəl ola bilər Tokenizatorlar mənbə sətirini işarələrə, məsələn, boşluqlara və durğu işarələrinə bölür. TokenFilters tokenləri dəyişdirə, silə və ya yenilərini əlavə edə bilər, məsələn, yalnız sözün kökünü tərk etməklə, ön sözləri silməklə və ya sinonimlər əlavə etməklə. CharFilters, məsələn, HTML teqlərini silməklə mənbə sətirini tamamilə dəyişdirir.
ES-də bir neçə var Məsələn, analizator .
Gəlin yararlanaq və standart və rus analizatorlarının "Kediciklər haqqında məzəli hekayələr" sətirini necə çevirdiyini görək:
# используем анализатор 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
} ]
}Standart analizator sətri boşluqlara böldü və hər şeyi kiçik hərflərə çevirdi, rus analizatoru əhəmiyyətsiz sözləri çıxardı, onları kiçik hərflərə çevirdi və sözün köklərini buraxdı.
Rus analizatorunun hansı Tokenizer, TokenFilters və CharFilters istifadə etdiyinə baxaq:
{
"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 отсутствуют */
}
}
}Gəlin HTML teqlərini siləcək öz rus əsaslı analizatorumuzu təsvir edək. Biz onu defolt adlandıracağıq, çünki bu, standart analizator olacaq.
{
"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"
]
}
}
}Əvvəlcə bütün HTML teqləri mənbə sətirindən silinəcək, sonra o, tokenizer standartı ilə tokenlərə bölünəcək, nəticədə yaranan tokenlər kiçik hərflərə çevriləcək, əhəmiyyətsiz sözlər silinəcək və qalan tokenlər sözün kökü olacaq.
İndeksin yaradılması
Yuxarıda standart analizatoru təsvir etdik. O, bütün sətir sahələrinə tətbiq olunacaq. Yazımızda bir sıra etiketlər var, ona görə də teqlər analizator tərəfindən işlənəcək. Dəqiq etiketə uyğun yazılar axtardığımız üçün "teqlər" sahəsi üçün təhlili deaktiv etməliyik.
Analizator və xəritəçəkmə ilə blog2 indeksi yaradaq, burada etiketlər sahəsinin təhlili qeyri-aktivdir:
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"
}
}
}
}
}'Gəlin bu indeksə (blog2) eyni üç yazı əlavə edək. Bu prosesi atlayacağam, çünki bu, blog indeksinə sənədlər əlavə etməyə bənzəyir.
İfadə dəstəyi ilə tam mətn axtarışı
Başqa bir sorğu növü ilə tanış olaq:
# найдем документы, в которых встречается слово 'истории'
# 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"
]
}
}
}'Rus mənşəli analizatordan istifadə etdiyimiz üçün bu sorğu bütün sənədləri qaytaracaq, baxmayaraq ki, onlar yalnız "tarix" sözünü ehtiva edir.
Sorğuda xüsusi simvollar ola bilər, məsələn:
""fried eggs" +(eggplant | potato) -frittata"Sorğu sintaksisi:
+ 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 поста про котиковReferences
PS
Bənzər dərslik məqalələri ilə maraqlanırsınızsa, yeni məqalələr üçün ideyalarınız varsa və ya əməkdaşlıq təklifləriniz varsa, şəxsi mesaj və ya m.kuzmin+habr@darkleaf.ru elektron poçtu vasitəsilə sizinlə əlaqə saxlamaqdan məmnun olaram.
Mənbə: www.habr.com
