{"id":312592,"date":"2020-11-05T15:00:35","date_gmt":"2020-11-05T15:00:35","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=312592"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=312592","title":{"rendered":"\u0414\u0435\u043b\u0430\u0435\u043c \u043f\u043e\u0438\u0441\u043a \u0432 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0441 \u043d\u0443\u043b\u044f"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u00ab<a href=\"https:\/\/habr.com\/ru\/post\/444446\/\">\u0414\u0435\u043b\u0430\u0435\u043c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043d\u0443\u043b\u044f<\/a>\u00bb \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u043b \u0432 \u043e\u0431\u0449\u0438\u0445 \u0447\u0435\u0440\u0442\u0430\u0445, \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u044b\u0441\u043e\u043a\u043e\u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0445 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0438 \u0441\u043e\u0431\u0440\u0430\u043b \u0434\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0442\u0430\u043a\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043d\u0430 \u0441\u0442\u0435\u043a\u0435 \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u0435\u0434\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0438 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439 \u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432. \u041c\u044b \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b\u0438 single page application \u0441 server side rendering, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0449\u0435\u0435 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u043d\u0435\u043a\u0438\u0445 \u00ab\u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a\u00bb, \u043d\u0430\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0432 Markdown, \u0438 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438.<\/p>\n<p>  \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0437\u0430\u0442\u0440\u043e\u043d\u0443 \u0447\u0443\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0443\u044e (\u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u043c\u043d\u0435, \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e\u0438\u0441\u043a\u0430) \u0442\u0435\u043c\u0443: \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u043e\u0438\u0441\u043a. \u041c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043d\u0430\u0448 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043d\u044b\u0439 \u0440\u0430\u0439 \u043d\u043e\u0434\u0443 Elasticsearch, \u043d\u0430\u0443\u0447\u0438\u043c\u0441\u044f \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u043d\u0434\u0435\u043a\u0441 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0443, \u0432\u0437\u044f\u0432 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u044f\u0442\u0438 \u0442\u044b\u0441\u044f\u0447 \u0444\u0438\u043b\u044c\u043c\u043e\u0432 \u0438\u0437 <a href=\"https:\/\/www.kaggle.com\/tmdb\/tmdb-movie-metadata\">TMDB 5000 Movie Dataset<\/a>. \u0422\u0430\u043a\u0436\u0435 \u043c\u044b \u043d\u0430\u0443\u0447\u0438\u043c\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u044b \u0438 \u043a\u043e\u043f\u043d\u0451\u043c \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0436\u043a\u043e \u0432 \u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/vr\/iq\/p-\/vriqp-qiz6mnrb_9tlp0aynkck4.png\"><\/div>\n<p>  <a name=\"habracut\"><\/a>  <\/p>\n<h2>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430: Elasticsearch<\/h2>\n<p>  <a href=\"https:\/\/www.elastic.co\/elasticsearch\/\">Elasticsearch<\/a> \u2014 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u0443\u043c\u0435\u044e\u0449\u0435\u0435 \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u044b \u0438, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0435 \u0438\u043c\u0435\u043d\u043d\u043e \u043a\u0430\u043a \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0434\u0432\u0438\u0436\u043e\u043a. Elasticsearch \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u043a \u0434\u0432\u0438\u0436\u043a\u0443 <a href=\"https:\/\/lucene.apache.org\/\">Apache Lucene<\/a>, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043d \u043e\u0441\u043d\u043e\u0432\u0430\u043d, \u0448\u0430\u0440\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435, \u0440\u0435\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044e, \u0443\u0434\u043e\u0431\u043d\u044b\u0439 JSON API \u0438 \u0435\u0449\u0451 \u043c\u0438\u043b\u043b\u0438\u043e\u043d \u043c\u0435\u043b\u043e\u0447\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0435\u0433\u043e \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u0441\u0430\u043c\u044b\u0445 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430. <\/p>\n<p>  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043e\u0434\u043d\u0443 \u043d\u043e\u0434\u0443 Elasticsearch \u0432 \u043d\u0430\u0448 <code>docker-compose.yml<\/code>:<\/p>\n<pre><code class=\"plaintext\">services:   ...   elasticsearch:     image: &quot;elasticsearch:7.5.1&quot;     environment:       - discovery.type=single-node     ports:       - &quot;9200:9200&quot;   ... <\/code><\/pre>\n<p>  \u041f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u044f <code>discovery.type=single-node<\/code> \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 Elasticsearch, \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c\u0441\u044f \u043a \u0440\u0430\u0431\u043e\u0442\u0435 \u0432 \u043e\u0434\u0438\u043d\u043e\u0447\u043a\u0443, \u0430 \u043d\u0435 \u0438\u0441\u043a\u0430\u0442\u044c \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u043e\u0434\u044b \u0438 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0442\u044c\u0441\u044f \u0441 \u043d\u0438\u043c\u0438 \u0432 \u043a\u043b\u0430\u0441\u0442\u0435\u0440 (\u0442\u0430\u043a\u043e\u0432\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e).<\/p>\n<p>  \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043c\u044b \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c 9200 \u043f\u043e\u0440\u0442 \u043d\u0430\u0440\u0443\u0436\u0443, \u0445\u043e\u0442\u044f \u043d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0445\u043e\u0434\u0438\u0442 \u0432 \u043d\u0435\u0433\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u0441\u0435\u0442\u0438, \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0435\u043c\u043e\u0439 docker-compose. \u042d\u0442\u043e \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0438: \u0442\u0430\u043a \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0432 Elasticsearch \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0438\u0437 \u0442\u0435\u0440\u043c\u0438\u043d\u0430\u043b\u0430 (\u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u043d\u0435 \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u0435\u043c \u0431\u043e\u043b\u0435\u0435 \u0443\u043c\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u2014 \u043e\u0431 \u044d\u0442\u043e\u043c \u043d\u0438\u0436\u0435).<\/p>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442 Elasticsearch \u0432 \u043d\u0430\u0448 \u0432\u0430\u0439\u0440\u0438\u043d\u0433 <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/commit\/9faabde36a9e66a632b4d596b378c3a7cabcaae1\">\u043d\u0435 \u0441\u043e\u0441\u0442\u0430\u0432\u0438\u0442 \u0442\u0440\u0443\u0434\u0430<\/a> \u2014 \u0431\u043b\u0430\u0433\u043e, Elastic \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 <a href=\"https:\/\/github.com\/elastic\/elasticsearch-py\">\u043c\u0438\u043d\u0438\u043c\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u044b\u0439 Python-\u043a\u043b\u0438\u0435\u043d\u0442<\/a>.<\/p>\n<h2>\u0418\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u044f<\/h2>\n<p>  \u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u043f\u043e\u043b\u043e\u0436\u0438\u043b\u0438 \u043d\u0430\u0448\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u2014 \u00ab\u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438\u00bb \u0432 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u044e MongoDB. \u0418\u0437 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u043c\u044b \u0443\u043c\u0435\u0435\u043c \u0431\u044b\u0441\u0442\u0440\u043e \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u0438\u0445 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043f\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e MongoDB \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b\u0430 \u0434\u043b\u044f \u043d\u0430\u0441 <i>\u043f\u0440\u044f\u043c\u043e\u0439 \u0438\u043d\u0434\u0435\u043a\u0441<\/i> \u2014 \u0432 \u043d\u0435\u0439 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e <a href=\"https:\/\/docs.mongodb.com\/manual\/indexes\/#b-tree\">\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f<\/a> <a href=\"https:\/\/ru.wikipedia.org\/wiki\/B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE\">B-\u0434\u0435\u0440\u0435\u0432\u044c\u044f<\/a>.<\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0436\u0435 \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u043c\u0438 \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u2014 \u043f\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c\u0443 (\u0438\u043b\u0438 \u0435\u0433\u043e \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430\u043c) \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u044b \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a. \u0421\u0442\u0430\u043b\u043e \u0431\u044b\u0442\u044c, \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d <i>\u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441<\/i>. \u0414\u043b\u044f \u043d\u0435\u0433\u043e-\u0442\u043e \u043d\u0430\u043c \u0438 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u0441\u044f Elasticsearch!<\/p>\n<p>  \u041e\u0431\u0449\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u043e\u0431\u044b\u0447\u043d\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043a\u0430\u043a-\u0442\u043e \u0442\u0430\u043a.   <\/p>\n<ol>\n<li>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043d\u043e\u0432\u044b\u0439 \u043f\u0443\u0441\u0442\u043e\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u0441 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u043c \u0438\u043c\u0435\u043d\u0435\u043c, \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e \u043a\u0430\u043a \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e.<\/li>\n<li>\u041e\u0431\u0445\u043e\u0434\u0438\u043c \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0432 \u0431\u0430\u0437\u0435 \u0438 \u043a\u043b\u0430\u0434\u0451\u043c \u0438\u0445 \u0432 \u043d\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441.<\/li>\n<li>\u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430\u0447\u0430\u043b\u0438 \u0445\u043e\u0434\u0438\u0442\u044c \u0432 \u043d\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441.<\/li>\n<li>\u0423\u0434\u0430\u043b\u044f\u0435\u043c \u0441\u0442\u0430\u0440\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441. \u0422\u0443\u0442 \u043f\u043e \u0436\u0435\u043b\u0430\u043d\u0438\u044e \u2014 \u0432\u044b \u0432\u043f\u043e\u043b\u043d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0437\u0430\u0445\u043e\u0442\u0435\u0442\u044c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u0432, \u0447\u0442\u043e\u0431\u044b, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443\u0434\u043e\u0431\u043d\u0435\u0435 \u0431\u044b\u043b\u043e \u043e\u0442\u043b\u0430\u0436\u0438\u0432\u0430\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.<\/li>\n<\/ol>\n<p>  \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0441\u043a\u0435\u043b\u0435\u0442 \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u0442\u043e\u0440\u0430 \u0438 \u043f\u043e\u0442\u043e\u043c \u0440\u0430\u0437\u0431\u0435\u0440\u0451\u043c\u0441\u044f \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0441 \u043a\u0430\u0436\u0434\u044b\u043c \u0448\u0430\u0433\u043e\u043c.<\/p>\n<pre><code class=\"python\">import datetime  from elasticsearch import Elasticsearch, NotFoundError  from backend.storage.card import Card, CardDAO   class Indexer(object):      def __init__(self, elasticsearch_client: Elasticsearch, card_dao: CardDAO, cards_index_alias: str):         self.elasticsearch_client = elasticsearch_client         self.card_dao = card_dao         self.cards_index_alias = cards_index_alias      def build_new_cards_index(self) -&gt; str:         # \u041f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0430.         # \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u0440\u0438\u0434\u0443\u043c\u044b\u0432\u0430\u0435\u043c \u0434\u043b\u044f \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435.         index_name = &quot;cards-&quot; + datetime.datetime.now().strftime(&quot;%Y-%m-%d-%H-%M-%S&quot;)          # \u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u0443\u0441\u0442\u043e\u0439 \u0438\u043d\u0434\u0435\u043a\u0441.          # \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u0443\u043a\u0430\u0436\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438 \u043e\u043f\u0438\u0448\u0435\u043c \u0441\u0445\u0435\u043c\u0443 \u0434\u0430\u043d\u043d\u044b\u0445.         self.create_empty_cards_index(index_name)          # \u041a\u043b\u0430\u0434\u0451\u043c \u0432 \u0438\u043d\u0434\u0435\u043a\u0441 \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043e\u0434\u043d\u0443 \u0437\u0430 \u0434\u0440\u0443\u0433\u043e\u0439.         # \u0412 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0432\u044b \u043e\u0447\u0435\u043d\u044c \u0441\u043a\u043e\u0440\u043e \u0437\u0430\u0445\u043e\u0442\u0438\u0442\u0435          # \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u044d\u0442\u043e \u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u0432 \u043f\u0430\u043a\u0435\u0442\u043d\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435.         for card in self.card_dao.get_all():             self.put_card_into_index(card, index_name)         return index_name      def create_empty_cards_index(self, index_name):         ...       def put_card_into_index(self, card: Card, index_name: str):         ...      def switch_current_cards_index(self, new_index_name: str):         ...  <\/code><\/pre>\n<h2>\u0418\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u044f: \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0438\u043d\u0434\u0435\u043a\u0441<\/h2>\n<p>  \u0418\u043d\u0434\u0435\u043a\u0441 \u0432 Elasticsearch \u0441\u043e\u0437\u0434\u0430\u0451\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u043c PUT-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0432 <code>\/\u0438\u043c\u044f-\u0438\u043d\u0434\u0435\u043a\u0441\u0430<\/code> \u0438\u043b\u0438, \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f Python-\u043a\u043b\u0438\u0435\u043d\u0442\u0430 (\u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435), \u0432\u044b\u0437\u043e\u0432\u043e\u043c<\/p>\n<pre><code class=\"python\">elasticsearch_client.indices.create(index_name, {     ... }) <\/code><\/pre>\n<p>  \u0422\u0435\u043b\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0442\u0440\u0438 \u043f\u043e\u043b\u044f.<\/p>\n<ul>\n<li>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0430\u043b\u0438\u0430\u0441\u043e\u0432 (<code>&quot;aliases&quot;: ...<\/code>). \u0421\u0438\u0441\u0442\u0435\u043c\u0430 \u0430\u043b\u0438\u0430\u0441\u043e\u0432 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0437\u043d\u0430\u043d\u0438\u0435 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a\u043e\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u0441\u0435\u0439\u0447\u0430\u0441 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439, \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 Elasticsearch; \u043c\u044b \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043f\u0440\u043e \u043d\u0435\u0451 \u043d\u0438\u0436\u0435.<\/li>\n<li>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 (<code>&quot;settings&quot;: ...<\/code>). \u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438 \u0434\u044f\u0434\u044f\u043c\u0438 \u0441 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u043c \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u043e\u043c, \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0441\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0434\u0435\u0441\u044c \u0440\u0435\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044e, \u0448\u0430\u0440\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0440\u0430\u0434\u043e\u0441\u0442\u0438 SRE.<\/li>\n<li>\u0421\u0445\u0435\u043c\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 (<code>&quot;mappings&quot;: ...<\/code>). \u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0443\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u043a\u0430\u043a\u0438\u0435 \u043f\u043e\u043b\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0434\u043b\u044f \u043a\u0430\u043a\u0438\u0445 \u0438\u0437 \u044d\u0442\u0438\u0445 \u043f\u043e\u043b\u0435\u0439 \u043d\u0443\u0436\u043d\u044b \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u044b, \u043f\u043e \u043a\u0430\u043a\u0438\u043c \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u043d\u044b \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u0438 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435.<\/li>\n<\/ul>\n<p>  \u0421\u0435\u0439\u0447\u0430\u0441 \u043d\u0430\u0441 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0445\u0435\u043c\u0430, \u0438 \u0443 \u043d\u0430\u0441 \u043e\u043d\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u0430\u044f:<\/p>\n<pre><code class=\"json\">{     &quot;mappings&quot;: {         &quot;properties&quot;: {             &quot;name&quot;: {                 &quot;type&quot;: &quot;text&quot;,                 &quot;analyzer&quot;: &quot;english&quot;             },             &quot;text&quot;: {                 &quot;type&quot;: &quot;text&quot;,                 &quot;analyzer&quot;: &quot;english&quot;             },             &quot;tags&quot;: {                 &quot;type&quot;: &quot;keyword&quot;,                 &quot;fields&quot;: {                     &quot;text&quot;: {                         &quot;type&quot;: &quot;text&quot;,                         &quot;analyzer&quot;: &quot;english&quot;                     }                 }             }         }     } } <\/code><\/pre>\n<p>  \u041c\u044b \u043f\u043e\u043c\u0435\u0442\u0438\u043b\u0438 \u043f\u043e\u043b\u044f <code>name<\/code> \u0438 <code>text<\/code> \u043a\u0430\u043a \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0435 \u043d\u0430 \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u043c \u044f\u0437\u044b\u043a\u0435. \u0410\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 \u2014 \u044d\u0442\u043e \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0432 Elasticsearch, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0442\u0435\u043a\u0441\u0442 \u043f\u0435\u0440\u0435\u0434 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435\u043c \u0432 \u0438\u043d\u0434\u0435\u043a\u0441. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 <code>english<\/code> \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u0442\u0435\u043a\u0441\u0442 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u0431\u0438\u0442 \u043d\u0430 \u0442\u043e\u043a\u0435\u043d\u044b \u043f\u043e \u0433\u0440\u0430\u043d\u0438\u0446\u0430\u043c \u0441\u043b\u043e\u0432 (<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/analysis-standard-tokenizer.html\">\u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438<\/a>), \u043f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0442\u043e\u043a\u0435\u043d\u044b \u0431\u0443\u0434\u0443\u0442 \u043b\u0435\u043c\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u043f\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c \u0430\u043d\u0433\u043b\u0438\u0439\u0441\u043a\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043b\u043e\u0432\u043e <code>trees<\/code> \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u0441\u044f \u0434\u043e <code>tree<\/code>), \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043e\u0431\u0449\u0438\u0435 \u043b\u0435\u043c\u043c\u044b (\u0432\u0440\u043e\u0434\u0435 <code>the<\/code>) \u0431\u0443\u0434\u0443\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u044b \u0438 \u043e\u0441\u0442\u0430\u0432\u0448\u0438\u0435\u0441\u044f \u043b\u0435\u043c\u043c\u044b \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u0432 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441.<\/p>\n<p>  \u0421 \u043f\u043e\u043b\u0435\u043c <code>tags<\/code> \u0447\u0443\u0442\u044c-\u0447\u0443\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0435\u0435. \u0422\u0438\u043f <code>keyword<\/code> \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442, \u0447\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u2014 \u043d\u0435\u043a\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0435 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435 \u043d\u0430\u0434\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u043e\u043c; \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043f\u043e \u0438\u0445 \u00ab\u0441\u044b\u0440\u044b\u043c\u00bb \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c \u2014 \u0431\u0435\u0437 \u0442\u043e\u043a\u0435\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043b\u0435\u043c\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438. \u0417\u0430\u0442\u043e Elasticsearch \u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0447\u0442\u043e\u0431\u044b \u043f\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0447\u0442\u043e\u0431\u044b \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0441 \u043f\u043e\u0438\u0441\u043a\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0443\u0437\u043d\u0430\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0442\u0435\u0433\u0438 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u043b\u0438\u0441\u044c \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0445, \u0443\u0434\u043e\u0432\u043b\u0435\u0442\u0432\u043e\u0440\u044f\u044e\u0449\u0438\u0445 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0443, \u0438 \u0432 \u043a\u0430\u043a\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435). \u042d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u043e \u0434\u043b\u044f \u043f\u043e\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e \u0441\u0443\u0442\u0438 enum; \u043c\u044b \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u0441\u044f \u044d\u0442\u043e\u0439 \u0444\u0438\u0447\u0435\u0439, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043b\u0451\u0432\u044b\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u044b.<\/p>\n<p>  \u041d\u043e \u0447\u0442\u043e\u0431\u044b \u043f\u043e \u0442\u0435\u043a\u0441\u0442\u0443 \u0442\u0435\u0433\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0438\u0441\u043a\u0430\u0442\u044c \u0438 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u043c \u043f\u043e\u0438\u0441\u043a\u043e\u043c \u0442\u043e\u0436\u0435, \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a \u043d\u0435\u043c\u0443 \u043f\u043e\u0434\u043f\u043e\u043b\u0435 <code>&quot;text&quot;<\/code>, \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0435 \u043f\u043e \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0438 \u0441 <code>name<\/code> \u0438 <code>text<\/code> \u0432\u044b\u0448\u0435 \u2014 \u043f\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443 \u044d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e Elasticsearch \u0432\u043e \u0432\u0441\u0435\u0445 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0449\u0438\u0445 \u0435\u043c\u0443 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0445 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0435\u0449\u0451 \u043e\u0434\u043d\u043e \u00ab\u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0435\u00bb \u043f\u043e\u043b\u0435 \u043f\u043e\u0434 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c <code>tags.text<\/code>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 <code>tags<\/code>, \u043d\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u043f\u043e \u0434\u0440\u0443\u0433\u0438\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c.<\/p>\n<h2>\u0418\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u044f: \u043d\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0438\u043d\u0434\u0435\u043a\u0441<\/h2>\n<p>  \u0414\u043b\u044f \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c PUT-\u0437\u0430\u043f\u0440\u043e\u0441 \u0432 <code>\/\u0438\u043c\u044f-\u0438\u043d\u0434\u0435\u043a\u0441\u0430\/_create\/id-\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430<\/code> \u0438\u043b\u0438, \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 Python-\u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434. \u041d\u0430\u0448\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\">    def put_card_into_index(self, card: Card, index_name: str):         self.elasticsearch_client.create(index_name, card.id, {             &quot;name&quot;: card.name,             &quot;text&quot;: card.markdown,             &quot;tags&quot;: card.tags,         }) <\/code><\/pre>\n<p>  \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u043f\u043e\u043b\u0435 <code>tags<\/code>. \u0425\u043e\u0442\u044f \u043c\u044b \u043e\u043f\u0438\u0441\u0430\u043b\u0438 \u0435\u0433\u043e \u043a\u0430\u043a \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0435\u0435 keyword, \u043c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u0435 \u043e\u0434\u043d\u0443 \u0441\u0442\u0440\u043e\u043a\u0443, \u0430 \u0441\u043f\u0438\u0441\u043e\u043a \u0441\u0442\u0440\u043e\u043a. Elasticsearch \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u0430\u043a\u043e\u0435; \u043d\u0430\u0448 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u043f\u043e \u043b\u044e\u0431\u043e\u043c\u0443 \u0438\u0437 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439.<\/p>\n<h2>\u0418\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u044f: \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0438\u043d\u0434\u0435\u043a\u0441<\/h2>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0438\u0441\u043a, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0437\u043d\u0430\u0442\u044c \u0438\u043c\u044f \u0441\u0430\u043c\u043e\u0433\u043e \u0441\u0432\u0435\u0436\u0435\u0433\u043e \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0434\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u043e\u0433\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0430. \u041c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0430\u043b\u0438\u0430\u0441\u043e\u0432 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u044d\u0442\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 Elasticsearch.<\/p>\n<p>  \u0410\u043b\u0438\u0430\u0441 \u2014 \u044d\u0442\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c \u043d\u0430 \u043d\u043e\u043b\u044c \u0438\u043b\u0438 \u0431\u043e\u043b\u0435\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u0432. API Elasticsearch \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043c\u044f \u0430\u043b\u0438\u0430\u0441\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u0438\u043c\u0435\u043d\u0438 \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u043f\u0440\u0438 \u043f\u043e\u0438\u0441\u043a\u0435 (POST <code>\/\u0438\u043c\u044f-\u0430\u043b\u0438\u0430\u0441\u0430\/_search<\/code> \u0432\u043c\u0435\u0441\u0442\u043e POST <code>\/\u0438\u043c\u044f-\u0438\u043d\u0434\u0435\u043a\u0441\u0430\/_search<\/code>); \u0432 \u0442\u0430\u043a\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 Elasticsearch \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c \u043f\u043e \u0432\u0441\u0435\u043c \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u043c, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0430\u043b\u0438\u0430\u0441.<\/p>\n<p>  \u041c\u044b \u0437\u0430\u0432\u0435\u0434\u0451\u043c \u0430\u043b\u0438\u0430\u0441 \u043f\u043e\u0434 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c <code>cards<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441. \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043d\u0430 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\">    def switch_current_cards_index(self, new_index_name: str):         try:             # \u041d\u0443\u0436\u043d\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0441\u0442\u0430\u0440\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441, \u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u0435\u0441\u0442\u044c.             remove_actions = [                 {                     &quot;remove&quot;: {                         &quot;index&quot;: index_name,                          &quot;alias&quot;: self.cards_index_alias,                     }                 }                 for index_name in self.elasticsearch_client.indices.get_alias(name=self.cards_index_alias)             ]         except NotFoundError:             # \u041e\u0433\u043e, \u0441\u0442\u0430\u0440\u043e\u0433\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0430-\u0442\u043e \u0438 \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u0432\u043e\u0432\u0441\u0435.             # \u041d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043c\u044b \u0432\u043f\u0435\u0440\u0432\u044b\u0435 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043b\u0438 \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u044e.             remove_actions = []          # \u041e\u0434\u043d\u0438\u043c \u043c\u0430\u0445\u043e\u043c \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0441\u0442\u0430\u0440\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441          # \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043d\u043e\u0432\u044b\u0439.         self.elasticsearch_client.indices.update_aliases({             &quot;actions&quot;: remove_actions + [{                 &quot;add&quot;: {                     &quot;index&quot;: new_index_name,                      &quot;alias&quot;: self.cards_index_alias,                 }             }]         }) <\/code><\/pre>\n<p>  \u042f \u043d\u0435 \u0441\u0442\u0430\u043d\u0443 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 alias API; \u0432\u0441\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/indices-aliases.html\">\u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438<\/a>.<\/p>\n<p>  \u0417\u0434\u0435\u0441\u044c \u043d\u0430\u0434\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0435\u043c\u0430\u0440\u043a\u0443, \u0447\u0442\u043e \u0432 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u044b\u0441\u043e\u043a\u043e\u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u043e\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u0442\u0430\u043a\u043e\u0435 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u043e\u043b\u0435\u0437\u043d\u0435\u043d\u043d\u044b\u043c \u0438 \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0441\u043c\u044b\u0441\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0433\u0440\u0435\u0432 \u2014 \u043d\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u043f\u0443\u043b\u043e\u043c \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<\/p>\n<p>  \u0412\u0435\u0441\u044c \u043a\u043e\u0434, \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0439 \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u044e, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/commit\/ae64a516bd2e6512d361cdb9667ccafa670ea6a7\">\u0432 \u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043c\u0438\u0442\u0435<\/a>.<\/p>\n<h2>\u0418\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u044f: \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043d\u0442<\/h2>\n<p>  \u0414\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 <a href=\"https:\/\/www.kaggle.com\/tmdb\/tmdb-movie-metadata\">TMDB 5000 Movie Dataset<\/a>. \u0427\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0441 \u0430\u0432\u0442\u043e\u0440\u0441\u043a\u0438\u043c\u0438 \u043f\u0440\u0430\u0432\u0430\u043c\u0438, \u044f \u043b\u0438\u0448\u044c \u043f\u0440\u0438\u0432\u043e\u0436\u0443 <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/commit\/2f8e97a9ec900db2370132d83a0311f237cbc054\">\u043a\u043e\u0434 \u0443\u0442\u0438\u043b\u0438\u0442\u044b<\/a>, \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u044e\u0449\u0435\u0439 \u0438\u0445 \u0438\u0437 CSV-\u0444\u0430\u0439\u043b\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0432\u0430\u043c \u0441\u043a\u0430\u0447\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0441 \u0441\u0430\u0439\u0442\u0430 Kaggle. \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443<\/p>\n<pre><code class=\"bash\">docker-compose exec -T backend python -m tools.add_movies &lt; ~\/Downloads\/tmdb-movie-metadata\/tmdb_5000_movies.csv <\/code><\/pre>\n<p>  , \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u044f\u0442\u044c \u0442\u044b\u0441\u044f\u0447 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a, \u043f\u043e\u0441\u0432\u044f\u0449\u0451\u043d\u043d\u044b\u0445 \u043a\u0438\u043d\u043e, \u0438 \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <\/p>\n<pre><code class=\"bash\">docker-compose exec backend python -m tools.build_index <\/code><\/pre>\n<p>  , \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u043d\u0434\u0435\u043a\u0441. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043d\u0435 \u0441\u0442\u0440\u043e\u0438\u0442 \u0438\u043d\u0434\u0435\u043a\u0441, \u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u0432\u0438\u0442 \u0437\u0430\u0434\u0430\u0447\u0443 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0437\u0430\u0434\u0430\u0447, \u043f\u043e\u0441\u043b\u0435 \u0447\u0435\u0433\u043e \u043e\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0441\u044f \u043d\u0430 \u0432\u043e\u0440\u043a\u0435\u0440\u0435 \u2014 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u0432 <a href=\"https:\/\/habr.com\/ru\/post\/444446\/\">\u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435<\/a>. <code>docker-compose logs worker<\/code> \u043f\u043e\u043a\u0430\u0436\u0443\u0442 \u0432\u0430\u043c, \u043a\u0430\u043a \u0432\u043e\u0440\u043a\u0435\u0440 \u0441\u0442\u0430\u0440\u0430\u043b\u0441\u044f!<\/p>\n<p>  \u041f\u0440\u0435\u0436\u0434\u0435, \u0447\u0435\u043c \u043c\u044b \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043f\u043e\u0438\u0441\u043a\u0443, \u043d\u0430\u043c \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0441\u0432\u043e\u0438\u043c\u0438 \u0433\u043b\u0430\u0437\u0430\u043c\u0438 \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u0437\u0430\u043f\u0438\u0441\u0430\u043b\u043e\u0441\u044c \u043b\u0438 \u0447\u0442\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u0432 Elasticsearch, \u0438 \u0435\u0441\u043b\u0438 \u0434\u0430, \u0442\u043e \u043a\u0430\u043a \u043e\u043d\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442!<\/p>\n<p>  \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u044f\u043c\u043e\u0439 \u0438 \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u2014 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f HTTP API Elasticsearch. \u0421\u043f\u0435\u0440\u0432\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c, \u043a\u0443\u0434\u0430 \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0430\u043b\u0438\u0430\u0441:<\/p>\n<pre><code class=\"bash\">$ curl -s localhost:9200\/_cat\/aliases cards                cards-2020-09-20-16-14-18 - - - - <\/code><\/pre>\n<p>  \u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u0438\u043d\u0434\u0435\u043a\u0441 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442! \u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u0435\u0433\u043e \u043f\u0440\u0438\u0441\u0442\u0430\u043b\u044c\u043d\u043e:<\/p>\n<pre><code class=\"bash\">$ curl -s localhost:9200\/cards-2020-09-20-16-14-18 | jq {   &quot;cards-2020-09-20-16-14-18&quot;: {     &quot;aliases&quot;: {       &quot;cards&quot;: {}     },     &quot;mappings&quot;: {       ...     },     &quot;settings&quot;: {       &quot;index&quot;: {         &quot;creation_date&quot;: &quot;1600618458522&quot;,         &quot;number_of_shards&quot;: &quot;1&quot;,         &quot;number_of_replicas&quot;: &quot;1&quot;,         &quot;uuid&quot;: &quot;iLX7A8WZQuCkRSOd7mjgMg&quot;,         &quot;version&quot;: {           &quot;created&quot;: &quot;7050199&quot;         },         &quot;provided_name&quot;: &quot;cards-2020-09-20-16-14-18&quot;       }     }   } } <\/code><\/pre>\n<p>  \u041d\u0443 \u0438, \u043d\u0430\u043a\u043e\u043d\u0435\u0446, \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0435\u0433\u043e \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435:<\/p>\n<pre><code class=\"bash\">$ curl -s localhost:9200\/cards-2020-09-20-16-14-18\/_search | jq {   &quot;took&quot;: 2,   &quot;timed_out&quot;: false,   &quot;_shards&quot;: {     &quot;total&quot;: 1,     &quot;successful&quot;: 1,     &quot;skipped&quot;: 0,     &quot;failed&quot;: 0   },   &quot;hits&quot;: {     &quot;total&quot;: {       &quot;value&quot;: 4704,       &quot;relation&quot;: &quot;eq&quot;     },     &quot;max_score&quot;: 1,     &quot;hits&quot;: [       ...     ]   } } <\/code><\/pre>\n<p>  \u0418\u0442\u043e\u0433\u043e \u0432 \u043d\u0430\u0448\u0435\u043c \u0438\u043d\u0434\u0435\u043a\u0441\u0435 4704 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430, \u0430 \u0432 \u043f\u043e\u043b\u0435 <code>hits<\/code> (\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u044f \u043f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u043b, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d\u043e \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u043e\u0435) \u043c\u043e\u0436\u043d\u043e \u0434\u0430\u0436\u0435 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u0437 \u043d\u0438\u0445. \u0423\u0441\u043f\u0435\u0445!<\/p>\n<p>  \u0411\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0433\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u0432\u0441\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0433\u043e \u0431\u0430\u043b\u043e\u0432\u0441\u0442\u0432\u0430 \u0441 Elasticsearch \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <a href=\"https:\/\/www.elastic.co\/kibana\">Kibana<\/a>. \u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440 \u0432 <code>docker-compose.yml<\/code>:<\/p>\n<pre><code class=\"plaintext\">services:   ...   kibana:     image: &quot;kibana:7.5.1&quot;     ports:       - &quot;5601:5601&quot;     depends_on:       - elasticsearch   ... <\/code><\/pre>\n<p>  \u041f\u043e\u0441\u043b\u0435 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0433\u043e <code>docker-compose up<\/code> \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0439\u0442\u0438 \u0432 Kibana \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <code>localhost:5601<\/code> (\u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u0435\u0442 \u0441\u0442\u0430\u0440\u0442\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0431\u044b\u0441\u0442\u0440\u043e) \u0438, \u043f\u043e\u0441\u043b\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u043e\u0439 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438, \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u043d\u0430\u0448\u0438\u0445 \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u0432 \u0432 \u0441\u0438\u043c\u043f\u0430\u0442\u0438\u0447\u043d\u043e\u043c \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/1k\/fb\/fi\/1kfbfiujlm7hq0r0lzpqpsjkxxc.png\"><\/p>\n<p>  \u041e\u0447\u0435\u043d\u044c \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0432\u043a\u043b\u0430\u0434\u043a\u0443 Dev Tools \u2014 \u043f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0432\u0430\u043c \u0447\u0430\u0441\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0435 \u0438\u043b\u0438 \u0438\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 Elasticsearch, \u0438 \u0432 \u0438\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 \u0441 \u0430\u0432\u0442\u043e\u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\u043c \u0438 \u0430\u0432\u0442\u043e\u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u044d\u0442\u043e \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0443\u0434\u043e\u0431\u043d\u0435\u0435.<\/p>\n<h2>\u041f\u043e\u0438\u0441\u043a<\/h2>\n<p>  \u041f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435\u0445 \u043d\u0435\u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e \u0441\u043a\u0443\u0447\u043d\u044b\u0445 \u043f\u0440\u0438\u0433\u043e\u0442\u043e\u0432\u043b\u0435\u043d\u0438\u0439 \u043f\u043e\u0440\u0430 \u043d\u0430\u043c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0438\u0441\u043a\u0430 \u0432 \u043d\u0430\u0448\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435!<\/p>\n<p>  \u0420\u0430\u0437\u0434\u0435\u043b\u0438\u043c \u044d\u0442\u0443 \u043d\u0435\u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043d\u0430 \u0442\u0440\u0438 \u044d\u0442\u0430\u043f\u0430 \u0438 \u043e\u0431\u0441\u0443\u0434\u0438\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438.<\/p>\n<ol>\n<li>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 \u0431\u044d\u043a\u0435\u043d\u0434 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 <code>Searcher<\/code>, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u043e\u0438\u0441\u043a\u0430. \u041e\u043d \u0431\u0443\u0434\u0435\u0442 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441 \u043a Elasticsearch \u0438 \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0432 \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043e\u0432\u0430\u0440\u0438\u043c\u044b\u0435 \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0431\u044d\u043a\u0435\u043d\u0434\u0430.<\/li>\n<li>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 API \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 (\u0440\u0443\u0447\u043a\u0443\/\u0440\u043e\u0443\u0442\/\u043a\u0430\u043a \u0443 \u0432\u0430\u0441 \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u044d\u0442\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442?) <code>\/cards\/search<\/code>, \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u043e\u0438\u0441\u043a. \u041e\u043d \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 <code>Searcher<\/code>, \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0443.<\/li>\n<li>\u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u043e\u0438\u0441\u043a\u0430 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0435. \u041e\u043d \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0432 <code>\/cards\/search<\/code>, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0441\u044f, \u0447\u0442\u043e \u043e\u043d \u0445\u043e\u0447\u0435\u0442 \u0438\u0441\u043a\u0430\u0442\u044c, \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b (\u0438, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044b).<\/li>\n<\/ol>\n<h2>\u041f\u043e\u0438\u0441\u043a: \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c<\/h2>\n<p>  \u041d\u0435 \u0442\u0430\u043a \u0441\u043b\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440, \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u043f\u043e\u0438\u0441\u043a, \u043a\u0430\u043a \u0435\u0433\u043e \u0437\u0430\u0434\u0438\u0437\u0430\u0439\u043d\u0438\u0442\u044c. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u043f\u0438\u0448\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e\u0438\u0441\u043a\u0430 \u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430 \u0438 \u043e\u0431\u0441\u0443\u0434\u0438\u043c, \u043f\u043e\u0447\u0435\u043c\u0443 \u043e\u043d \u0442\u0430\u043a\u043e\u0439, \u0430 \u043d\u0435 \u0438\u043d\u043e\u0439.<\/p>\n<pre><code class=\"python\"># backend\/backend\/search\/searcher.py  import abc from dataclasses import dataclass from typing import Iterable, Optional   @dataclass class CardSearchResult:     total_count: int     card_ids: Iterable[str]     next_card_offset: Optional[int]   class Searcher(metaclass=abc.ABCMeta):      @abc.abstractmethod     def search_cards(self, query: str = &quot;&quot;,                       count: int = 20, offset: int = 0) -&gt; CardSearchResult:         pass <\/code><\/pre>\n<p>  \u041a\u0430\u043a\u0438\u0435-\u0442\u043e \u0432\u0435\u0449\u0438 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u0430\u0433\u0438\u043d\u0430\u0446\u0438\u044f. \u041c\u044b \u0430\u043c\u0431\u0438\u0446\u0438\u043e\u0437\u043d\u044b\u0439 \u043c\u043e\u043b\u043e\u0434\u043e\u0439 <s>\u0443\u0431\u0438\u0439\u0446\u0430 IMDB<\/s> \u0441\u0442\u0430\u0440\u0442\u0430\u043f, \u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u043e\u0438\u0441\u043a\u0430 \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u0432\u043c\u0435\u0449\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043e\u0434\u043d\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443!<\/p>\n<p>  \u041a\u0430\u043a\u0438\u0435-\u0442\u043e \u043c\u0435\u043d\u0435\u0435 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0441\u043f\u0438\u0441\u043e\u043a ID, \u0430 \u043d\u0435 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430. Elasticsearch \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0445\u0440\u0430\u043d\u0438\u0442 \u043d\u0430\u0448\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0438\u0445 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u0445 \u043f\u043e\u0438\u0441\u043a\u0430. \u042d\u0442\u043e \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0442\u044c \u043d\u0430 \u0440\u0430\u0437\u043c\u0435\u0440\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0433\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0430, \u043d\u043e \u0434\u043b\u044f \u043d\u0430\u0441 \u044d\u0442\u043e \u044f\u0432\u043d\u043e \u043f\u0440\u0435\u0436\u0434\u0435\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044f. \u0422\u0430\u043a \u043f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u043d\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u0441\u0440\u0430\u0437\u0443 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438? \u041e\u0442\u0432\u0435\u0442: \u044d\u0442\u043e \u043d\u0430\u0440\u0443\u0448\u0438\u0442 single-responsibility principle. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u043c\u044b \u043d\u0430\u043a\u0440\u0443\u0442\u0438\u043c \u0432 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0435 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443, \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u044f\u0449\u0443\u044e \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0435 \u044f\u0437\u044b\u043a\u0438 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0420\u043e\u0432\u043d\u043e \u0432 \u044d\u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u0445 \u043f\u043e\u0438\u0441\u043a\u0430 \u0440\u0430\u0437\u044a\u0435\u0434\u0443\u0442\u0441\u044f, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0443 \u0436\u0435 \u0441\u0430\u043c\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u0432 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u043c\u044b \u0437\u0430\u0431\u0443\u0434\u0435\u043c. \u0418 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435 \u0438 \u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435.<\/p>\n<p>  \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043d\u0430\u0441\u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0441\u0442\u0430, \u0447\u0442\u043e \u043c\u043d\u0435 \u0431\u044b\u043b\u043e \u043b\u0435\u043d\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u0440\u0430\u0437\u0434\u0435\u043b \ud83d\ude41<\/p>\n<pre><code class=\"python\"># backend\/backend\/search\/searcher_impl.py  from typing import Any  from elasticsearch import Elasticsearch  from backend.search.searcher import CardSearchResult, Searcher   ElasticsearchQuery = Any  # \u0434\u043b\u044f \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0439 \u0442\u0438\u043f\u043e\u0432   class ElasticsearchSearcher(Searcher):      def __init__(self, elasticsearch_client: Elasticsearch, cards_index_name: str):         self.elasticsearch_client = elasticsearch_client         self.cards_index_name = cards_index_name      def search_cards(self, query: str = &quot;&quot;, count: int = 20, offset: int = 0) -&gt; CardSearchResult:         result = self.elasticsearch_client.search(index=self.cards_index_name, body={             &quot;size&quot;: count,             &quot;from&quot;: offset,             &quot;query&quot;: self._make_text_query(query) if query else self._match_all_query         })         total_count = result[&quot;hits&quot;][&quot;total&quot;][&quot;value&quot;]         return CardSearchResult(             total_count=total_count,             card_ids=[hit[&quot;_id&quot;] for hit in result[&quot;hits&quot;][&quot;hits&quot;]],             next_card_offset=offset + count if offset + count &lt; total_count else None,         )      def _make_text_query(self, query: str) -&gt; ElasticsearchQuery:         return {             # Multi-match query \u0434\u0435\u043b\u0430\u0435\u0442 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u043f\u043e              # \u0441\u043e\u0432\u043e\u043a\u0443\u043f\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u0435\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 (\u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 match             # query, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0449\u0435\u0442 \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443 \u043f\u043e\u043b\u044e).             &quot;multi_match&quot;: {                 &quot;query&quot;: query,                 # \u0427\u0438\u0441\u043b\u043e \u043f\u043e\u0441\u043b\u0435 ^ \u2013 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442. \u041d\u0430\u0439\u0442\u0438 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u0442\u0435\u043a\u0441\u0442\u0430                 # \u0432 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043b\u0443\u0447\u0448\u0435, \u0447\u0435\u043c \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0438 \u0442\u0435\u0433\u0430\u0445.                 &quot;fields&quot;: [&quot;name^3&quot;, &quot;tags.text&quot;, &quot;text&quot;],             }         }      _match_all_query: ElasticsearchQuery = {&quot;match_all&quot;: {}} <\/code><\/pre>\n<p>  \u041f\u043e \u0441\u0443\u0442\u0438 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0445\u043e\u0434\u0438\u043c \u0432 API Elasticsearch \u0438 \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u043e \u0434\u043e\u0441\u0442\u0430\u0451\u043c ID \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0438\u0437 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430.<\/p>\n<p>  \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430 \u0442\u043e\u0436\u0435 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0430: <\/p>\n<pre><code class=\"python\"># backend\/backend\/server.py  ...      def search_cards(self):         request = flask.request.json         search_result = self.wiring.searcher.search_cards(**request)         cards = self.wiring.card_dao.get_by_ids(search_result.card_ids)         return flask.jsonify({             &quot;totalCount&quot;: search_result.total_count,             &quot;cards&quot;: [                 {                     &quot;id&quot;: card.id,                     &quot;slug&quot;: card.slug,                     &quot;name&quot;: card.name,                     # \u0417\u0434\u0435\u0441\u044c \u043d\u0435 \u043d\u0443\u0436\u043d\u044b \u0432\u0441\u0435 \u043f\u043e\u043b\u044f, \u0438\u043d\u0430\u0447\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u043e\u0434\u043d\u043e\u0439                     # \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043f\u043e\u0438\u0441\u043a\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e, \u0438 \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442                     # \u0434\u043e\u043b\u0433\u043e \u0433\u0440\u0443\u0437\u0438\u0442\u044c\u0441\u044f.                 } for card in cards             ],             &quot;nextCardOffset&quot;: search_result.next_card_offset,         })  ... <\/code><\/pre>\n<p>  \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430, \u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0435\u0433\u043e\u0441\u044f \u044d\u0442\u0438\u043c \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u043c, \u0445\u043e\u0442\u044c \u0438 \u043e\u0431\u044a\u0451\u043c\u043d\u0430, \u043d\u043e \u0432 \u0446\u0435\u043b\u043e\u043c \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u044f\u043c\u043e\u043b\u0438\u043d\u0435\u0439\u043d\u0430 \u0438 \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043d\u0435 \u0445\u043e\u0447\u0443 \u0437\u0430\u043e\u0441\u0442\u0440\u044f\u0442\u044c \u043d\u0430 \u043d\u0435\u0439 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435. \u041d\u0430 \u0432\u0435\u0441\u044c \u043a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/commit\/aeb1d187672446d85d07213e0fd4e55c67f7cd73\">\u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043c\u0438\u0442\u0435<\/a>.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/di\/u_\/0c\/diu_0c__zfmwblogaapcfkwm1k8.png\"><\/p>\n<p>  So far so good, \u0438\u0434\u0451\u043c \u0434\u0430\u043b\u044c\u0448\u0435.<\/p>\n<h2><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/04\/a4\/ny\/04a4nyqvutjwbwcox9_oh4piwlu.png\" width=\"300\" align=\"right\">\u041f\u043e\u0438\u0441\u043a: \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0444\u0438\u043b\u044c\u0442\u0440\u044b<\/h2>\n<p>  \u041f\u043e\u0438\u0441\u043a \u043f\u043e \u0442\u0435\u043a\u0441\u0442\u0443 \u2014 \u044d\u0442\u043e \u043a\u043b\u0451\u0432\u043e, \u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u043f\u043e\u0438\u0441\u043a\u043e\u043c \u043d\u0430 \u0441\u0435\u0440\u044c\u0451\u0437\u043d\u044b\u0445 \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u0445, \u0432\u044b \u043d\u0430\u0432\u0435\u0440\u043d\u044f\u043a\u0430 \u0432\u0438\u0434\u0435\u043b\u0438 \u0432\u0441\u044f\u043a\u0438\u0435 \u043f\u043b\u044e\u0448\u043a\u0438 \u0432\u0440\u043e\u0434\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432.<\/p>\n<p>  \u0423 \u043d\u0430\u0448\u0438\u0445 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0439 \u0444\u0438\u043b\u044c\u043c\u043e\u0432 \u0438\u0437 \u0431\u0430\u0437\u044b TMDB 5000 \u043f\u043e\u043c\u0438\u043c\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0439 \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0439 \u0435\u0441\u0442\u044c \u0442\u0435\u0433\u0438, \u0442\u0430\u043a \u0447\u0442\u043e \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043b\u044f \u0442\u0440\u0435\u043d\u0438\u0440\u043e\u0432\u043a\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0444\u0438\u043b\u044c\u0442\u0440\u044b \u043f\u043e \u0442\u0435\u0433\u0430\u043c. \u041d\u0430\u0448\u0430 \u0446\u0435\u043b\u044c \u2014 \u043d\u0430 \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0435: \u043f\u0440\u0438 \u043a\u043b\u0438\u043a\u0435 \u043d\u0430 \u0442\u0435\u0433 \u0432 \u0432\u044b\u0434\u0430\u0447\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u043e\u0441\u0442\u0430\u0442\u044c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u0438\u043b\u044c\u043c\u044b \u0441 \u044d\u0442\u0438\u043c \u0442\u0435\u0433\u043e\u043c (\u0438\u0445 \u0447\u0438\u0441\u043b\u043e \u0443\u043a\u0430\u0437\u0430\u043d\u043e \u0432 \u0441\u043a\u043e\u0431\u043a\u0430\u0445 \u0440\u044f\u0434\u043e\u043c \u0441 \u043d\u0438\u043c).<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/aw\/qp\/dv\/awqpdvuaxmeisk9ijx3g_4z91pm.png\"><\/div>\n<p>  \u0427\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0438\u043b\u044c\u0442\u0440\u044b, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0440\u0435\u0448\u0438\u0442\u044c \u0434\u0432\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.   <\/p>\n<ul>\n<li>\u041d\u0430\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u043f\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c, \u043a\u0430\u043a\u043e\u0439 \u043d\u0430\u0431\u043e\u0440 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d. \u041c\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u0430 \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u044d\u043a\u0440\u0430\u043d\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0438\u0445 \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043a \u043f\u0443\u0441\u0442\u043e\u043c\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0443; \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043d\u044f\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0442\u0435\u0433\u0438 \u0435\u0441\u0442\u044c \u0443 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u043f\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0443, \u0438 \u0432 \u0438\u0434\u0435\u0430\u043b\u0435 \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c N \u0441\u0430\u043c\u044b\u0445 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445.<\/li>\n<li>\u041d\u0430\u0443\u0447\u0438\u0442\u044c\u0441\u044f, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0444\u0438\u043b\u044c\u0442\u0440 \u2014 \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 \u0432\u044b\u0434\u0430\u0447\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0441 \u0442\u0435\u0433\u0430\u043c\u0438, \u0444\u0438\u043b\u044c\u0442\u0440 \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0432\u044b\u0431\u0440\u0430\u043b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c.<\/li>\n<\/ul>\n<p>  \u0412\u0442\u043e\u0440\u043e\u0435 \u0432 Elasticsearch \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0440\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 API \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (\u0441\u043c. <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl-terms-query.html\">terms query<\/a>), \u043f\u0435\u0440\u0432\u043e\u0435 \u2014 \u0447\u0435\u0440\u0435\u0437 \u0447\u0443\u0442\u044c \u043c\u0435\u043d\u0435\u0435 \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0439 <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/search-aggregations.html\">\u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u0439<\/a>.<\/p>\n<p>  \u0418\u0442\u0430\u043a, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0437\u043d\u0430\u0442\u044c, \u043a\u0430\u043a\u0438\u0435 \u0442\u0435\u0433\u0438 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f \u0443 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a, \u0438 \u0443\u043c\u0435\u0442\u044c \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u0441 \u043d\u0443\u0436\u043d\u044b\u043c\u0438 \u0442\u0435\u0433\u0430\u043c\u0438. \u0421\u043f\u0435\u0440\u0432\u0430 \u043e\u0431\u043d\u043e\u0432\u0438\u043c \u0434\u0438\u0437\u0430\u0439\u043d \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0433\u043e \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430:<\/p>\n<pre><code class=\"python\"># backend\/backend\/search\/searcher.py  import abc from dataclasses import dataclass from typing import Iterable, Optional   @dataclass class TagStats:     tag: str     cards_count: int   @dataclass class CardSearchResult:     total_count: int     card_ids: Iterable[str]     next_card_offset: Optional[int]     tag_stats: Iterable[TagStats]   class Searcher(metaclass=abc.ABCMeta):      @abc.abstractmethod     def search_cards(self, query: str = &quot;&quot;,                       count: int = 20, offset: int = 0,                      tags: Optional[Iterable[str]] = None) -&gt; CardSearchResult:         pass <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438. \u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u2014 \u0437\u0430\u0432\u0435\u0441\u0442\u0438 \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u044e \u043f\u043e \u043f\u043e\u043b\u044e <code>tags<\/code>:<\/p>\n<pre><code class=\"python\">--- a\/backend\/backend\/search\/searcher_impl.py +++ b\/backend\/backend\/search\/searcher_impl.py @@ -10,6 +10,8 @@ ElasticsearchQuery = Any    class ElasticsearchSearcher(Searcher):   +    TAGS_AGGREGATION_NAME = &quot;tags_aggregation&quot; +      def __init__(self, elasticsearch_client: Elasticsearch, cards_index_name: str):          self.elasticsearch_client = elasticsearch_client          self.cards_index_name = cards_index_name @@ -18,7 +20,12 @@ class ElasticsearchSearcher(Searcher):          result = self.elasticsearch_client.search(index=self.cards_index_name, body={              &quot;size&quot;: count,              &quot;from&quot;: offset,              &quot;query&quot;: self._make_text_query(query) if query else self._match_all_query, +            &quot;aggregations&quot;: { +                self.TAGS_AGGREGATION_NAME: { +                    &quot;terms&quot;: {&quot;field&quot;: &quot;tags&quot;} +                } +            }          }) <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u043e\u0442 Elasticsearch \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u044c \u043f\u043e\u043b\u0435 <code>aggregations<\/code>, \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u043e \u043a\u043b\u044e\u0447\u0443 <code>TAGS_AGGREGATION_NAME<\/code> \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u044c <em>\u0431\u0430\u043a\u0435\u0442\u044b<\/em>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0435 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e \u0442\u043e\u043c, \u043a\u0430\u043a\u0438\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043b\u0435\u0436\u0430\u0442 \u0432 \u043f\u043e\u043b\u0435 <code>tags<\/code> \u0443 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u043e \u043e\u043d\u0438 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0438\u0437\u0432\u043b\u0435\u0447\u0451\u043c \u044d\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438 \u0432\u0435\u0440\u043d\u0451\u043c \u0432 \u0443\u0434\u043e\u0431\u043e\u0432\u0430\u0440\u0438\u043c\u043e\u043c \u0432\u0438\u0434\u0435 (as designed above):<\/p>\n<pre><code class=\"python\">--- a\/backend\/backend\/search\/searcher_impl.py +++ b\/backend\/backend\/search\/searcher_impl.py @@ -28,10 +28,15 @@ class ElasticsearchSearcher(Searcher):          total_count = result[&quot;hits&quot;][&quot;total&quot;][&quot;value&quot;] +        tag_stats = [ +            TagStats(tag=bucket[&quot;key&quot;], cards_count=bucket[&quot;doc_count&quot;]) +            for bucket in result[&quot;aggregations&quot;][self.TAGS_AGGREGATION_NAME][&quot;buckets&quot;] +        ]          return CardSearchResult(              total_count=total_count,              card_ids=[hit[&quot;_id&quot;] for hit in result[&quot;hits&quot;][&quot;hits&quot;]],              next_card_offset=offset + count if offset + count &lt; total_count else None, +            tag_stats=tag_stats,          ) <\/code><\/pre>\n<p>  \u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u0430 \u2014 \u0441\u0430\u043c\u0430\u044f \u043b\u0451\u0433\u043a\u0430\u044f \u0447\u0430\u0441\u0442\u044c:<\/p>\n<pre><code class=\"python\">--- a\/backend\/backend\/search\/searcher_impl.py +++ b\/backend\/backend\/search\/searcher_impl.py @@ -16,11 +16,17 @@ class ElasticsearchSearcher(Searcher):          self.elasticsearch_client = elasticsearch_client          self.cards_index_name = cards_index_name   -    def search_cards(self, query: str = &quot;&quot;, count: int = 20, offset: int = 0) -&gt; CardSearchResult: +    def search_cards(self, query: str = &quot;&quot;, count: int = 20, offset: int = 0, +                     tags: Optional[Iterable[str]] = None) -&gt; CardSearchResult:          result = self.elasticsearch_client.search(index=self.cards_index_name, body={              &quot;size&quot;: count,              &quot;from&quot;: offset, -            &quot;query&quot;: self._make_text_query(query) if query else self._match_all_query, +            &quot;query&quot;: { +                &quot;bool&quot;: { +                    &quot;must&quot;: self._make_text_queries(query), +                    &quot;filter&quot;: self._make_filter_queries(tags), +                } +            },              &quot;aggregations&quot;: { <\/code><\/pre>\n<p>  \u041f\u043e\u0434\u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0432\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u044b\u0435 \u0432 must-\u043a\u043b\u0430\u0443\u0437\u0443, \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b \u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044e, \u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0443\u0442 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0440\u0430\u0441\u0447\u0451\u0442\u0435 \u0441\u043a\u043e\u0440\u043e\u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438; \u0435\u0441\u043b\u0438 \u043c\u044b \u043a\u043e\u0433\u0434\u0430-\u043d\u0438\u0431\u0443\u0434\u044c \u0431\u0443\u0434\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0435\u0449\u0451 \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0443\u0441\u043b\u043e\u0432\u0438\u044f \u043d\u0430 \u0442\u0435\u043a\u0441\u0442\u044b, \u0438\u0445 \u043b\u0443\u0447\u0448\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u044e\u0434\u0430. \u041f\u043e\u0434\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 filter-\u043a\u043b\u0430\u0443\u0437\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u044e\u0442, \u043d\u0435 \u0432\u043b\u0438\u044f\u044f \u043d\u0430 \u0441\u043a\u043e\u0440\u044b \u0438 \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. <\/p>\n<p>  \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c <code>_make_filter_queries()<\/code>:<\/p>\n<pre><code class=\"python\">    def _make_filter_queries(self, tags: Optional[Iterable[str]] = None) -&gt; List[ElasticsearchQuery]:         return [] if tags is None else [{             &quot;term&quot;: {                 &quot;tags&quot;: {                     &quot;value&quot;: tag                 }             }         } for tag in tags] <\/code><\/pre>\n<p>  \u041d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434-\u0447\u0430\u0441\u0442\u0438 \u043e\u043f\u044f\u0442\u044c-\u0442\u0430\u043a\u0438 \u043d\u0435 \u0441\u0442\u0430\u043d\u0443 \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f; \u0432\u0435\u0441\u044c \u043a\u043e\u0434 \u2014 \u0432 <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/commit\/65bc7676e7e60d590957811c9e371564c70a00e1\">\u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043c\u0438\u0442\u0435<\/a>.<\/p>\n<h2>\u0420\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>  \u0418\u0442\u0430\u043a, \u043d\u0430\u0448 \u043f\u043e\u0438\u0441\u043a \u0438\u0449\u0435\u0442 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438, \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u0442 \u0438\u0445 \u043f\u043e \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u0441\u043f\u0438\u0441\u043a\u0443 \u0442\u0435\u0433\u043e\u0432 \u0438 \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0432 \u043a\u0430\u043a\u043e\u043c-\u0442\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0435. \u041d\u043e \u0432 \u043a\u0430\u043a\u043e\u043c? \u041f\u043e\u0440\u044f\u0434\u043e\u043a \u043e\u0447\u0435\u043d\u044c \u0432\u0430\u0436\u0435\u043d \u0434\u043b\u044f \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u0430, \u043d\u043e \u0432\u0441\u0451, \u0447\u0442\u043e \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0437\u0430 \u0432\u0440\u0435\u043c\u044f \u043d\u0430\u0448\u0438\u0445 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u0435\u043b\u044c\u0441\u0442\u0432 \u0432 \u043f\u043b\u0430\u043d\u0435 \u043f\u043e\u0440\u044f\u0434\u043a\u0430 \u2014 \u044d\u0442\u043e \u043d\u0430\u043c\u0435\u043a\u043d\u0443\u043b\u0438 Elasticsearch, \u0447\u0442\u043e \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c \u0441\u043b\u043e\u0432\u0430 \u0432 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0435 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u0432\u044b\u0433\u043e\u0434\u043d\u0435\u0435, \u0447\u0435\u043c \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0438\u043b\u0438 \u0442\u0435\u0433\u0430\u0445, \u0443\u043a\u0430\u0437\u0430\u0432 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 <code>^3<\/code> \u0432 multi-match query.<\/p>\n<p>  \u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e Elasticsearch \u0440\u0430\u043d\u0436\u0438\u0440\u0443\u0435\u0442 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e <a href=\"https:\/\/compose.com\/articles\/how-scoring-works-in-elasticsearch\/#thepracticalscoringfunction\">\u0445\u0438\u0442\u0440\u043e\u0439 \u0444\u043e\u0440\u043c\u0443\u043b\u043e\u0439<\/a> \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/TF-IDF\">TF-IDF<\/a>, \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u0432\u043e\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u043c\u043e\u0433\u043e \u0430\u043c\u0431\u0438\u0446\u0438\u043e\u0437\u043d\u043e\u0433\u043e \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u0430 \u044d\u0442\u043e\u0433\u043e \u0432\u0440\u044f\u0434 \u043b\u0438 \u0445\u0432\u0430\u0442\u0438\u0442. \u0415\u0441\u043b\u0438 \u043d\u0430\u0448\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u2014 \u044d\u0442\u043e \u0442\u043e\u0432\u0430\u0440\u044b, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0443\u043c\u0435\u0442\u044c \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0438\u0445 \u043f\u0440\u043e\u0434\u0430\u0436\u0438; \u0435\u0441\u043b\u0438 \u044d\u0442\u043e user-generated \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u2014 \u0443\u043c\u0435\u0442\u044c \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0441\u0432\u0435\u0436\u0435\u0441\u0442\u044c, \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041d\u043e \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e \u0447\u0438\u0441\u043b\u0443 \u043f\u0440\u043e\u0434\u0430\u0436\/\u0434\u0430\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0442\u043e\u0433\u0434\u0430 \u043c\u044b \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u0443\u0447\u0442\u0451\u043c \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0443.<\/p>\n<p>  \u0420\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u2014 \u044d\u0442\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0438 \u0437\u0430\u043f\u0443\u0442\u0430\u043d\u043d\u043e\u0435 \u0446\u0430\u0440\u0441\u0442\u0432\u043e \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u043f\u043e\u043a\u0440\u044b\u0442\u044c \u0437\u0430 \u043e\u0434\u0438\u043d \u0440\u0430\u0437\u0434\u0435\u043b \u0432 \u043a\u043e\u043d\u0446\u0435 \u0441\u0442\u0430\u0442\u044c\u0438. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0434\u0435\u0441\u044c \u044f \u043f\u0435\u0440\u0435\u0445\u043e\u0436\u0443 \u0432 \u0440\u0435\u0436\u0438\u043c \u043a\u0440\u0443\u043f\u043d\u044b\u0445 \u043c\u0430\u0437\u043a\u043e\u0432; \u044f \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u044e \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u0432 \u0441\u0430\u043c\u044b\u0445 \u043e\u0431\u0449\u0438\u0445 \u0441\u043b\u043e\u0432\u0430\u0445, \u043a\u0430\u043a \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0435\u043d\u043e industrial grade \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u043f\u043e\u0438\u0441\u043a\u0435, \u0438 \u0440\u0430\u0441\u043a\u0440\u043e\u044e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0434\u0435\u0442\u0430\u043b\u0435\u0439 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441 Elasticsearch.<\/p>\n<p>  \u0417\u0430\u0434\u0430\u0447\u0430 \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0447\u0435\u043d\u044c \u0441\u043b\u043e\u0436\u043d\u0430, \u0442\u0430\u043a \u0447\u0442\u043e \u043d\u0435\u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0447\u0442\u043e \u043e\u0434\u0438\u043d \u0438\u0437 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0435\u0451 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u2014 \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0435 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435. \u041f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439 \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f \u043a \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f <a href=\"https:\/\/en.wikipedia.org\/wiki\/Learning_to_rank\">learning to rank<\/a>.<\/p>\n<p>  \u0422\u0438\u043f\u0438\u0447\u043d\u044b\u0439 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a.<\/p>\n<p>  <b>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c\u0441\u044f, \u0447\u0442\u043e \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/b>. \u041c\u044b \u043a\u043b\u0430\u0434\u0451\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0438\u0435 \u043d\u0430\u0441 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0432 \u0438\u043d\u0434\u0435\u043a\u0441, \u043d\u0430\u0443\u0447\u0430\u0435\u043c\u0441\u044f \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u0440\u0430\u0437\u0443\u043c\u043d\u044b\u0439 \u0442\u043e\u043f (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u0430\u043a\u043e\u0439-\u043d\u0438\u0431\u0443\u0434\u044c \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u043a\u043e\u0439 \u0438 \u043e\u0442\u0441\u0435\u0447\u0435\u043d\u0438\u0435\u043c) \u044d\u0442\u0438\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u0438 \u0442\u0435\u043f\u0435\u0440\u044c \u0445\u043e\u0442\u0438\u043c \u043d\u0430\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0435\u0433\u043e \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0443\u043c\u043d\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c.<\/p>\n<p>  <b>\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c\u0441\u044f, \u043a\u0430\u043a \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/b>. \u041c\u044b \u0440\u0435\u0448\u0430\u0435\u043c, \u043f\u043e \u043a\u0430\u043a\u043e\u0439 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0435 \u043d\u0430\u0434\u043e \u043e\u0442\u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u0432\u044b\u0434\u0430\u0447\u0443, \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0431\u0438\u0437\u043d\u0435\u0441-\u0446\u0435\u043b\u044f\u043c\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0435\u0441\u043b\u0438 \u043d\u0430\u0448\u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u2014 \u044d\u0442\u043e \u0442\u043e\u0432\u0430\u0440\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043f\u0440\u043e\u0434\u0430\u0451\u043c, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0445\u043e\u0442\u0435\u0442\u044c \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u043f\u043e \u0443\u0431\u044b\u0432\u0430\u043d\u0438\u044e \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043a\u0443\u043f\u043a\u0438; \u0435\u0441\u043b\u0438 \u043c\u0435\u043c\u044b \u2014 \u043f\u043e \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u0438 \u043b\u0430\u0439\u043a\u0430 \u0438\u043b\u0438 \u0448\u0435\u0440\u0430 \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u042d\u0442\u0438 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u0438 \u043c\u044b, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043d\u0435 \u0443\u043c\u0435\u0435\u043c \u0441\u0447\u0438\u0442\u0430\u0442\u044c \u2014 \u0432 \u043b\u0443\u0447\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0440\u0438\u043a\u0438\u0434\u044b\u0432\u0430\u0442\u044c, \u0434\u0430 \u0438 \u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0441\u0442\u0430\u0440\u044b\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0443 \u043d\u0430\u0441 \u043d\u0430\u0431\u0440\u0430\u043d\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438, \u2014 \u043d\u043e \u043c\u044b \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u043d\u0430\u0443\u0447\u0438\u0442\u044c \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u0445, \u0438\u0441\u0445\u043e\u0434\u044f \u0438\u0437 \u043a\u043e\u0441\u0432\u0435\u043d\u043d\u044b\u0445 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432.<\/p>\n<p>  <b>\u0418\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438<\/b>. \u041c\u044b \u043f\u0440\u0438\u0434\u0443\u043c\u044b\u0432\u0430\u0435\u043c \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u043f\u043e\u043c\u043e\u0447\u044c \u043d\u0430\u043c \u043e\u0446\u0435\u043d\u0438\u0442\u044c \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c. \u041f\u043e\u043c\u0438\u043c\u043e \u0442\u043e\u0433\u043e \u0436\u0435 TF-IDF, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0436\u0435 \u0443\u043c\u0435\u0435\u0442 \u0434\u043b\u044f \u043d\u0430\u0441 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0442\u044c Elasticsearch, \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u2014 CTR (click-through rate): \u043c\u044b \u0431\u0435\u0440\u0451\u043c \u043b\u043e\u0433\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0437\u0430 \u0432\u0441\u0451 \u0432\u0440\u0435\u043c\u044f, \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u043f\u0430\u0440\u044b \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c+\u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u0447\u0438\u0442\u0430\u0435\u043c, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u044f\u0432\u043b\u044f\u043b\u0430\u0441\u044c \u0432 \u0432\u044b\u0434\u0430\u0447\u0435 \u043f\u043e \u044d\u0442\u043e\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0435\u0451 \u043a\u043b\u0438\u043a\u0430\u043b\u0438, \u0434\u0435\u043b\u0438\u043c \u043e\u0434\u043d\u043e \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u0435, et voil\u00e0 \u2014 \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0430\u044f \u043e\u0446\u0435\u043d\u043a\u0430 \u0443\u0441\u043b\u043e\u0432\u043d\u043e\u0439 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u0438 \u043a\u043b\u0438\u043a\u0430 \u0433\u043e\u0442\u043e\u0432\u0430. \u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u0442\u044c \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0430\u0440\u043d\u044b\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c-\u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u043c. \u041f\u0440\u0438\u0434\u0443\u043c\u0430\u0432 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438, \u043c\u044b \u043f\u0438\u0448\u0435\u043c \u043a\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0445 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u0442, \u043a\u043b\u0430\u0434\u0451\u0442 \u0432 \u043a\u0430\u043a\u043e\u0435-\u0442\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0438 \u0443\u043c\u0435\u0435\u0442 \u043e\u0442\u0434\u0430\u0432\u0430\u0442\u044c \u0432 real time \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043d\u0430\u0431\u043e\u0440\u0430 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439.<\/p>\n<p>  <b>\u0421\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u043e\u0431\u0443\u0447\u0430\u044e\u0449\u0438\u0439 \u0434\u0430\u0442\u0430\u0441\u0435\u0442<\/b>. \u0422\u0443\u0442 \u043c\u043d\u043e\u0433\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432, \u043d\u043e \u0432\u0441\u0435 \u043e\u043d\u0438, \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0438\u0437 \u043b\u043e\u0433\u043e\u0432 \u00ab\u0445\u043e\u0440\u043e\u0448\u0438\u0445\u00bb (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043b\u0438\u043a \u0438 \u043f\u043e\u0442\u043e\u043c \u043f\u043e\u043a\u0443\u043f\u043a\u0430) \u0438 \u00ab\u043f\u043b\u043e\u0445\u0438\u0445\u00bb (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u043b\u0438\u043a \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0442 \u043d\u0430 \u0432\u044b\u0434\u0430\u0447\u0443) \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0435. \u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u0441\u043e\u0431\u0440\u0430\u043b\u0438 \u0434\u0430\u0442\u0430\u0441\u0435\u0442, \u0431\u0443\u0434\u044c \u0442\u043e \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0439 \u00ab\u043e\u0446\u0435\u043d\u043a\u0430 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u0438 \u0442\u043e\u0432\u0430\u0440\u0430 X \u0437\u0430\u043f\u0440\u043e\u0441\u0443 Q \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0440\u0430\u0432\u043d\u0430 P\u00bb, \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0430\u0440 \u00ab\u0442\u043e\u0432\u0430\u0440 X \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u0435\u0435 \u0442\u043e\u0432\u0430\u0440\u0430 Y \u0437\u0430\u043f\u0440\u043e\u0441\u0443 Q\u00bb \u0438\u043b\u0438 \u043d\u0430\u0431\u043e\u0440 \u0441\u043f\u0438\u0441\u043a\u043e\u0432 \u00ab\u0434\u043b\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u0430 Q \u0442\u043e\u0432\u0430\u0440\u044b P<sub>1<\/sub>, P<sub>2<\/sub>, \u2026 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0430\u043a-\u0442\u043e\u00bb, \u043c\u044b \u043a\u043e \u0432\u0441\u0435\u043c \u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0443\u044e\u0449\u0438\u043c \u0432 \u043d\u0451\u043c \u0441\u0442\u0440\u043e\u043a\u0430\u043c \u043f\u043e\u0434\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u043c \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438.<\/p>\n<p>  <b>\u041e\u0431\u0443\u0447\u0430\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c<\/b>. \u0422\u0443\u0442 \u0432\u0441\u044f \u043a\u043b\u0430\u0441\u0441\u0438\u043a\u0430 ML: train\/test, \u0433\u0438\u043f\u0435\u0440\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u043f\u0435\u0440\u0435\u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435, <s>\u043f\u0435\u0440\u0444\u043e<\/s>\u0432\u0438\u0434\u0435\u043e\u043a\u0430\u0440\u0442\u044b \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041c\u043e\u0434\u0435\u043b\u0435\u0439, \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u0445 (\u0438 \u043f\u043e\u0432\u0441\u0435\u043c\u0435\u0441\u0442\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0449\u0438\u0445\u0441\u044f) \u0434\u043b\u044f \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u043c\u043d\u043e\u0433\u043e; \u0443\u043f\u043e\u043c\u044f\u043d\u0443 \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c <a href=\"https:\/\/xgboost.readthedocs.io\/en\/latest\/\">XGBoost<\/a> \u0438 <a href=\"https:\/\/catboost.ai\/\">CatBoost<\/a>.<\/p>\n<p>  <b>\u0412\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c<\/b>. \u041d\u0430\u043c \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0442\u0430\u043a \u0438\u043b\u0438 \u0438\u043d\u0430\u0447\u0435 \u043f\u0440\u0438\u043a\u0440\u0443\u0442\u0438\u0442\u044c \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043c\u043e\u0434\u0435\u043b\u0438 \u043d\u0430 \u043b\u0435\u0442\u0443 \u0434\u043b\u044f \u0432\u0441\u0435\u0433\u043e \u0442\u043e\u043f\u0430, \u0447\u0442\u043e\u0431\u044b \u0434\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0434\u043e\u043b\u0435\u0442\u0430\u043b\u0438 \u0443\u0436\u0435 \u043e\u0442\u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b. \u0422\u0443\u0442 \u043c\u043d\u043e\u0433\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432; \u0432 \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0442\u0438\u0432\u043d\u044b\u0445 \u0446\u0435\u043b\u044f\u0445 \u044f (\u043e\u043f\u044f\u0442\u044c-\u0442\u0430\u043a\u0438) \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u044e\u0441\u044c \u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u043e\u043c \u2014 Elasticsearch-\u043f\u043b\u0430\u0433\u0438\u043d\u0435 <a href=\"https:\/\/elasticsearch-learning-to-rank.readthedocs.io\/en\/latest\/\">Learning to Rank<\/a>.<\/p>\n<h2>\u0420\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435: \u043f\u043b\u0430\u0433\u0438\u043d Elasticsearch Learning to Rank<\/h2>\n<p>  <a href=\"https:\/\/elasticsearch-learning-to-rank.readthedocs.io\/en\/latest\/\">Elasticsearch Learning to Rank<\/a> \u2014 \u044d\u0442\u043e \u043f\u043b\u0430\u0433\u0438\u043d, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0432 Elasticsearch \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u044c ML-\u043c\u043e\u0434\u0435\u043b\u044c \u043d\u0430 \u0432\u044b\u0434\u0430\u0447\u0435 \u0438 \u0442\u0443\u0442 \u0436\u0435 \u043e\u0442\u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u043c \u0435\u044e \u0441\u043a\u043e\u0440\u0430\u043c. \u041e\u043d \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043d\u0430\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438, \u0438\u0434\u0435\u043d\u0442\u0438\u0447\u043d\u044b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u043c \u0432 real time, \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0432 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438 Elasticsearch (TF-IDF \u0438 \u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435).<\/p>\n<p>  \u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043f\u043b\u0430\u0433\u0438\u043d \u0432 \u043d\u0430\u0448\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435 \u0441 Elasticsearch. \u041d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 Dockerfile<\/p>\n<pre><code class=\"plaintext\"># elasticsearch\/Dockerfile  FROM elasticsearch:7.5.1 RUN .\/bin\/elasticsearch-plugin install --batch http:\/\/es-learn-to-rank.labs.o19s.com\/ltr-1.1.2-es7.5.1.zip <\/code><\/pre>\n<p>  \u0438 \u0441\u043e\u043f\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 <code>docker-compose.yml<\/code>:<\/p>\n<pre><code class=\"plaintext\">--- a\/docker-compose.yml +++ b\/docker-compose.yml @@ -5,7 +5,8 @@ services:    elasticsearch: -    image: &quot;elasticsearch:7.5.1&quot; +    build: +      context: elasticsearch      environment:        - discovery.type=single-node <\/code><\/pre>\n<p>  \u0422\u0430\u043a\u0436\u0435 \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043f\u043b\u0430\u0433\u0438\u043d\u0430 \u0432 Python-\u043a\u043b\u0438\u0435\u043d\u0442\u0435. \u0421 \u0438\u0437\u0443\u043c\u043b\u0435\u043d\u0438\u0435\u043c \u044f \u043e\u0431\u043d\u0430\u0440\u0443\u0436\u0438\u043b, \u0447\u0442\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0434\u043b\u044f Python \u043d\u0435 \u0438\u0434\u0451\u0442 \u0432 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0442\u0435 \u0441 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u043c, \u0442\u0430\u043a \u0447\u0442\u043e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u044f \u0435\u0451 <a href=\"https:\/\/github.com\/Saluev\/elasticsearch-ltr-py\">\u0437\u0430\u043f\u0438\u043b\u0438\u043b<\/a>. \u0414\u043e\u0431\u0430\u0432\u0438\u043c <code>elasticsearch_ltr<\/code> \u0432 <code>requirements.txt<\/code> \u0438 \u043f\u0440\u043e\u0430\u043f\u0433\u0440\u0435\u0439\u0434\u0438\u043c \u043a\u043b\u0438\u0435\u043d\u0442 \u0432 \u0432\u0430\u0439\u0440\u0438\u043d\u0433\u0435:<\/p>\n<pre><code class=\"python\">--- a\/backend\/backend\/wiring.py +++ b\/backend\/backend\/wiring.py @@ -1,5 +1,6 @@  import os   +from elasticsearch_ltr import LTRClient  from celery import Celery  from elasticsearch import Elasticsearch  from pymongo import MongoClient @@ -39,5 +40,6 @@ class Wiring(object):          self.task_manager = TaskManager(self.celery_app)            self.elasticsearch_client = Elasticsearch(hosts=self.settings.ELASTICSEARCH_HOSTS) +        LTRClient.infect_client(self.elasticsearch_client)          self.indexer = Indexer(self.elasticsearch_client, self.card_dao, self.settings.CARDS_INDEX_ALIAS)          self.searcher: Searcher = ElasticsearchSearcher(self.elasticsearch_client, self.settings.CARDS_INDEX_ALIAS) <\/code><\/pre>\n<h2>\u0420\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435: \u043f\u0438\u043b\u0438\u043c \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438<\/h2>\n<p>  \u041a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432 Elasticsearch \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u0438\u0441\u043e\u043a ID \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0448\u043b\u0438\u0441\u044c, \u043d\u043e \u0438 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u0445 \u0441\u043a\u043e\u0440\u044b (\u043a\u0430\u043a \u0432\u044b \u0431\u044b \u043f\u0435\u0440\u0435\u0432\u0435\u043b\u0438 \u043d\u0430 \u0440\u0443\u0441\u0441\u043a\u0438\u0439 \u044f\u0437\u044b\u043a \u0441\u043b\u043e\u0432\u043e score?). \u0422\u0430\u043a, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl-match-query.html\">match<\/a> \u0438\u043b\u0438 <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl-multi-match-query.html\">multi-match query<\/a>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c, \u0442\u043e \u0441\u043a\u043e\u0440 \u2014 \u044d\u0442\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0442\u043e\u0439 \u0441\u0430\u043c\u043e\u0439 <a href=\"https:\/\/compose.com\/articles\/how-scoring-works-in-elasticsearch\/#thepracticalscoringfunction\">\u0445\u0438\u0442\u0440\u043e\u0439 \u0444\u043e\u0440\u043c\u0443\u043b\u044b<\/a> \u0441 \u0443\u0447\u0430\u0441\u0442\u0438\u0435\u043c TF-IDF; \u0435\u0441\u043b\u0438 <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl-bool-query.html\">bool query<\/a> \u2014 \u043a\u043e\u043c\u0431\u0438\u043d\u0430\u0446\u0438\u044f \u0441\u043a\u043e\u0440\u043e\u0432 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432; \u0435\u0441\u043b\u0438 <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl-function-score-query.html\">function score query<\/a> \u2014 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u0430\u043a\u043e\u0433\u043e-\u0442\u043e \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435) \u0438 \u0442\u0430\u043a \u0434\u0430\u043b\u0435\u0435. \u041f\u043b\u0430\u0433\u0438\u043d ELTR \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043a\u043e\u0440 \u043b\u044e\u0431\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043a\u0430\u043a \u043f\u0440\u0438\u0437\u043d\u0430\u043a, \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044f \u043b\u0435\u0433\u043a\u043e \u0441\u043a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043e \u0442\u043e\u043c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0443 (\u0447\u0435\u0440\u0435\u0437 multi-match query) \u0438 \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u043f\u0440\u0435\u0434\u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u043a\u043b\u0430\u0434\u0451\u043c \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 (\u0447\u0435\u0440\u0435\u0437 function score query).<\/p>\n<p>  \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0430 \u0440\u0443\u043a\u0430\u0445 \u0443 \u043d\u0430\u0441 \u0431\u0430\u0437\u0430 TMDB 5000, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043b\u0435\u0436\u0430\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0444\u0438\u043b\u044c\u043c\u043e\u0432 \u0438, \u043f\u043e\u043c\u0438\u043c\u043e \u043f\u0440\u043e\u0447\u0435\u0433\u043e, \u0438\u0445 \u0440\u0435\u0439\u0442\u0438\u043d\u0433\u0438, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0432\u043e\u0437\u044c\u043c\u0451\u043c \u0440\u0435\u0439\u0442\u0438\u043d\u0433 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0431\u0440\u0430\u0437\u0446\u043e\u0432\u043e-\u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430.<\/p>\n<p>  \u0412 <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/commit\/8eb377a5d3efb20846ea58d27351aaaef3749b49\">\u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043c\u0438\u0442\u0435<\/a> \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0432 \u0431\u044d\u043a\u0435\u043d\u0434 \u043d\u0430\u0448\u0435\u0433\u043e \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435\u043a\u0443\u044e \u0431\u0430\u0437\u043e\u0432\u0443\u044e \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u043b \u043f\u043e\u0434\u0433\u0440\u0443\u0437\u043a\u0443 \u0440\u0435\u0439\u0442\u0438\u043d\u0433\u0430 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 \u0441 \u0444\u0438\u043b\u044c\u043c\u0430\u043c\u0438. \u0414\u0430\u0431\u044b \u043d\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u0430\u0441 \u0447\u0438\u0442\u0430\u0442\u044c \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u0443\u044e \u043a\u0443\u0447\u0443 \u043a\u043e\u0434\u0430, \u043e\u043f\u0438\u0448\u0443 \u0441\u0430\u043c\u043e\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0435.<\/p>\n<ul>\n<li>\u041f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0439 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u043e\u043c. \u0421\u0432\u0430\u043b\u0438\u0432\u0430\u0442\u044c \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043e\u0434\u043d\u0443 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u2014 \u043f\u043e\u0440\u043e\u0447\u043d\u0430\u044f \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0430.<\/li>\n<li>\u0412 \u044d\u0442\u043e\u0442 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u0438 \u0438 \u043a\u043b\u0430\u0441\u0442\u044c \u0432\u0441\u0435 \u0438\u043c\u0435\u044e\u0449\u0438\u0435\u0441\u044f \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0432 \u0438\u043d\u0434\u0435\u043a\u0441\u0438\u0440\u0443\u0435\u043c\u044b\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b.<\/li>\n<li>\u0427\u0442\u043e\u0431\u044b \u0437\u043d\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u0443 \u0438\u043d\u0434\u0435\u043a\u0441\u0430, \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u043f\u0435\u0440\u0435\u0434 \u043d\u0430\u0447\u0430\u043b\u043e\u043c \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u0437\u043d\u0430\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432. \u042d\u0442\u043e\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u043c\u044b \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0437\u0430\u0445\u0430\u0440\u0434\u043a\u043e\u0434\u0438\u043c.<\/li>\n<li>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u043d\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c\u0441\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u043f\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432, \u0430 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u0438\u0445 \u0438\u0437 \u0443\u0436\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043e\u0431\u0441\u0447\u0451\u0442\u0430 \u043c\u043e\u0434\u0435\u043b\u0438, \u043c\u044b \u0432\u044b\u043a\u043b\u044e\u0447\u0438\u043c \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 \u043f\u043e \u043d\u043e\u0432\u044b\u043c \u043f\u043e\u043b\u044f\u043c \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0445 \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u0432 \u043e\u043f\u0446\u0438\u0435\u0439 <code>index: false<\/code> \u0432 \u0441\u0445\u0435\u043c\u0435 \u0438 \u0441\u044d\u043a\u043e\u043d\u043e\u043c\u0438\u043c \u0437\u0430 \u0441\u0447\u0451\u0442 \u044d\u0442\u043e\u0433\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u0430.<\/li>\n<\/ul>\n<h2>\u0420\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435: \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c \u0434\u0430\u0442\u0430\u0441\u0435\u0442<\/h2>\n<p>  \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443, \u0432\u043e-\u043f\u0435\u0440\u0432\u044b\u0445, \u0443 \u043d\u0430\u0441 \u043d\u0435\u0442 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u0430, \u0430 \u0432\u043e-\u0432\u0442\u043e\u0440\u044b\u0445, \u043f\u043e\u043b\u044f \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u0430\u043b\u044b \u0434\u043b\u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430 \u043f\u0440\u043e \u0442\u0435\u043b\u0435\u043c\u0435\u0442\u0440\u0438\u044e, Kafka, NiFi, Hadoop, Spark \u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u0435 ETL-\u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432, \u044f \u043f\u0440\u043e\u0441\u0442\u043e <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/blob\/7433f86242465e2fe595ebe2f188c20f8ac64eda\/backend\/tools\/generate_movie_events.py\">\u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u044e<\/a> \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0435 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u044b \u0438 \u043a\u043b\u0438\u043a\u0438 \u0434\u043b\u044f \u043d\u0430\u0448\u0438\u0445 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0438 \u043a\u0430\u043a\u0438\u0445-\u0442\u043e \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0432\u0448\u0438\u0445\u0441\u044f \u043f\u0430\u0440 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0430-\u0437\u0430\u043f\u0440\u043e\u0441.<\/p>\n<p>  \u041f\u0440\u0438\u0448\u043b\u0430 \u043f\u043e\u0440\u0430 \u0437\u0430\u043a\u043e\u043f\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0433\u043b\u0443\u0431\u0436\u0435 \u0432 API \u043f\u043b\u0430\u0433\u0438\u043d\u0430 ELTR. \u0427\u0442\u043e\u0431\u044b \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c feature store (\u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u044f \u043f\u043e\u043d\u0438\u043c\u0430\u044e, \u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u043d\u0434\u0435\u043a\u0441 \u0432 Elasticsearch, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043b\u0430\u0433\u0438\u043d \u0445\u0440\u0430\u043d\u0438\u0442 \u0432\u0441\u0435 \u0441\u0432\u043e\u0438 \u0434\u0430\u043d\u043d\u044b\u0435), \u043f\u043e\u0442\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c feature set \u2014 \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c, \u043a\u0430\u043a \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445. \u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0445\u043e\u0434\u0438\u0442\u044c \u0432 Elasticsearch \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0432\u0438\u0434\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0432\u0435\u043a\u0442\u043e\u0440 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435.<\/p>\n<p>  \u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f feature set:<\/p>\n<pre><code class=\"python\"># backend\/backend\/search\/ranking.py  from typing import Iterable, List, Mapping  from elasticsearch import Elasticsearch from elasticsearch_ltr import LTRClient  from backend.search.features import CardFeaturesManager   class SearchRankingManager:      DEFAULT_FEATURE_SET_NAME = &quot;card_features&quot;      def __init__(self, elasticsearch_client: Elasticsearch,                   card_features_manager: CardFeaturesManager,                  cards_index_name: str):         self.elasticsearch_client = elasticsearch_client         self.card_features_manager = card_features_manager         self.cards_index_name = cards_index_name      def initialize_ranking(self, feature_set_name=DEFAULT_FEATURE_SET_NAME):         ltr: LTRClient = self.elasticsearch_client.ltr         try:             # \u0421\u043e\u0437\u0434\u0430\u0442\u044c feature store \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b,             # \u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u0435\u0433\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0434\u0432\u0430\u0436\u0434\u044b \u00af\\_(\u30c4)_\/\u00af             ltr.create_feature_store()         except Exception as exc:             if &quot;resource_already_exists_exception&quot; not in str(exc):                 raise         # \u0421\u043e\u0437\u0434\u0430\u0451\u043c feature set \u0441 \u043d\u0435\u0432\u0435\u0440\u043e\u044f\u0442\u043d\u044b\u043c\u0438 \u0422\u0420\u0415\u041c\u042f \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430\u043c\u0438!         ltr.create_feature_set(feature_set_name, {             &quot;featureset&quot;: {                 &quot;features&quot;: [                     # \u0421\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c                     # \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0441\u0438\u043b\u044c\u043d\u044b\u043c \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u043c,                      # \u0447\u0435\u043c \u0441\u043e\u0432\u043f\u0430\u0434\u0435\u043d\u0438\u0435 \u0441\u043e \u0432\u0441\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u044b\u043c, \u043f\u043e\u044d\u0442\u043e\u043c\u0443                      # \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u0438\u0437\u043d\u0430\u043a \u043f\u0440\u043e \u044d\u0442\u043e.                     self._make_feature(&quot;name_tf_idf&quot;, [&quot;query&quot;], {                         &quot;match&quot;: {                             # ELTR \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u043e\u0432\u0430\u0442\u044c                             # \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u044e\u0449\u0438\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438. \u0412 \u0434\u0430\u043d\u043d\u043e\u043c                             # \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0430\u043c, \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u043d\u0443\u0436\u0435\u043d \u0442\u0435\u043a\u0441\u0442                              # \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c                              # \u0441\u043a\u043e\u0440 match query.                             &quot;name&quot;: &quot;{{query}}&quot;                         }                     }),                     # \u0421\u043a\u043e\u0440 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u044b \u0438\u0449\u0435\u043c \u0441\u0435\u0439\u0447\u0430\u0441.                     self._make_feature(&quot;combined_tf_idf&quot;, [&quot;query&quot;], {                         &quot;multi_match&quot;: {                             &quot;query&quot;: &quot;{{query}}&quot;,                             &quot;fields&quot;: [&quot;name^3&quot;, &quot;tags.text&quot;, &quot;text&quot;]                         }                     }),                     *(                         # \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432\u0441\u0435 \u0438\u043c\u0435\u044e\u0449\u0438\u0435\u0441\u044f \u043f\u0440\u0435\u0434\u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435                         # \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c function score.                         # \u0415\u0441\u043b\u0438 \u043f\u043e \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u0435 \u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435                          # \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0438\u0441\u043a\u043e\u043c\u043e\u0435 \u043f\u043e\u043b\u0435, \u0431\u0435\u0440\u0451\u043c 0.                         # (\u0412 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u043c \u043f\u0440\u043e\u0435\u043a\u0442\u0435 \u0432\u0430\u043c \u0441\u0442\u043e\u0438\u0442                         # \u043f\u0440\u0435\u0434\u0443\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0448\u0435!)                         self._make_feature(feature_name, [], {                             &quot;function_score&quot;: {                                 &quot;field_value_factor&quot;: {                                     &quot;field&quot;: feature_name,                                     &quot;missing&quot;: 0                                  }                             }                         })                         for feature_name in sorted(self.card_features_manager.get_all_feature_names_set())                     )                 ]             }         })       @staticmethod     def _make_feature(name, params, query):         return {             &quot;name&quot;: name,             &quot;params&quot;: params,             &quot;template_language&quot;: &quot;mustache&quot;,             &quot;template&quot;: query,         } <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u044e\u0449\u0430\u044f \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a:<\/p>\n<pre><code class=\"python\">    def compute_cards_features(self, query: str, card_ids: Iterable[str],                                 feature_set_name=DEFAULT_FEATURE_SET_NAME) -&gt; Mapping[str, List[float]]:         card_ids = list(card_ids)         result = self.elasticsearch_client.search({             &quot;query&quot;: {                 &quot;bool&quot;: {                     # \u041d\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c, \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u043b\u0438 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438                     # \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u043f\u043e \u0442\u0430\u043a\u043e\u043c\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0443 \u2014 \u0435\u0441\u043b\u0438 \u043d\u0435\u0442,                      # \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0431\u0443\u0434\u0443\u0442 \u043d\u0443\u043b\u0435\u0432\u044b\u043c\u0438.                     # \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u0444\u0438\u043b\u044c\u0442\u0440 \u043f\u043e ID.                     &quot;filter&quot;: [                         {                             &quot;terms&quot;: {                                 &quot;_id&quot;: card_ids                             }                         },                         # \u042d\u0442\u043e \u2014 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043d\u043e\u0432\u044b\u0439 \u0442\u0438\u043f \u0437\u0430\u043f\u0440\u043e\u0441\u0430,                         # \u0432\u0432\u043e\u0434\u0438\u043c\u044b\u0439 \u043f\u043b\u0430\u0433\u0438\u043d\u043e\u043c SLTR. \u041e\u043d \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442                         # \u043f\u043b\u0430\u0433\u0438\u043d \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0432\u0441\u0435 \u0444\u0430\u043a\u0442\u043e\u0440\u044b \u0438\u0437 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0433\u043e                         # feature set.                         # (\u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u0432\u0441\u0451 \u0435\u0449\u0451 \u0432 \u0440\u0430\u0437\u0434\u0435\u043b\u0435                         # filter, \u044d\u0442\u043e\u0442 \u0437\u0430\u043f\u0440\u043e\u0441 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u0443\u0435\u0442.)                         {                             &quot;sltr&quot;: {                                 &quot;_name&quot;: &quot;logged_featureset&quot;,                                 &quot;featureset&quot;: feature_set_name,                                 &quot;params&quot;: {                                     # \u0422\u0430 \u0441\u0430\u043c\u0430\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u0430\u0446\u0438\u044f.                                      # \u0421\u0442\u0440\u043e\u043a\u0430, \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u0430\u044f \u0441\u044e\u0434\u0430,                                     # \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0441\u044f \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445                                     # \u0432\u043c\u0435\u0441\u0442\u043e {{query}}.                                     &quot;query&quot;: query                                 }                             }                         }                     ]                 }             },             # \u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u0437\u0430\u0441\u0442\u0430\u0432\u0438\u0442 \u043f\u043b\u0430\u0433\u0438\u043d \u0437\u0430\u043f\u043e\u043c\u043d\u0438\u0442\u044c \u0432\u0441\u0435             # \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u0445 \u0432 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e\u0438\u0441\u043a\u0430.             &quot;ext&quot;: {                 &quot;ltr_log&quot;: {                     &quot;log_specs&quot;: {                         &quot;name&quot;: &quot;log_entry1&quot;,                         &quot;named_query&quot;: &quot;logged_featureset&quot;                     }                 }             },             &quot;size&quot;: len(card_ids),         })         # \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u0438\u0437 (\u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e         # \u0437\u0430\u043c\u044b\u0441\u043b\u043e\u0432\u0430\u0442\u043e\u0433\u043e) \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u043f\u043e\u0438\u0441\u043a\u0430.         # (\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c, \u0433\u0434\u0435 \u0432 \u043d\u0435\u0434\u0440\u0430\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043d\u0443\u0436\u043d\u044b\u0435 \u043c\u043d\u0435          # \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f, \u044f \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u0435\u043b\u0430\u044e \u043f\u0440\u043e\u0431\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 Kibana.)         return {             hit[&quot;_id&quot;]: [feature.get(&quot;value&quot;, float(&quot;nan&quot;)) for feature in hit[&quot;fields&quot;][&quot;_ltrlog&quot;][0][&quot;log_entry1&quot;]]             for hit in result[&quot;hits&quot;][&quot;hits&quot;]         } <\/code><\/pre>\n<p>  \u041f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439 \u0441\u043a\u0440\u0438\u043f\u0442, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u0439 \u043d\u0430 \u0432\u0445\u043e\u0434 CSV \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u0438 ID \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0438 \u0432\u044b\u0434\u0430\u044e\u0449\u0438\u0439 CSV \u0441 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430\u043c\u0438:<\/p>\n<pre><code class=\"python\"># backend\/tools\/compute_movie_features.py  import csv import itertools import sys  import tqdm  from backend.wiring import Wiring  if __name__ == &quot;__main__&quot;:     wiring = Wiring()      reader = iter(csv.reader(sys.stdin))     header = next(reader)      feature_names = wiring.search_ranking_manager.get_feature_names()     writer = csv.writer(sys.stdout)     writer.writerow([&quot;query&quot;, &quot;card_id&quot;] + feature_names)      query_index = header.index(&quot;query&quot;)     card_id_index = header.index(&quot;card_id&quot;)      chunks = itertools.groupby(reader, lambda row: row[query_index])     for query, rows in tqdm.tqdm(chunks):         card_ids = [row[card_id_index] for row in rows]         features = wiring.search_ranking_manager.compute_cards_features(query, card_ids)         for card_id in card_ids:             writer.writerow((query, card_id, *features[card_id])) <\/code><\/pre>\n<p>  \u041d\u0430\u043a\u043e\u043d\u0435\u0446 \u043c\u043e\u0436\u043d\u043e \u044d\u0442\u043e \u0432\u0441\u0451 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c!<\/p>\n<pre><code class=\"bash\"># \u0421\u043e\u0437\u0434\u0430\u0451\u043c feature set docker-compose exec backend python -m tools.initialize_search_ranking  # \u0413\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0441\u043e\u0431\u044b\u0442\u0438\u044f docker-compose exec -T backend \\     python -m tools.generate_movie_events \\     &lt; ~\/Downloads\/tmdb-movie-metadata\/tmdb_5000_movies.csv \\     &gt; ~\/Downloads\/habr-app-demo-dataset-events.csv  # \u0421\u0447\u0438\u0442\u0430\u0435\u043c \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 docker-compose exec -T backend \\     python -m tools.compute_features \\     &lt; ~\/Downloads\/habr-app-demo-dataset-events.csv \\     &gt; ~\/Downloads\/habr-app-demo-dataset-features.csv <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0444\u0430\u0439\u043b\u0430 \u2014 \u0441 \u0441\u043e\u0431\u044b\u0442\u0438\u044f\u043c\u0438 \u0438 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430\u043c\u0438 \u2014 \u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044e.<\/p>\n<h2>\u0420\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435: \u043e\u0431\u0443\u0447\u0430\u0435\u043c \u0438 \u0432\u043d\u0435\u0434\u0440\u044f\u0435\u043c \u043c\u043e\u0434\u0435\u043b\u044c<\/h2>\n<p>  \u041e\u043f\u0443\u0441\u0442\u0438\u043c \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u043e\u0432 (\u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/commit\/fa01fcc53cd8608fc914aec9b67026936ec23735\">\u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043c\u0438\u0442\u0435<\/a>) \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0451\u043c \u0441\u0440\u0430\u0437\u0443 \u043a \u0434\u0435\u043b\u0443.<\/p>\n<pre><code class=\"python\"># backend\/tools\/train_model.py  ...   if __name__ == &quot;__main__&quot;:     args = parser.parse_args()      feature_names, features = read_features(args.features)     events = read_events(args.events)      # \u0420\u0430\u0437\u0434\u0435\u043b\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 train \u0438 test \u0432 \u0441\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0438 4 \u043a 1.     all_queries = set(events.keys())     train_queries = random.sample(all_queries, int(0.8 * len(all_queries)))     test_queries = all_queries - set(train_queries)      # DMatrix \u2014 \u044d\u0442\u043e \u0442\u0438\u043f \u0434\u0430\u043d\u043d\u044b\u0445, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 xgboost.     # \u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u044d\u0442\u043e \u043c\u0430\u0441\u0441\u0438\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f\u043c\u0438      # \u0438 \u043b\u0435\u0439\u0431\u043b\u0430\u043c\u0438. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043b\u0435\u0439\u0431\u043b\u0430 \u043c\u044b \u0431\u0435\u0440\u0451\u043c 1, \u0435\u0441\u043b\u0438 \u0431\u044b\u043b \u043a\u043b\u0438\u043a,      # \u0438 0, \u0435\u0441\u043b\u0438 \u043d\u0435 \u0431\u044b\u043b\u043e (\u0434\u0435\u0442\u0430\u043b\u0438 \u0441\u043c. \u0432 \u043a\u043e\u043c\u043c\u0438\u0442\u0435).     train_dmatrix = make_dmatrix(train_queries, events, feature_names, features)     test_dmatrix = make_dmatrix(test_queries, events, feature_names, features)      # \u0423\u0447\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u044c!     # \u041f\u043e\u043b\u044f \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0432\u0441\u0451 \u0435\u0449\u0451 \u043a\u0440\u0430\u0439\u043d\u0435 \u043c\u0430\u043b\u044b \u0434\u043b\u044f \u0434\u043e\u043b\u0433\u043e\u0433\u043e \u0440\u0430\u0437\u0433\u043e\u0432\u043e\u0440\u0430      # \u043f\u0440\u043e ML, \u0442\u0430\u043a \u0447\u0442\u043e \u044f \u0432\u043e\u0437\u044c\u043c\u0443 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440      # \u0438\u0437 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b\u0430 \u043a XGBoost.     param = {         &quot;max_depth&quot;: 2,         &quot;eta&quot;: 0.3,         &quot;objective&quot;: &quot;binary:logistic&quot;,         &quot;eval_metric&quot;: &quot;auc&quot;,     }     num_round = 10     booster = xgboost.train(param, train_dmatrix, num_round, evals=((train_dmatrix, &quot;train&quot;), (test_dmatrix, &quot;test&quot;)))      # \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043e\u0431\u0443\u0447\u0435\u043d\u043d\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u0432 \u0444\u0430\u0439\u043b.      booster.dump_model(args.output, dump_format=&quot;json&quot;)       # \u0421\u0430\u043d\u0438\u0442\u0430\u0440\u043d\u044b\u0439 \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u043f\u0440\u043e\u0448\u043b\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435: \u0434\u0430\u0432\u0430\u0439\u0442\u0435     # \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0442\u043e\u043f \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u043f\u043e \u0437\u043d\u0430\u0447\u0438\u043c\u043e\u0441\u0442\u0438 \u0438 \u043d\u0430 ROC-\u043a\u0440\u0438\u0432\u0443\u044e.     xgboost.plot_importance(booster)      plt.figure()     build_roc(test_dmatrix.get_label(), booster.predict(test_dmatrix))      plt.show() <\/code><\/pre>\n<p>  \u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <\/p>\n<pre><code class=\"bash\">python backend\/tools\/train_search_ranking_model.py \\     --events ~\/Downloads\/habr-app-demo-dataset-events.csv \\     --features ~\/Downloads\/habr-app-demo-dataset-features.csv \\      -o ~\/Downloads\/habr-app-demo-model.xgb <\/code><\/pre>\n<p>  \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0432\u0441\u0435 \u043d\u0443\u0436\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u043c\u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u0430\u043c\u0438, \u044d\u0442\u043e\u0442 \u0441\u043a\u0440\u0438\u043f\u0442 \u0443\u0436\u0435 \u043d\u0435 \u043d\u0430\u0434\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u0438 \u0434\u043e\u043a\u0435\u0440\u0430 \u2014 \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0430 \u0432\u0430\u0448\u0435\u0439 \u043c\u0430\u0448\u0438\u043d\u0435, \u043f\u0440\u0435\u0434\u0432\u0430\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0432 <code>xgboost<\/code> \u0438 <code>sklearn<\/code>. \u0410\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u043c \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u0435 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u044b \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0433\u0434\u0435-\u0442\u043e, \u0433\u0434\u0435 \u0435\u0441\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d-\u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435, \u0430 \u044d\u0442\u043e\u0442 \u2014 \u043d\u0435\u0442.<\/p>\n<p>  \u0415\u0441\u043b\u0438 \u0432\u0441\u0451 \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e, \u043c\u043e\u0434\u0435\u043b\u044c \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u043e\u0431\u0443\u0447\u0438\u0442\u0441\u044f, \u0438 \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u0434\u0432\u0435 \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0445 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. \u041f\u0435\u0440\u0432\u0430\u044f \u2014 \u0433\u0440\u0430\u0444\u0438\u043a \u0437\u043d\u0430\u0447\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/uh\/iv\/ra\/uhivra2yx5kpj3hdb4o1acyq7ug.png\"><\/p>\n<p>  \u0425\u043e\u0442\u044f \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e, <code>combined_tf_idf<\/code> \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u0441\u0438\u043b\u044c\u043d\u043e \u0437\u043d\u0430\u0447\u0438\u043c\u0435\u0435 \u0434\u0440\u0443\u0433\u0438\u0445 \u2014 \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u0444\u043e\u043a\u0443\u0441 \u0438 \u0438\u0441\u043a\u0443\u0441\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043f\u043e\u043d\u0438\u0437\u0438\u043b \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u043a\u043b\u0438\u043a\u0430 \u0434\u043b\u044f \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0438\u0436\u0435 \u0432 \u0432\u044b\u0434\u0430\u0447\u0435, \u043e\u0442\u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u043d\u0430\u0448\u0438\u043c \u0441\u0442\u0430\u0440\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c. \u0422\u043e, \u0447\u0442\u043e \u043c\u043e\u0434\u0435\u043b\u044c \u044d\u0442\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u043b\u0430 \u2014 \u0434\u043e\u0431\u0440\u044b\u0439 \u0437\u043d\u0430\u043a \u0438 \u043f\u0440\u0438\u0437\u043d\u0430\u043a \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u043d\u0435 \u0441\u043e\u0432\u0435\u0440\u0448\u0438\u043b\u0438 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u0430\u043a\u0438\u0445-\u0442\u043e \u0441\u043e\u0432\u0441\u0435\u043c \u0433\u043b\u0443\u043f\u044b\u0445 \u043e\u0448\u0438\u0431\u043e\u043a.<\/p>\n<p>  \u0412\u0442\u043e\u0440\u043e\u0439 \u0433\u0440\u0430\u0444\u0438\u043a \u2014 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/ROC-%D0%BA%D1%80%D0%B8%D0%B2%D0%B0%D1%8F\">ROC-\u043a\u0440\u0438\u0432\u0430\u044f<\/a>:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/og\/nc\/og\/ogncoglpwwigl8ewayp-qarczp0.png\"><\/p>\n<p>  \u0421\u0438\u043d\u044f\u044f \u043b\u0438\u043d\u0438\u044f \u0432\u044b\u0448\u0435 \u043a\u0440\u0430\u0441\u043d\u043e\u0439, \u0430 \u0437\u043d\u0430\u0447\u0438\u0442, \u043d\u0430\u0448\u0430 \u043c\u043e\u0434\u0435\u043b\u044c \u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u043b\u0435\u0439\u0431\u043b\u044b \u0447\u0443\u0442\u044c-\u0447\u0443\u0442\u044c \u043b\u0443\u0447\u0448\u0435, \u0447\u0435\u043c \u0431\u0440\u043e\u0441\u043e\u043a \u043c\u043e\u043d\u0435\u0442\u043a\u0438. (\u041a\u0440\u0438\u0432\u0430\u044f ML-\u0438\u043d\u0436\u0435\u043d\u0435\u0440\u0430 \u043c\u0430\u043c\u0438\u043d\u043e\u0439 \u043f\u043e\u0434\u0440\u0443\u0433\u0438 \u0434\u043e\u043b\u0436\u043d\u0430 \u043f\u043e\u0447\u0442\u0438 \u043a\u0430\u0441\u0430\u0442\u044c\u0441\u044f \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u043b\u0435\u0432\u043e\u0433\u043e \u0443\u0433\u043b\u0430).<\/p>\n<p>  \u0414\u0435\u043b\u043e \u0441\u043e\u0432\u0441\u0435\u043c \u0437\u0430 \u043c\u0430\u043b\u044b\u043c \u2014 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/commit\/43a730bf4c1718c47d77fb82874f86c44889bebd\">\u0441\u043a\u0440\u0438\u043f\u0442 \u0434\u043b\u044f \u0437\u0430\u043b\u0438\u0432\u043a\u0438 \u043c\u043e\u0434\u0435\u043b\u0438<\/a>, \u0437\u0430\u043b\u0438\u0432\u0430\u0435\u043c \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u0439 \u043d\u043e\u0432\u044b\u0439 \u043f\u0443\u043d\u043a\u0442 \u0432 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u0440\u0435\u0441\u043a\u043e\u0440\u0438\u043d\u0433:<\/p>\n<pre><code class=\"python\">--- a\/backend\/backend\/search\/searcher_impl.py +++ b\/backend\/backend\/search\/searcher_impl.py @@ -27,6 +30,19 @@ class ElasticsearchSearcher(Searcher):                      &quot;filter&quot;: list(self._make_filter_queries(tags, ids)),                  }              }, +            &quot;rescore&quot;: { +                &quot;window_size&quot;: 1000, +                &quot;query&quot;: { +                    &quot;rescore_query&quot;: { +                        &quot;sltr&quot;: { +                            &quot;params&quot;: { +                                &quot;query&quot;: query +                            }, +                            &quot;model&quot;: self.ranking_manager.get_current_model_name() +                        } +                    } +                } +            },              &quot;aggregations&quot;: {                  self.TAGS_AGGREGATION_NAME: {                      &quot;terms&quot;: {&quot;field&quot;: &quot;tags&quot;} <\/code><\/pre>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a Elasticsearch \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0451\u0442 \u043d\u0443\u0436\u043d\u044b\u0439 \u043d\u0430\u043c \u043f\u043e\u0438\u0441\u043a \u0438 \u043e\u0442\u0440\u0430\u043d\u0436\u0438\u0440\u0443\u0435\u0442 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0441\u0432\u043e\u0438\u043c (\u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u044b\u043c) \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u043c, \u043c\u044b \u0432\u043e\u0437\u044c\u043c\u0451\u043c \u0442\u043e\u043f-1000 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u0438 \u043f\u0435\u0440\u0435\u0440\u0430\u043d\u0436\u0438\u0440\u0443\u0435\u043c, \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0432 \u043d\u0430\u0448\u0443 (\u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0443\u044e) \u043c\u0430\u0448\u0438\u043d\u043d\u043e-\u043e\u0431\u0443\u0447\u0435\u043d\u043d\u0443\u044e \u0444\u043e\u0440\u043c\u0443\u043b\u0443. \u0423\u0441\u043f\u0435\u0445!<\/p>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>  \u041c\u044b \u0432\u0437\u044f\u043b\u0438 \u043d\u0430\u0448\u0435 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u043e\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438 \u043f\u0440\u043e\u0448\u043b\u0438 \u043f\u0443\u0442\u044c \u043e\u0442 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044f \u0444\u0438\u0447\u0438 \u043f\u043e\u0438\u0441\u043a\u0430 \u043a\u0430\u043a \u0442\u0430\u043a\u043e\u0432\u043e\u0439 \u0434\u043e \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0441\u043e \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e\u043c \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439. \u0421\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u043d\u0435 \u0442\u0430\u043a \u0443\u0436 \u043f\u0440\u043e\u0441\u0442\u043e. \u041d\u043e \u0438 \u043d\u0435 \u0442\u0430\u043a \u0443\u0436 \u0441\u043b\u043e\u0436\u043d\u043e! \u0418\u0442\u043e\u0433\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043b\u0435\u0436\u0438\u0442 \u0432 <a href=\"https:\/\/github.com\/Saluev\/habr-app-demo\/tree\/feature\/search\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u043d\u0430 Github<\/a> \u0432 \u0432\u0435\u0442\u043a\u0435 \u0441\u043e \u0441\u043a\u0440\u043e\u043c\u043d\u044b\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c <code>feature\/search<\/code> \u0438 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 Docker \u0438 Python 3 \u0441 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c\u0438 \u0434\u043b\u044f \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f.<\/p>\n<p>  \u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u044d\u0442\u043e \u0432 \u0446\u0435\u043b\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u043a\u0430\u043a\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u044e\u0442\u0441\u044f \u0438 \u043a\u0430\u043a \u0438\u0445 \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0448\u0438\u0442\u044c, \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b Elasticsearch, \u043d\u043e \u044d\u0442\u043e, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043d\u0435 \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0431\u0440\u0430\u0442\u044c. <a href=\"https:\/\/lucene.apache.org\/solr\/\">Solr<\/a>, <a href=\"https:\/\/www.postgresql.org\/docs\/9.5\/textsearch.html\">\u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u044b PostgreSQL<\/a> \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0434\u0432\u0438\u0436\u043a\u0438 \u0442\u043e\u0447\u043d\u043e \u0442\u0430\u043a \u0436\u0435 \u0437\u0430\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u044e\u0442 \u0432\u0430\u0448\u0435\u0433\u043e \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043f\u0440\u0438 \u0432\u044b\u0431\u043e\u0440\u0435, \u043d\u0430 \u0447\u0451\u043c \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0432\u043e\u044e <s>\u043c\u043d\u043e\u0433\u043e\u043c\u0438\u043b\u043b\u0438\u0430\u0440\u0434\u043d\u0443\u044e \u043a\u043e\u0440\u043f\u043e\u0440\u0430\u0446\u0438\u044e<\/s> \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0443\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443.<\/p>\n<p>  \u0418, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u044d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0435 \u043f\u0440\u0435\u0442\u0435\u043d\u0434\u0443\u0435\u0442 \u043d\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u0438 \u0433\u043e\u0442\u043e\u0432\u043d\u043e\u0441\u0442\u044c \u043a \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d\u0443, \u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0435\u0439 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u0432\u0441\u0451 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u043d\u043e. \u0423\u043b\u0443\u0447\u0448\u0430\u0442\u044c \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u043e!  <\/p>\n<ul>\n<li>\u0418\u043d\u043a\u0440\u0435\u043c\u0435\u043d\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u044f. \u041f\u0440\u0438 \u043c\u043e\u0434\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043d\u0430\u0448\u0438\u0445 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0447\u0435\u0440\u0435\u0437 <code>CardManager<\/code> \u0445\u043e\u0440\u043e\u0448\u043e \u0431\u044b \u0441\u0440\u0430\u0437\u0443 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u0432 \u0438\u043d\u0434\u0435\u043a\u0441\u0435. \u0427\u0442\u043e\u0431\u044b <code>CardManager<\/code> \u043d\u0435 \u0437\u043d\u0430\u043b, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u0435 \u0435\u0441\u0442\u044c \u0435\u0449\u0451 \u0438 \u043f\u043e\u0438\u0441\u043a, \u0438 \u043e\u0431\u043e\u0448\u043b\u043e\u0441\u044c \u0431\u0435\u0437 \u0446\u0438\u043a\u043b\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0435\u0439, \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043f\u0440\u0438\u043a\u0440\u0443\u0442\u0438\u0442\u044c <a href=\"https:\/\/en.wikipedia.org\/wiki\/Dependency_inversion_principle\">dependency inversion<\/a> \u0432 \u0442\u043e\u043c \u0438\u043b\u0438 \u0438\u043d\u043e\u043c \u0432\u0438\u0434\u0435.<\/li>\n<li>\u0414\u043b\u044f \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u0446\u0438\u0438 \u0432 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u0432\u044f\u0437\u043a\u0438 MongoDB \u0441 Elasticsearch \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0432\u0440\u043e\u0434\u0435 <a href=\"https:\/\/github.com\/yougov\/mongo-connector\">mongo-connector<\/a>.<\/li>\n<li>\u041f\u043e\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0432\u043e\u0434\u0438\u0442 \u0437\u0430\u043f\u0440\u043e\u0441, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0442\u044c \u0435\u043c\u0443 \u043f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0438 &mdash; \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432 Elasticsearch \u0435\u0441\u0442\u044c <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/search-suggesters.html\">\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c<\/a>.<\/li>\n<li>\u041a\u043e\u0433\u0434\u0430 \u0437\u0430\u043f\u0440\u043e\u0441 \u0432\u0432\u0435\u0434\u0451\u043d, \u0441\u0442\u043e\u0438\u0442 \u043f\u043e\u043f\u044b\u0442\u0430\u0442\u044c\u0441\u044f \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0432 \u043d\u0451\u043c \u043e\u043f\u0435\u0447\u0430\u0442\u043a\u0438, \u0438 \u044d\u0442\u043e <a href=\"https:\/\/habr.com\/ru\/company\/joom\/blog\/433554\/\">\u0442\u043e\u0436\u0435 \u0446\u0435\u043b\u043e\u0435 \u0434\u0435\u043b\u043e<\/a>.<\/li>\n<li>\u0414\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0443\u0436\u043d\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441 \u043f\u043e\u0438\u0441\u043a\u043e\u043c, \u0438\u0445 \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u044e \u0438 \u0440\u0430\u0441\u0447\u0451\u0442 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u043e\u0432 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u0447\u0451\u0442\u0447\u0438\u043a\u043e\u0432. \u041f\u0440\u0438\u0437\u043d\u0430\u043a\u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c-\u0437\u0430\u043f\u0440\u043e\u0441, \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c-\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c-\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u041c\u0435\u0440\u043a\u0443\u0440\u0438\u044f\u2026 \u0442\u044b\u0441\u044f\u0447\u0438 \u0438\u0445!<\/li>\n<li>\u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0432\u0435\u0441\u0435\u043b\u043e \u043f\u0438\u043b\u0438\u0442\u044c \u0430\u0433\u0440\u0435\u0433\u0430\u0446\u0438\u0438 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043d\u0435 \u043e\u0444\u043b\u0430\u0439\u043d\u043e\u0432\u044b\u0435 (\u0440\u0430\u0437 \u0432 \u0434\u0435\u043d\u044c, \u0440\u0430\u0437 \u0432 \u043d\u0435\u0434\u0435\u043b\u044e), \u0430 \u0440\u0435\u0430\u043b\u0442\u0430\u0439\u043c\u043e\u0432\u044b\u0435 (\u0437\u0430\u0434\u0435\u0440\u0436\u043a\u0430 \u043e\u0442 \u0441\u043e\u0431\u044b\u0442\u0438\u044f \u0434\u043e \u0443\u0447\u0451\u0442\u0430 \u0432 \u043f\u0440\u0438\u0437\u043d\u0430\u043a\u0430\u0445 \u0432 \u043f\u0440\u0435\u0434\u0435\u043b\u0430\u0445 \u043f\u044f\u0442\u0438 \u043c\u0438\u043d\u0443\u0442). \u0412\u0434\u0432\u043e\u0439\u043d\u0435 \u0432\u0435\u0441\u0435\u043b\u043e, \u043a\u043e\u0433\u0434\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0441\u043e\u0442\u043d\u0438 \u043c\u0438\u043b\u043b\u0438\u043e\u043d\u043e\u0432.<\/li>\n<li>\u041f\u0440\u0435\u0434\u0441\u0442\u043e\u0438\u0442 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0441 \u043f\u0440\u043e\u0433\u0440\u0435\u0432\u043e\u043c, \u043d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u043c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c, \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430\u043c\u0438.<\/li>\n<li>\u041e\u0440\u043a\u0435\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0442\u0435\u0440 \u043d\u043e\u0434 \u0441 \u0448\u0430\u0440\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0438 \u0440\u0435\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0435\u0439 &mdash; \u044d\u0442\u043e \u0446\u0435\u043b\u043e\u0435 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u0441\u043b\u0430\u0436\u0434\u0435\u043d\u0438\u0435.<\/li>\n<\/ul>\n<p>  \u041d\u043e \u0447\u0442\u043e\u0431\u044b \u0441\u0442\u0430\u0442\u044c\u044f \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0447\u0438\u0442\u0430\u0431\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430, \u044f \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u044e\u0441\u044c \u043d\u0430 \u044d\u0442\u043e\u043c \u0438 \u043e\u0441\u0442\u0430\u0432\u043b\u044e \u0432\u0430\u0441 \u043d\u0430\u0435\u0434\u0438\u043d\u0435 \u0441 \u044d\u0442\u0438\u043c\u0438 \u0447\u0435\u043b\u043b\u0435\u043d\u0434\u0436\u0430\u043c\u0438. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/joom\/blog\/526550\/\"> https:\/\/habr.com\/ru\/company\/joom\/blog\/526550\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u00ab<a href=\"https:\/\/habr.com\/ru\/post\/444446\/\">\u0414\u0435\u043b\u0430\u0435\u043c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441 \u043d\u0443\u043b\u044f<\/a>\u00bb \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u043b \u0432 \u043e\u0431\u0449\u0438\u0445 \u0447\u0435\u0440\u0442\u0430\u0445, \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u044b\u0441\u043e\u043a\u043e\u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0445 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439, \u0438 \u0441\u043e\u0431\u0440\u0430\u043b \u0434\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0441\u0442\u0435\u0439\u0448\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0442\u0430\u043a\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043d\u0430 \u0441\u0442\u0435\u043a\u0435 \u0438\u0437 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043f\u0440\u0435\u0434\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 \u0438 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439 \u0438 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u043e\u0432. \u041c\u044b \u043f\u043e\u0441\u0442\u0440\u043e\u0438\u043b\u0438 single page application \u0441 server side rendering, \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044e\u0449\u0435\u0435 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u043d\u0435\u043a\u0438\u0445 \u00ab\u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a\u00bb, \u043d\u0430\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0432 Markdown, \u0438 \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044e \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438.<\/p>\n<p>  \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0437\u0430\u0442\u0440\u043e\u043d\u0443 \u0447\u0443\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u0443\u044e \u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0443\u044e (\u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c \u043c\u043d\u0435, \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0443 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u043f\u043e\u0438\u0441\u043a\u0430) \u0442\u0435\u043c\u0443: \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u043e\u0438\u0441\u043a. \u041c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043d\u0430\u0448 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043d\u044b\u0439 \u0440\u0430\u0439 \u043d\u043e\u0434\u0443 Elasticsearch, \u043d\u0430\u0443\u0447\u0438\u043c\u0441\u044f \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u043d\u0434\u0435\u043a\u0441 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0443, \u0432\u0437\u044f\u0432 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043f\u044f\u0442\u0438 \u0442\u044b\u0441\u044f\u0447 \u0444\u0438\u043b\u044c\u043c\u043e\u0432 \u0438\u0437 <a href=\"https:\/\/www.kaggle.com\/tmdb\/tmdb-movie-metadata\">TMDB 5000 Movie Dataset<\/a>. \u0422\u0430\u043a\u0436\u0435 \u043c\u044b \u043d\u0430\u0443\u0447\u0438\u043c\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u044b\u0435 \u0444\u0438\u043b\u044c\u0442\u0440\u044b \u0438 \u043a\u043e\u043f\u043d\u0451\u043c \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0436\u043a\u043e \u0432 \u0441\u0442\u043e\u0440\u043e\u043d\u0443 \u0440\u0430\u043d\u0436\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<div style=\"text-align:center;\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/vr\/iq\/p-\/vriqp-qiz6mnrb_9tlp0aynkck4.png\"><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-312592","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/312592","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=312592"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/312592\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=312592"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=312592"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=312592"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}