Bingehên Elasticsearch

Elasticsearch motorek lêgerînê ye bi json rest api, ku Lucene bikar tîne û bi Java-yê hatî nivîsandin. Danasînek hemî avantajên vê motorê li vir heye malpera fermî. Di tiştê jêrîn de em ê Elasticsearch wekî ES binav bikin.

Motorên bi vî rengî ji bo lêgerînên tevlihev ên di databasa belgeyê de têne bikar anîn. Mînakî, lêgerîn li gorî morfolojiya ziman an li gorî hevrêzên erdnîgarî bigere.

Di vê gotarê de ez ê li ser bingehên ES-ê bi karanîna mînaka navnîşkirina postên blogê biaxivim. Ez ê nîşanî we bidim ka meriv çawa pelgeyan fîlter, rêzkirin û lêgerînê dike.

Ji bo ku ez bi pergala xebitandinê ve nemînim, ez ê hemî daxwazan ji ES-ê re bi karanîna CURL-ê bikim. Ji bo google chrome jî pêvekek heye ku jê re tê gotin de daseke.

Di nivîsê de girêdanên belge û çavkaniyên din hene. Di dawiyê de lînkên ji bo gihîştina bilez a belgeyê hene. Pênaseyên peyvên nenas di nav de têne dîtin ferhengok.

Sazkirina ES

Ji bo vê yekê, em pêşî Java hewce ne. Developers pêşnîyarkirin guhertoyên Java ji nûvekirina Java 8 20 an Java 7 nûvekirina 55 nûtir saz bikin.

Dabeşkirina ES li wir heye malpera pêşdebiran. Piştî vekirina arşîvê hûn hewce ne ku hûn birevin bin/elasticsearch. Jî heye pakêtên ji bo apt û yum. Heye wêneya fermî ji bo docker. Zêdetir li ser sazkirinê.

Piştî sazkirinê û destpêkirinê, em fonksiyonê kontrol bikin:

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

curl -X GET $ES_URL

Em ê tiştek weha bistînin:

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

Indekskirin

Ka em postek li ES zêde bikin:

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

bersiva server:

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

ES bixweber hate afirandin index blog û tîp koz. Em dikarin analojiyek şertî derxînin: index databasek e, û celeb tabloyek di vê databasê de ye. Her cure nexşeya xwe heye - Maşallah, mîna tabloyek pêwendiyê. Dema ku belge tête navnîş kirin nexşe bixweber tête çêkirin:

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

Di bersiva serverê de, min nirxên qadên belgeya pêvekirî di şîroveyan de zêde kir:

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

Hêjayî gotinê ye ku ES di navbera nirxek yek û rêzek nirxan de ferq nake. Mînakî, qada sernavê bi tenê sernavek dihewîne, û qada etîketan komek rêzan dihewîne, her çend ew di nexşeyê de bi heman rengî têne destnîşan kirin.
Em ê paşê bêtir li ser nexşeyê biaxivin.

Daxwazên

Vegerandina belgeyek bi nasnameya wê:

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

Bişkojkên nû di bersivê de xuya bûn: _version и _source. Bi gelemperî, hemî kilît bi dest pê dikin _ wekî fermî têne dabeş kirin.

Ключ _version guhertoya belgeyê nîşan dide. Ji bo ku mekanîzmaya girtina xweşbîn bixebite pêdivî ye. Mînakî, em dixwazin belgeyek ku guhertoya 1 heye biguherînin. Em belgeya guherî pêşkêş dikin û destnîşan dikin ku ev guhertoyek belgeyek bi guhertoya 1-ê ye. Ger kesek din jî belgeyek bi guhertoya 1-ê re sererast kir û guheztinan pêşkêşî me kir, hingê ES dê guhertinên me qebûl neke, ji ber ew belgeyê bi guhertoya 2-ê hilîne.

Ключ _source belgeya ku me îndeks kir heye. ES vê nirxê ji bo operasyonên lêgerînê bikar nayne ji ber ku Indeks ji bo lêgerînê têne bikar anîn. Ji bo cîhê xilas bike, ES belgeyek çavkaniyek pêçandî hilîne. Ger em tenê hewceyê nasnameyê ne, û ne hemî belgeya çavkaniyê, wê hingê em dikarin hilanîna çavkaniyê neçalak bikin.

Heke hewcedariya me bi agahdariya zêde tune, em dikarin tenê naveroka _source bistînin:

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

Her weha hûn dikarin tenê hin qadan hilbijêrin:

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

Werin em çend postên din îndeks bikin û pirsên tevlihevtir bimeşînin.

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

Sort

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

Me posta dawî hilbijart. size hejmara belgeyên ku têne derxistin sînor dike. total hejmara giştî ya belgeyên li gorî daxwazê ​​nîşan dide. sort di derketinê de rêzek ji hejmarên bêkêmasî hene ku bi wan veqetandek tê kirin. Ewan. tarîx veguherî jimareke bêkêmasî. Zêdetir agahdarî di derbarê veqetandinê de dikare were dîtin belgekirin.

Parzûn û pirsan

ES ji ber ku guhertoya 2-ê di şûna fîlter û pirsan de ferq nake têgeha konteksan tê destnîşan kirin.
Di çarçoveyek pirsê de ji çarçoveyek fîlterê cûda dibe ku pirs jimareyek _score çêdike û ne cache ye. Ez ê paşê nîşanî we bidim ka _score çi ye.

Parzûna li gorî dîrokê

Em daxwazê ​​bikar tînin dirêjahî di çarçoveya parzûnê de:

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

Parzûna bi etîketan

Em bikar tînin pirsiyariya termê ji bo lêgerîna nasnameyên belgeyên ku peyvek diyarkirî hene:

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

Lêgerîna nivîsê ya tevahî

Sê belgeyên me di qada naverokê de jêrîn hene:

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

Em bikar tînin pirsiyariya hevberdanê ji bo lêgerîna nasnameyên belgeyên ku peyvek diyarkirî hene:

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

Lêbelê, heke em di qada naverokê de li "çîrok" bigerin, em ê tiştek nebînin, ji ber ku Indeks tenê peyvên resen hene, ne kokên wan. Ji bo ku hûn lêgerînek bilind-kalîteyê bikin, hûn hewce ne ku analîzkerê mîheng bikin.

warê _score nîşan dide relevance. Ger daxwazî ​​di çarçoveyek parzûnê de were bicîh kirin, wê hingê nirxa _score dê her gav bi 1 re be, ku tê vê wateyê ku bi parzûnê re bi tevahî lihevhatinek e.

Analyzer

Analyzer ji bo veguhertina nivîsa çavkaniyê nav komek nîşanan hewce ne.
Analîzator ji yek pêk tê Tokenizer û çend vebijarkî TokenFilters. Tokenizer dikare ji hêla çend kesan ve were pêş CharFilters. Tokenîzator rêzika çavkaniyê di nav nîşanan de dişkînin, wek mekan û tîpên xalbendiyê. TokenFilter dikare nîşanan biguhezîne, jêbibe an yên nû lê zêde bike, mînakî, tenê stûna peyvê bihêle, gotinên pêşiyan jê bike, hevwateyan lê zêde bike. CharFilter - tevahiya rêzika çavkaniyê diguhezîne, mînakî, etîketên html qut dike.

ES gelek hene analyzerên standard. Mînakî, analîzator russian.

Werin em sûd werbigirin api û ka em bibînin ka analîzkerên standard û rûsî çawa rêzika "Çîrokên dilşewat ên li ser kitan" vediguherînin:

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

Analîzatora standard rêz bi valahiyan veqetand û her tişt veguherand tîpên piçûk, analîzatorê rûsî peyvên ne girîng jê kir, veguhart bi tîpên piçûk û hîmên peyvan hişt.

Ka em bibînin ka analîzkerê rûsî kîjan Tokenizer, TokenFilters, CharFilters bikar tîne:

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

Ka em analîzerê xwe li ser bingeha rûsî rave bikin, ku dê tagên html-ê qut bike. Ka em jê re bibêjin default, ji ber analyzerek bi vî navî dê ji hêla xwerû ve were bikar anîn.

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

Pêşîn, hemî etîketên HTML-ê dê ji rêzika çavkaniyê werin rakirin, dûv re standarda tokenîzatorê wê di nav nîşanan de veqetîne, nîşaneyên ku têne encamdan dê biçin tîpên piçûk, dê peyvên negirîng werin jêbirin, û nîşaneyên mayî dê stûna peyvê bimînin.

Çêkirina Endeksek

Li jor me analîzkera xwerû diyar kir. Ew ê ji bo hemî qadên rêzikê bicîh bibe. Posta me rêzek nîşanan dihewîne, ji ber vê yekê etîket dê ji hêla analyzerê ve jî bêne hilberandin. Bo Em li mesajan li gorî lihevhatina tam a nîşanekê digerin, wê hingê divê em analîzê ji bo qada nîşanan neçalak bikin.

Ka em bi analîzator û nexşeyê re blogek index2 biafirînin, ku tê de analîza qada nîşanan neçalak e:

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

Werin em heman 3 postan li vê navnîşê (blog2) zêde bikin. Ez ê vê pêvajoyê ji ber ku ... ew dişibe lêzêdekirina belgeyan li navnîşana blogê.

Lêgerîna nivîsê ya tevahî bi piştgiriya îfadeyê

Ka em li celebek daxwazek din binêrin:

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

Bo Em analîzerek bi stema rûsî bikar tînin, wê hingê ev daxwaz dê hemî belgeyan vegerîne, her çend ew tenê peyva "dîrok" vedigirin.

Daxwaz dikare karakterên taybetî hebin, wek nimûne:

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

Daxwaza hevoksaziyê:

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

references

PS

Heke hûn bi gotar-dersên wekhev re eleqedar dibin, ji bo gotarên nû ramanên we hene, an ji bo hevkariyê pêşniyarên we hene, wê hingê ez ê kêfxweş bibim ku di peyamek kesane an bi e-nameyê de peyamek bistînim. [email parastî].

Source: www.habr.com