Elasticsearchi põhitõed

Elasticsearch on json rest api-ga otsingumootor, mis kasutab Lucene'i ja on kirjutatud Java keeles. Selle mootori kõigi eeliste kirjeldus on saadaval aadressil ametlikul kodulehel. Järgnevalt viitame Elasticsearchile kui ES-ile.

Sarnaseid mootoreid kasutatakse keerukate otsingute jaoks dokumendi andmebaasis. Näiteks otsige, võttes arvesse keele morfoloogiat või otsige geokoordinaatide järgi.

Selles artiklis räägin ES-i põhitõdedest ajaveebipostituste indekseerimise näitel. Näitan teile, kuidas dokumente filtreerida, sortida ja otsida.

Et mitte sõltuda operatsioonisüsteemist, esitan kõik päringud ES-le, kasutades CURL-i. Google chrome'i jaoks on ka plugin nimega tunne.

Tekst sisaldab linke dokumentatsioonile ja muudele allikatele. Lõpus on lingid dokumentidele kiireks juurdepääsuks. Võõraste terminite definitsioonid leiate aadressilt sõnastikke.

Paigaldamine

Selleks vajame esmalt Java-d. Arendajad Soovitama installige Java versioonid, mis on uuemad kui Java 8 värskendus 20 või Java 7 värskendus 55.

ES-i levitamine on saadaval aadressil arendaja sait. Pärast arhiivi lahtipakkimist peate jooksma bin/elasticsearch. Saadaval ka paketid apt ja yum. On olemas ametlik pilt dockerile. Lisateavet paigaldamise kohta.

Pärast installimist ja käivitamist kontrollime funktsionaalsust:

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

curl -X GET $ES_URL

Me saame midagi sellist:

{
  "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"
}

Indekseerimine

Lisame ES-i postituse:

# Добавим документ 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"
}'

serveri vastus:

{
  "_index" : "blog",
  "_type" : "post",
  "_id" : "1",
  "_version" : 1,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : false
}

ES loodud automaatselt indeks ajaveebi ja Tüüp postitus. Võime tuua tingimusliku analoogia: indeks on andmebaas ja tüüp on selles andmebaasis tabel. Igal tüübil on oma skeem − kaardistus, täpselt nagu relatsioonitabel. Kaardistamine genereeritakse automaatselt, kui dokument on indekseeritud:

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

Serveri vastuses lisasin kommentaaridesse indekseeritud dokumendi väljade väärtused:

{
  "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"
          }
        }
      }
    }
  }
}

Väärib märkimist, et ES ei tee vahet ühel väärtusel ja väärtuste massiivi vahel. Näiteks pealkirjaväli sisaldab lihtsalt pealkirja ja siltide väli sisaldab stringide massiivi, kuigi neid esitatakse kaardistamisel samamoodi.
Räägime kaardistamisest lähemalt hiljem.

taotlused

Dokumendi otsimine selle ID järgi:

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

Vastusesse ilmusid uued võtmed: _version и _source. Üldiselt kõik klahvid algavad tähega _ on klassifitseeritud ametlikeks.

võti _version näitab dokumendi versiooni. See on vajalik optimistliku lukustusmehhanismi toimimiseks. Näiteks soovime muuta dokumenti, millel on versioon 1. Esitame muudetud dokumendi ja märgime, et tegemist on versiooniga 1 dokumendi redigeerimisega. Kui keegi teine ​​muutis ka 1. versiooniga dokumenti ja esitas muudatused enne meid, siis ES ei aktsepteeri meie muudatusi, sest see salvestab dokumendi versiooniga 2.

võti _source sisaldab dokumenti, mille indekseerisime. ES ei kasuta seda väärtust otsingutoimingute jaoks, kuna Otsinguks kasutatakse indekseid. Ruumi säästmiseks salvestab ES tihendatud lähtedokumendi. Kui vajame ainult ID-d, mitte kogu lähtedokumenti, saame lähtesalvestuse keelata.

Kui me lisateavet ei vaja, saame ainult allika _source sisu:

curl -XGET "$ES_URL/blog/post/1/_source?pretty"
{
  "title" : "Веселые котята",
  "content" : "<p>Смешная история про котят<p>",
  "tags" : [ "котята", "смешная история" ],
  "published_at" : "2014-09-12T20:44:42+00:00"
}

Samuti saate valida ainult teatud väljad:

# извлечем только поле title
curl -XGET "$ES_URL/blog/post/1?_source=title&pretty"
{
  "_index" : "blog",
  "_type" : "post",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source" : {
    "title" : "Веселые котята"
  }
}

Indekseerime veel mõned postitused ja käivitame keerulisemad päringud.

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

Valisime viimase postituse. size piirab väljastatavate dokumentide arvu. total näitab päringule vastavate dokumentide koguarvu. sort väljund sisaldab täisarvude massiivi, mille järgi sorteerimine toimub. Need. kuupäev teisendati täisarvuks. Lisateavet sortimise kohta leiate aadressilt dokumentatsioon.

Filtrid ja päringud

ES, kuna versioon 2 ei tee vahet filtritel ja päringutel tutvustatakse kontekstide mõistet.
Päringu kontekst erineb filtri kontekstist selle poolest, et päring genereerib _score'i ja seda ei salvestata vahemällu. Ma näitan teile hiljem, mis on _score.

Filtreeri kuupäeva järgi

Kasutame taotlust valik filtri kontekstis:

# получим посты, опубликованные 1ого сентября или позже
curl -XGET "$ES_URL/blog/post/_search?pretty" -d'
{
  "filter": {
    "range": {
      "published_at": { "gte": "2014-09-01" }
    }
  }
}'

Filtreerimine siltide järgi

Me kasutame termin päring antud sõna sisaldavate dokumendi ID-de otsimiseks:

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

Täisteksti otsing

Kolm meie dokumenti sisaldavad sisuväljal järgmist.

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

Me kasutame vaste päringule antud sõna sisaldavate dokumendi ID-de otsimiseks:

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

Kui aga sisuväljalt otsida “lugusid”, ei leia me midagi, sest Indeks sisaldab ainult algsõnu, mitte nende tüvesid. Kvaliteetse otsingu tegemiseks peate analüsaatori konfigureerima.

Väli _score näitab asjakohasust. Kui taotlus täidetakse filtri kontekstis, on _score väärtus alati võrdne 1-ga, mis tähendab täielikku vastavust filtrile.

Analüsaatorid

Analüsaatorid on vajalikud lähteteksti teisendamiseks märkide komplektiks.
Analüsaatorid koosnevad ühest Tokenisaator ja mitu valikulist TokenFilters. Tokeniseerijale võib eelneda mitu CharFilters. Tokenisaatorid jagavad lähtestringi märkideks, nagu tühikud ja kirjavahemärgid. TokenFilter saab muuta märke, kustutada või lisada uusi, näiteks jätta ainult sõnatüve, eemaldada eessõnu, lisada sünonüüme. CharFilter – muudab kogu lähtestringi, näiteks lõikab välja html-sildid.

ES-l on mitu standardsed analüsaatorid. Näiteks analüsaator vene.

Kasutame ära API ja vaatame, kuidas standardsed ja vene analüsaatorid muudavad stringi "Naljakad lood kassipoegadest":

# используем анализатор 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
  } ]
}

Tavaline analüsaator jagas stringi tühikuteks ja teisendas kõik väiketähtedeks, vene analüsaator eemaldas ebaolulised sõnad, teisendas selle väiketähtedeks ja jättis sõnade tüve.

Vaatame, millist Tokenizerit, TokenFilters, CharFilters vene analüsaator kasutab:

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

Kirjeldame oma venekeelset analüsaatorit, mis lõikab välja html-sildid. Nimetagem seda vaikimisi, sest vaikimisi kasutatakse selle nimega analüsaatorit.

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

Esiteks eemaldatakse lähtestringist kõik HTML-i sildid, seejärel jagab tokenisaatori standard selle märkideks, saadud märgid liiguvad väiketähtedesse, ebaolulised sõnad eemaldatakse ja ülejäänud märgid jäävad sõna tüveks.

Indeksi loomine

Eespool kirjeldasime vaikeanalüsaatorit. See rakendub kõigile stringiväljadele. Meie postitus sisaldab rida silte, seega töötleb silte ka analüsaator. Sest Otsime postitusi märgendi täpse vaste järgi, seejärel peame keelama siltide välja analüüsi.

Loome analüsaatori ja kaardistamisega indeksi blogi2, milles on siltide välja analüüs keelatud:

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"
        }
      }
    }
  }
}'

Lisame siia registrisse (blog3) samad 2 postitust. Jätan selle protsessi vahele, sest... see sarnaneb dokumentide lisamisega ajaveebi registrisse.

Täisteksti otsing väljenditoega

Vaatame teist tüüpi taotlusi:

# найдем документы, в которых встречается слово 'истории'
# 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"
      ]
    }
  }
}'

Sest Kasutame vene tüvega analüsaatorit, siis see päring tagastab kõik dokumendid, kuigi need sisaldavad ainult sõna "ajalugu".

Taotlus võib sisaldada erimärke, näiteks:

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

Taotle süntaksit:

+ 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 поста про котиков

Viited

PS

Kui olete huvitatud sarnastest artiklitest-tundidest, teil on ideid uuteks artikliteks või on koostööettepanekuid, siis võtan hea meelega sõnumi isikliku sõnumi või meili teel [meiliga kaitstud].

Allikas: www.habr.com