All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.handler.component.ShardDoc Maven / Gradle / Ivy

/**
 * 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 org.apache.lucene.search.SortField;
import org.apache.lucene.search.FieldComparatorSource;
import org.apache.lucene.util.PriorityQueue;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.search.MissingStringLastComparatorSource;

import java.text.Collator;
import java.util.Comparator;
import java.util.Locale;
import java.util.List;
import java.util.ArrayList;

public class ShardDoc {
  public String shard;
  public String shardAddress;  // TODO
  
  int orderInShard;
    // the position of this doc within the shard... this can be used
    // to short-circuit comparisons if the shard is equal, and can
    // also be used to break ties within the same shard.

  Object id;
    // this is currently the uniqueKeyField but
    // may be replaced with internal docid in a future release.

  Float score;

  NamedList sortFieldValues;
  // sort field values for *all* docs in a particular shard.
  // this doc's values are in position orderInShard

  // TODO: store the SolrDocument here?
  // Store the order in the merged list for lookup when getting stored fields?
  // (other components need this ordering to store data in order, like highlighting)
  // but we shouldn't expose uniqueKey (have a map by it) until the stored-field
  // retrieval stage.

  int positionInResponse;
  // the ordinal position in the merged response arraylist  

  public String toString(){
    return "id="+id
            +" ,score="+score
            +" ,shard="+shard
            +" ,orderInShard="+orderInShard
            +" ,positionInResponse="+positionInResponse
            +" ,sortFieldValues="+sortFieldValues;
  }
}



// used by distributed search to merge results.
class ShardFieldSortedHitQueue extends PriorityQueue {

  /** Stores a comparator corresponding to each field being sorted by */
  protected Comparator[] 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 fieldNames = new ArrayList();

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

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

      String fieldname = fields[i].getField();
      comparators[i] = getCachedComparator(fieldname, fields[i]
          .getType(), fields[i].getLocale(), fields[i].getComparatorSource());

     if (fields[i].getType() == SortField.STRING) {
        this.fields[i] = new SortField(fieldname, fields[i].getLocale(),
            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());
    }

    initialize(size);
  }

  @Override
  protected boolean lessThan(Object objA, Object objB) {
    ShardDoc docA = (ShardDoc)objA;
    ShardDoc docB = (ShardDoc)objB;

    // 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 getCachedComparator(String fieldname, int type, Locale locale, FieldComparatorSource factory) {
    Comparator comparator = null;
    switch (type) {
    case SortField.SCORE:
      comparator = comparatorScore(fieldname);
      break;
    case SortField.STRING:
      if (locale != null)
        comparator = comparatorStringLocale(fieldname, locale);
      else
        comparator = comparatorNatural(fieldname);
      break;
    case SortField.CUSTOM:
      if (factory instanceof MissingStringLastComparatorSource){
        comparator = comparatorMissingStringLast(fieldname);
      } else {
        // TODO: support other types such as random... is there a way to
        // support generically?  Perhaps just comparing Object
        comparator = comparatorNatural(fieldname);
        // throw new RuntimeException("Custom sort not supported factory is "+factory.getClass());
      }
      break;
    case SortField.DOC:
      // TODO: we can support this!
      throw new RuntimeException("Doc sort not supported");
    default:
      comparator = comparatorNatural(fieldname);
      break;
    }
    return comparator;
  }

  class ShardComparator implements Comparator {
    String fieldName;
    int fieldNum;
    public ShardComparator(String fieldName) {
      this.fieldName = fieldName;
      this.fieldNum=0;
      for (int i=0; i f2)
          return 1;
        return 0;
      }
    };
  }

  // The lucene natural sort ordering corresponds to numeric
  // and string natural sort orderings (ascending).  Since
  // the PriorityQueue keeps the biggest elements by default,
  // we need to reverse the natural compare ordering so that the
  // smallest elements are kept instead of the largest... hence
  // the negative sign on the final compareTo().
  Comparator comparatorNatural(String fieldName) {
    return new ShardComparator(fieldName) {
      public final int compare(final Object o1, final Object o2) {
        ShardDoc sd1 = (ShardDoc) o1;
        ShardDoc sd2 = (ShardDoc) o2;
        Comparable v1 = (Comparable)sortVal(sd1);
        Comparable v2 = (Comparable)sortVal(sd2);
        if (v1==v2)
          return 0;
        if (v1==null)
          return 1;
        if(v2==null)
          return -1;
        return -v1.compareTo(v2);
      }
    };
  }


  Comparator comparatorStringLocale(final String fieldName,
      Locale locale) {
    final Collator collator = Collator.getInstance(locale);
    return new ShardComparator(fieldName) {
      public final int compare(final Object o1, final Object o2) {
        ShardDoc sd1 = (ShardDoc) o1;
        ShardDoc sd2 = (ShardDoc) o2;
        Comparable v1 = (Comparable)sortVal(sd1);
        Comparable v2 = (Comparable)sortVal(sd2);
        if (v1==v2)
          return 0;
        if (v1==null)
          return 1;
        if(v2==null)
          return -1;
        return -collator.compare(v1,v2);
      }
    };
  }


  Comparator comparatorMissingStringLast(final String fieldName) {
     return new ShardComparator(fieldName) {
      public final int compare(final Object o1, final Object o2) {
        ShardDoc sd1 = (ShardDoc) o1;
        ShardDoc sd2 = (ShardDoc) o2;
        Comparable v1 = (Comparable)sortVal(sd1);
        Comparable v2 = (Comparable)sortVal(sd2);
        if (v1==v2)
          return 0;
        if (v1==null)
          return -1;
        if(v2==null)
          return 1;
        return -v1.compareTo(v2);
      }
    };
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy