Elasticsearchin perusteet

Elasticsearch on Json Rest API:lla varustettu hakukone, joka käyttää Lucenea ja on kirjoitettu Javalla. Kuvaus tämän moottorin kaikista eduista on saatavilla osoitteessa virallisilla verkkosivuilla. Lisäksi tekstiä kutsutaan nimellä Elasticsearch ES.

Tällaisia ​​​​koneita käytetään vaikeassa tietokannan haussa. Esimerkiksi haku, jossa otetaan huomioon kielen morfologia tai haku maantieteellisten koordinaattien mukaan.

Tässä artikkelissa kerron sinulle ES:n perusteista blogiviestien indeksoinnin esimerkissä. Näytän kuinka asiakirjoja suodatetaan, lajitellaan ja etsitään.

Jotta en olisi riippuvainen käyttöjärjestelmästä, teen kaikki pyynnöt ES:lle Curlilla. Google Chromelle on myös plugin nimeltään tunne.

Tekstiin on sijoitettu linkit dokumentaatioon ja muihin lähteisiin. Lopussa on linkkejä, joilla pääset nopeasti käsiksi dokumentaatioon. Tuntemattomien termien määritelmät voidaan lukea sanastot.

Asennus ES

Tätä varten tarvitsemme ensin Java. Kehittäjät suositella Asenna Java enemmän kuin Java 8 Update 20 tai Java 7 Update 55.

ES-jakelu on saatavilla osoitteessa kehittäjäsivusto. Kun olet purkanut arkiston, sinun on aloitettava bin/elasticsearch. Myös saatavilla APT ja Yum paketit. On olemassa virallinen kuva dockerille. Lisää asennuksesta.

Asennuksen ja käynnistyksen jälkeen tarkistamme suorituskyvyn:

# для удобства запомним адрес в переменную
#export ES_URL=$(docker-machine ip dev):9200
export ES_URL=localhost:9200

curl -X GET $ES_URL

Saamme suunnilleen tällaisen vastauksen:

{
  "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ää viesti ES:hen:

# Добавим документ 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 luotu automaattisesti indeksi blogi ja tyyppi Lähettää. Voit piirtää ehdollisen analogian: indeksi on tietokanta ja tyyppi on taulukko tässä tietokannassa. Jokaisella tyypillä on oma mallinsa - kartoitussekä relaatiotaulukko. Karttaus luodaan automaattisesti dokumenttia indeksoitaessa:

# Получим mapping всех типов индекса blog
curl -XGET "$ES_URL/blog/_mapping?pretty"

Palvelimen vastauksessa lisäsin kommentteihin indeksoidun asiakirjan kenttien arvot:

{
  "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 tee eroja yhden arvon ja joukon merkitysten välillä. Esimerkiksi Otsikko-kenttä sisältää vain otsikon ja Tunnisteet-kenttä on rivijoukko, vaikka ne esitetään Mappingissa samalla tavalla.
Kerromme myöhemmin lisää kartoituksesta.

pyynnöt

Asiakirjan poimiminen henkilöllisyystodistuksesta:

# извлечем документ с 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"
  }
}

Uudet avaimet ilmestyivät vastaukseen: _version и _source. Yleensä kaikki avaimet alkaen _ Liittyy virkamiehiin.

avain _version näyttää asiakirjan version. Sitä tarvitaan optimististen lukkojen mekanismin toimintaan. Haluamme esimerkiksi muuttaa asiakirjaa, jolla on versio 1. Lähetämme muutetun dokumentin ja ilmoitamme, että tämä on version 1 dokumentin muokkaus. Jos joku muu muokkasi asiakirjaa versiolla 1 ja lähetti muutokset ennen meitä, niin ES ei hyväksy muutoksiamme, koska Hän tallentaa asiakirjan version 2 kanssa.

avain _source Sisältää indeksoimamme asiakirjan. ES ei käytä tätä arvoa hakutoimintoihin, koska Haussa käytetään indeksejä. Tilan säästämiseksi ES tallentaa pakatun lähdeasiakirjan. Jos tarvitsemme vain tunnuksen, emme koko alkuperäistä asiakirjaa, voit sammuttaa lähteen tallennuksen.

Jos emme tarvitse lisätietoja, voit saada vain sisällön _Lähde:

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 täytetään monimutkaisempia pyyntöjä.

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 ]
    } ]
  }
}

Valitsimme viimeisen postauksen. size rajoittaa myönnettävien asiakirjojen määrää. total Näyttää pyyntöön sopivien asiakirjojen kokonaismäärän. sort Liikkeeseenlasku sisältää joukon kokonaislukuja, joiden mukaan lajittelu suoritetaan. Nuo. Päivämäärä muutettiin kokonaisluvuksi. Voit lukea lisää lajittelusta dokumentointi.

Suodattimet ja pyynnöt

ES versiosta 2 ei erota sen sijaan suodattimia ja pyyntöjä Esitetään kontekstien käsite.
Pyynnön konteksti eroaa suodattimen kontekstista siinä, että pyyntö luo _Score-arvon, eikä sitä tallenneta välimuistiin. Mikä on _Pistemäärä, näytän myöhemmin.

Suodata päivämäärän mukaan

Käytämme pyyntöä alue 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ö Termikysely etsiäksesi tietyn sanan sisältäviä asiakirjatunnuksia:

# найдем все документы, в поле 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

Kolme asiakirjaamme sisältävät seuraavat tiedot Sisältö-kentässä:

  • <p>Смешная история про котят<p>
  • <p>Смешная история про щенков<p>
  • <p>Душераздирающая история про бедного котенка с улицы<p>

käyttö Vastaa kyselyä etsiäksesi tietyn sanan sisältäviä asiakirjatunnuksia:

# 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 etsimme "tarinoita" sisältökentästä, emme löydä mitään, koska Hakemisto sisältää vain alkuperäiset sanat, ei niiden varsia. Jotta voit tehdä korkealaatuisen haun, sinun on määritettävä analysaattori.

Kenttä _score osoittaa merkityksellisyys. Jos pyyntö käytetään suodattimen kontekstissa, _Score-arvo on aina 1, mikä tarkoittaa täydellistä suodattimen noudattamista.

Analysaattorit

Analysaattorit Meidän on muunnettava lähdeteksti merkkijoukoksi.
Analysaattorit koostuvat yhdestä Tokenizer ja useita valinnaisia TokenFilters. Tokeniseria voi edeltää useita Hiilisuodattimet. Tokenizer on jaettu alkuperäiseen riviin tokeneiksi esimerkiksi välilyönneillä ja välimerkkien symboleilla. Tokenfilter voi muuttaa tunnuksia, poistaa tai lisätä uusia, esimerkiksi jättää vain sanan perustan, poistaa prepositiot, lisätä synonyymejä. Charfilter - muuttaa alkuperäisen rivin kokonaisuutena, esimerkiksi leikkaa tunnisteet html.

ES:llä on useita tavallisia analysaattoreita. Esimerkiksi analysaattori Venäjän kieli.

Hyödynnetään api Katsotaanpa, kuinka standardi- ja venäläiset analysaattorit muuntavat rivin "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
  } ]
}

Tavallinen analysaattori jakoi merkkijonon välilyönnillä ja muunsi kaiken pieniksi, venäläinen analysaattori poisti merkityksettömät sanat, muunsi ne pieniksi ja jätti sanojen varren.

Katsotaanpa mikä tokenizer, tokenfilterit, Charfilters käyttää venäläistä analysaattoria:

{
  "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 отсутствуют */
    }
  }
}

Kuvaamme venäläistä analysaattoriamme, joka katkaisee HTML-tunnisteet. Kutsutaan sitä oletukseksi, koska Tämännimistä analysaattoria käytetään oletuksena.

{
  "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 poistetaan kaikki HTML-tunnisteet alkuperäiseltä riviltä, ​​sitten Tokenizer Standard jaetaan tokeneiksi, tuloksena olevat tunnukset siirtyvät alempaan rekisteriin, merkityksettömät sanat poistetaan ja sanan peruste jää jäljelle jäävistä tokeneista. .

Indeksin luominen

Yllä kuvailimme oletusanalysaattoria. Sitä käytetään kaikissa merkkijonokentissä. Viestimme sisältää joukon tageja, ja analysaattori käsittelee myös tunnisteet. Koska Etsimme viestejä tarkan tunnisteen noudattamisen perusteella, jolloin TAGS-kentän analyysi on kytkettävä pois päältä.

Luo BLOG2-indeksi analysaattorilla ja kartoituksella, jossa TAGS-kenttäanalyysi 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ää samat 3 viestiä tähän hakemistoon (Blog2). Jätän tämän prosessin väliin, koska Se on samanlainen kuin asiakirjojen lisääminen BLOG-hakemistoon.

Kokotekstihaku lausekkeiden tuella

Tutustutaanpa toisentyyppiseen pyyntöön:

# найдем документы, в которых встречается слово 'истории'
# 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 venäläisellä stangauksella, tämä pyyntö palauttaa kaikki asiakirjat, vaikka niistä löytyy vain sana "historia".

Pyyntö voi sisältää erikoismerkkejä, esimerkiksi:

""fried eggs" +(eggplant | potato) -frittata"

Pyydä 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 tällaiset artikkelit kiinnostavat, on ideoita uusista artikkeleista tai yhteistyöehdotuksia, raportoin mielelläni henkilökohtaisesti tai postitse [sähköposti suojattu].

Lähde: will.com