Elasticsearch estas serĉilo kun json rest api, uzante Lucene kaj skribita en Java. Priskribo de ĉiuj avantaĝoj de ĉi tiu motoro estas havebla ĉe
Similaj motoroj estas uzataj por kompleksaj serĉoj en dokumenta datumbazo. Ekzemple, serĉu konsiderante la morfologion de la lingvo aŭ serĉu laŭ geokoordinatoj.
En ĉi tiu artikolo mi parolos pri la bazaĵoj de ES uzante la ekzemplon de indeksado de blogaĵoj. Mi montros al vi kiel filtri, ordigi kaj serĉi dokumentojn.
Por ne dependi de la operaciumo, mi faros ĉiujn petojn al ES per CURL. Ekzistas ankaŭ kromaĵo por google chrome nomita
La teksto enhavas ligilojn al dokumentaro kaj aliaj fontoj. Fine estas ligiloj por rapida aliro al la dokumentaro. Difinoj de nekonataj terminoj troviĝas en
Instalante ES
Por fari tion, ni unue bezonas Java. Programistoj
La distribuo de ES haveblas ĉe bin/elasticsearch
. Ankaŭ havebla
Post instalado kaj lanĉo, ni kontrolu la funkciojn:
# для удобства запомним адрес в переменную
#export ES_URL=$(docker-machine ip dev):9200
export ES_URL=localhost:9200
curl -X GET $ES_URL
Ni ricevos ion tian:
{
"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"
}
Indeksado
Ni aldonu afiŝon al 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"
}'
servilo respondo:
{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : false
}
ES aŭtomate kreita
# Получим mapping всех типов индекса blog
curl -XGET "$ES_URL/blog/_mapping?pretty"
En la servila respondo, mi aldonis la valorojn de la kampoj de la indeksita dokumento en la komentoj:
{
"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"
}
}
}
}
}
}
Indas noti, ke ES ne diferencas inter unu valoro kaj tabelo da valoroj. Ekzemple, la titolkampo simple enhavas titolon, kaj la etikedkampo enhavas tabelon da ŝnuroj, kvankam ili estas reprezentitaj en la sama maniero en mapado.
Ni parolos pli pri mapado poste.
Petoj
Prenante dokumenton per ĝia 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"
}
}
Novaj ŝlosiloj aperis en la respondo: _version
и _source
. Ĝenerale, ĉiuj klavoj komencante per _
estas klasifikitaj kiel oficialaj.
Ключ _version
montras la dokumentversion. Ĝi estas necesa por ke la optimisma ŝlosa mekanismo funkcii. Ekzemple, ni volas ŝanĝi dokumenton, kiu havas version 1. Ni sendas la ŝanĝitan dokumenton kaj indikas, ke tio estas redakto de dokumento kun versio 1. Se iu alia ankaŭ redaktis dokumenton kun versio 1 kaj sendis ŝanĝojn antaŭ ni, tiam ES ne akceptos niajn ŝanĝojn, ĉar ĝi konservas la dokumenton kun versio 2.
Ключ _source
enhavas la dokumenton, kiun ni indeksis. ES ne uzas ĉi tiun valoron por serĉaj operacioj ĉar Indeksoj estas uzataj por serĉado. Por ŝpari spacon, ES konservas kunpremitan fontdokumenton. Se ni bezonas nur la identigilon, kaj ne la tutan fontdokumenton, tiam ni povas malŝalti fontan stokadon.
Se ni ne bezonas pliajn informojn, ni povas ricevi nur la enhavon de _fonto:
curl -XGET "$ES_URL/blog/post/1/_source?pretty"
{
"title" : "Веселые котята",
"content" : "<p>Смешная история про котят<p>",
"tags" : [ "котята", "смешная история" ],
"published_at" : "2014-09-12T20:44:42+00:00"
}
Vi ankaŭ povas elekti nur iujn kampojn:
# извлечем только поле title
curl -XGET "$ES_URL/blog/post/1?_source=title&pretty"
{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"title" : "Веселые котята"
}
}
Ni indeksu kelkajn pliajn afiŝojn kaj faru pli kompleksajn demandojn.
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"
}'
Ordigado
# найдем последний пост по дате публикации и извлечем поля 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 ]
} ]
}
}
Ni elektis la lastan afiŝon. size
limigas la nombron de eldonendaj dokumentoj. total
montras la totalan nombron de dokumentoj kongruaj kun la peto. sort
en la eligo enhavas tabelon de entjeroj per kiuj ordigo estas farita. Tiuj. la dato estis konvertita al entjero. Pliaj informoj pri ordigo troveblas en
Filtriloj kaj demandoj
ES ekde versio 2 ne distingas inter filtriloj kaj demandoj, anstataŭe
Demanda kunteksto diferencas de filtrila kunteksto, ke la demando generas _poentaron kaj ne estas kaŝmemorigita. Mi montros al vi kia _poentaro estas poste.
Filtru laŭ dato
Ni uzas la peton
# получим посты, опубликованные 1ого сентября или позже
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
"filter": {
"range": {
"published_at": { "gte": "2014-09-01" }
}
}
}'
Filtri per etikedoj
Ni uzas
# найдем все документы, в поле 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" : [ "котята" ]
}
} ]
}
}
Plenteksta serĉo
Tri el niaj dokumentoj enhavas la jenajn en la enhavkampo:
<p>Смешная история про котят<p>
<p>Смешная история про щенков<p>
<p>Душераздирающая история про бедного котенка с улицы<p>
Ni uzas
# 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
} ]
}
}
Tamen, se ni serĉas "rakontojn" en la enhavkampo, ni trovos nenion, ĉar La indekso enhavas nur la originalajn vortojn, ne iliajn radikojn. Por fari altkvalitan serĉon, vi devas agordi la analizilon.
kampo _score
montras
Analiziloj
Analiziloj konsistas el unu
ES havas plurajn
Ni profitu
# используем анализатор 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
} ]
}
La norma analizilo disfendis la ŝnuron per spacoj kaj konvertis ĉion al minusklo, la rusa analizilo forigis negravajn vortojn, konvertis ĝin al minusklo kaj lasis la radikon de la vortoj.
Ni vidu kiujn Tokenizer, TokenFilters, CharFilters uzas la rusa analizilo:
{
"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 отсутствуют */
}
}
}
Ni priskribu nian analizilon bazitan sur la rusa, kiu eltranĉos html-etikedojn. Ni nomu ĝin defaŭlta, ĉar analizilo kun ĉi tiu nomo estos uzata defaŭlte.
{
"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"
]
}
}
}
Unue, ĉiuj HTML-etikedoj estos forigitaj de la fonta ĉeno, tiam la tokenizer normo dividos ĝin en ĵetonojn, la rezultaj ĵetonoj moviĝos al minusklo, sensignifaj vortoj estos forigitaj, kaj la ceteraj ĵetonoj restos la tigo de la vorto.
Kreante Indekson
Supre ni priskribis la defaŭltan analizilon. Ĝi aplikiĝos al ĉiuj kordaj kampoj. Nia afiŝo enhavas aron da etikedoj, do la etikedoj ankaŭ estos prilaboritaj de la analizilo. Ĉar Ni serĉas afiŝojn laŭ ĝusta kongruo al etikedo, tiam ni devas malŝalti analizon por la kampo de etikedoj.
Ni kreu indeksan blogon2 kun analizilo kaj mapado, en kiu la analizo de la kampo de etikedoj estas malŝaltita:
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"
}
}
}
}
}'
Ni aldonu la samajn 3 afiŝojn al ĉi tiu indekso (blogo2). Mi preterlasos ĉi tiun procezon ĉar... ĝi similas al aldoni dokumentojn al la bloga indekso.
Plenteksta serĉo kun esprimo-subteno
Ni rigardu alian specon de peto:
# найдем документы, в которых встречается слово 'истории'
# 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"
]
}
}
}'
Ĉar Ni uzas analizilon kun rusa deveno, tiam ĉi tiu peto resendos ĉiujn dokumentojn, kvankam ili enhavas nur la vorton 'historio'.
La peto povas enhavi specialajn signojn, ekzemple:
""fried eggs" +(eggplant | potato) -frittata"
Peti sintakson:
+ 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 поста про котиков
referencoj
PS
Se vi interesiĝas pri similaj artikoloj-lecionoj, havas ideojn por novaj artikoloj aŭ havas proponojn por kunlaboro, tiam mi ĝojos ricevi mesaĝon en persona mesaĝo aŭ retpoŝte. [retpoŝte protektita].
fonto: www.habr.com