Bobo的收集器基类是SortCollector继承于lucene的Collector类,其中函数是获取收集器的外部接口:
public static SortCollector buildSortCollector(Browsable browser,Query q,SortField[] sort,int offset,int count,boolean forceScoring,boolean fetchStoredFields){ boolean doScoring=forceScoring; if (sort == null || sort.length==0){ if (q!=null && !(q instanceof MatchAllDocsQuery)){ sort = new SortField[]{SortField.FIELD_SCORE}; } } if (sort==null || sort.length==0){ sort = new SortField[]{SortField.FIELD_DOC}; } Set<String> facetNames = browser.getFacetNames(); for (SortField sf : sort){ if (sf.getType() == SortField.SCORE) { doScoring= true; break; } } DocComparatorSource compSource; if (sort.length==1){ //对sort[]记性转换 SortField sf = convert(browser,sort[0]); compSource = getComparatorSource(browser,sf); } else{ DocComparatorSource[] compSources = new DocComparatorSource[sort.length]; for (int i = 0; i<sort.length;++i){ compSources[i]=getComparatorSource(browser,convert(browser,sort[i])); } //可以对结果以多个field进行排序,排序的优先级由sort[]中的位置确定,例:sort[0]优先于sort[1] compSource = new MultiDocIdComparatorSource(compSources); } return new SortCollectorImpl(compSource, sort, browser, offset, count, doScoring, fetchStoredFields); }
其中convert(browsable , sortField[])函数是对sortField进行转换,如果有该field的facetHandler,那么就必须使用该facetHandler自定义的Comparator(比较器)进行收集(collect):
private static SortField convert(Browsable browser,SortField sort){ String field =sort.getField(); FacetHandler<?> facetHandler = browser.getFacetHandler(field); if (facetHandler!=null){ //有该sortField所在域的FacetHandler的时候使用facet自己的Comparator BoboCustomSortField sortField = new BoboCustomSortField(field, sort.getReverse(), facetHandler.getDocComparatorSource()); return sortField; } else{ return sort; } }
上面的转换的影响从下边函数里可以体现出来,上面有facet的SortField被转换成BoboCustomSortField,在下面函数中
if (sf instanceof BoboCustomSortField){ BoboCustomSortField custField = (BoboCustomSortField)sf; DocComparatorSource src = custField.getCustomComparatorSource(); assert src!=null; compSource = src; }
使用了facetHandler的比较器的产生器来生产Comparator
private static DocComparatorSource getComparatorSource(Browsable browser,SortField sf){ DocComparatorSource compSource = null; //按照文档的id进行排序收集 if (SortField.FIELD_DOC.equals(sf)){ compSource = new DocIdDocComparatorSource(); }//按照文档的得分进行收集 else if (SortField.FIELD_SCORE.equals(sf) || sf.getType() == SortField.SCORE){ // we want to do reverse sorting regardless for relevance compSource = new ReverseDocComparatorSource(new RelevanceDocComparatorSource()); } else if (sf instanceof BoboCustomSortField){ BoboCustomSortField custField = (BoboCustomSortField)sf; DocComparatorSource src = custField.getCustomComparatorSource(); assert src!=null; compSource = src; } else{ Set<String> facetNames = browser.getFacetNames(); String sortName = sf.getField(); if (facetNames.contains(sortName)){ FacetHandler<?> handler = browser.getFacetHandler(sortName); assert handler!=null; compSource = handler.getDocComparatorSource(); } else{ // default lucene field compSource = getNonFacetComparatorSource(sf); } } boolean reverse = sf.getReverse(); if (reverse){ compSource = new ReverseDocComparatorSource(compSource); } compSource.setReverse(reverse); return compSource; }
SortCollectorImpl是sortCollector类的一个实现类,实现Collector类的接口SetNextReader()函数:
@Override public void setNextReader(IndexReader reader, int docBase) throws IOException { assert reader instanceof BoboIndexReader; _currentReader = (BoboIndexReader)reader; //得到当前Reader下的比较器 _currentComparator = _compSource.getComparator(reader,docBase); //用于收集当前Reader搜索结果的最小堆,用上面的比较器 _currentQueue = new DocIDPriorityQueue(_currentComparator, _numHits, docBase); MyScoreDoc myScoreDoc = (MyScoreDoc)_tmpScoreDoc; myScoreDoc.queue = _currentQueue; myScoreDoc.reader = _currentReader; myScoreDoc.sortValue = null; _pqList.add(_currentQueue); _queueFull = false; }
最后对多个IndexReader的结果进行merge(合并)
@Override public BrowseHit[] topDocs() throws IOException{ ArrayList<Iterator<MyScoreDoc>> iterList = new ArrayList<Iterator<MyScoreDoc>>(_pqList.size()); for (DocIDPriorityQueue pq : _pqList){ int count = pq.size(); MyScoreDoc[] resList = new MyScoreDoc[count]; for (int i = count - 1; i >= 0; i--) { resList[i] = (MyScoreDoc)pq.pop(); } //将每个IndexReader的结果从最小堆转移到List中,将每个List的iterator添加到一个List<iterator>中去 iterList.add(Arrays.asList(resList).iterator()); } //对多个IndexReader的搜索结果队列进行多路归并 ArrayList<MyScoreDoc> resList = ListMerger.mergeLists(_offset, _count, iterList, MERGE_COMPATATOR); Map<String,FacetHandler<?>> facetHandlerMap = _boboBrowser.getFacetHandlerMap(); return buildHits(resList.toArray(new MyScoreDoc[resList.size()]), _sortFields, facetHandlerMap, _fetchStoredFields); } //将搜索结果转换成BrowseHit protected static BrowseHit[] buildHits(MyScoreDoc[] scoreDocs,SortField[] sortFields,Map<String,FacetHandler<?>> facetHandlerMap,boolean fetchStoredFields) throws IOException { BrowseHit[] hits = new BrowseHit[scoreDocs.length]; Collection<FacetHandler<?>> facetHandlers= facetHandlerMap.values(); for (int i =scoreDocs.length-1; i >=0 ; i--) { MyScoreDoc fdoc = scoreDocs[i]; BoboIndexReader reader = fdoc.reader; BrowseHit hit=new BrowseHit(); if (fetchStoredFields){ hit.setStoredFields(reader.document(fdoc.doc)); } Map<String,String[]> map = new HashMap<String,String[]>(); Map<String,Object[]> rawMap = new HashMap<String,Object[]>(); for (FacetHandler<?> facetHandler : facetHandlers) { map.put(facetHandler.getName(),facetHandler.getFieldValues(reader,fdoc.doc)); rawMap.put(facetHandler.getName(),facetHandler.getRawFieldValues(reader,fdoc.doc)); } hit.setFieldValues(map); hit.setRawFieldValues(rawMap); hit.setDocid(fdoc.doc+fdoc.queue.base); hit.setScore(fdoc.score); hit.setComparable(fdoc.getValue()); hits[i] = hit; } return hits; }