Grunnatriði Elasticsearch

Elasticsearch er leitarvél með json rest api, sem notar Lucene og er skrifuð í Java. Lýsing á öllum kostum þessarar vélar er að finna á opinber vefsíða. Í því sem hér fer á eftir munum við vísa til Elasticsearch sem ES.

Svipaðar vélar eru notaðar fyrir flókna leit í skjalagagnagrunni. Til dæmis, leit með hliðsjón af formgerð tungumálsins eða leit eftir landhnitum.

Í þessari grein mun ég tala um grunnatriði ES með því að nota dæmið um að skrá bloggfærslur. Ég skal sýna þér hvernig á að sía, flokka og leita í skjölum.

Til þess að vera ekki háður stýrikerfinu mun ég senda allar beiðnir til ES með CURL. Það er líka viðbót fyrir google króm sem heitir skilningi.

Textinn inniheldur tengla á skjöl og aðrar heimildir. Í lokin eru tenglar fyrir skjótan aðgang að skjölunum. Skilgreiningar á ókunnugum hugtökum er að finna í orðalistar.

Uppsetning

Til að gera þetta þurfum við fyrst Java. Hönnuðir Mælt með setja upp Java útgáfur nýrri en Java 8 uppfærslu 20 eða Java 7 uppfærslu 55.

ES dreifing er aðgengileg á vefsíðu þróunaraðila. Eftir að hafa pakkað niður skjalasafninu þarftu að keyra bin/elasticsearch. Einnig í boði pakkar fyrir apt og nammi. Það er opinber mynd fyrir docker. Meira um uppsetningu.

Eftir uppsetningu og ræsingu skulum við athuga virkni:

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

curl -X GET $ES_URL

Við munum fá eitthvað á þessa leið:

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

Flokkun

Bætum færslu við 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"
}'

svar miðlara:

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

ES búið til sjálfkrafa vísitölu blogg og Tegund færslu. Við getum dregið upp skilyrta líkingu: Vísitala er gagnagrunnur og tegund er tafla í þessum gagnagrunni. Hver tegund hefur sitt eigið kerfi - kortlagning, alveg eins og venslatafla. Kortlagning er mynduð sjálfkrafa þegar skjalið er skráð:

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

Í svari netþjónsins bætti ég við gildum reitanna í verðtryggða skjalinu í athugasemdunum:

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

Það er athyglisvert að ES gerir ekki greinarmun á einu gildi og fjölda gilda. Til dæmis inniheldur titilreiturinn einfaldlega titil og merkisreiturinn inniheldur fjölda strengja, þó að þeir séu táknaðir á sama hátt í kortlagningu.
Við tölum meira um kortlagningu síðar.

Beiðnir

Að sækja skjal eftir auðkenni þess:

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

Nýir lyklar birtust í svarinu: _version и _source. Almennt séð eru allir lyklar sem byrja á _ eru flokkaðir sem opinberir.

Lykill _version sýnir skjalútgáfuna. Það er nauðsynlegt til að bjartsýnn læsibúnaður virki. Til dæmis viljum við breyta skjali sem hefur útgáfu 1. Við sendum inn breytta skjalið og gefum til kynna að þetta sé breyting á skjali með útgáfu 1. Ef einhver annar breytti líka skjali með útgáfu 1 og lagði fram breytingar á undan okkur, þá ES mun ekki samþykkja breytingar okkar, vegna þess það geymir skjalið með útgáfu 2.

Lykill _source inniheldur skjalið sem við skráðum. ES notar ekki þetta gildi fyrir leitaraðgerðir vegna þess Vísitölur eru notaðar til að leita. Til að spara pláss geymir ES þjappað frumskjal. Ef við þurfum aðeins auðkennið, en ekki allt upprunaskjalið, þá getum við slökkt á upprunageymslu.

Ef við þurfum ekki frekari upplýsingar getum við aðeins fengið innihald _source:

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

Þú getur líka valið aðeins ákveðna reiti:

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

Við skulum skrá nokkrar færslur í viðbót og keyra flóknari fyrirspurnir.

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

Raða

# найдем последний пост по дате публикации и извлечем поля 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 ]
    } ]
  }
}

Við völdum síðustu færsluna. size takmarkar fjölda skjala sem á að gefa út. total sýnir heildarfjölda skjala sem passa við beiðnina. sort í úttakinu inniheldur fjölda heiltalna sem flokkun er framkvæmd eftir. Þeir. dagsetningunni var breytt í heila tölu. Frekari upplýsingar um flokkun er að finna í skjöl.

Síur og fyrirspurnir

ES þar sem útgáfa 2 gerir ekki greinarmun á síum og fyrirspurnum í staðinn hugtakið samhengi er kynnt.
Fyrirspurnarsamhengi er frábrugðið síusamhengi að því leyti að fyrirspurnin býr til _score og er ekki í skyndiminni. Ég mun sýna þér hvað _score er seinna.

Sía eftir dagsetningu

Við notum beiðnina svið í samhengi við síu:

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

Sía eftir merkjum

Við notum hugtaksfyrirspurn til að leita að skjalaauðkennum sem innihalda tiltekið orð:

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

Full textaleit

Þrjú skjöl okkar innihalda eftirfarandi í efnisreitnum:

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

Við notum samsvarandi fyrirspurn til að leita að skjalaauðkennum sem innihalda tiltekið orð:

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

Hins vegar, ef við leitum að „sögum“ í innihaldsreitnum, finnum við ekki neitt, vegna þess að Skráin inniheldur aðeins upprunalegu orðin, ekki stofna þeirra. Til að gera hágæða leit þarftu að stilla greiningartækið.

Field _score sýnir mikilvægi. Ef beiðnin er framkvæmd í síusamhengi, þá mun _score gildið alltaf vera jafnt og 1, sem þýðir fullkomið samsvörun við síuna.

Greiningartæki

Greiningartæki eru nauðsynlegar til að breyta frumtextanum í sett af táknum.
Greiningartæki samanstanda af einum Tokenizer og nokkrir valfrjálsir TokenFilters. Nokkrir geta verið á undan Tokenizer CharFilters. Táknvísarar brjóta upprunastrenginn í tákn, svo sem bil og greinarmerki. TokenFilter getur breytt táknum, eytt eða bætt við nýjum, til dæmis, skilið aðeins eftir stofn orðsins, fjarlægt forsetningar, bætt við samheitum. CharFilter - breytir öllum upprunastrengnum, til dæmis, klippir út html tög.

ES hefur nokkra staðlaðar greiningartæki. Til dæmis greiningartæki Rússneska.

Nýtum okkur api og við skulum sjá hvernig venjulegu og rússnesku greiningartækin umbreyta strengnum „Fyndnar sögur um kettlinga“:

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

Staðlaða greiningartækið klofnaði strenginn í bil og breytti öllu í lágstafi, rússneski greiningartækið fjarlægði ómikilvæg orð, breytti því í lágstafi og skildi eftir stofn orðanna.

Við skulum sjá hvaða Tokenizer, TokenFilters, CharFilters rússneski greiningartækið notar:

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

Við skulum lýsa greiningartækinu okkar byggt á rússnesku, sem mun klippa út html merki. Við skulum kalla það sjálfgefið, vegna þess að greiningartæki með þessu nafni verður sjálfgefið notað.

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

Í fyrsta lagi verða öll HTML merki fjarlægð úr upprunastrengnum, síðan mun tákngerarstaðalinn skipta honum í tákn, táknin sem myndast munu færast yfir í lágstafi, óveruleg orð verða fjarlægð og táknin sem eftir eru verða áfram stofn orðsins.

Að búa til vísitölu

Hér að ofan lýstum við sjálfgefna greiningartækinu. Það mun gilda um alla strengareiti. Færslan okkar inniheldur fjölda merkja, þannig að merkin verða einnig unnin af greiningartækinu. Vegna þess að Við erum að leita að færslum eftir nákvæmri samsvörun við merki, þá þurfum við að slökkva á greiningu fyrir merki reitinn.

Við skulum búa til vísitölublogg2 með greiningartæki og kortlagningu, þar sem greining á merkjareitnum er óvirk:

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

Við skulum bæta sömu 3 færslunum við þessa skrá (blogg2). Ég mun sleppa þessu ferli vegna þess að... það er svipað og að bæta skjölum við bloggskrána.

Full textaleit með tjáningarstuðningi

Við skulum skoða aðra tegund af beiðni:

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

Vegna þess að Við erum að nota greiningartæki með rússneskri stemmningu, þá mun þessi beiðni skila öllum skjölum, þó þau innihaldi aðeins orðið 'saga'.

Beiðnin getur innihaldið sérstafi, til dæmis:

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

Biðja um setningafræði:

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

tilvísanir

PS

Ef þú hefur áhuga á sambærilegum greinarkennslu, hefur hugmyndir að nýjum greinum eða ert með tillögur að samstarfi, þá mun ég glaður fá skilaboð í persónulegum skilaboðum eða með tölvupósti [netvarið].

Heimild: www.habr.com