Grunnatriði Elasticsearch

Elasticsearch er leitarvél með JSON REST API, sem notar Lucene og er skrifuð í Java. Lýsing á öllum kostum þessarar leitarvélar er að finna á opinber vefsíðaHéðan í frá munum við vísa til Elasticsearch sem ES.

Slíkar leitarvélar eru notaðar fyrir flóknar leitir í gagnagrunnum, svo sem leit byggða á málfræðilegri formgerð eða leit eftir landfræðilegum hnitum.

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

Til að vera óháður stýrikerfinu mun ég nota CURL til að senda allar ES beiðnir mínar. Það er líka viðbót fyrir Google Chrome sem kallast skilningi.

Tenglar á skjöl og aðrar heimildir eru í gegnum allan textann. Tenglar með fljótlegum aðgangi að skjölunum eru í lokin. Skilgreiningar á ókunnuglegum hugtökum er að finna í orðalistar.

Uppsetning ES

Til þess þurfum við fyrst Java. Forritarar Mælt með Settu upp Java útgáfur nýrri en Java 8 uppfærsla 20 eða Java 7 uppfærsla 55.

Dreifing ES er aðgengileg á vefsíðu þróunaraðilaEftir að þú hefur tekið upp skjalasafnið þarftu að keyra bin/elasticsearchEinnig fáanlegt pakkar fyrir apt og yum. Það er Opinber mynd fyrir Docker. Meira um uppsetningu.

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

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

curl -X GET $ES_URL

Við munum fá svar sem lítur eitthvað svona út:

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

Við skulum bæta við færslu í 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 netþjóns:

{
  "_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 staða. Hægt er að draga grófa samlíkingu: vísitala er gagnagrunnur og tegund er tafla innan þess gagnagrunns. Hver tegund hefur sitt eigið skema— kortlagning, rétt eins og tengslatafla. Vörpun er búin til sjálfkrafa þegar skjal er skráð:

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

Í svari netþjónsins bætti ég við reitgildum vísitöluskrárinnar í 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 vert að taka fram að ES gerir ekki greinarmun á einu gildi og fylki af gildum. Til dæmis inniheldur titilreiturinn aðeins titil, en tagsreiturinn inniheldur fylki af strengjum, jafnvel þótt þeir séu táknaðir eins í vörpuninni.
Við munum ræða kortlagningu nánar síðar.

Beiðnir

Að draga út 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 и _sourceAlmennt séð allir lyklar sem byrja á _ eru flokkaðar sem þjónustuvörur.

Lykill _version Sýnir útgáfu skjalsins. Þetta er nauðsynlegt til þess að bjartsýnislæsingarkerfið virki. Til dæmis viljum við breyta skjali með útgáfu 1. Við sendum inn breytta skjalið og gefum til kynna að þetta sé útgáfa af skjalinu með útgáfu 1. Ef einhver annar breytti einnig skjalinu með útgáfu 1 og sendi breytingarnar inn fyrir okkur, mun ES ekki samþykkja breytingar okkar, þar sem það geymir skjalið með útgáfu 2.

Lykill _source Inniheldur skjalið sem við flokkuðum. ES notar ekki þetta gildi fyrir leitaraðgerðir, þar sem vísitölur eru notaðar til leitar. Til að spara pláss geymir ES þjappaða útgáfu af upprunalega skjalinu. Ef við þurfum aðeins auðkennið en ekki allt upprunalega skjalið getum við gert geymslu á upprunalega skjalinu óvirka.

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 vísitöluera 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ð höfum valið síðustu færsluna. size takmarkar fjölda útgefinna skjala. total sýnir heildarfjölda skjala sem passa við fyrirspurnina. sort Úttakið inniheldur fylki af heiltölum sem flokkunin er framkvæmd með. Það er að segja, dagsetningin hefur verið breytt í heiltölu. Nánari upplýsingar um flokkun er að finna í skjöl.

Síur og fyrirspurnir

ES frá útgáfu 2 gerir ekki greinarmun á síum og fyrirspurnum, heldur Hugtakið samhengi er kynnt.
Fyrirspurnarsamhengið er frábrugðið síunarsamhenginu að því leyti að fyrirspurnin býr til _stig og er ekki geymd í skyndiminni. Ég mun útskýra hvað _stig er síðar.

Sía eftir dagsetningu

Að nota fyrirspurnina 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 fyrirspurn um hugtak Til að leita að skjalakennum 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" : [ "котята" ]
      }
    } ]
  }
}

Leit í fullum texta

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

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

Við notum samsvörunarfyrirspurn Til að leita að skjalakennum 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“ í efnisreitnum, munum við ekki finna neitt, þar sem leitarorðaskráin inniheldur aðeins frumorð, ekki stofna þeirra. Til að framkvæma gæðaleit þurfum við að stilla greiningartækið.

Field _score sýnir mikilvægiEf fyrirspurnin er keyrð í síusamhengi verður _score gildið alltaf 1, sem þýðir að sían passar fullkomlega.

Greiningartæki

Greiningartæki eru nauðsynleg til að umbreyta frumtextanum í safn af táknum.
Greiningartækin samanstanda af einum Tokenizer og nokkrir valfrjálsir TáknsíurTáknmyndari getur komið á undan nokkrum CharFiltersTáknmyndavélar brjóta upprunastreng niður í tákn, til dæmis með bilum og greinarmerkjatáknum. Táknmyndavélar geta breytt táknum, fjarlægt þau eða bætt við nýjum, til dæmis með því að skilja aðeins eftir orðstofninn, fjarlægja forsetningar eða bæta við samheitum. Merkjamyndavélar breyta upprunastrengnum alveg, til dæmis með því að fjarlægja HTML-merki.

Það eru nokkrir í ES staðlaðar greiningartækiTil dæmis, greiningartæki Rússneska.

Nýtum okkur api og við skulum sjá hvernig staðlaðir og rússneskir greiningarforrit umbreyta strengnum „Fyndar 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ð skipti strengnum við bil og breytti öllu í lágstafi, rússneska greiningartækið fjarlægði ómerkileg orð, breytti þeim í lágstafi og skildi orðstofnana eftir.

Við skulum sjá hvaða Tokenizer, TokenFilters og 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 okkar eigin rússneska greiningarforriti sem fjarlægir HTML-tagga. Við munum kalla það sjálfgefið forrit, þar sem þetta verður sjálfgefið forrit.

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

Fyrst verða öll HTML-tögg fjarlægð úr upprunastrengnum, síðan verður honum skipt í tákn með táknunarstaðlinum (tokenizer), táknin sem myndast verða breytt í lágstafi, ómerkileg orð verða fjarlægð og táknin sem eftir eru verða orðstofninn.

Að búa til vísitölu

Hér að ofan lýstum við sjálfgefna greiningartækinu. Það verður notað á alla strengjasvið. Færslan okkar inniheldur fylki af merkjum, þannig að greiningartækið mun einnig vinna úr merkjunum. Þar sem við erum að leita að færslum sem passa nákvæmlega við merkið þurfum við að slökkva á greiningu fyrir reitinn „merki“.

Við skulum búa til blog2 vísitölu með greiningartæki og vörpun, þar sem greining á merkjasviðinu 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 þremur færslunum við þessa vísitölu (blogg2). Ég sleppi þessu ferli, þar sem það er svipað og að bæta skjölum við bloggvísitöluna.

Leit að fullum texta með stuðningi við tjáningar

Við skulum kynna okkur aðra tegund fyrirspurnar:

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

Þar sem við notum greiningartæki með rússneskri stofnsetningu mun þessi fyrirspurn skila öllum skjölum, jafnvel þótt þau innihaldi aðeins orðið „history“.

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

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

Fyrirspurnarsetningafræð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 á svipuðum kennslugreinum, hefur hugmyndir að nýjum greinum eða hefur einhverjar tillögur að samstarfi, þá myndi ég gjarnan heyra frá þér í gegnum einkaskilaboð eða tölvupóst á m.kuzmin+habr@darkleaf.ru.

Heimild: www.habr.com