/* * Copyright 2013 Websquared, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.fastcatsearch.ir.search; import java.io.IOException; import java.util.List; import org.apache.lucene.util.BytesRef; import org.fastcatsearch.error.CoreErrorCode; import org.fastcatsearch.error.SearchError; import org.fastcatsearch.ir.field.DistanceField; import org.fastcatsearch.ir.field.HitField; import org.fastcatsearch.ir.field.ScoreField; import org.fastcatsearch.ir.io.FixedMaxPriorityQueue; import org.fastcatsearch.ir.query.Sort; import org.fastcatsearch.ir.settings.FieldIndexSetting; import org.fastcatsearch.ir.settings.FieldSetting; import org.fastcatsearch.ir.settings.Schema; import org.fastcatsearch.ir.sort.SortFunction; /** * 쿼리에서 요청한 Sorts조건에 따라 정렬해주는 랭커. * HitElement 에는 byte[] 데이터만 있기 때문에 비교시에는 sort조건에 따라 동작하는 SortFunction이 사용된다. * 이 heap에서 pop한 결과는 역순으로 이용된다. * * 2014-7-30 bundle key가 동일하면 push하지 않는 기능추가됨. * @see HitMerger * @author swsong * */ public class HitRanker extends FixedMaxPriorityQueue<HitElement>{ private SortFunction[] sortFunctions; public HitRanker(List<Sort> querySortList, Schema schema, int maxSize) throws IOException{ super(maxSize); int size = querySortList.size(); sortFunctions = new SortFunction[size]; for (int i = 0; i < size; i++) { Sort sort = querySortList.get(i); String fieldIndexId = sort.fieldIndexId(); int idx = schema.getFieldIndexSequence(fieldIndexId); ////////_HIT , _SCORE 필드의 경우 처리해준다. if(idx == -1){ if(fieldIndexId.equalsIgnoreCase(ScoreField.fieldName)){ sortFunctions[i] = sort.createSortFunction(ScoreField.field); }else if(fieldIndexId.equalsIgnoreCase(HitField.fieldName)){ sortFunctions[i] = sort.createSortFunction(HitField.field); }else if(fieldIndexId.equalsIgnoreCase(DistanceField.fieldName)){ sortFunctions[i] = sort.createSortFunction(DistanceField.field); }else{ throw new SearchError(CoreErrorCode.FIELD_INDEX_NOT_EXIST, fieldIndexId); } }else{ FieldIndexSetting fieldIndexSetting = schema.getFieldIndexSetting(fieldIndexId); String refId = fieldIndexSetting.getRef(); FieldSetting fieldSetting = schema.getFieldSetting(refId); sortFunctions[i] = sort.createSortFunction(fieldSetting); } logger.debug("sortFunctions[{}]=[{}]=", i, sortFunctions[i]); } } @Override public boolean push(HitElement e) { if (e.getBundleKey() != null) { BytesRef bundleKey = e.getBundleKey(); for (int i = 1; i <= size; i++) { if (bundleKey.equals(((HitElement) heap[i]).getBundleKey())) { /* * 동일 bundle 이 존재하면 어느것이 더 적합한지 체크한다. */ if (compare(e, (HitElement) heap[i]) < 0) { // 크거나 같으면 그냥 패스. // 작으면 바꾼다. replaceEl(i, e); // break; return true; } // logger.debug("Do no push > {}", e.docNo()); return false; } } } // logger.debug("Continue to push > {}", e.docNo()); // bundle을 사용하지 않거나 동일 bundle이 없으면 push한다. return super.push(e); } @Override protected int compare(HitElement one, HitElement two) { for (int i = 0; i < sortFunctions.length; i++) { //하나씩 비교해가면서 각 funtion의 비교결과가 0이 아닐때 까지 비교한다. int r = sortFunctions[i].compare(one.rankData(i), two.rankData(i)); if(r != 0){ return r; } } return one.compareTo(two); // //최신세그먼트 우선. // if(one.segmentSequence() != two.segmentSequence()){ // return two.segmentSequence() - one.segmentSequence(); // } // // //정렬 데이터가 모두 같다면 문서번호가 최신인걸 보여준다. // return two.docNo() - one.docNo(); } }