Elasticsearch-ийн үндэс

Elasticsearch нь Java хэл дээр бичигдсэн, Lucene ашигладаг json rest api-тай хайлтын систем юм. Энэ хөдөлгүүрийн бүх давуу талуудын тайлбарыг эндээс авах боломжтой албан ёсны вэбсайт. Дараах зүйлд бид Elasticsearch-ийг ES гэж нэрлэх болно.

Баримт бичгийн мэдээллийн сангаас нарийн төвөгтэй хайлт хийхэд ижил төстэй хөдөлгүүрүүдийг ашигладаг. Жишээлбэл, хэлний морфологийг харгалзан хайх эсвэл гео координатаар хайх.

Энэ нийтлэлд би блог нийтлэлийг индексжүүлэх жишээг ашиглан ES-ийн үндсүүдийн талаар ярих болно. Би бичиг баримтыг хэрхэн шүүх, эрэмбэлэх, хайх талаар танд үзүүлэх болно.

Үйлдлийн системээс хамаарахгүйн тулд би бүх хүсэлтийг ES-д CURL ашиглан хийх болно. Мөн google chrome-д зориулсан залгаас байдаг scythe.

Текст нь баримт бичиг болон бусад эх сурвалжийн холбоосыг агуулна. Төгсгөлд нь баримт бичигт хурдан нэвтрэх холбоосууд байна. Танихгүй нэр томъёоны тодорхойлолтыг эндээс олж болно толь бичиг.

ES суулгаж байна

Үүний тулд бидэнд эхлээд 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"
}

Индексжүүлэх

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 нь нэг утга болон утгын массивыг ялгадаггүй гэдгийг тэмдэглэх нь зүйтэй. Жишээлбэл, гарчгийн талбар нь зүгээр л гарчгийг агуулж байдаг ба tags талбар нь зураглалд ижил байдлаар дүрслэгдсэн мөртүүдийн массивыг агуулдаг.
Бид дараа нь зураглалын талаар дэлгэрэнгүй ярих болно.

хүсэлтүүд

Баримт бичгийг 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"
}'

Эрэмбэлж байна

# найдем последний пост по дате публикации и извлечем поля 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. Токенизаторууд эхийн мөрийг зай, цэг таслал зэрэг тэмдэгт болгон хуваадаг. 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"
        }
      }
    }
  }
}'

Энэ индекст (blog3) ижил 2 бичлэгийг оруулъя. Би энэ үйл явцыг орхих болно, учир нь ... Энэ нь блогийн индекст бичиг баримт нэмэхтэй адил юм.

Илэрхийллийн дэмжлэг бүхий бүрэн текст хайлт

Өөр төрлийн хүсэлтийг авч үзье:

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

Хэрэв та ижил төстэй нийтлэл-хичээл сонирхож байгаа бол, шинэ нийтлэл гаргах санаатай эсвэл хамтран ажиллах санал байвал би хувийн мессеж эсвэл имэйлээр мессеж хүлээн авахдаа баяртай байх болно. [имэйлээр хамгаалагдсан].

Эх сурвалж: www.habr.com