content: /**
   * 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.search.function;
  
  import org.apache.lucene.index.IndexReader;
  import org.apache.lucene.search.*;
  import org.apache.solr.common.SolrException;
  
  import java.io.IOException;
  import java.util.Map;
  
  /**
   * <code>QueryValueSource</code> returns the relevance score of the query
   */
  public class QueryValueSource extends ValueSource {
    final Query q;
    final float defVal;
  
    public QueryValueSource(Query q, float defVal) {
      this.q = q;
      this.defVal = defVal;
    }
  
    public Query getQuery() { return q; }
    public float getDefaultValue() { return defVal; }
  
    @Override
    public String description() {
      return "query(" + q + ",def=" + defVal + ")";
    }
  
    @Override
    public DocValues getValues(Map context, IndexReader reader) throws IOException {
      return new QueryDocValues(reader, q, defVal, context==null ? null : (Weight)context.get(this));
    }
  
    @Override
    public int hashCode() {
      return q.hashCode() * 29;
    }
  
    @Override
    public boolean equals(Object o) {
      if (QueryValueSource.class != o.getClass()) return false;
      QueryValueSource other = (QueryValueSource)o;
      return this.q.equals(other.q) && this.defVal==other.defVal;
    }
  
    @Override
    public void createWeight(Map context, Searcher searcher) throws IOException {
      Weight w = searcher.createNormalizedWeight(q);
      context.put(this, w);
    }
  }
  
  
  class QueryDocValues extends DocValues {
    final Query q;
    final IndexReader reader;
    final Weight weight;
    final float defVal;
  
    Scorer scorer;
    int scorerDoc; // the document the scorer is on
    boolean noMatches = false;
  
    // the last document requested... start off with high value
    // to trigger a scorer reset on first access.
    int lastDocRequested=Integer.MAX_VALUE;
  
    public QueryDocValues(IndexReader reader, Query q, float defVal, Weight w) throws IOException {
      this.reader = reader;
      this.q = q;
      this.defVal = defVal;
      weight = w!=null ? w : new IndexSearcher(reader).createNormalizedWeight(q);
    }
  
    @Override
    public float floatVal(int doc) {
      try {
        if (doc < lastDocRequested) {
          if (noMatches) return defVal;
          scorer = weight.scorer(reader, true, false);
          if (scorer==null) {
            noMatches = true;
            return defVal;
          }
          scorerDoc = -1;
        }
        lastDocRequested = doc;
  
        if (scorerDoc < doc) {
          scorerDoc = scorer.advance(doc);
        }
  
        if (scorerDoc > doc) {
          // query doesn't match this document... either because we hit the
          // end, or because the next doc is after this doc.
          return defVal;
        }
  
        // a match!
        return scorer.score();
      } catch (IOException e) {
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "caught exception in QueryDocVals("+q+") doc="+doc, e);
      }
    }  
  
    @Override
    public int intVal(int doc) {
      return (int)floatVal(doc);
    }
    @Override
    public long longVal(int doc) {
      return (long)floatVal(doc);
    }
    @Override
    public double doubleVal(int doc) {
      return (double)floatVal(doc);
    }
    @Override
    public String strVal(int doc) {
      return Float.toString(floatVal(doc));
    }
    @Override
    public String toString(int doc) {
      return "query(" + q + ",def=" + defVal + ")=" + floatVal(doc);
    }
  }