org.apache.solr.handler.component.ShardFieldSortedHitQueue.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.handler.component.ShardFieldSortedHitQueue.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.solr.handler.component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import org.apache.lucene.search.FieldComparator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.SortField;
import org.apache.lucene.util.PriorityQueue;
import org.apache.solr.common.SolrException;

import static org.apache.solr.common.SolrException.ErrorCode.SERVER_ERROR;

// used by distributed search to merge results.
public class ShardFieldSortedHitQueue extends PriorityQueue<ShardDoc> {

    /** Stores a comparator corresponding to each field being sorted by */
    protected Comparator<ShardDoc>[] comparators;

    /** Stores the sort criteria being used. */
    protected SortField[] fields;

    /** The order of these fieldNames should correspond to the order of sort field values retrieved from the shard */
    protected List<String> fieldNames = new ArrayList<>();

    public ShardFieldSortedHitQueue(SortField[] fields, int size, IndexSearcher searcher) {
        super(size);
        final int n = fields.length;
        //noinspection unchecked
        comparators = new Comparator[n];
        this.fields = new SortField[n];
        for (int i = 0; i < n; ++i) {

            // keep track of the named fields
            SortField.Type type = fields[i].getType();
            if (type != SortField.Type.SCORE && type != SortField.Type.DOC) {
                fieldNames.add(fields[i].getField());
            }

            String fieldname = fields[i].getField();
            comparators[i] = getCachedComparator(fields[i], searcher);

            if (fields[i].getType() == SortField.Type.STRING) {
                this.fields[i] = new SortField(fieldname, SortField.Type.STRING, fields[i].getReverse());
            } else {
                this.fields[i] = new SortField(fieldname, fields[i].getType(), fields[i].getReverse());
            }

            //System.out.println("%%%%%%%%%%%%%%%%%% got "+fields[i].getType() +"   for "+ fieldname +"  fields[i].getReverse(): "+fields[i].getReverse());
        }
    }

    @Override
    protected boolean lessThan(ShardDoc docA, ShardDoc docB) {
        // If these docs are from the same shard, then the relative order
        // is how they appeared in the response from that shard.    
        if (docA.shard == docB.shard) {
            // if docA has a smaller position, it should be "larger" so it
            // comes before docB.
            // This will handle sorting by docid within the same shard

            // comment this out to test comparators.
            return !(docA.orderInShard < docB.orderInShard);
        }

        // run comparators
        final int n = comparators.length;
        int c = 0;
        for (int i = 0; i < n && c == 0; i++) {
            c = (fields[i].getReverse()) ? comparators[i].compare(docB, docA) : comparators[i].compare(docA, docB);
        }

        // solve tiebreaks by comparing shards (similar to using docid)
        // smaller docid's beat larger ids, so reverse the natural ordering
        if (c == 0) {
            c = -docA.shard.compareTo(docB.shard);
        }

        return c < 0;
    }

    Comparator<ShardDoc> getCachedComparator(SortField sortField, IndexSearcher searcher) {
        SortField.Type type = sortField.getType();
        if (type == SortField.Type.SCORE) {
            return (o1, o2) -> {
                final float f1 = o1.score;
                final float f2 = o2.score;
                if (f1 < f2)
                    return -1;
                if (f1 > f2)
                    return 1;
                return 0;
            };
        } else if (type == SortField.Type.REWRITEABLE) {
            try {
                sortField = sortField.rewrite(searcher);
            } catch (IOException e) {
                throw new SolrException(SERVER_ERROR, "Exception rewriting sort field " + sortField, e);
            }
        }
        return comparatorFieldComparator(sortField);
    }

    abstract class ShardComparator implements Comparator<ShardDoc> {
        final SortField sortField;
        final String fieldName;
        final int fieldNum;

        public ShardComparator(SortField sortField) {
            this.sortField = sortField;
            this.fieldName = sortField.getField();
            int fieldNum = 0;
            for (int i = 0; i < fieldNames.size(); i++) {
                if (fieldNames.get(i).equals(fieldName)) {
                    fieldNum = i;
                    break;
                }
            }
            this.fieldNum = fieldNum;
        }

        Object sortVal(ShardDoc shardDoc) {
            assert (shardDoc.sortFieldValues.getName(fieldNum).equals(fieldName));
            List lst = (List) shardDoc.sortFieldValues.getVal(fieldNum);
            return lst.get(shardDoc.orderInShard);
        }
    }

    Comparator<ShardDoc> comparatorFieldComparator(SortField sortField) {
        final FieldComparator fieldComparator = sortField.getComparator(0, 0);
        return new ShardComparator(sortField) {
            // Since the PriorityQueue keeps the biggest elements by default,
            // we need to reverse the field compare ordering so that the
            // smallest elements are kept instead of the largest... hence
            // the negative sign.
            @Override
            public int compare(final ShardDoc o1, final ShardDoc o2) {
                //noinspection unchecked
                return -fieldComparator.compareValues(sortVal(o1), sortVal(o2));
            }
        };
    }
}