Elasticsearch یک موتور جستجو با Json Rest API است که از Lucene استفاده می کند و بر روی جاوا نوشته شده است. شرح تمام مزایای این موتور در دسترس است
موتورهای مشابه برای جستجوهای پیچیده در پایگاه داده اسناد استفاده می شود. به عنوان مثال، جستجو با در نظر گرفتن مورفولوژی زبان یا جستجو بر اساس مختصات جغرافیایی.
در این مقاله به شما در مورد اصول ES در مثال نمایه سازی پست های وبلاگ خواهم گفت. من نحوه فیلتر کردن، مرتب سازی و جستجوی اسناد را نشان خواهم داد.
برای اینکه به سیستم عامل وابسته نباشم، تمام درخواست ها را با استفاده از Curl به ES می کنم. همچنین یک افزونه برای گوگل کروم به نام وجود دارد
پیوندهایی به اسناد و سایر منابع روی متن قرار داده شده است. در پایان پیوندهایی برای دسترسی سریع به اسناد وجود دارد. تعاریف اصطلاحات ناآشنا را می توان در اینجا خواند
نصب ES
برای این کار ابتدا به جاوا نیاز داریم. توسعه دهندگان
توزیع ES در دسترس است bin/elasticsearch
. همچنین موجود است
پس از نصب و راه اندازی، اجازه دهید عملکرد را بررسی کنیم:
# для удобства запомним адрес в переменную
#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 هیچ تفاوتی بین یک مقدار واحد و یک آرایه از معانی ایجاد نمی کند. به عنوان مثال، فیلد Title فقط حاوی یک عنوان است و فیلد Tags آرایه ای از خطوط است، اگرچه آنها در Mapping به همین شکل ارائه می شوند.
بعداً در مورد نقشه برداری بیشتر صحبت خواهیم کرد.
درخواست ها
بازیابی یک سند با شناسه آن:
# извлечем документ с 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 یک سند منبع فشرده را ذخیره می کند. اگر فقط به شناسه نیاز داشته باشیم و نه کل سند منبع، میتوانیم ذخیرهسازی منبع را غیرفعال کنیم.
اگر به اطلاعات اضافی نیاز نداریم، فقط می توانید مطالب را دریافت کنید _ منبع:
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 را ایجاد می کند و در حافظه پنهان ذخیره نمی شود. _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
نشان می دهد
آنالایزرها
آنالایزرها از یک عدد تشکیل شده اند
ES چندین دارد
بهره ببریم
# используем анализатор 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
} ]
}
تحلیلگر استاندارد رشته را با فاصله تقسیم کرد و همه چیز را به حروف کوچک تبدیل کرد، تحلیلگر روسی کلمات بی اهمیت را حذف کرد، آن را به حروف کوچک تبدیل کرد و ریشه کلمات را رها کرد.
بیایید ببینیم از چه توکنایزر، فیلتر توکن، 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 از خط اصلی حذف میشوند، سپس Tokenizer Standard به توکنها تقسیم میشود، توکنهای بهدستآمده به ثبت پایینتر میروند، کلمات بیاهمیت حذف میشوند و اساس کلمه از توکنهای باقیمانده باقی میماند. .
ایجاد یک شاخص
در بالا، ما تحلیلگر پیش فرض را توضیح دادیم. برای تمام فیلدهای رشته اعمال خواهد شد. پست ما شامل آرایه ای از برچسب ها است، به ترتیب، برچسب ها نیز توسط تحلیلگر پردازش می شوند. زیرا ما به دنبال پست هایی با انطباق دقیق با برچسب هستیم، سپس لازم است تجزیه و تحلیل را برای قسمت TAGS خاموش کنیم.
نمایه BLOG2 را با یک تحلیلگر و یک نقشه ایجاد کنید، که در آن تجزیه و تحلیل فیلد TAGS غیرفعال است:
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). من این روند را حذف می کنم، زیرا این شبیه به افزودن اسناد به فهرست BLOG است.
جستجوی متن کامل با پشتیبانی از عبارات
بیایید با نوع دیگری از درخواست آشنا شویم:
# найдем документы, в которых встречается слово 'истории'
# 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