Асосҳои 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... ҳаст тасвири расмӣ барои докер. Бештар дар бораи насб.

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

# для удобства запомним адрес в переменную
#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" }
    }
  }
}'

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

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

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

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

# 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. Токенизаторҳо сатри манбаъро ба аломатҳо, ба монанди фосилаҳо ва аломатҳои пунктуатсия мешикананд. TokenFilter метавонад аломатҳоро тағир диҳад, навҳоро нест кунад ё илова кунад, масалан, танҳо пояи калимаро тарк кунад, пешвандҳоро хориҷ кунад, синонимҳоро илова кунад. CharFilter - тамоми сатри манбаъро тағир медиҳад, масалан, тегҳои 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 аз сатри манбаъ хориҷ карда мешаванд, пас стандарти токенизатор онро ба аломатҳо тақсим мекунад, аломатҳои натиҷавӣ ба ҳарфҳои хурд мегузаранд, калимаҳои ночиз хориҷ карда мешаванд ва аломатҳои боқимонда пояи калима боқӣ мемонанд.

Эҷоди Индекс

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

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

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

Биёед ҳамон 3 паёмро ба ин индекс (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

Агар шумо ба ин мақолаҳо-дарсҳо таваҷҷӯҳ дошта бошед, идеяҳо барои мақолаҳои нав дошта бошед ё пешниҳоди ҳамкорӣ дошта бошед, ман бо хушнудӣ дар паёми шахсӣ ё почтаи электронӣ паём мегирам. [почтаи электронӣ ҳифз карда шудааст].

Манбаъ: will.com