{"id":272327,"date":"2016-01-18T11:17:02","date_gmt":"2016-01-18T08:17:02","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=272327"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=272327","title":{"rendered":"\u0421\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a \u043f\u043e \u0440\u0430\u0437\u0434\u0430\u0447\u0430\u043c The Pirate Bay"},"content":{"rendered":"<p>       \u0412 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u0445\u0430\u0431\u0440\u0435 <a href=\"http:\/\/habrahabr.ru\/post\/274449\/\">\u0441\u0442\u0430\u043b\u043e \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e<\/a> \u0434\u0435\u043b\u0430\u0442\u044c <a href=\"http:\/\/habrahabr.ru\/post\/273777\/\">\u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0438<\/a> \u043f\u043e RuTracker. \u041c\u043d\u0435 \u044d\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u044b\u043c \u043f\u043e\u0432\u043e\u0434\u043e\u043c \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043e\u0439\u0442\u0438 \u043e\u0442 \u0441\u043a\u0443\u0447\u043d\u043e\u0439 enterprise \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u043d\u043e\u0432\u043e\u0435.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/15a\/5f1\/222\/15a5f1222fcb40aab6f759e182a0cf2b.png\"\/><\/p>\n<p>  \u0418\u0442\u0430\u043a, \u0437\u0430\u0434\u0430\u0447\u0430: \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u043b\u043e\u043a\u0430\u043b\u0445\u043e\u0441\u0442\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a \u043f\u043e \u0431\u0430\u0437\u0435 The Pirate Bay \u0438 \u043f\u043e\u043f\u0443\u0442\u043d\u043e \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u0436\u0435 \u0442\u0430\u043a\u043e\u0435 frontend \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0438 \u0441 \u0447\u0435\u043c \u0435\u0451 \u0435\u0434\u044f\u0442. \u0417\u0430\u0434\u0430\u0447\u0430 \u043e\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u0435\u043c, \u0447\u0442\u043e TPB \u043d\u0435 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u0442 \u0441\u0432\u043e\u0438\u0445 \u0434\u0430\u043c\u043f\u043e\u0432, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 RuTracker, \u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043c\u043f\u043e\u0432 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0440\u0430\u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0438\u0445 \u0441\u0430\u0439\u0442. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0433\u0443\u0433\u043b\u0435\u043d\u0438\u044f \u0438 \u043e\u0441\u043c\u044b\u0441\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/www.elastic.co\/products\/elasticsearch\">Elasticsearch<\/a>, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c client-side only \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0430 <a href=\"https:\/\/angularjs.org\/\">AngularJS<\/a>. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440 \u0441\u0430\u0439\u0442\u0430 TPB \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u0435\u043b\u044c \u0434\u0430\u043c\u043f\u0430 \u0432 \u0438\u043d\u0434\u0435\u043a\u0441, \u043e\u0431\u0430 \u043d\u0430 Go. \u041f\u0438\u043a\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0431\u043e\u0440\u0443 \u043f\u0440\u0438\u0434\u0430\u0432\u0430\u043b \u0442\u043e\u0442 \u0444\u0430\u043a\u0442, \u0447\u0442\u043e \u043d\u0438 \u043a Elasticsearch, \u043d\u0438 \u043a AngularJS \u044f \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043d\u0438 \u0440\u0430\u0437\u0443 \u043d\u0435 \u043f\u0440\u0438\u043a\u0430\u0441\u0430\u043b\u0441\u044f \u0438 \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0445 \u043e\u043f\u0440\u043e\u0431\u044b\u0432\u0430\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043c\u043e\u0435\u0439 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0439 \u0446\u0435\u043b\u044c\u044e.<br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<h3>\u041f\u0430\u0440\u0441\u0435\u0440<\/h3>\n<p>  \u041a\u0440\u0430\u0442\u043a\u0438\u0439 \u043e\u0441\u043c\u043e\u0442\u0440 \u0441\u0430\u0439\u0442\u0430 TPB \u043f\u043e\u043a\u0430\u0437\u0430\u043b, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0442\u043e\u0440\u0440\u0435\u043d\u0442 \u0438\u043c\u0435\u0435\u0442 \u0441\u0432\u043e\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 &quot;\/torrent\/{id}&quot;. \u0413\u0434\u0435 id \u0432\u043e-\u043f\u0435\u0440\u0432\u044b\u0445 \u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0435, \u0432\u043e-\u0432\u0442\u043e\u0440\u044b\u0445 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u044e\u0442\u0441\u044f, \u0432-\u0442\u0440\u0435\u0442\u044c\u0438\u0445 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 id \u043f\u043e\u0441\u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 &quot;\/recent&quot; \u0438 \u043f\u043e\u0442\u043e\u043c \u043f\u0435\u0440\u0435\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0435 id \u043c\u0435\u043d\u044c\u0448\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e. \u041f\u0440\u0430\u043a\u0442\u0438\u043a\u0430 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u0430, \u0447\u0442\u043e id \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0435 \u043c\u043e\u043d\u043e\u0442\u043e\u043d\u043d\u043e \u0438 \u043d\u0435 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e id \u0435\u0441\u0442\u044c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441 \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u043e\u043c, \u0447\u0442\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0438 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u0430 id.<\/p>\n<p>  \u0422\u0430\u043a \u043a\u0430\u043a \u043f\u0430\u0440\u0441\u0435\u0440 \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0441\u0435\u0442\u044c\u044e \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0442\u043e\u043a\u043e\u0432, \u0432\u044b\u0431\u043e\u0440 Go \u0431\u044b\u043b \u043e\u0447\u0435\u0432\u0438\u0434\u0435\u043d. \u0414\u043b\u044f \u0440\u0430\u0437\u0431\u043e\u0440\u0430 HTML \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u043c\u043e\u0434\u0443\u043b\u044c <a href=\"http:\/\/github.com\/PuerkitoBio\/goquery\">goquery<\/a>. <\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/384\/fab\/1f9\/384fab1f97d047ed9096e904eee19033.png\"\/><\/p>\n<p>  \u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043f\u0430\u0440\u0441\u0435\u0440\u0430 \u0432\u0435\u0441\u044c\u043c\u0430 \u043f\u0440\u043e\u0441\u0442\u043e: \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442\u0441\u044f &quot;\/recent&quot; \u0438 \u0438\u0437 \u043d\u0435\u0451 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 id:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 id<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"go\">func getRecentId(topUrl string) int { \tvar url bytes.Buffer \turl.WriteString(topUrl) \turl.WriteString(&quot;\/recent&quot;)  \tlog.Info(&quot;Processing recent torrents page at: %s&quot;, url.String()) \tdoc, err := goquery.NewDocument(url.String()) \tif err != nil { \t\tlog.Critical(&quot;Can't download recent torrents page from TPB: %v&quot;, err) \t\treturn 0 \t}  \ttopTorrent := doc.Find(&quot;#searchResult .detName a&quot;).First() \tt, pT := topTorrent.Attr(&quot;title&quot;) \tu, pU := topTorrent.Attr(&quot;href&quot;) \tif pT && pU { \t\trx, _ := regexp.Compile(`\\\/torrent\\\/(\\d+)\\\/.*`) \t\tif rx.MatchString(u) { \t\t\tid, err := strconv.Atoi(rx.FindStringSubmatch(u)[1]) \t\t\tif err != nil { \t\t\t\tlog.Critical(&quot;Can't retrieve latest torrent id&quot;) \t\t\t\treturn 0 \t\t\t} \t\t\tlog.Info(&quot;The most recent torrent is %s and it's id is %d&quot;, t, id) \t\t\treturn id \t\t} \t} \treturn 0 } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0417\u0430\u0442\u0435\u043c \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0431\u0435\u0436\u0438\u043c \u043f\u043e \u0432\u0441\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c id \u043e\u0442 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u043e \u043d\u0443\u043b\u0435\u0432\u043e\u0433\u043e \u0438 \u0441\u043a\u0430\u0440\u043c\u043b\u0438\u0432\u0430\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0446\u0438\u0444\u0440\u044b \u0432 \u043a\u0430\u043d\u0430\u043b:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0421\u043a\u0443\u0447\u043d\u044b\u0439 \u0446\u0438\u043a\u043b \u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438.<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"go\">func (d *Downloader) run() { \td.wg.Add(streams) \tfor w := 0; w &lt;= streams; w++ { \t\tgo d.processPage() \t} \tfor w := d.initialId; w &gt;= 0; w-- { \t\td.pageId &lt;- w \t} \tclose(d.pageId) \tlog.Info(&quot;Processing complete, waiting for goroutines to finish&quot;) \td.wg.Wait() \td.output.Done() } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041a\u0430\u043a \u0432\u0438\u0434\u043d\u043e \u0438\u0437 \u043a\u043e\u0434\u0430, \u0441 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b \u043a\u0430\u043d\u0430\u043b\u0430 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0433\u043e\u0440\u0443\u0442\u0438\u043d, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u0445 id \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u0430, \u0441\u043a\u0430\u0447\u0438\u0432\u0430\u044e\u0449\u0438\u0445 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0438 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0449\u0443\u044e \u0435\u0451:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u0430<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"go\">func (d *Downloader) processPage() { \tfor id := range d.pageId { \t\tvar url bytes.Buffer \t\turl.WriteString(d.topUrl) \t\turl.WriteString(&quot;\/torrent\/&quot;) \t\turl.WriteString(strconv.Itoa(id))  \t\tlog.Info(&quot;Parsing torrent page at: %s&quot;, url.String()) \t\tdoc, err := goquery.NewDocument(url.String())  \t\tif err != nil { \t\t\tlog.Warning(&quot;Can't download torrent page %s from TPB: %v&quot;, url, err) \t\t\tcontinue \t\t}  \t\ttorrentData := doc.Find(&quot;#detailsframe&quot;) \t\tif torrentData.Length() &lt; 1 { \t\t\tlog.Warning(&quot;Erroneous torrent %d: \\&quot;%s\\&quot;&quot;, id, url.String()) \t\t\tcontinue \t\t}  \t\ttorrent := TorrentEntry{Id: id} \t\ttorrent.processTitle(torrentData) \t\ttorrent.processFirstColumn(torrentData) \t\ttorrent.processSecondColumn(torrentData) \t\ttorrent.processHash(torrentData) \t\ttorrent.processMagnet(torrentData) \t\ttorrent.processInfo(torrentData)  \t\td.output.Put(&torrent)  \t\tlog.Info(&quot;Processed torrent %d: \\&quot;%s\\&quot;&quot;, id, torrent.Title)  \t} \td.wg.Done() } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u043e\u0441\u043b\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 OutputModule, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0443\u0436\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0435\u0433\u043e \u0432 \u0442\u043e\u043c \u0438\u043b\u0438 \u0438\u043d\u043e\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u0435. \u042f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0434\u0432\u0430 \u043c\u043e\u0434\u0443\u043b\u044f \u0432\u044b\u0432\u043e\u0434\u0430, \u0432 csv \u0438 \u0432 \u00ab\u043f\u043e\u0447\u0442\u0438\u00bb json.<\/p>\n<p>  \u0424\u043e\u0440\u043c\u0430\u0442 csv:<\/p>\n<p>  <code> id \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u0430, \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435, \u0440\u0430\u0437\u043c\u0435\u0440, \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0444\u0430\u0439\u043b\u043e\u0432, \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f, \u043f\u043e\u0434\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f, \u0430\u0432\u0442\u043e\u0440 \u0437\u0430\u043a\u0430\u0447\u043a\u0438, \u0445\u044d\u0448, \u0434\u0430\u0442\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f, \u043c\u0430\u0433\u043d\u0435\u0442 \u0441\u0441\u044b\u043b\u043a\u0430 <\/code><\/p>\n<p>  Json friendly \u0444\u043e\u0440\u043c\u0430\u0442 \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c Json: \u043a\u0430\u0436\u0434\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 json \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u0442\u0435\u043c\u0438 \u0436\u0435 \u043f\u043e\u043b\u044f\u043c\u0438, \u0447\u0442\u043e \u0438 \u0432 csv, \u043f\u043b\u044e\u0441 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u0430.<\/p>\n<p>  \u041f\u043e\u043b\u043d\u044b\u0439 \u0434\u0430\u043c\u043f \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 3828894 \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u043e\u0432 \u0438 \u0437\u0430\u043d\u044f\u043b \u043f\u043e\u0447\u0442\u0438 30 \u0447\u0430\u0441\u043e\u0432 \u043d\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443.<\/p>\n<h3>\u0418\u043d\u0434\u0435\u043a\u0441<\/h3>\n<p>  \u041f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c \u043a\u0430\u043a \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 Elasticsearch, \u0435\u0433\u043e \u043d\u0430\u0434\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c. <\/p>\n<p>  \u0422\u0430\u043a \u043a\u0430\u043a \u044f \u0431\u044b \u0445\u043e\u0442\u0435\u043b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u0439 \u043f\u043e\u0438\u0441\u043a \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f\u043c \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u044f\u0437\u044b\u043a\u0430\u0445, \u0442\u043e \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c Unicode friendly \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Unicode \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440 \u0441 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u0438 \u043f\u0440\u043e\u0447\u0438\u043c\u0438 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f\u043c\u0438.<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">{   &quot;index&quot;: {     &quot;analysis&quot;: {       &quot;analyzer&quot;: {         &quot;customHTMLSnowball&quot;: {          &quot;type&quot;: &quot;custom&quot;,           &quot;char_filter&quot;: [             &quot;html_strip&quot;           ],           &quot;tokenizer&quot;: &quot;icu_tokenizer&quot;,           &quot;filter&quot;: [             &quot;icu_normalizer&quot;,             &quot;icu_folding&quot;,             &quot;lowercase&quot;,             &quot;stop&quot;,             &quot;snowball&quot;           ]           }       }     }   } } <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"bash\">curl -XPUT http:\/\/127.0.0.1:9200\/tpb -d @tpb-settings.json  <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u0435\u0440\u0435\u0434 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/guide\/current\/icu-plugin.html\">ICU plugin<\/a>, \u0430 \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430 \u043d\u0443\u0436\u043d\u043e \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0435\u0433\u043e \u0441 \u043f\u043e\u043b\u044f\u043c\u0438 \u0432 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0438 \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u0430:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u0442\u0438\u043f\u0430 \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u0430<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">{       &quot;properties&quot; : {         &quot;Id&quot; : {           &quot;type&quot; :    &quot;long&quot;,           &quot;index&quot; : &quot;no&quot;         },         &quot;Title&quot; : {           &quot;type&quot; :   &quot;string&quot;,           &quot;index&quot; : &quot;analyzed&quot;,           &quot;analyzer&quot; : &quot;customHTMLSnowball&quot;         },         &quot;Size&quot; : {           &quot;type&quot; :    &quot;long&quot;,           &quot;index&quot; : &quot;no&quot;         },         &quot;Files&quot; : {           &quot;type&quot; :    &quot;long&quot;,           &quot;index&quot; : &quot;no&quot;         },         &quot;Category&quot; : {           &quot;type&quot; :    &quot;string&quot;,           &quot;index&quot; : &quot;not_analyzed&quot;         },         &quot;Subcategory&quot; : {           &quot;type&quot; :    &quot;string&quot;,           &quot;index&quot; : &quot;not_analyzed&quot;         },         &quot;By&quot; : {           &quot;type&quot; :    &quot;string&quot;,           &quot;index&quot; : &quot;no&quot;         },         &quot;Hash&quot; : {           &quot;type&quot; :    &quot;string&quot;,           &quot;index&quot; : &quot;not_analyzed&quot;         },         &quot;Uploaded&quot; : {           &quot;type&quot; :    &quot;date&quot;,           &quot;index&quot; : &quot;no&quot;         },         &quot;Magnet&quot; : {           &quot;type&quot; :    &quot;string&quot;,           &quot;index&quot; : &quot;no&quot;         },         &quot;Info&quot; : {           &quot;type&quot; :   &quot;string&quot;,           &quot;index&quot; : &quot;analyzed&quot;,           &quot;analyzer&quot; : &quot;customHTMLSnowball&quot;         }       } } <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"bash\">curl -XPUT http:\/\/127.0.0.1:9200\/tpb\/_mappings\/torrent -d @tpb-mapping.json  <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0418 \u0442\u0435\u043f\u0435\u0440\u044c \u0441\u0430\u043c\u043e\u0435 \u0433\u043b\u0430\u0432\u043d\u043e\u0435 \u2014 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445. \u0417\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a \u044f \u0442\u043e\u0436\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043d\u0430 Go, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 Elasticsearch \u0438\u0437 Go. <\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/903\/bc4\/647\/903bc464740a4d9fb987536ffbeda53f.png\"\/><\/p>\n<p>  \u0421\u0430\u043c \u0437\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a \u0435\u0449\u0451 \u043f\u0440\u043e\u0449\u0435 \u043f\u0430\u0440\u0441\u0435\u0440\u0430: \u0447\u0438\u0442\u0430\u0435\u043c \u0444\u0430\u0439\u043b \u043f\u043e\u0441\u0442\u0440\u043e\u0447\u043d\u043e, \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0442\u0440\u043e\u0447\u043a\u0443 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0438\u043c \u0438\u0437 json \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0432 Elastisearch. \u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u0435\u0435 \u0431\u044b\u043b\u043e \u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c bulk indexing, \u043d\u043e \u043c\u043d\u0435, \u0447\u0435\u0441\u0442\u043d\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u0431\u044b\u043b\u043e \u043b\u0435\u043d\u044c. \u041a\u0441\u0442\u0430\u0442\u0438, \u0441\u0430\u043c\u043e\u0439 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u044e \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a\u0430 \u0431\u044b\u043b \u043f\u043e\u0438\u0441\u043a \u0434\u043b\u044f \u0441\u043a\u0440\u0438\u043d\u0448\u043e\u0442\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043b\u0438\u043d\u043d\u043e\u0433\u043e \u043a\u0443\u0441\u043a\u0430 \u043b\u043e\u0433\u0430 \u0431\u0435\u0437 \u043f\u043e\u0440\u043d\u0443\u0445\u0438.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0417\u0430\u0433\u0440\u0443\u0437\u0447\u0438\u043a \u0432 Elasticsearch<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"go\">func (i *Indexer) Run() { \tfor i.scaner.Scan() { \t\tvar t TorrentEntry \t\terr := json.Unmarshal(i.scaner.Bytes(), &t) \t\tif err != nil { \t\t\tlog.Warning(&quot;Failed to parse entry %s&quot;, i.scaner.Text()) \t\t\tcontinue \t\t} \t\t_, err = i.es.Index().Index(i.index).Type(&quot;torrent&quot;).BodyJson(t).Do() \t\tif err != nil { \t\t\tlog.Warning(&quot;Failed to index torrent entry %s with id %d&quot;, t.Title, t.Id) \t\t\tcontinue \t\t} \t\tlog.Info(&quot;Indexed %s&quot;, t.Title) \t} \ti.file.Close() } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0421\u0430\u043c \u0438\u043d\u0434\u0435\u043a\u0441 \u0437\u0430\u043d\u044f\u043b \u0442\u0435 \u0436\u0435 \u0441\u0430\u043c\u044b\u0435 ~6GB \u0438 \u0441\u0442\u0440\u043e\u0438\u043b\u0441\u044f \u043f\u043e\u0440\u044f\u0434\u043a\u0430 2\u0445 \u0447\u0430\u0441\u043e\u0432.<\/p>\n<h3>Frontend<\/h3>\n<p>  \u0421\u0430\u043c\u0430\u044f \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0434\u043b\u044f \u043c\u0435\u043d\u044f. \u042f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u0432\u0438\u0434\u0435\u0442\u044c \u0432\u0441\u0435 \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u044b \u0432 \u0431\u0430\u0437\u0435 \u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u043f\u043e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\u043c\/\u043f\u043e\u0434\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\u043c \u0438 \u043f\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044e\/\u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044e \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u0430. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0441\u043b\u0435\u0432\u0430 \u0444\u0438\u043b\u044c\u0442\u0440\u044b, \u0441\u043f\u0440\u0430\u0432\u0430 \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u044b.<\/p>\n<p>  \u0417\u0430 \u043e\u0441\u043d\u043e\u0432\u0443 \u0434\u043b\u044f \u0432\u0451\u0440\u0441\u0442\u043a\u0438 \u044f \u0432\u0437\u044f\u043b <a href=\"http:\/\/getbootstrap.com\/\">Bootstrap<\/a>. \u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u044d\u0442\u043e \u0432\u0438\u0434\u0438\u043c\u043e \u0431\u043e\u044f\u043d, \u043d\u043e \u043c\u043d\u0435 \u0432 \u043d\u043e\u0432\u0438\u043d\u043a\u0443. <\/p>\n<p>  \u0418\u0442\u0430\u043a, \u043f\u043e \u043b\u0435\u0432\u0443\u044e \u0440\u0443\u043a\u0443 \u0443 \u043c\u0435\u043d\u044f \u0444\u0438\u043b\u044c\u0442\u0440 \u043f\u043e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u043c \u0438 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u043c\u0443:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0424\u0438\u043b\u044c\u0442\u0440 \u043f\u043e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u043c<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"html\">                    &lt;form class=&quot;form-horizontal&quot;&gt;                         &lt;div class=&quot;form-group&quot;&gt;                             &lt;label for=&quot;queryInput&quot; class=&quot;col-sm-2 control-label&quot;&gt;Title&lt;\/label&gt;                              &lt;div class=&quot;col-sm-10&quot;&gt;                                 &lt;input type=&quot;text&quot; class=&quot;form-control input-sm&quot; id=&quot;queryInput&quot;                                        placeholder=&quot;Big Buck Bunny&quot; ng-model=&quot;query&quot;&gt;                             &lt;\/div&gt;                         &lt;\/div&gt;                         &lt;div class=&quot;form-group&quot;&gt;                             &lt;div class=&quot;col-sm-offset-2 col-sm-10&quot;&gt;                                 &lt;div class=&quot;checkbox&quot;&gt;                                     &lt;label&gt;                                         &lt;input type=&quot;checkbox&quot; ng-model=&quot;useInfo&quot;&gt; Look in torrent info too.                                     &lt;\/label&gt;                                 &lt;\/div&gt;                             &lt;\/div&gt;                         &lt;\/div&gt;                         &lt;div class=&quot;form-group text-right&quot;&gt;                             &lt;div class=&quot;col-sm-offset-2 col-sm-10&quot;&gt;                                 &lt;button type=&quot;submit&quot; class=&quot;btn btn-default&quot; ng-click=&quot;searchClick()&quot;&gt;Search&lt;\/button&gt;                             &lt;\/div&gt;                         &lt;\/div&gt;                     &lt;\/form&gt; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u043e\u0434 \u043d\u0438\u043c \u0444\u0438\u043b\u044c\u0442\u0440\u044b \u043f\u043e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\u043c \u0438 \u0441\u0443\u0431\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\u043c:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0424\u0438\u043b\u044c\u0442\u0440 \u043f\u043e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\u043c<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"html\">            &lt;div class=&quot;panel panel-warning&quot; ng-cloak ng-show=&quot;categories.length &gt;0&quot;&gt;                 &lt;div class=&quot;panel-heading&quot;&gt;                     &lt;h3 class=&quot;panel-title&quot;&gt;Categories:&lt;\/h3&gt;                 &lt;\/div&gt;                 &lt;div class=&quot;panel-body&quot;&gt;                     &lt;div ng-repeat=&quot;cat in categories | orderBy: 'key'&quot;&gt;                         &lt;p class=&quot;text-justify&quot;&gt;                             &lt;button class=&quot;btn btn-warning wide_button&quot; ng-class=&quot;{'active': cat.active}&quot;                                     ng-click=&quot;categoryClick(cat)&quot;&gt;{{cat.key}}&nbsp;&lt;span                                     class=&quot;badge&quot;&gt;{{cat.doc_count}}&lt;\/span&gt;&lt;\/button&gt;                         &lt;\/p&gt;                     &lt;\/div&gt;                 &lt;\/div&gt;             &lt;\/div&gt;             &lt;div class=&quot;panel panel-warning&quot; ng-cloak ng-show=&quot;SubCategories.length &gt;0 && filterCategories.length &gt;0&quot;&gt;                 &lt;div class=&quot;panel-heading&quot;&gt;                     &lt;h3 class=&quot;panel-title&quot;&gt;Sub categories:&lt;\/h3&gt;                 &lt;\/div&gt;                 &lt;div class=&quot;panel-body&quot;&gt;                     &lt;div ng-repeat=&quot;cat in SubCategories | orderBy: 'key'&quot;&gt;                         &lt;p class=&quot;text-justify&quot;&gt;                             &lt;button class=&quot;btn btn-success wide_button&quot; ng-class=&quot;{'active': cat.active}&quot;                                     ng-click=&quot;subCategoryClick(cat)&quot;&gt;{{cat.key}}&nbsp;&lt;span                                     class=&quot;badge&quot;&gt;{{cat.doc_count}}&lt;\/span&gt;&lt;\/button&gt;                         &lt;\/p&gt;                     &lt;\/div&gt;                 &lt;\/div&gt;             &lt;\/div&gt; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0421\u043f\u0438\u0441\u043e\u043a \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439 \u043d\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u0438 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 TermsAggregation \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u0440\u0430\u0437\u0443 \u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439 \u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0442\u043e\u0440\u0440\u0435\u043d\u0442\u043e\u0432 \u0432 \u044d\u0442\u0438\u0445 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f\u0445. \u0413\u043e\u0432\u043e\u0440\u044f \u0431\u043e\u043b\u0435\u0435 \u0441\u0442\u0440\u043e\u0433\u043e \u2014 \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 \u043f\u043e\u043b\u044f Category \u0438 \u0447\u0438\u0441\u043b\u043e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0430\u043a\u043e\u0433\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">client.search({         index: 'tpb',         type: 'torrent',         body: ejs.Request().agg(ejs.TermsAggregation('categories').field('Category'))     }).then(function (resp) {         $scope.categories = resp.aggregations.categories.buckets;         $scope.errorCategories = null;     }).catch(function (err) {         $scope.categories = null;         $scope.errorCategories = err;         \/\/ if the err is a NoConnections error, then the client was not able to         \/\/ connect to elasticsearch. In that case, create a more detailed error         \/\/ message         if (err instanceof esFactory.errors.NoConnections) {             $scope.errorCategories = new Error('Unable to connect to elasticsearch.');         }     }); <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u0440\u0438 \u043a\u043b\u0438\u043a\u0435 \u043d\u0430 \u043e\u0434\u043d\u0443 \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439, \u043e\u043d\u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u044e\u0442\u0441\u044f \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0441\u044f \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0445 \u043f\u043e\u0434\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043f\u043e\u0434\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">    $scope.categoryClick = function (category) {         \/* Mark button *\/         category.active = !category.active;          \/* Reload sub categories list *\/         $scope.filterCategories = [];         $scope.categories.forEach(function (item) {             if (item.active) {                 $scope.filterCategories.push(item.key);             }         });          if ($scope.filterCategories.length &gt; 0) {             $scope.loading = true;             client.search({                 index: 'tpb',                 type: 'torrent',                 body: ejs.Request().agg(ejs.FilterAggregation('SubCategoryFilter').filter(ejs.TermsFilter('Category', $scope.filterCategories)).agg(ejs.TermsAggregation('categories').field('Subcategory').size(50)))             }).then(function (resp) {                     $scope.SubCategories = resp.aggregations.SubCategoryFilter.categories.buckets;                     $scope.errorSubCategories = null;                     \/\/Restore selection                     $scope.SubCategories.forEach(function (item) {                         if ($scope.selectedSubCategories[item.key]) {                             item.active = true;                         }                     });                 }             ).catch(function (err) {                 $scope.SubCategories = null;                 $scope.errorSubCategories = err;                 \/\/ if the err is a NoConnections error, then the client was not able to                 \/\/ connect to elasticsearch. In that case, create a more detailed error                 \/\/ message                 if (err instanceof esFactory.errors.NoConnections) {                     $scope.errorSubCategories = new Error('Unable to connect to elasticsearch.');                 }             });         } else {             $scope.selectedSubCategories = {};             $scope.filterSubCategories = [];         }          $scope.searchClick();     }; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u043e\u0434\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0438 \u0442\u043e\u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0432\u044b\u0431\u0438\u0440\u0430\u0442\u044c. \u041f\u0440\u0438 \u0441\u043c\u0435\u043d\u0435 \u0432\u044b\u0431\u043e\u0440\u0430 \u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439\/\u043f\u043e\u0434\u043a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u0439 \u0438\u043b\u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u0444\u043e\u0440\u043c\u044b \u043f\u043e\u0438\u0441\u043a\u0430 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442\u0441\u044f Elasticsearch query, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u0432\u0441\u0451 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0435 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432 Elasticsearch.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043a Elasticsearch \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u043e\u0433\u043e \u0441\u043b\u0435\u0432\u0430.<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">    $scope.buildQuery = function () {         var match = null;         if ($scope.query) {             if ($scope.useInfo) {                 match = ejs.MultiMatchQuery(['Title', 'Info'], $scope.query);             } else {                 match = ejs.MatchQuery('Title', $scope.query);             }         } else {             match = ejs.MatchAllQuery();         }          var filter = null;         if ($scope.filterSubCategories.length &gt; 0) {             filter = ejs.TermsFilter('Subcategory', $scope.filterSubCategories);         }         if ($scope.filterCategories.length &gt; 0) {             var categoriesFilter = ejs.TermsFilter('Category', $scope.filterCategories);             if (filter !== null) {                 filter = ejs.AndFilter([categoriesFilter, filter]);             } else {                 filter = categoriesFilter;             }         }          var request = ejs.Request();         if (filter !== null) {             request = request.query(ejs.FilteredQuery(match, filter));         } else {             request = request.query(match);         }          request = request.from($scope.pageNo*10);          return request;     }; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0441\u043f\u0440\u0430\u0432\u0430:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0428\u0430\u0431\u043b\u043e\u043d \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"html\">            &lt;div class=&quot;panel panel-info&quot; ng-repeat=&quot;doc in searchResults&quot;&gt;                 &lt;div class=&quot;panel-heading&quot;&gt;                     &lt;h3 class=&quot;panel-title&quot;&gt;                         &lt;a href=&quot;{{doc._source.Magnet}}&quot;&gt;{{doc._source.Title}}&lt;\/a&gt;                         &lt;!-- build:[src] img\/ --&gt;                         &lt;a href=&quot;{{doc._source.Magnet}}&quot;&gt;&lt;img class=&quot;magnet_icon&quot;                                                               src=&quot;assets\/dist\/img\/magnet_link.png&quot;&gt;&lt;\/a&gt;                         &lt;!-- \/build --&gt;                     &lt;\/h3&gt;                 &lt;\/div&gt;                 &lt;div class=&quot;panel-body&quot;&gt;                     &lt;p class=&quot;text-left text-warning&quot;&gt;                         {{doc._source.Category}}&nbsp;\/&nbsp;{{doc._source.Subcategory}}&lt;\/p&gt;                      &lt;p class=&quot;text-center&quot;&gt;&lt;span                             class=&quot;badge&quot;&gt;#{{doc._source.Id}}&lt;\/span&gt;&nbsp;&lt;b&gt;{{doc._source.Title}}&lt;\/b&gt;                     &lt;\/p&gt;                     &lt;dl class=&quot;dl-horizontal&quot;&gt;                         &lt;dt&gt;Size&lt;\/dt&gt;                         &lt;dd&gt;{{doc._source.Size}}&lt;\/dd&gt;                         &lt;dt&gt;Files&lt;\/dt&gt;                         &lt;dd&gt;{{doc._source.Files}}&lt;\/dd&gt;                         &lt;dt&gt;Hash&lt;\/dt&gt;                         &lt;dd&gt;{{doc._source.Hash}}&lt;\/dd&gt;                     &lt;\/dl&gt;                     &lt;div class=&quot;well&quot; ng-bind-html=&quot;doc._source.Info&quot;&gt;&lt;\/div&gt;                     &lt;p class=&quot;text-right text-muted&quot;&gt;                         &lt;small&gt;Uploaded at {{doc._source.Uploaded}} by {{doc._source.By}}&lt;\/small&gt;                     &lt;\/p&gt;                 &lt;\/div&gt;             &lt;\/div&gt; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0412\u043e\u0442 \u0438 \u0432\u0441\u0451. \u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043c\u0435\u043d\u044f \u0435\u0441\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a \u043f\u043e The Pirate Bay, \u0438 \u044f \u0443\u0437\u043d\u0430\u043b, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0441\u0430\u0439\u0442 \u0437\u0430 \u043f\u0430\u0440\u0443 \u0447\u0430\u0441\u043e\u0432.<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/akashihi\/tpb-parser\">tpb-parser<\/a> \u2014 \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0430\u0440\u0441\u0435\u0440\u0430 The Pirate Bay<\/li>\n<li><a href=\"https:\/\/github.com\/akashihi\/estorrent\">estorrent<\/a> \u2014 \u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430 \u0438 \u0438\u043d\u0434\u0435\u043a\u0441\u0430\u0442\u043e\u0440\u0430<\/li>\n<li><a href=\"https:\/\/mega.nz\/#!EUsyWKxa!cdnF2_tDSKSE6LnefEu9GWltLSyZ5_42Nvhq6sz9I28\">\u0414\u0430\u043c\u043f TPB \u0434\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u0445\u043e\u0447\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0430 \u0435\u0433\u043e \u043e\u0441\u043d\u043e\u0432\u0435 \u0447\u0442\u043e-\u0442\u043e \u043d\u043e\u0432\u043e\u0435 (\u0430\u0440\u0445\u0438\u0432 ~1.2GB)<\/a><\/li>\n<li><a href=\"https:\/\/mega.nz\/#!RBd2jDDZ!l_tS7UfeLKxyh5zz1L6jZ8FuhwBcH4qcmXZwU1gcK8c\">\u041e\u0431\u0440\u0430\u0437 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0439 \u043c\u0430\u0448\u0438\u043d\u044b \u0441 Elasticsearch \u0438 \u0432\u0435\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c (~6GB)<\/a><\/li>\n<\/ul>\n<div class=\"clear\"><\/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=\"http:\/\/habrahabr.ru\/post\/275339\/\"> http:\/\/habrahabr.ru\/post\/275339\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>       \u0412 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0430 \u0445\u0430\u0431\u0440\u0435 <a href=\"http:\/\/habrahabr.ru\/post\/274449\/\">\u0441\u0442\u0430\u043b\u043e \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u043e<\/a> \u0434\u0435\u043b\u0430\u0442\u044c <a href=\"http:\/\/habrahabr.ru\/post\/273777\/\">\u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0438<\/a> \u043f\u043e RuTracker. \u041c\u043d\u0435 \u044d\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u044b\u043c \u043f\u043e\u0432\u043e\u0434\u043e\u043c \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043e\u0439\u0442\u0438 \u043e\u0442 \u0441\u043a\u0443\u0447\u043d\u043e\u0439 enterprise \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u043d\u0438\u0431\u0443\u0434\u044c \u043d\u043e\u0432\u043e\u0435.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/files\/15a\/5f1\/222\/15a5f1222fcb40aab6f759e182a0cf2b.png\"\/><\/p>\n<p>  \u0418\u0442\u0430\u043a, \u0437\u0430\u0434\u0430\u0447\u0430: \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430 \u043b\u043e\u043a\u0430\u043b\u0445\u043e\u0441\u0442\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a \u043f\u043e \u0431\u0430\u0437\u0435 The Pirate Bay \u0438 \u043f\u043e\u043f\u0443\u0442\u043d\u043e \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u0436\u0435 \u0442\u0430\u043a\u043e\u0435 frontend \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u0438 \u0441 \u0447\u0435\u043c \u0435\u0451 \u0435\u0434\u044f\u0442. \u0417\u0430\u0434\u0430\u0447\u0430 \u043e\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u0435\u043c, \u0447\u0442\u043e TPB \u043d\u0435 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u0442 \u0441\u0432\u043e\u0438\u0445 \u0434\u0430\u043c\u043f\u043e\u0432, \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 RuTracker, \u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043c\u043f\u043e\u0432 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0440\u0430\u0441\u043f\u0430\u0440\u0441\u0438\u0442\u044c \u0438\u0445 \u0441\u0430\u0439\u0442. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0433\u0443\u0433\u043b\u0435\u043d\u0438\u044f \u0438 \u043e\u0441\u043c\u044b\u0441\u043b\u0435\u043d\u0438\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u044f \u0440\u0435\u0448\u0438\u043b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/www.elastic.co\/products\/elasticsearch\">Elasticsearch<\/a>, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c client-side only \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0430 <a href=\"https:\/\/angularjs.org\/\">AngularJS<\/a>. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043f\u0430\u0440\u0441\u0435\u0440 \u0441\u0430\u0439\u0442\u0430 TPB \u0438 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u0435\u043b\u044c \u0434\u0430\u043c\u043f\u0430 \u0432 \u0438\u043d\u0434\u0435\u043a\u0441, \u043e\u0431\u0430 \u043d\u0430 Go. \u041f\u0438\u043a\u0430\u043d\u0442\u043d\u043e\u0441\u0442\u044c \u0432\u044b\u0431\u043e\u0440\u0443 \u043f\u0440\u0438\u0434\u0430\u0432\u0430\u043b \u0442\u043e\u0442 \u0444\u0430\u043a\u0442, \u0447\u0442\u043e \u043d\u0438 \u043a Elasticsearch, \u043d\u0438 \u043a AngularJS \u044f \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043d\u0438 \u0440\u0430\u0437\u0443 \u043d\u0435 \u043f\u0440\u0438\u043a\u0430\u0441\u0430\u043b\u0441\u044f \u0438 \u0438\u043c\u0435\u043d\u043d\u043e \u0438\u0445 \u043e\u043f\u0440\u043e\u0431\u044b\u0432\u0430\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u043c\u043e\u0435\u0439 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0439 \u0446\u0435\u043b\u044c\u044e.  <\/p>\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-272327","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/272327","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=272327"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/272327\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=272327"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=272327"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=272327"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}