- 自己動手寫分布式搜索引擎
- 羅剛
- 1330字
- 2020-11-28 15:52:50
3.3.9 排序
為了實現(xiàn)情景搜索,根據(jù)用戶IP判斷用戶所在地區(qū),然后把與用戶所在區(qū)域相同的文檔排在前面,即通過排序把地名相同的搜索結(jié)果放在前面。
若采用自定義排序?qū)ο蟮姆椒▽崿F(xiàn)把和用戶所在地域相同的結(jié)果放在前面,需要實現(xiàn)FieldComparatorSource和FieldComparator接口。FieldComparatorSource功能是返回一個用來排序的比較器。這個接口只定義一個方法newComparator(),用來生成FieldComparator對象。生成的FieldComparator對象可作為指定文檔列的排序比較器。
當使用TopFieldCollector收集最前面的結(jié)果時,要用FieldComparator比較命中的文檔,從而確定它們的排列順序。
具體的FieldComparator類對應(yīng)不同的SortField類型。
FieldComparator對象需要實現(xiàn)compare()、compareBottom()、copy()、setBottom()、setNextReader()、value()等方法,如果需要用到文檔的評分值,還需要實現(xiàn)setScorer()方法。
● compare(int, int):比較槽a和槽b的命中文檔。
● setBottom(int):通過FieldValueHitQueue調(diào)用此方法,以通知FieldComparator當前最底部的槽。
● compareBottom(int):新的命中文檔(docID)和隊列中的最弱的底部進行比較。
● copy(int, int) 安排一個新的命中文檔到優(yōu)先隊列中去。當一個新的命中文檔具有競爭力時,F(xiàn)ieldValueHitQueue調(diào)用這個方法。
● setNextReader(org.apache.lucene.index.IndexReader, int):當搜索切換到下一個段時調(diào)用這個方法。可能需要更新比較器的內(nèi)部狀態(tài),例如從FieldCache檢索新值。
● value(int):返回存儲在指定位置的值。當返回頂部結(jié)果時,為了填充FieldDoc.fields,才在搜索結(jié)束時調(diào)用它。
首先要定義一個繼承抽象類FieldComparatorSource的實現(xiàn)類CityFieldComparator,重寫其中的抽象方法:
public FieldComparator newComparator(String field, int numHits, int sortPos, boolean reversed) throws IOException;
該方法返回一個FieldComparator實例,實現(xiàn)自定義排序。
然后定義一個類CitySortComparator繼承自FieldComparator抽象類。重寫其中的抽象方法,在public int compare(int slot1, int slot2)方法中實現(xiàn)排序業(yè)務(wù)邏輯。
TopFieldCollector是一個用SortField排序的Collector。SortField使用FieldComparator構(gòu)造。
最后再調(diào)用該自定義排序類。
//這里設(shè)置為true是正序,false為倒序。調(diào)用CityFieldComparator的構(gòu)造方法初始化 //城市名,城市名稱可以根據(jù)用戶IP獲得。 SortField citySort = new SortField("city", new CityFieldComparator(city), true); //按時間排序,true為時間減序,false為時間增序。 SortField dateSort = new SortField("date", SortField.INT, true); //首先按城市排序,然后按日期排序 Sort sort = new Sort(new SortField[] {citySort, dateSort}); TopFieldCollector collector = TopFieldCollector.create(sort, offset+rows, false, true, false, false); //isearcher是IndexSearcher的實例 isearcher.search(bq, collector); ScoreDoc[] hits = collector.topDocs().scoreDocs;
排序速度很慢,因為排序列的值都緩存在FieldCache中。對于要用來排序的字段,先從索引中將每篇文檔中該字段的值都讀出來,放到一個大小為maxDoc的數(shù)組中。maxDoc是文檔編號的最大值。倒排索引的詞列是字符串或者字節(jié)數(shù)組類型,從倒排索引的詞列解析出值的過程叫作反倒排。
有兩點需要注意:
(1) FieldCache中的字段值是從倒排表中讀出來的,它不是索引文件中存儲的字段值,所以排序的字段必須是索引字段。
(2) 用來排序的字段在索引的時候不能拆分,因為FieldCache數(shù)組中,每個文檔只對應(yīng)一個字段值,拆分的話,緩存中只會保存詞典中靠后的值。
FieldCache是Lucene最占用內(nèi)存的部分,大部分內(nèi)存溢出的錯誤都是由它引起的,需要特別注意。
每個商品只有一個價格,也就是說,商品的價格是唯一的。用按列存儲值的方法依價格排序。
每個文檔有幾個數(shù)值類型的字段。可根據(jù)這些列的加權(quán)和來排序。例如:
field1=100 field2=002 field3=014
加權(quán)函數(shù)類似:
f(d) = field1 * 0.5 + field2 * 1.4 + field3 * 1.8
結(jié)果通過f(d)的值排序,這里的變量d表示文檔。排序方法應(yīng)該是非靜態(tài)的,并且在不同的搜索之間是不同的,因為對執(zhí)行搜索的不同用戶來說,常量是不一樣的。也就是說,排序方法是個性化的。
public class ScaledComparator extends FieldComparator { private String[] fields; private float[] scalars; private int[][] slotValues; private int[][] currentReaderValues; private int bottomSlot; public ScaledComparator(int numHits, String[] fields, float[] scalars) { this.fields = fields; this.scalars = scalars; this.slotValues = new int[this.fields.length][]; for (int fieldIndex = 0; fieldIndex < this.fields.length; fieldIndex++) { this.slotValues[fieldIndex] = new int[numHits]; } this.currentReaderValues = new int[this.fields.length][]; } protected float score(int[][] values, int secondaryIndex) { float score = 0; for (int fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) { int value = values[fieldIndex][secondaryIndex]; float scalar = scalars[fieldIndex]; score += (value * scalar); } return score; } protected float scoreSlot(int slot) { return score(slotValues, slot); } protected float scoreDoc(int doc) { return score(currentReaderValues, doc); } @Override public int compare(int slot1, int slot2) { float score1 = scoreSlot(slot1); float score2 = scoreSlot(slot2); return Float.compare(score1, score2); } @Override public int compareBottom(int doc) throws IOException { float bottomScore = scoreSlot(bottomSlot); float docScore = scoreDoc(doc); return Float.compare(bottomScore, docScore); } @Override public void copy(int slot, int doc) throws IOException { for (int fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) { slotValues[fieldIndex][slot] = currentReaderValues[fieldIndex][doc]; } } @Override public void setBottom(int slot) { bottomSlot = slot; } @Override public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException { for (int fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) { String field = fields[fieldIndex]; currentReaderValues[fieldIndex] = FieldCache.DEFAULT.getInts(reader, field); } } @Override public int sortType() { return SortField.CUSTOM; } @Override public Comparable<? > value(int slot) { float score = scoreSlot(slot); return Float.valueOf(score); } }
使用ScaledComparator實現(xiàn)排序的功能。FieldComparatorSource的匿名類重寫newComparator()方法。newComparator()方法返回ScaledComparator類的一個實例。
//列名數(shù)組 final String[] fields = new String[]{ "field1", "field2", "field3" }; final float[] scalars = new float[]{ 0.5f, 1.4f, 1.8f }; //列的權(quán)重 Sort sort = new Sort( new SortField( "", new FieldComparatorSource() { public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException { return new ScaledComparator(numHits, fields, scalars); } } ) );
- 數(shù)碼攝影后期零基礎(chǔ)入門教程
- JasperReports for Java Developers
- PPT設(shè)計實用教程
- Rhino 6.0中文版入門、精通與實戰(zhàn)
- Instant MuseScore
- AutoCAD 2019中文版計算機輔助繪圖全攻略
- WordPress Theme Design
- Django 1.0 Template Development
- Photoshop手繪從新手到高手
- 中文版Photoshop CS5實用教程(第2版)
- 剪映專業(yè)版:短視頻創(chuàng)作案例教程(全彩慕課版)
- FLUENT 15.0流場分析實戰(zhàn)指南
- Maya Paint Effect 特效應(yīng)用手冊
- SAI+Photoshop漫畫/插畫繪制技法全解析
- AutoCAD 2016中文版基礎(chǔ)實例教程(附教學(xué)視頻)