static boolean canEarlyTerminate(Sort searchSort, Sort indexSort) { final SortField[] fields1 = searchSort.getSort(); final SortField[] fields2 = indexSort.getSort(); // early termination is possible if fields1 is a prefix of fields2 if (fields1.length > fields2.length) { return false; } return Arrays.asList(fields1).equals(Arrays.asList(fields2).subList(0, fields1.length)); }
/** Returns whether collection can be early-terminated if it sorts with the * provided {@link Sort} and if segments are merged with the provided * {@link Sort}. */ public static boolean canEarlyTerminate(Sort searchSort, Sort mergePolicySort) { final SortField[] fields1 = searchSort.getSort(); final SortField[] fields2 = mergePolicySort.getSort(); // early termination is possible if fields1 is a prefix of fields2 if (fields1.length > fields2.length) { return false; } return Arrays.asList(fields1).equals(Arrays.asList(fields2).subList(0, fields1.length)); }
/** * Set the {@link Sort} order to use for all (flushed and merged) segments. */ public IndexWriterConfig setIndexSort(Sort sort) { for(SortField sortField : sort.getSort()) { final SortField.Type sortType = Sorter.getSortFieldType(sortField); if (ALLOWED_INDEX_SORT_TYPES.contains(sortType) == false) { throw new IllegalArgumentException("invalid SortField type: must be one of " + ALLOWED_INDEX_SORT_TYPES + " but got: " + sortField); } } this.indexSort = sort; this.indexSortFields = Arrays.stream(sort.getSort()).map(SortField::getField).collect(Collectors.toSet()); return this; }
public MergeSortQueue(Sort sort, TopDocs[] shardHits) { super(shardHits.length); this.shardHits = new ScoreDoc[shardHits.length][]; for(int shardIDX=0;shardIDX<shardHits.length;shardIDX++) { final ScoreDoc[] shard = shardHits[shardIDX].scoreDocs; //System.out.println(" init shardIdx=" + shardIDX + " hits=" + shard); if (shard != null) { this.shardHits[shardIDX] = shard; // Fail gracefully if API is misused: for(int hitIDX=0;hitIDX<shard.length;hitIDX++) { final ScoreDoc sd = shard[hitIDX]; if (!(sd instanceof FieldDoc)) { throw new IllegalArgumentException("shard " + shardIDX + " was not sorted by the provided Sort (expected FieldDoc but got ScoreDoc)"); } final FieldDoc fd = (FieldDoc) sd; if (fd.fields == null) { throw new IllegalArgumentException("shard " + shardIDX + " did not set sort field values (FieldDoc.fields is null); you must pass fillFields=true to IndexSearcher.search on each shard"); } } } } final SortField[] sortFields = sort.getSort(); comparators = new FieldComparator[sortFields.length]; reverseMul = new int[sortFields.length]; for(int compIDX=0;compIDX<sortFields.length;compIDX++) { final SortField sortField = sortFields[compIDX]; comparators[compIDX] = sortField.getComparator(1, compIDX); reverseMul[compIDX] = sortField.getReverse() ? -1 : 1; } }
private Sorter.DocMap maybeSortSegment(SegmentWriteState state) throws IOException { Sort indexSort = state.segmentInfo.getIndexSort(); if (indexSort == null) { return null; } List<Sorter.DocComparator> comparators = new ArrayList<>(); for (int i = 0; i < indexSort.getSort().length; i++) { SortField sortField = indexSort.getSort()[i]; PerField perField = getPerField(sortField.getField()); if (perField != null && perField.docValuesWriter != null && finishedDocValues.contains(perField.fieldInfo.name) == false) { perField.docValuesWriter.finish(state.segmentInfo.maxDoc()); Sorter.DocComparator cmp = perField.docValuesWriter.getDocComparator(state.segmentInfo.maxDoc(), sortField); comparators.add(cmp); finishedDocValues.add(perField.fieldInfo.name); } else { // safe to ignore, sort field with no values or already seen before } } Sorter sorter = new Sorter(indexSort); // returns null if the documents are already sorted return sorter.sort(state.segmentInfo.maxDoc(), comparators.toArray(new Sorter.DocComparator[comparators.size()])); }
/** * Returns a mapping from the old document ID to its new location in the * sorted index. Implementations can use the auxiliary * {@link #sort(int, DocComparator)} to compute the old-to-new permutation * given a list of documents and their corresponding values. * <p> * A return value of <tt>null</tt> is allowed and means that * <code>reader</code> is already sorted. * <p> * <b>NOTE:</b> deleted documents are expected to appear in the mapping as * well, they will however be marked as deleted in the sorted view. */ DocMap sort(LeafReader reader) throws IOException { SortField fields[] = sort.getSort(); final DocComparator comparators[] = new DocComparator[fields.length]; for (int i = 0; i < fields.length; i++) { comparators[i] = getDocComparator(reader, fields[i]); } return sort(reader.maxDoc(), comparators); }
private void validateIndexSortDVType(Sort indexSort, String fieldName, DocValuesType dvType) { for (SortField sortField : indexSort.getSort()) { if (sortField.getField().equals(fieldName)) { switch (dvType) {
SortField fields[] = sort.getSort(); final int reverseMul[] = new int[fields.length]; final LeafFieldComparator comparators[] = new LeafFieldComparator[fields.length];
@Override public Explanation explain(IndexSearcher searcher, Explanation firstPassExplanation, int docID) throws IOException { TopDocs oneHit = new TopDocs(1, new ScoreDoc[] {new ScoreDoc(docID, firstPassExplanation.getValue())}); TopDocs hits = rescore(searcher, oneHit, 1); assert hits.totalHits == 1; List<Explanation> subs = new ArrayList<>(); // Add first pass: Explanation first = Explanation.match(firstPassExplanation.getValue(), "first pass score", firstPassExplanation); subs.add(first); FieldDoc fieldDoc = (FieldDoc) hits.scoreDocs[0]; // Add sort values: SortField[] sortFields = sort.getSort(); for(int i=0;i<sortFields.length;i++) { subs.add(Explanation.match(0.0f, "sort field " + sortFields[i].toString() + " value=" + fieldDoc.fields[i])); } // TODO: if we could ask the Sort to explain itself then // we wouldn't need the separate ExpressionRescorer... return Explanation.match(0.0f, "sort field values for sort=" + sort.toString(), subs); } }
return new TopDocs(totalHitCount, hits, maxScore); } else { return new TopFieldDocs(totalHitCount, hits, sort.getSort(), maxScore);
SortField fields[] = sort.getSort(); final ComparableProvider[][] comparables = new ComparableProvider[fields.length][]; final int[] reverseMuls = new int[fields.length];
public SearchAfterSortedDocQuery(Sort sort, FieldDoc after) { if (sort.getSort().length != after.fields.length) { throw new IllegalArgumentException("after doc has " + after.fields.length + " value(s) but sort has " + sort.getSort().length + "."); } this.sort = sort; this.after = after; int numFields = sort.getSort().length; this.fieldComparators = new FieldComparator[numFields]; this.reverseMuls = new int[numFields]; for (int i = 0; i < numFields; i++) { SortField sortField = sort.getSort()[i]; FieldComparator<?> fieldComparator = sortField.getComparator(1, i); @SuppressWarnings("unchecked") FieldComparator<Object> comparator = (FieldComparator<Object>) fieldComparator; comparator.setTopValue(after.fields[i]); fieldComparators[i] = fieldComparator; reverseMuls[i] = sortField.getReverse() ? -1 : 1; } }
public static FieldDoc buildFieldDoc(SortAndFormats sort, Object[] values) { if (sort == null || sort.sort.getSort() == null || sort.sort.getSort().length == 0) { throw new IllegalArgumentException("Sort must contain at least one field."); } SortField[] sortFields = sort.sort.getSort(); if (sortFields.length != values.length) { throw new IllegalArgumentException( SEARCH_AFTER.getPreferredName() + " has " + values.length + " value(s) but sort has " + sort.sort.getSort().length + "."); } Object[] fieldValues = new Object[sortFields.length]; for (int i = 0; i < sortFields.length; i++) { SortField sortField = sortFields[i]; DocValueFormat format = sort.formats[i]; if (values[i] != null) { fieldValues[i] = convertValueFromSortField(values[i], sortField, format); } else { fieldValues[i] = null; } } /* * We set the doc id to Integer.MAX_VALUE in order to make sure that the search starts "after" the first document that is equal to * the field values. */ return new FieldDoc(Integer.MAX_VALUE, 0, fieldValues); }
MergeSortQueue(Sort sort, CollapseTopFieldDocs[] shardHits) { super(shardHits.length); this.shardHits = new ScoreDoc[shardHits.length][]; for (int shardIDX = 0; shardIDX < shardHits.length; shardIDX++) { final ScoreDoc[] shard = shardHits[shardIDX].scoreDocs; if (shard != null) { this.shardHits[shardIDX] = shard; // Fail gracefully if API is misused: for (int hitIDX = 0; hitIDX < shard.length; hitIDX++) { final ScoreDoc sd = shard[hitIDX]; final FieldDoc gd = (FieldDoc) sd; assert gd.fields != null; } } } final SortField[] sortFields = sort.getSort(); comparators = new FieldComparator[sortFields.length]; reverseMul = new int[sortFields.length]; for (int compIDX = 0; compIDX < sortFields.length; compIDX++) { final SortField sortField = sortFields[compIDX]; comparators[compIDX] = sortField.getComparator(1, compIDX); reverseMul[compIDX] = sortField.getReverse() ? -1 : 1; } }
private static void toXContent(XContentBuilder builder, Sort sort) throws IOException { builder.startArray("sort"); for (SortField field : sort.getSort()) { builder.startObject(); builder.field("field", field.getField()); if (field instanceof SortedNumericSortField) { builder.field("mode", ((SortedNumericSortField) field).getSelector() .toString().toLowerCase(Locale.ROOT)); } else if (field instanceof SortedSetSortField) { builder.field("mode", ((SortedSetSortField) field).getSelector() .toString().toLowerCase(Locale.ROOT)); } if (field.getMissingValue() != null) { builder.field("missing", field.getMissingValue().toString()); } builder.field("reverse", field.getReverse()); builder.endObject(); } builder.endArray(); }
@Override public InternalTopHits buildEmptyAggregation() { TopDocs topDocs; if (subSearchContext.sort() != null) { topDocs = new TopFieldDocs(0, new FieldDoc[0], subSearchContext.sort().sort.getSort(), Float.NaN); } else { topDocs = Lucene.EMPTY_TOP_DOCS; } return new InternalTopHits(name, subSearchContext.from(), subSearchContext.size(), topDocs, SearchHits.empty(), pipelineAggregators(), metaData()); }
@Override public Scorer scorer(LeafReaderContext context) throws IOException { Sort segmentSort = context.reader().getMetaData().getSort(); if (EarlyTerminatingSortingCollector.canEarlyTerminate(sort, segmentSort) == false) { throw new IOException("search sort :[" + sort.getSort() + "] does not match the index sort:[" + segmentSort + "]"); } final int afterDoc = after.doc - context.docBase; TopComparator comparator = getTopComparator(fieldComparators, reverseMuls, context, afterDoc); final int maxDoc = context.reader().maxDoc(); final int firstDoc = searchAfterDoc(comparator, 0, context.reader().maxDoc()); if (firstDoc >= maxDoc) { return null; } final DocIdSetIterator disi = new MinDocQuery.MinDocIterator(firstDoc, maxDoc); return new ConstantScoreScorer(this, score(), disi); }