Elasticsearch негіздері

Elasticsearch — Lucene қолданатын және Java тілінде жазылған JSON REST API бар іздеу жүйесі. Бұл қозғалтқыштың барлық артықшылықтарының сипаттамасы мына жерден қол жетімді ресми сайтЕнді біз Elasticsearch-ті ES деп атайтын боламыз.

Мұндай қозғалтқыштар тіл морфологиясы негізінде іздеу немесе географиялық координаттар бойынша іздеу сияқты күрделі құжат деректер базасын іздеу үшін қолданылады.

Бұл мақалада мен блог жазбаларын индекстеу мысалында ES негіздерін қарастырамын. Мен құжаттарды сүзу, сұрыптау және іздеу жолын көрсетемін.

Операциялық жүйеден тәуелсіз болу үшін мен барлық ES сұрауларымды CURL арқылы жасаймын. Сондай-ақ Google Chrome үшін плагин деп аталатын плагин бар сезім.

Құжаттамаға және басқа көздерге сілтемелер бүкіл мәтінде берілген. Құжаттамаға жылдам қол жеткізу сілтемелері соңында берілген. Таныс емес терминдердің анықтамаларын мына жерден табуға болады глоссарийлер.

ES орнату

Ол үшін алдымен Java керек. Әзірлеушілер ұсынамыз Java 8 жаңарту 20 немесе Java 7 жаңарту 55 қарағанда жаңарақ Java нұсқаларын орнатыңыз.

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

Индекстеу

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 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 бастапқы құжаттың қысылған нұсқасын сақтайды. Бүкіл түпнұсқа құжат емес, тек идентификатор қажет болса, түпнұсқаны сақтауды өшіре аламыз.

Егер бізге қосымша ақпарат қажет болмаса, біз тек _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 Шығаруда сұрыптау орындалатын бүтін сандар массиві бар. Яғни, күн бүтін санға түрлендірілді. Сұрыптау туралы көбірек оқи аласыз құжаттама.

Сүзгілер мен сұраулар

Оның орнына 2-нұсқадағы ES сүзгілер мен сұрауларды ажыратпайды контекстер ұғымы енгізіледі.
Сұрау мәтінмәні сүзгі мәтінмәнінен ерекшеленеді, себебі сұрау _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Токенизаторлар бастапқы жолды таңбалауыштарға бөледі, мысалы, бос орындар және тыныс белгілері арқылы. 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 тегтері бастапқы жолдан жойылады, содан кейін ол токенизатор стандарты бойынша токендерге бөлінеді, нәтижесінде алынған таңбалауыштар кіші әріптерге түрлендіріледі, елеусіз сөздер жойылады, ал қалған таңбалауыштар сөздің түбірі болады.

Индекс құру

Жоғарыда біз әдепкі анализаторды сипаттадық. Ол барлық жол өрістеріне қолданылады. Біздің хабарламада тегтер массиві бар, сондықтан тегтер анализатормен де өңделеді. Біз дәл тегке сәйкес келетін жазбаларды іздеп жатқандықтан, «тегтер» өрісі үшін талдауды өшіруіміз керек.

Тегтер өрісін талдау өшірілген анализатор және салыстыру арқылы блог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"
        }
      }
    }
  }
}'

Осы индекске (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 электрондық поштасы арқылы хабарлауға қуаныштымын.

Ақпарат көзі: www.habr.com