Elasticsearch hè un mutore di ricerca cù json rest api, cù Lucene è scrittu in Java. Una descrizzione di tutti i vantaghji di stu mutore hè dispunibule à . In ciò chì seguita faremu riferite à Elasticsearch cum'è ES.
I mutori simili sò usati per ricerche cumplessi in una basa di dati di documentu. Per esempiu, cercate tenendu in contu a morfologia di a lingua o cercate per coordenate geo.
In questu articulu parraraghju di i principii di ES cù l'esempiu di l'indexazione di i blog posts. Vi mustraraghju cumu filtrà, sorte è cercà i ducumenti.
Per ùn dipende micca di u sistema operatore, aghju da fà tutte e dumande à ES usendu CURL. Ci hè ancu un plugin per google chrome chjamatu .
U testu cuntene ligami à documentazione è altre fonti. À a fine ci sò ligami per un accessu rapidu à a documentazione. Definizioni di termini micca familiari ponu esse truvati in .
Installazione di ES
Per fà questu, avemu prima bisognu di Java. Sviluppatori installate versioni Java più recenti di Java 8 update 20 o Java 7 update 55.
A distribuzione ES hè dispunibule à . Dopu à unpacking l 'archiviu vi tuccherà à curriri bin/elasticsearch. Disponibile ancu ... ci hè . .
Dopu a stallazione è u lanciu, verificate a funziunalità:
# для удобства запомним адрес в переменную
#export ES_URL=$(docker-machine ip dev):9200
export ES_URL=localhost:9200
curl -X GET $ES_URLRiceveremu qualcosa cum'è questu:
{
"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"
}Indexazione
Aghjunghjite un postu à 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"
}'
risposta di u servitore:
{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : false
}
ES creatu automaticamente blog è postu. Pudemu disegnà una analogia cundizionale: un indexu hè una basa di dati, è un tipu hè una tavola in questa basa di dati. Ogni tipu hà u so propiu schema - , cum'è una tavola relazionale. A mappatura hè generata automaticamente quandu u documentu hè indexatu:
# Получим mapping всех типов индекса blog
curl -XGET "$ES_URL/blog/_mapping?pretty"In a risposta di u servitore, aghju aghjustatu i valori di i campi di u documentu indexatu in i cumenti:
{
"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"
}
}
}
}
}
}Hè da nutà chì ES ùn distingue micca trà un valore unicu è un array di valori. Per esempiu, u campu di tìtulu cuntene solu un titulu, è u campu di tags cuntene un array of strings, ancu s'elli sò rapprisintati in u listessu modu in mapping.
Parleremu di più nantu à a mappa più tardi.
E dumande
Ritruvà un documentu da u so 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"
}
}Novi chjavi apparsu in a risposta: _version и _source. In generale, tutte e chjave cumincianu cù _ sò classificate cum'è ufficiali.
Chjave _version mostra a versione di u documentu. Hè necessariu per u mecanismu di serratura ottimistu per travaglià. Per esempiu, vulemu cambià un documentu chì hà a versione 1. Inviamu u documentu cambiatu è indichemu chì questu hè un editu di un documentu cù a versione 1. Se qualchissia altru hà ancu editatu un documentu cù a versione 1 è hà sottumessu cambiamenti davanti à noi, allora ES ùn accetta micca i nostri cambiamenti, perchè guarda u documentu cù a versione 2.
Chjave _source cuntene u documentu chì avemu indexatu. ES ùn usa micca stu valore per l'operazioni di ricerca perchè L'indici sò usati per a ricerca. Per risparmià spaziu, ES guarda un documentu fonte cumpressu. Se avemu solu bisognu di l'id, è micca u documentu surghjente tutale, allora pudemu disattivà l'almacenamiento di fonte.
Se ùn avemu micca bisognu di infurmazioni supplementari, pudemu avè solu u cuntenutu di _source:
curl -XGET "$ES_URL/blog/post/1/_source?pretty"{
"title" : "Веселые котята",
"content" : "<p>Смешная история про котят<p>",
"tags" : [ "котята", "смешная история" ],
"published_at" : "2014-09-12T20:44:42+00:00"
}
Pudete ancu selezziunate solu certi campi:
# извлечем только поле title
curl -XGET "$ES_URL/blog/post/1?_source=title&pretty"{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"title" : "Веселые котята"
}
}Indiciemu uni pochi di più posti è eseguite dumande più cumplesse.
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"
}'Ordine
# найдем последний пост по дате публикации и извлечем поля 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 ]
} ]
}
}Avemu sceltu l'ultimu postu. size limita u numeru di ducumenti chì deve esse emessu. total mostra u numeru tutale di documenti chì currispondenu à a dumanda. sort in l'output cuntene un array di integer per quale l'ordinamentu hè realizatu. Quelli. a data hè stata cunvertita in un integer. Più infurmazione nantu à a classificazione pò esse truvata in .
Filtri è dumande
ES da a versione 2 ùn distingue micca i filtri è e dumande, invece .
Un cuntestu di dumanda difiere da un cuntestu di filtru in chì a dumanda genera un _score è ùn hè micca cache. Vi mustraraghju ciò chì _score hè più tardi.
Filtrà per data
Avemu aduprà a dumanda in u cuntestu di filtru:
# получим посты, опубликованные 1ого сентября или позже
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
"filter": {
"range": {
"published_at": { "gte": "2014-09-01" }
}
}
}'Filtrazione per tag
Avemu aduprà per circà l'ID di documentu chì cuntene una parola data:
# найдем все документы, в поле 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" : [ "котята" ]
}
} ]
}
}Ricerca di testu cumpletu
Trè di i nostri documenti cuntenenu i seguenti in u campu di cuntenutu:
<p>Смешная история про котят<p><p>Смешная история про щенков<p><p>Душераздирающая история про бедного котенка с улицы<p>
Avemu aduprà per circà l'ID di documentu chì cuntene una parola data:
# 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
} ]
}
}In ogni casu, s'ellu circhemu "storie" in u campu di cuntenutu, ùn truvamu nunda, perchè L'indici cuntene solu e parolle originali, micca i so steli. Per fà una ricerca d'alta qualità, avete bisognu di cunfigurà l'analizzatore.
chjosu _score mostra . Se a dumanda hè eseguita in un cuntestu di filtru, allora u valore _score serà sempre uguale à 1, chì significa una partita cumpleta cù u filtru.
Analyzers
sò necessarii per cunvertisce u testu fonte in un set di tokens.
L'analizzatori sò custituiti da unu è parechji opzionali . Tokenizer pò esse precedutu da parechji . Tokenizers rompe a stringa fonte in tokens, cum'è spazii è caratteri di puntuazione. TokenFilter pò cambià i tokens, sguassate o aghjunghje novi, per esempiu, lasciate solu u troncu di a parolla, sguassate preposizioni, aghjunghje sinonimi. CharFilter - cambia tutta a stringa fonte, per esempiu, taglia i tag html.
ES hà parechji . Per esempiu, un analizzatore .
Aprovechemu è vedemu cumu l'analizzatori standard è russi trasformanu a stringa "Storie divertenti nantu à i gattini":
# используем анализатор 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
} ]
}L'analizzatore standard divisu a stringa per spazii è cunvertisce tuttu in minuscule, l'analizzatore russu sguassate e parolle senza impurtanza, cunvertisce in minuscule è lasciò u troncu di e parolle.
Videmu quale Tokenizer, TokenFilters, CharFilters usa l'analizzatore russu:
{
"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 отсутствуют */
}
}
}Descrivimu u nostru analizzatore basatu annantu à u russu, chì hà da taglià e tag html. Chjamemu predeterminatu, perchè un analizzatore cù stu nome serà utilizatu per difettu.
{
"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"
]
}
}
}Prima, tutte e tag HTML seranu sguassate da a stringa di fonte, dopu u standard di tokenizer u sparterà in tokens, i tokens resultanti si moveranu in minuscule, e parolle insignificanti seranu eliminate, è i tokens rimanenti fermanu u gambu di a parolla.
Crià un indice
Sopra avemu descrittu l'analizzatore predeterminatu. S'applicà à tutti i campi di stringa. U nostru postu cuntene una serie di tags, cusì i tags seranu ancu trattati da l'analizzatore. Perchè Cerchemu i posti per corrispondenza esatta à una tag, allora avemu bisognu di disattivà l'analisi per u campu di tag.
Creemu un index blog2 cù un analizatore è mapping, in quale l'analisi di u campu di tags hè disattivata:
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"
}
}
}
}
}'Aghjunghjemu i stessi 3 posti à questu indice (blog2). Omette stu prucessu perchè ... hè simile à aghjunghje documenti à l'indici di u blog.
Ricerca di testu cumpletu cù supportu di spressione
Fighjemu un altru tipu di dumanda:
# найдем документы, в которых встречается слово 'истории'
# 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"
]
}
}
}'Perchè Avemu aduprà un analizzatore cù stemming russa, allora sta dumanda restituverà tutti i documenti, ancu s'ellu cuntene solu a parolla "storia".
A dumanda pò cuntene caratteri speciali, per esempiu:
""fried eggs" +(eggplant | potato) -frittata"Sintassi di dumanda:
+ 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 поста про котиковreferenze
PS
Sè vo site interessatu à tali articuli-lezioni, avete idee per novi articuli o avete pruposte di cuuperazione, saraghju cuntentu di riceve un missaghju in un missaghju persunale o per mail m.kuzmin+habr@darkleaf.ru.
Source: www.habr.com
