Elasticsearch ass eng Sichmotor mat json rest api, benotzt Lucene a geschriwwen op Java. Eng Beschreiwung vun all de Virdeeler vun dësem Motor ass verfügbar op . An deem folgend wäerte mir op Elasticsearch als ES bezeechnen.
Ähnlech Motore gi fir komplex Recherchen an enger Dokumentdatenbank benotzt. Zum Beispill, Sich ënner Beuechtung vun der Morphologie vun der Sprooch oder Sich no Geo Koordinaten.
An dësem Artikel wäert ech iwwer d'Basis vun ES schwätzen andeems Dir d'Beispill vun der Indexéierung vu Blogposte benotzt. Ech weisen Iech wéi Dir Dokumenter filtert, sortéiert a sicht.
Fir net vum Betribssystem ofhänken, wäert ech all Ufro un ES mat CURL maachen. Et gëtt och e Plugin fir Google Chrome genannt .
Den Text enthält Linken op Dokumentatioun an aner Quellen. Um Enn ginn et Linken fir séier Zougang zu der Dokumentatioun. Definitioune vun onbekannte Begrëffer kënnen an .
Installatioun vun ES
Fir dëst ze maachen, brauche mir als éischt Java. Entwéckler installéiere Java Versioune méi nei wéi Java 8 Update 20 oder Java 7 Update 55.
D'ES Verdeelung ass verfügbar op . Nodeems Dir den Archiv ausgepackt hutt, musst Dir lafen bin/elasticsearch. Och verfügbar ... do ass . .
No der Installatioun a Start, loosst eis d'Funktionalitéit kontrolléieren:
# для удобства запомним адрес в переменную
#export ES_URL=$(docker-machine ip dev):9200
export ES_URL=localhost:9200
curl -X GET $ES_URLMir kréien esou eppes:
{
"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"
}Indexéierung
Loosst eis e Post op ES addéieren:
# Добавим документ 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 Äntwert:
{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : false
}
ES automatesch erstallt blog an posten. Mir kënnen eng bedingt Analogie zéien: en Index ass eng Datebank, an en Typ ass eng Tabell an dëser Datebank. All Typ huet säin eegene Schema - , grad wéi e relationalen Dësch. Mapping gëtt automatesch generéiert wann d'Dokument indexéiert ass:
# Получим mapping всех типов индекса blog
curl -XGET "$ES_URL/blog/_mapping?pretty"An der ServerÄntwert hunn ech d'Wäerter vun de Felder vum indexéierten Dokument an de Kommentarer bäigefüügt:
{
"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"
}
}
}
}
}
}Et ass derwäert ze notéieren datt ES net ënnerscheet tëscht engem eenzege Wäert an enger Rei vu Wäerter. Zum Beispill enthält den Titelfeld einfach en Titel, an d'Tagsfeld enthält eng Array vu Strings, obwuel se an der selwechter Aart a Mapping vertruede sinn.
Mir schwätze méi iwwer d'Mapping spéider.
Demanden
En Dokument duerch seng ID zréckzéien:
# извлечем документ с 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"
}
}Nei Schlësselen erschéngen an der Äntwert: _version и _source. Am Allgemengen, all Schlëssel ugefaange mat _ ginn als offiziell klasséiert.
Schlëssel _version weist d'Dokumentversioun. Et ass néideg fir den optimistesche Sperrmechanismus ze schaffen. Zum Beispill wëlle mir en Dokument änneren, deen d'Versioun 1 huet. Mir stellen dat geännert Dokument of a weisen datt dëst eng Ännerung vun engem Dokument mat der Versioun 1 ass. Wann een aneren och en Dokument mat der Versioun 1 geännert huet an Ännerunge virun eis presentéiert huet, dann ES wäert eis Ännerungen net akzeptéieren, well et späichert d'Dokument mat der Versioun 2.
Schlëssel _source enthält dat Dokument dat mir indexéiert hunn. ES benotzt dëse Wäert net fir Sich Operatiounen well Indexe gi benotzt fir ze sichen. Fir Plaz ze spueren, späichert ES e kompriméierte Quelldokument. Wa mir nëmmen d'ID brauchen, an net de ganze Quelldokument, da kënne mir d'Quellspäicherung auszeschalten.
Wa mir keng zousätzlech Informatioun brauchen, kënne mir nëmmen den Inhalt vun _source kréien:
curl -XGET "$ES_URL/blog/post/1/_source?pretty"{
"title" : "Веселые котята",
"content" : "<p>Смешная история про котят<p>",
"tags" : [ "котята", "смешная история" ],
"published_at" : "2014-09-12T20:44:42+00:00"
}
Dir kënnt och nëmme bestëmmte Felder auswielen:
# извлечем только поле title
curl -XGET "$ES_URL/blog/post/1?_source=title&pretty"{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"title" : "Веселые котята"
}
}Loosst eis e puer méi Posts indexéieren a méi komplex Ufroen lafen.
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"
}'Zortéieren
# найдем последний пост по дате публикации и извлечем поля 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 ]
} ]
}
}Mir hunn de leschte Post gewielt. size limitéiert d'Zuel vun den Dokumenter déi ausgestallt ginn. total weist d'Gesamtzuel vun den Dokumenter, déi d'Ufro passen. sort am Ausgang enthält eng Rei vun ganz Zuelen, duerch déi d'Sortéierung duerchgefouert gëtt. Déi. den Datum gouf an eng ganz Zuel ëmgerechent. Méi Informatioun iwwer d'Sortéierung fannt Dir an .
Filteren an Ufroen
ES zënter Versioun 2 ënnerscheet net tëscht Filteren an Ufroen, amplaz .
En Ufrokontext ënnerscheet sech vun engem Filterkontext an datt d'Ufro en _Score generéiert an net am Cache gëtt. Ech weisen Iech méi spéit wat _score ass.
Filter no Datum
Mir benotzen d'Ufro am Kader vum Filter:
# получим посты, опубликованные 1ого сентября или позже
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
"filter": {
"range": {
"published_at": { "gte": "2014-09-01" }
}
}
}'Filter no Tags
Mir benotzen Sich no Dokument-IDen mat engem bestëmmte Wuert:
# найдем все документы, в поле 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" : [ "котята" ]
}
} ]
}
}Voll Text Sich
Dräi vun eisen Dokumenter enthalen déi folgend am Inhaltsfeld:
<p>Смешная история про котят<p><p>Смешная история про щенков<p><p>Душераздирающая история про бедного котенка с улицы<p>
Mir benotzen Sich no Dokument-IDen mat engem bestëmmte Wuert:
# 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
} ]
}
}Wéi och ëmmer, wa mir no "Geschichten" am Inhaltsfeld sichen, fanne mir näischt, well Den Index enthält nëmmen déi originell Wierder, net hir Stämm. Fir eng qualitativ héichwäerteg Sich ze maachen, musst Dir den Analyser konfiguréieren.
Beräich _score weist . Wann d'Ufro an engem Filterkontext ausgefouert gëtt, da wäert de _score Wäert ëmmer gläich 1 sinn, dat heescht e komplette Match zum Filter.
Analyséierer
sinn néideg fir de Quelltext an eng Rei vun Tokens ze konvertéieren.
Analysatoren besteet aus engem an e puer fakultativ . Tokenizer ka vun e puer virausgesat ginn . Tokenizer briechen d'Quellstring an Tokens, wéi Plazen a Punktuatiounszeechen. TokenFilter kann Tokens änneren, läschen oder nei addéieren, zum Beispill nëmmen de Stamm vum Wuert verloossen, Präpositioune läschen, Synonyme derbäisetzen. CharFilter - ännert de ganze Quellstring, zum Beispill, schneid HTML-Tags aus.
ES huet e puer . Zum Beispill en Analyser .
Loosst eis profitéieren a loosst eis kucken wéi d'Standard a russesch Analysatoren d'String "Komesch Geschichten iwwer Kitten" transforméieren:
# используем анализатор 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
} ]
}De Standard Analyser huet d'String duerch Plazen opgedeelt an alles an kleng Buschtawen ëmgewandelt, de russesche Analyser huet onwichteg Wierder ewechgeholl, an kleng Buschtawen ëmgewandelt an de Stamm vun de Wierder verlooss.
Loosst eis kucken wéi eng Tokenizer, TokenFilters, CharFilters de russesche Analyser benotzt:
{
"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 отсутствуют */
}
}
}Loosst eis eisen Analyser op Russesch beschreiwen, deen HTML-Tags ausschneiden. Loosst eis et Standard nennen, well en Analyser mat dësem Numm gëtt Par défaut benotzt.
{
"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"
]
}
}
}Als éischt ginn all HTML-Tags aus der Quellstring geläscht, da wäert den Tokenizer-Standard et an Tokens opdeelen, déi resultéierend Tokens ginn op kleng Buschtawen réckelen, onbedeitend Wierder ginn ewechgeholl, an déi verbleiwen Tokens bleiwen de Stamm vum Wuert.
En Index erstellen
Uewen hu mir de Standardanalysator beschriwwen. Et gëlt fir all Stringfelder. Eise Post enthält eng ganz Rëtsch vun Tags, sou datt d'Tags och vum Analysator veraarbecht ginn. Well Mir sichen no Posts no genaue Match zu engem Tag, da musse mir d'Analyse fir d'Tagsfeld auszeschalten.
Loosst eis en Index Blog2 mat engem Analysator a Mapping erstellen, an deem d'Analyse vum Tagsfeld behënnert ass:
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"
}
}
}
}
}'Loosst eis déi selwecht 3 Posts an dësen Index addéieren (blog2). Ech wäert dëse Prozess ausgoen well ... et ass ähnlech wéi Dokumenter an de Blogindex bäizefügen.
Voll Text Sich mat Ausdrock Ënnerstëtzung
Loosst eis eng aner Aart vun Ufro kucken:
# найдем документы, в которых встречается слово 'истории'
# 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"
]
}
}
}'Well Mir benotzen en Analyser mat russescher Stemmung, da wäert dës Ufro all Dokumenter zréckginn, obwuel se nëmmen d'Wuert "Geschicht" enthalen.
D'Ufro kann speziell Zeechen enthalen, zum Beispill:
""fried eggs" +(eggplant | potato) -frittata"Ufro Syntax:
+ 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
Wann Dir un esou Artikel-Lektioune interesséiert sidd, Iddien fir nei Artikelen hutt oder Virschléi fir Kooperatioun hutt, da géif ech mech freeën, eng Noriicht an enger perséinlecher Noriicht oder per E-Mail m.kuzmin+habr@darkleaf.ru ze kréien.
Source: will.com
