Асосҳои Elasticsearch

Elasticsearch як системаи ҷустуҷӯӣ бо JSON REST API мебошад, ки бо истифода аз Lucene ва дар Java навишта шудааст. Тавсифи тамоми бартариҳои ин муҳаррик дар ин ҷо дастрас аст сомонаи расмӣМинбаъд мо ба Elasticsearch ҳамчун ES муроҷиат хоҳем кард.

Чунин муҳаррикҳо барои ҷустуҷӯҳои мураккаби пойгоҳи додаҳои ҳуҷҷатҳо, ба монанди ҷустуҷӯ дар асоси морфологияи забон ё ҷустуҷӯ аз рӯи координатҳои ҷуғрофӣ истифода мешаванд.

Дар ин мақола, ман бо истифода аз мисоли индексатсияи паёмҳои блог асосҳои ES-ро меомӯзам. Ман ба шумо чӣ гуна филтр кардан, ҷудо кардан ва ҷустуҷӯ кардани ҳуҷҷатҳоро нишон медиҳам.

Барои мустақил будан аз системаи амалиётӣ, ман ҳама дархостҳои ES-и худро бо истифода аз CURL иҷро мекунам. Инчунин як плагин барои Google Chrome мавҷуд аст, ки номида мешавад scythe.

Истинодҳо ба ҳуҷҷатҳо ва дигар манбаъҳо дар тамоми матн оварда шудаанд. Истинодҳои дастрасии зуд ба ҳуҷҷатҳо дар охир оварда шудаанд. Таърифҳои истилоҳоти ношиносро дар инҷо пайдо кардан мумкин аст луғатҳо.

Насб кардани ES

Барои ин ба мо аввал Java лозим аст. Таҳиягарон тавсия Версияҳои Java навтар аз навсозии Java 8 20 ё Java 7 навсозии 55 насб кунед.

Тақсимоти ES дар ин ҷо дастрас аст вебсайти таҳиякунандаПас аз кушодани бойгонӣ, шумо бояд кор кунед bin/elasticsearch. Инчунин дастрас бастаҳо барои apt ва yum... ҳаст тасвири расмӣ барои Docker. Бештар дар бораи насб.

Пас аз насб ва оғозёбӣ, биёед функсияро тафтиш кунем:

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

curl -X GET $ES_URL

Мо посухе мегирем, ки чунин менамояд:

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

Indexing

Биёед ба 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"
}'

вокуниши сервер:

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

ES ба таври худкор сохта шудааст индекс блог ва намуди пост. Аналогияи дағалонаро метавон кашидан мумкин аст: индекс пойгоҳи додаҳост ва намуд ҷадвал дар дохили он пойгоҳи додаҳост. Ҳар як намуд схемаи худро дорад - харитасозӣ, ба мисли ҷадвали муносибатҳо. Ҳангоми индексатсия кардани ҳуҷҷат, харитасозӣ ба таври худкор тавлид мешавад:

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

Дар посухи сервер, ман арзишҳои майдони ҳуҷҷати индексатсияшударо дар шарҳҳо илова кардам:

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

Бояд қайд кард, ки ES байни як арзиш ва массиви арзишҳо фарқ намекунад. Масалан, майдони унвон танҳо унвонро дар бар мегирад, дар ҳоле ки майдони барчаспҳо массиви сатрҳоро дар бар мегирад, гарчанде ки онҳо дар харитасозӣ якхела нишон дода шудаанд.
Мо дар бораи харитасозӣ баъдтар муфассалтар сӯҳбат хоҳем кард.

Пурсишҳо

Баровардани ҳуҷҷат аз рӯи ID:

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

Дар ҷавоб калидҳои нав пайдо шуданд: _version и _sourceУмуман, ҳамаи калидҳое, ки бо оғоз мекунанд _ ба сифати ашёи хидматӣ тасниф карда мешаванд.

Калидвожа _version Версияи ҳуҷҷатро нишон медиҳад. Ин барои кор кардани механизми оптимистии қулфкунӣ зарур аст. Масалан, мо мехоҳем ҳуҷҷатро бо версияи 1 тағир диҳем. Мо ҳуҷҷати таҳриршударо пешниҳод мекунем ва нишон медиҳем, ки ин таҷдиди ҳуҷҷат бо версияи 1 аст. Агар ягон каси дигар низ ҳуҷҷатро бо версияи 1 таҳрир карда бошад ва тағиротро пеш аз мо пешниҳод карда бошад, ES тағиротҳои моро қабул намекунад, зеро он ҳуҷҷатро бо версияи 2 нигоҳ медорад.

Калидвожа _source Ҳуҷҷатеро, ки мо индексатсия кардаем, дар бар мегирад. ES ин арзишро барои амалиёти ҷустуҷӯ истифода намебарад, зеро индексҳо барои ҷустуҷӯ истифода мешаванд. Барои сарфаи ҷой, ES версияи фишурдашудаи ҳуҷҷати аслиро нигоҳ медорад. Агар ба мо танҳо ID лозим бошад, на тамоми ҳуҷҷати аслӣ, мо метавонем нигоҳдории аслиро ғайрифаъол кунем.

Агар ба мо ягон маълумоти иловагӣ лозим набошад, мо метавонем танҳо мундариҷаи _source:

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

Шумо инчунин метавонед танҳо майдонҳои муайянро интихоб кунед:

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

Биёед боз чанд паёми дигарро индексатсия кунем ва дархостҳои мураккабтарро иҷро кунем.

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

Мо паёми охиринро интихоб кардем. size микдори хуччатхои додашударо махдуд мекунад. total шумораи умумии ҳуҷҷатҳои мувофиқи дархостро нишон медиҳад. sort Натиҷа як қатор ададҳои бутунро дар бар мегирад, ки тавассути онҳо мураттабсозӣ анҷом дода мешавад. Яъне сана ба адади бутун табдил дода шудааст. Шумо метавонед бештар дар бораи ҷудокунӣ хонед хуччатхо.

Филтрҳо ва дархостҳо

ES аз версияи 2, ба ҷои ин, байни филтрҳо ва дархостҳо фарқ намекунад мафхуми контекстхо чорй карда мешавад.
Контексти дархост аз контексти филтр бо он фарқ мекунад, ки дархост _score тавлид мекунад ва кэш карда намешавад. Ман баъдтар мефаҳмонам, ки _хол чист.

Аз рӯи сана филтр кунед

Истифодаи пурсиш диапазон дар заминаи филтр:

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

Филтр аз рӯи тегҳо

Мо истифода мебарем дархости мӯҳлат Барои ҷустуҷӯи ID-и ҳуҷҷати дорои калимаи додашуда:

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

Ҷустуҷӯи матни пурра

Се ҳуҷҷати мо дар соҳаи мундариҷа инҳоро дар бар мегирад:

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

Мо истифода мебарем дархости мувофиқат Барои ҷустуҷӯи ID-и ҳуҷҷати дорои калимаи додашуда:

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

Аммо, агар мо дар майдони мундариҷа "ҳикояҳо" -ро ҷустуҷӯ кунем, мо чизе намеёбем, зеро шохис танҳо калимаҳои аслиро дар бар мегирад, на яти онҳо. Барои анҷом додани ҷустуҷӯи босифат, мо бояд анализаторро танзим кунем.

соҳа _score нишон медиҳад алоқамандӣАгар дархост дар контексти филтр иҷро карда шавад, арзиши _score ҳамеша 1 хоҳад буд, яъне маънои филтр пурра мувофиқат мекунад.

Таҳлилгарон

Таҳлилгарон барои табдил додани матни сарчашма ба маҷмӯи нишонаҳо заруранд.
Анализаторҳо аз як адад иборатанд Токенизатор ва якчанд ихтиёрӣ ТокенФилтрҳоТокенизатор метавонад пеш аз якчанд CharFiltersТокенизаторҳо сатри манбаъро ба аломатҳо, масалан, аз рӯи фосилаҳо ва аломатҳои пунктуатсия ҷудо мекунанд. TokenFilters метавонад аломатҳоро тағир диҳад, хориҷ кунад ё нав илова кунад, масалан, танҳо бо гузоштани бунёди калима, нест кардани пешвандҳо ё илова кардани синонимҳо. CharFilters сатри манбаъро комилан тағир медиҳанд, масалан, тавассути барҳам додани тегҳои HTML.

Дар ES якчанд вуҷуд доранд анализаторҳои стандартӣМасалан, анализатор Русия.

Биёед бартарӣ гирем API ва биёед бубинем, ки чӣ гуна таҳлилгарони стандартӣ ва русӣ сатри "Ҳикояҳои хандовар дар бораи гурбачахо" -ро табдил медиҳанд:

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

Анализатори стандартӣ сатрро ба фосилаҳо тақсим кард ва ҳама чизро ба ҳарфҳои хурд табдил дод, анализатори русӣ калимаҳои номуҳимро хориҷ кард, онҳоро ба ҳарфҳои хурд табдил дод ва яти калимаро тарк кард.

Биёед бубинем, ки анализатори русӣ кадом Tokenizer, TokenFilters ва CharFilters -ро истифода мебарад:

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

Биёед анализатори русии худро тавсиф кунем, ки барчаспҳои HTML-ро рахна мекунад. Мо онро пешфарз меномем, зеро ин таҳлилгари пешфарз хоҳад буд.

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

Аввалан, ҳама тегҳои HTML аз сатри манбаъ хориҷ карда мешаванд, баъд аз он аз рӯи стандарти токенизатор ба аломатҳо тақсим карда мешаванд, аломатҳои натиҷавӣ ба ҳарфҳои хурд табдил дода мешаванд, калимаҳои ночиз хориҷ карда мешаванд ва аломатҳои боқимонда решаи калима хоҳанд буд.

Эҷоди индекс

Дар боло, мо таҳлилгари пешфарзро тавсиф кардем. Он ба ҳама майдонҳои сатр татбиқ карда мешавад. Паёми мо як қатор тегҳоро дар бар мегирад, бинобар ин тегҳо инчунин аз ҷониби анализатор коркард карда мешаванд. Азбаски мо паёмҳоеро меҷӯем, ки ба теги дақиқ мувофиқат мекунанд, мо бояд таҳлилро барои майдони "тегҳо" хомӯш кунем.

Биёед индекси blog2-ро бо анализатор ва харитасозӣ созем, ки дар он таҳлили майдони барчаспҳо ғайрифаъол аст:

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

Биёед ҳамон се хабарро ба ин индекс (blog2) илова кунем. Ман ин равандро гузаред, зеро он ба илова кардани ҳуҷҷатҳо ба индекси блог монанд аст.

Ҷустуҷӯи пурраи матн бо дастгирии ифода

Биёед бо навъи дигари пурсиш шинос шавем:

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

Азбаски мо як анализатори дорои яти русиро истифода мебарем, ин дархост ҳама ҳуҷҷатҳоро бармегардонад, гарчанде ки онҳо танҳо калимаи "таърих"-ро дар бар мегиранд.

Дархост метавонад аломатҳои махсус дошта бошад, масалан:

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

Синтаксиси дархост:

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

мурожиат

PS

Агар шумо ба мақолаҳои омӯзишии шабеҳ таваҷҷӯҳ дошта бошед, барои мақолаҳои нав ғояҳо дошта бошед ё ягон пешниҳоди ҳамкорӣ дошта бошед, ман бо хушнудӣ аз шумо тавассути паёми хусусӣ ё почтаи электронӣ ба суроғаи m.kuzmin+habr@darkleaf.ru шунидам.

Манбаъ: will.com