Elasticsearch on hakukone, jossa on JSON REST API, Lucene ja joka on kirjoitettu Javalla. Kuvaus tämän hakukoneen kaikista eduista on saatavilla osoitteessa Tästä eteenpäin kutsumme Elasticsearchiä nimellä ES.
Tällaisia hakukoneita käytetään monimutkaisiin asiakirjatietokantojen hakuihin, kuten kielen morfologiaan tai maantieteellisten koordinaattien perusteella tehtävään hakuun.
Tässä artikkelissa käsittelen ES:n perusteita blogikirjoitusten indeksoinnin esimerkin avulla. Näytän, miten dokumentteja suodatetaan, lajitellaan ja haetaan.
Ollakseni käyttöjärjestelmästä riippumaton, teen kaikki ES-pyyntöni CURL:n avulla. Google Chromelle on myös olemassa laajennus nimeltä .
Linkkejä dokumentaatioon ja muihin lähteisiin on tarjolla koko tekstissä. Nopeat linkit dokumentaatioon ovat lopussa. Tuntemattomien termien määritelmät löytyvät kohdasta .
ES:n asentaminen
Tätä varten tarvitsemme ensin Javan. Kehittäjät Asenna Java-versiot, jotka ovat uudempia kuin Java 8 update 20 tai Java 7 update 55.
ES-jakelu on saatavilla osoitteessa Arkiston purkamisen jälkeen sinun on suoritettava bin/elasticsearchMyös saatavilla . On olemassa . .
Asennuksen ja käynnistyksen jälkeen tarkistamme toiminnallisuuden:
# для удобства запомним адрес в переменную
#export ES_URL=$(docker-machine ip dev):9200
export ES_URL=localhost:9200
curl -X GET $ES_URLSaamme vastauksen, joka näyttää suunnilleen tältä:
{
"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"
}indeksointi
Lisätäänpä ES:ään viesti:
# Добавим документ 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"
}'
palvelimen vastaus:
{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"created" : false
}
ES luotiin automaattisesti blogi ja viesti. Voidaan vetää karkea analogia: indeksi on tietokanta ja tyyppi on taulukko kyseisen tietokannan sisällä. Jokaisella tyypillä on oma skeemansa— , aivan kuten relaatiotaulukko. Yhdistämismääritykset luodaan automaattisesti, kun asiakirja indeksoidaan:
# Получим mapping всех типов индекса blog
curl -XGET "$ES_URL/blog/_mapping?pretty"Palvelimen vastauksessa lisäsin indeksoidun dokumentin kenttäarvot kommentteihin:
{
"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"
}
}
}
}
}
}On syytä huomata, että ES ei erottele yksittäistä arvoa ja arvotaulukkoa. Esimerkiksi otsikkokenttä sisältää vain otsikon, kun taas tags-kenttä sisältää merkkijonotaulukon, vaikka ne on esitetty identtisesti kartoituksessa.
Kerromme kartoituksesta tarkemmin myöhemmin.
pyynnöt
Dokumentin poimiminen sen id:n perusteella:
# извлечем документ с 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"
}
}Vastauksessa näkyi uusia avaimia: _version и _sourceYleisesti ottaen kaikki avaimet, jotka alkavat _ luokitellaan palvelutuotteiksi.
avain _version Näyttää dokumentin version. Tämä on välttämätöntä, jotta optimistinen lukitusmekanismi toimisi. Esimerkiksi haluamme muuttaa dokumenttia, jonka versio on 1. Lähetämme muokatun dokumentin ja ilmoitamme, että tämä on version 1 dokumentin revisio. Jos joku muu on myös muokannut dokumenttia, jonka versio on 1, ja lähettänyt muutokset ennen meitä, ES ei hyväksy muutoksiamme, koska se tallentaa dokumentin, jonka versio on 2.
avain _source Sisältää indeksoimamme dokumentin. ES ei käytä tätä arvoa hakutoimintoihin, koska indeksejä käytetään hakuun. Tilan säästämiseksi ES tallentaa pakatun version alkuperäisestä dokumentista. Jos tarvitsemme vain tunnuksen emmekä koko alkuperäistä dokumenttia, voimme poistaa alkuperäisen tallennuksen käytöstä.
Jos emme tarvitse lisätietoja, voimme saada vain _source-muuttujasta saatavan sisällön:
curl -XGET "$ES_URL/blog/post/1/_source?pretty"{
"title" : "Веселые котята",
"content" : "<p>Смешная история про котят<p>",
"tags" : [ "котята", "смешная история" ],
"published_at" : "2014-09-12T20:44:42+00:00"
}
Voit myös valita vain tietyt kentät:
# извлечем только поле title
curl -XGET "$ES_URL/blog/post/1?_source=title&pretty"{
"_index" : "blog",
"_type" : "post",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"title" : "Веселые котята"
}
}Indeksoidaan muutama viesti lisää ja suoritetaan monimutkaisempia kyselyitä.
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"
}'Сортировка
# найдем последний пост по дате публикации и извлечем поля 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 ]
} ]
}
}Olemme valinneet viimeisen postauksen. size rajoittaa myönnettyjen asiakirjojen määrää. total näyttää kyselyä vastaavien dokumenttien kokonaismäärän. sort Tuloste sisältää kokonaislukutaulukon, jonka mukaan lajittelu suoritetaan. Toisin sanoen päivämäärä on muunnettu kokonaisluvuksi. Lisätietoja lajittelusta löytyy kohdasta .
Suodattimet ja kyselyt
ES versiosta 2 lähtien ei erottele suodattimia ja kyselyitä, vaan .
Kyselykonteksti eroaa suodatinkontekstista siten, että kysely luo _score-arvon eikä sitä tallenneta välimuistiin. Selitän myöhemmin, mitä _score on.
Suodata päivämäärän mukaan
Kyselyn käyttäminen suodattimen yhteydessä:
# получим посты, опубликованные 1ого сентября или позже
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
"filter": {
"range": {
"published_at": { "gte": "2014-09-01" }
}
}
}'Suodata tunnisteiden mukaan
käyttö Voit etsiä tietyn sanan sisältäviä dokumenttitunnuksia seuraavasti:
# найдем все документы, в поле 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" : [ "котята" ]
}
} ]
}
}Koko tekstihaku
Kolmen dokumenttimme sisältökentässä on seuraavat tiedot:
<p>Смешная история про котят<p><p>Смешная история про щенков<p><p>Душераздирающая история про бедного котенка с улицы<p>
käyttö Voit etsiä tietyn sanan sisältäviä dokumenttitunnuksia seuraavasti:
# 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
} ]
}
}Jos kuitenkin haemme sisältökentästä "tarinoita", emme löydä mitään, koska hakemisto sisältää vain alkuperäisiä sanoja, ei niiden vartaloita. Laadukkaan haun suorittamiseksi meidän on konfiguroitava analysaattori.
Kenttä _score osoittaa Jos kysely suoritetaan suodatinkontekstissa, _score-arvo on aina 1, mikä tarkoittaa, että suodatin on täysin osunut.
Analysaattorit
tarvitaan lähdetekstin muuntamiseksi joukoksi tokeneja.
Analysaattorit koostuvat yhdestä ja useita valinnaisia Tokenisaattori voi edeltää useita Tokenisoijat jakavat lähdemerkkijonon tokeneiksi esimerkiksi välilyönneillä ja välimerkeillä. TokenFilterit voivat muokata tokeneita, poistaa niitä tai lisätä uusia esimerkiksi jättämällä vain sanarungon, poistamalla prepositioita tai lisäämällä synonyymejä. CharFilterit muokkaavat lähdemerkkijonon kokonaan esimerkiksi poistamalla HTML-tagit.
ES:ssä on useita Esimerkiksi analysaattori .
Hyödynnetään ja katsotaanpa, miten standardi ja venäläinen jäsennin muuntavat merkkijonon "Hauskoja tarinoita kissanpennuista":
# используем анализатор 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
} ]
}Vakioanalysaattori jakoi merkkijonon välilyöntien kohdalta ja muunsi kaiken pieniksi kirjaimiksi, venäläinen analysaattori poisti merkityksettömät sanat, muunsi ne pieniksi kirjaimiksi ja jätti sanarunkonsa.
Katsotaanpa, mitä Tokenizer-, TokenFilter- ja CharFilter-suodattimia venäläinen analysaattori käyttää:
{
"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 отсутствуют */
}
}
}Kuvaillaan omaa venäläistä analysaattoriamme, joka poistaa HTML-tagit. Kutsumme sitä oletusarvoiseksi, koska tämä on oletusarvoinen analysaattori.
{
"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"
]
}
}
}Ensin kaikki HTML-tagit poistetaan lähdemerkkijonosta, sitten tokenizer-standardi jakaa sen tokeneihin, tuloksena olevat tokenit muunnetaan pieniksi kirjaimiksi, merkityksettömät sanat poistetaan ja loput tokenit ovat sanarunko.
Indeksin luominen
Yllä kuvailimme oletusanalysaattoria. Sitä käytetään kaikkiin merkkijonokenttiin. Viestimme sisältää taulukon tageja, joten analysaattori käsittelee myös tagit. Koska etsimme viestejä, jotka vastaavat täsmälleen samaa tagia, meidän on poistettava käytöstä "tagit"-kentän analyysi.
Luodaan blog2-indeksi analysaattorilla ja kartoituksella, jossa tags-kentän analysointi on poistettu käytöstä:
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"
}
}
}
}
}'Lisätään samat kolme julkaisua tähän hakemistoon (blog2). Ohitan tämän prosessin, koska se on samanlainen kuin dokumenttien lisääminen blogihakemistoon.
Kokotekstihaku lausekkeilla
Tutustutaanpa toisenlaiseen kyselytyyppiin:
# найдем документы, в которых встречается слово 'истории'
# 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"
]
}
}
}'Koska käytämme analysaattoria, jossa on venäjänkieliset sanat, tämä kysely palauttaa kaikki dokumentit, vaikka ne sisältäisivät vain sanan 'historia'.
Kysely voi sisältää erikoismerkkejä, esimerkiksi:
""fried eggs" +(eggplant | potato) -frittata"Kyselyn syntaksi:
+ 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 поста про котиковviittaukset
PS
Jos olet kiinnostunut vastaavanlaisista opetusohjelmista, sinulla on ideoita uusiin artikkeleihin tai yhteistyöehdotuksia, kuulen mielelläni sinusta yksityisviestillä tai sähköpostitse osoitteeseen m.kuzmin+habr@darkleaf.ru.
Lähde: will.com
