@Override public void collect(int doc) throws IOException { ++totalHits; if (queueFull) { if ((reverseMul * comparator.compareBottom(doc)) <= 0) { // since docs are visited in doc Id order, if compare is 0, it means // this document is larger than anything else in the queue, and // therefore not competitive. return; } // This hit is competitive - replace bottom element in queue & adjustTop comparator.copy(bottom.slot, doc); updateBottom(doc); comparator.setBottom(bottom.slot); } else { // Startup transient: queue hasn't gathered numHits yet final int slot = totalHits - 1; // Copy hit into queue comparator.copy(slot, doc); add(slot, doc, Float.NaN); if (queueFull) { comparator.setBottom(bottom.slot); } } }
public PagingFieldCollector( Sort sort, FieldValueHitQueue<Entry> queue, FieldDoc after, int numHits, boolean fillFields, boolean trackDocScores, boolean trackMaxScore) { super(queue, numHits, fillFields, trackDocScores || trackMaxScore || sort.needsScores()); this.queue = queue; this.trackDocScores = trackDocScores; this.trackMaxScore = trackMaxScore; this.after = after; this.mayNeedScoresTwice = sort.needsScores() && (trackDocScores || trackMaxScore); // Must set maxScore to NEG_INF, or otherwise Math.max always returns NaN. maxScore = Float.NEGATIVE_INFINITY; FieldComparator<?>[] comparators = queue.comparators; // Tell all comparators their top value: for (int i = 0; i < comparators.length; i++) { @SuppressWarnings("unchecked") FieldComparator<Object> comparator = (FieldComparator<Object>) comparators[i]; comparator.setTopValue(after.fields[i]); } }
// Returns true if first is < second @Override public boolean lessThan(ShardRef first, ShardRef second) { assert first != second; final FieldDoc firstFD = (FieldDoc) shardHits[first.shardIndex][first.hitIndex]; final FieldDoc secondFD = (FieldDoc) shardHits[second.shardIndex][second.hitIndex]; // System.out.println(" lessThan:\n first=" + first + " doc=" + firstFD.doc + " score=" + // firstFD.score + "\n second=" + second + " doc=" + secondFD.doc + " score=" + // secondFD.score); for (int compIDX = 0; compIDX < comparators.length; compIDX++) { final FieldComparator comp = comparators[compIDX]; // System.out.println(" cmp idx=" + compIDX + " cmp1=" + firstFD.fields[compIDX] + " // cmp2=" + secondFD.fields[compIDX] + " reverse=" + reverseMul[compIDX]); final int cmp = reverseMul[compIDX] * comp.compareValues(firstFD.fields[compIDX], secondFD.fields[compIDX]); if (cmp != 0) { // System.out.println(" return " + (cmp < 0)); return cmp < 0; } } // Tie break: earlier shard wins if (first.shardIndex < second.shardIndex) { // System.out.println(" return tb true"); return true; } else if (first.shardIndex > second.shardIndex) { // System.out.println(" return tb false"); return false; } else { // Tie break in same shard: resolve however the // shard had resolved it: // System.out.println(" return tb " + (first.hitIndex < second.hitIndex)); assert first.hitIndex != second.hitIndex; return first.hitIndex < second.hitIndex; } }
protected void doFieldSortValues(ResponseBuilder rb, SolrIndexSearcher searcher) throws IOException { SolrQueryRequest req = rb.req; SolrQueryResponse rsp = rb.rsp; // The query cache doesn't currently store sort field values, and SolrIndexSearcher doesn't // currently have an option to return sort field values. Because of this, we // take the documents given and re-derive the sort values. boolean fsv = req.getParams().getBool(ResponseBuilder.FIELD_SORT_VALUES, false); if (fsv) { Sort sort = rb.getSortSpec().getSort(); SortField[] sortFields = sort == null ? new SortField[] {SortField.FIELD_SCORE} : sort.getSort(); NamedList sortVals = new NamedList(); // order is important for the sort fields Field field = new Field("dummy", "", Field.Store.YES, Field.Index.NO); // a dummy Field SolrIndexReader reader = searcher.getReader(); SolrIndexReader[] readers = reader.getLeafReaders(); SolrIndexReader subReader = reader; if (readers.length == 1) { // if there is a single segment, use that subReader and avoid looking up each time subReader = readers[0]; readers = null; } int[] offsets = reader.getLeafOffsets(); for (SortField sortField : sortFields) { int type = sortField.getType(); if (type == SortField.SCORE || type == SortField.DOC) continue; FieldComparator comparator = null; FieldComparator comparators[] = (readers == null) ? null : new FieldComparator[readers.length]; String fieldname = sortField.getField(); FieldType ft = fieldname == null ? null : req.getSchema().getFieldTypeNoEx(fieldname); DocList docList = rb.getResults().docList; ArrayList<Object> vals = new ArrayList<Object>(docList.size()); DocIterator it = rb.getResults().docList.iterator(); int offset = 0; int idx = 0; while (it.hasNext()) { int doc = it.nextDoc(); if (readers != null) { idx = SolrIndexReader.readerIndex(doc, offsets); subReader = readers[idx]; offset = offsets[idx]; comparator = comparators[idx]; } if (comparator == null) { comparator = sortField.getComparator(1, 0); comparator = comparator.setNextReader(subReader, offset); if (comparators != null) comparators[idx] = comparator; } doc -= offset; // adjust for what segment this is in comparator.copy(0, doc); Object val = comparator.value(0); // Sortable float, double, int, long types all just use a string // comparator. For these, we need to put the type into a readable // format. One reason for this is that XML can't represent all // string values (or even all unicode code points). // indexedToReadable() should be a no-op and should // thus be harmless anyway (for all current ways anyway) if (val instanceof String) { field.setValue((String) val); val = ft.toObject(field); } // Must do the same conversion when sorting by a // String field in Lucene, which returns the terms // data as BytesRef: if (val instanceof BytesRef) { field.setValue(((BytesRef) val).utf8ToString()); val = ft.toObject(field); } vals.add(val); } sortVals.add(fieldname, vals); } rsp.add("sort_values", sortVals); } }
@Override public void setScorer(Scorer scorer) throws IOException { comparator.setScorer(scorer); }
@Override public void setNextReader(AtomicReaderContext context) throws IOException { this.docBase = context.docBase; queue.setComparator(0, comparator.setNextReader(context)); comparator = queue.firstComparator; }
@SuppressWarnings({"unchecked", "rawtypes"}) @Override public void collect(int doc) throws IOException { totalHits++; // System.out.println(" collect doc=" + doc); // Check if this hit was already collected on a // previous page: boolean sameValues = true; for (int compIDX = 0; compIDX < comparators.length; compIDX++) { final FieldComparator comp = comparators[compIDX]; final int cmp = reverseMul[compIDX] * comp.compareDocToValue(doc, after.fields[compIDX]); if (cmp < 0) { // Already collected on a previous page // System.out.println(" skip: before"); return; } else if (cmp > 0) { // Not yet collected sameValues = false; // System.out.println(" keep: after"); break; } } // Tie-break by docID: if (sameValues && doc <= afterDoc) { // Already collected on a previous page // System.out.println(" skip: tie-break"); return; } collectedHits++; float score = Float.NaN; if (trackMaxScore) { score = scorer.score(); if (score > maxScore) { maxScore = score; } } if (queueFull) { // Fastmatch: return if this hit is not competitive for (int i = 0; ; i++) { final int c = reverseMul[i] * comparators[i].compareBottom(doc); if (c < 0) { // Definitely not competitive. return; } else if (c > 0) { // Definitely competitive. break; } else if (i == comparators.length - 1) { // This is the equals case. if (doc + docBase > bottom.doc) { // Definitely not competitive return; } break; } } // This hit is competitive - replace bottom element in queue & adjustTop for (int i = 0; i < comparators.length; i++) { comparators[i].copy(bottom.slot, doc); } // Compute score only if it is competitive. if (trackDocScores && !trackMaxScore) { score = scorer.score(); } updateBottom(doc, score); for (int i = 0; i < comparators.length; i++) { comparators[i].setBottom(bottom.slot); } } else { // Startup transient: queue hasn't gathered numHits yet final int slot = collectedHits - 1; // System.out.println(" slot=" + slot); // Copy hit into queue for (int i = 0; i < comparators.length; i++) { comparators[i].copy(slot, doc); } // Compute score only if it is competitive. if (trackDocScores && !trackMaxScore) { score = scorer.score(); } bottom = pq.add(new Entry(slot, docBase + doc, score)); queueFull = collectedHits == numHits; if (queueFull) { for (int i = 0; i < comparators.length; i++) { comparators[i].setBottom(bottom.slot); } } } }