Elasticsearch插件開發——Rescore篇

      網友投稿 1597 2025-03-31

      一、前言


      elasticsearch中,重打分是一個對指定數目的查詢結果進行再次打分的一個過程。通常情況下,一個查詢可能會匹配成千上萬的結果,但用戶很可能只對結果的前幾頁感興趣。這種情況下就可以使用重打分功能來優化性能。但是,當前elasticsearch中只默認實現了rescore_query功能,當我們需要自定義重打分過程時,默認的功能就不適用了。這時我們就需要通過Rescore插件的方式實現。

      本文通過分析Elasticsearch源碼中自帶的重打分插件用例來講解如何開發Rescore插件。

      二、插件入口

      用例插件路徑在Elasticsearch源碼的plugins/examples/rescore路徑下,可以看到除測試用例之外又有兩個源碼文件,其中ExampleRescorePlugin類定義了插件的入口

      public class ExampleRescorePlugin extends Plugin implements SearchPlugin { @Override public List> getRescorers() { return singletonList( new RescorerSpec<>(ExampleRescoreBuilder.NAME, ExampleRescoreBuilder::new, ExampleRescoreBuilder::fromXContent)); } }

      可以看到只需要重寫SearchPlugin接口的getRescore方法就好。

      三、重打分邏輯

      Elasticsearch插件開發——Rescore篇

      然后我們來看核心類ExampleRescoreBuilder的實現:

      首先定義了兩個實例變量factor和factorField,這兩個變量就作為我們自定義重打分的兩個參數

      public class ExampleRescoreBuilder extends RescorerBuilder { public static final String NAME = "example"; // example作為自定義重打分的名字 private final float factor; private final String factorField; public ExampleRescoreBuilder(float factor, @Nullable String factorField) { this.factor = factor; this.factorField = factorField; } ... }

      然后是實際進行重打分的代碼部分,如下:

      @Override public TopDocs rescore(TopDocs topDocs, IndexSearcher searcher, RescoreContext rescoreContext) throws IOException { ExampleRescoreContext context = (ExampleRescoreContext) rescoreContext; int end = Math.min(topDocs.scoreDocs.length, rescoreContext.getWindowSize()); // 自定義的第一部分邏輯,將重打分前的得分乘以factor參數 for (int i = 0; i < end; i++) { topDocs.scoreDocs[i].score *= context.factor; } if (context.factorField != null) { /* * Since this example looks up a single field value it should * access them in docId order because that is the order in * which they are stored on disk and we want reads to be * forwards and close together if possible. * * If accessing multiple fields we'd be better off accessing * them in (reader, field, docId) order because that is the * order they are on disk. */ ScoreDoc[] sortedByDocId = new ScoreDoc[topDocs.scoreDocs.length]; System.arraycopy(topDocs.scoreDocs, 0, sortedByDocId, 0, topDocs.scoreDocs.length); Arrays.sort(sortedByDocId, (a, b) -> a.doc - b.doc); // Safe because doc ids >= 0 Iterator leaves = searcher.getIndexReader().leaves().iterator(); LeafReaderContext leaf = null; SortedNumericDoubleValues data = null; int endDoc = 0; for (int i = 0; i < end; i++) { if (topDocs.scoreDocs[i].doc >= endDoc) { do { leaf = leaves.next(); endDoc = leaf.docBase + leaf.reader().maxDoc(); } while (topDocs.scoreDocs[i].doc >= endDoc); LeafFieldData fd = context.factorField.load(leaf); if (false == (fd instanceof LeafNumericFieldData)) { throw new IllegalArgumentException("[" + context.factorField.getFieldName() + "] is not a number"); } // 拿到了factor_field參數對應字段的值 data = ((LeafNumericFieldData) fd).getDoubleValues(); } if (false == data.advanceExact(topDocs.scoreDocs[i].doc - leaf.docBase)) { throw new IllegalArgumentException("document [" + topDocs.scoreDocs[i].doc + "] does not have the field [" + context.factorField.getFieldName() + "]"); } if (data.docValueCount() > 1) { throw new IllegalArgumentException("document [" + topDocs.scoreDocs[i].doc + "] has more than one value for [" + context.factorField.getFieldName() + "]"); } // 自定義的第二部分邏輯,將邏輯一之后的得分再乘以factor_field對應字段的值 topDocs.scoreDocs[i].score *= data.nextValue(); } } // Sort by score descending, then docID ascending, just like lucene's QueryRescorer // 將最終返回的doc降序排列 Arrays.sort(topDocs.scoreDocs, (a, b) -> { if (a.score > b.score) { return -1; } if (a.score < b.score) { return 1; } // Safe because doc ids >= 0 return a.doc - b.doc; }); return topDocs; }

      代碼的主要邏輯部分都用注釋說明了。可以看到,這個自定義插件實現了兩部分邏輯:

      將前window_size個得分乘以factor(window_size是父類定義的參數,可以在rescore時指定,實際作用就是指定重打分的文檔數量)

      如果factor_field參數存在,那么將第一步重打分的文檔得分再乘以factor_field對應字段的值

      雖然邏輯有點冗長,但是代碼是很清晰的。接下來是幾個實際的例子:

      寫入

      PUT test/_bulk?refresh {"index":{"_id":1}} {"test_field1":1, "test_field2": 3} {"index":{"_id":2}} {"test_field1":2, "test_field2": 2} {"index":{"_id":3}} {"test_field1":3, "test_field2": 1}

      重打分查詢

      GET test/_search { "query": { "match_all": {} }, "rescore": { "example": { "factor": 3, "factor_field": "test_field2" }, "window_size": 2 } }

      結果

      { "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 3, "relation" : "eq" }, "max_score" : 9.0, "hits" : [ { "_index" : "test", "_type" : "_doc", "_id" : "1", "_score" : 9.0, "_source" : { "test_field1" : 1, "test_field2" : 3 } }, { "_index" : "test", "_type" : "_doc", "_id" : "2", "_score" : 6.0, "_source" : { "test_field1" : 2, "test_field2" : 2 } }, { "_index" : "test", "_type" : "_doc", "_id" : "3", "_score" : 1.0, "_source" : { "test_field1" : 3, "test_field2" : 1 } } ] } }

      可以看到查詢時候指定的rescore名字是example,就是在代碼中指定的NAME。前置查詢是match_all,我們的寫入文檔得分都是1.0,match_all的結果會按照文檔的創建時間排序。重打分中指定了factor是3,factor_field是test_field2,window_size是2。此時rescore只對前兩個文檔進行操作,先用初始得分乘以3,再將得分乘以每個文檔test_field2對應的值。文檔1的結果是1.0*3*3=9.0,文檔2的結果是1.0*3*2=6.0,文檔3不參與重打分,結果仍是1.0

      四、總結

      這個插件demo雖然代碼量非常少,但卻很好地實現了重打分的邏輯,很多代碼也都可以在實際的重打分功能邏輯中復用,非常方便。云搜索服務(Cloud Search Service)支持自定義插件功能,可以上傳自己開發的插件并集成Elasticsearch使用。

      Elasticsearch 云搜索服務 CSS

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:word文檔如何換底板色(word底板顏色怎么更改)
      下一篇:Excel如何修改表格線條粗細
      相關文章
      亚洲精品成人无码中文毛片不卡| 久久精品国产亚洲网站| 亚洲成a人片在线观看中文动漫 | 处破女第一次亚洲18分钟| 亚洲 欧洲 视频 伦小说| 亚洲国色天香视频| 亚洲欧洲国产综合| 亚洲精品视频在线观看免费 | 亚洲精品无码中文久久字幕| 在线aⅴ亚洲中文字幕| 亚洲一区二区三区高清在线观看| 久久亚洲精品成人| 在线A亚洲老鸭窝天堂| 亚洲日韩精品无码专区加勒比| 亚洲福利视频网站| 区久久AAA片69亚洲| 亚洲精品国产字幕久久不卡| 亚洲精品狼友在线播放| 亚洲AV无码乱码在线观看富二代| 亚洲成AV人片在| 中文字幕亚洲综合久久2| 亚洲人成人77777网站不卡| 亚洲精品视频免费在线观看| 亚洲免费中文字幕| 亚洲一卡一卡二新区无人区| 亚洲AV无码成人精品区日韩| 亚洲欧洲一区二区三区| 亚洲一区二区三区影院| 久久久久无码精品亚洲日韩| 亚洲国产人成网站在线电影动漫| 亚洲精品动漫在线| 亚洲自偷自偷在线成人网站传媒| 亚洲乱理伦片在线观看中字| 无码不卡亚洲成?人片| 狠狠综合久久综合88亚洲| 亚洲五月综合缴情在线观看| 亚洲av无码国产精品夜色午夜| 亚洲精品天天影视综合网| 亚洲冬月枫中文字幕在线看| 亚洲乱码日产精品一二三| 亚洲精品456播放|