You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ds...@apache.org on 2015/08/06 19:47:25 UTC

svn commit: r1694543 - in /lucene/dev/trunk: lucene/ lucene/queries/src/java/org/apache/lucene/queries/function/ lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/ lucene/queries/src/java/org/apache/lucene/queries/function/valuesourc...

Author: dsmiley
Date: Thu Aug  6 17:47:24 2015
New Revision: 1694543

URL: http://svn.apache.org/r1694543
Log:
LUCENE-6720: new FunctionRangeQuery wrapper around ValueSourceScorer.
And ValueSourceScorer improvements.

Added:
    lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionRangeQuery.java   (with props)
    lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java   (with props)
Removed:
    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/util/ValueSourceFilter.java
Modified:
    lucene/dev/trunk/lucene/CHANGES.txt
    lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
    lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionValues.java
    lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
    lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DocTermsIndexDocValues.java
    lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DoubleDocValues.java
    lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/IntDocValues.java
    lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/LongDocValues.java
    lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/EnumFieldSource.java
    lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java
    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java

Modified: lucene/dev/trunk/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/CHANGES.txt?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/CHANGES.txt (original)
+++ lucene/dev/trunk/lucene/CHANGES.txt Thu Aug  6 17:47:24 2015
@@ -45,11 +45,19 @@ API Changes
 
 ======================= Lucene 5.4.0 =======================
 
+New Features
+
+* LUCENE-6720: New FunctionRangeQuery wrapper around ValueSourceScorer
+  (returned from ValueSource/FunctionValues.getRangeScorer()). (David Smiley)
+
 Optimizations
 
 * LUCENE-6708: TopFieldCollector does not compute the score several times on the
   same document anymore. (Adrien Grand)
 
+* LUCENE-6720: ValueSourceScorer, returned from
+  FunctionValues.getRangeScorer(), now uses TwoPhaseIterator. (David Smiley)
+
 ======================= Lucene 5.3.0 =======================
 
 New Features

Modified: lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java (original)
+++ lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java Thu Aug  6 17:47:24 2015
@@ -29,16 +29,14 @@ import org.apache.lucene.search.IndexSea
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Weight;
-import org.apache.lucene.util.Bits;
 
 
 /**
  * Returns a score for each document based on a ValueSource,
  * often some function of the value of a field.
  *
- * <b>Note: This API is experimental and may change in non backward-compatible ways in the future</b>
- *
- *
+ * @see ValueSourceScorer
+ * @lucene.experimental
  */
 public class FunctionQuery extends Query {
   final ValueSource func;

Added: lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionRangeQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionRangeQuery.java?rev=1694543&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionRangeQuery.java (added)
+++ lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionRangeQuery.java Thu Aug  6 17:47:24 2015
@@ -0,0 +1,149 @@
+package org.apache.lucene.queries.function;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Weight;
+
+/**
+ * A Query wrapping a {@link ValueSource} that matches docs in which the values in the value source match a configured
+ * range.  The score is the float value.  This can be a slow query if run by itself since it must visit all docs;
+ * ideally it's combined with other queries.
+ * It's mostly a wrapper around
+ * {@link FunctionValues#getRangeScorer(IndexReader, String, String, boolean, boolean)}.
+ *
+ * A similar class is {@code org.apache.lucene.search.DocValuesRangeQuery} in the sandbox module.  That one is
+ * constant scoring.
+ *
+ * @see FunctionQuery (constant scoring)
+ * @lucene.experimental
+ */
+public class FunctionRangeQuery extends Query {
+
+  private final ValueSource valueSource;
+
+  // These two are declared as strings because FunctionValues.getRangeScorer takes String args and parses them.
+  private final String lowerVal;
+  private final String upperVal;
+  private final boolean includeLower;
+  private final boolean includeUpper;
+
+  public FunctionRangeQuery(ValueSource valueSource, Number lowerVal, Number upperVal,
+                            boolean includeLower, boolean includeUpper) {
+    this(valueSource, lowerVal == null ? null : lowerVal.toString(), upperVal == null ? null : upperVal.toString(),
+        includeLower, includeUpper);
+  }
+
+  public FunctionRangeQuery(ValueSource valueSource, String lowerVal, String upperVal,
+                            boolean includeLower, boolean includeUpper) {
+    this.valueSource = valueSource;
+    this.lowerVal = lowerVal;
+    this.upperVal = upperVal;
+    this.includeLower = includeLower;
+    this.includeUpper = includeUpper;
+  }
+
+  @Override
+  public String toString(String field) {
+    return "frange(" + valueSource + "):"
+        + (includeLower ? '[' : '{')
+        + (lowerVal == null ? "*" : lowerVal) + " TO " + (upperVal == null ? "*" : upperVal)
+        + (includeUpper ? ']' : '}');
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof FunctionRangeQuery)) return false;
+    if (!super.equals(o)) return false;
+    FunctionRangeQuery that = (FunctionRangeQuery) o;
+    return Objects.equals(includeLower, that.includeLower) &&
+        Objects.equals(includeUpper, that.includeUpper) &&
+        Objects.equals(valueSource, that.valueSource) &&
+        Objects.equals(lowerVal, that.lowerVal) &&
+        Objects.equals(upperVal, that.upperVal);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), valueSource, lowerVal, upperVal, includeLower, includeUpper);
+  }
+
+  @Override
+  public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+    return new FunctionRangeWeight(searcher);
+  }
+
+  private class FunctionRangeWeight extends Weight {
+    @SuppressWarnings("rawtypes")
+    private final Map vsContext;
+
+    public FunctionRangeWeight(IndexSearcher searcher) throws IOException {
+      super(FunctionRangeQuery.this);
+      vsContext = ValueSource.newContext(searcher);
+      valueSource.createWeight(vsContext, searcher);//callback on valueSource tree
+    }
+
+    @Override
+    public void extractTerms(Set<Term> terms) {
+      //none
+    }
+
+    //Note: this uses the functionValue's floatVal() as the score; queryNorm/boost is ignored.
+    @Override
+    public float getValueForNormalization() throws IOException {
+      return 1f;
+    }
+
+    @Override
+    public void normalize(float norm, float topLevelBoost) {
+      //no-op
+    }
+
+    @Override
+    public Explanation explain(LeafReaderContext context, int doc) throws IOException {
+      FunctionValues functionValues = valueSource.getValues(vsContext, context);
+      //note: by using ValueSourceScorer directly, we avoid calling scorer.advance(doc) and checking if true,
+      //  which can be slow since if that doc doesn't match, it has to linearly find the next matching
+      ValueSourceScorer scorer = scorer(context);
+      if (scorer.matches(doc)) {
+        scorer.advance(doc);
+        return Explanation.match(scorer.score(), FunctionRangeQuery.this.toString(), functionValues.explain(doc));
+      } else {
+        return Explanation.noMatch(FunctionRangeQuery.this.toString(), functionValues.explain(doc));
+      }
+    }
+
+    @Override
+    public ValueSourceScorer scorer(LeafReaderContext context) throws IOException {
+      FunctionValues functionValues = valueSource.getValues(vsContext, context);
+      // getRangeScorer takes String args and parses them. Weird.
+      return functionValues.getRangeScorer(context.reader(), lowerVal, upperVal, includeLower, includeUpper);
+    }
+  }
+}

Modified: lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionValues.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionValues.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionValues.java (original)
+++ lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionValues.java Thu Aug  6 17:47:24 2015
@@ -17,8 +17,9 @@ package org.apache.lucene.queries.functi
  * limitations under the License.
  */
 
-import org.apache.lucene.search.*;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Scorer;
 import org.apache.lucene.util.BytesRefBuilder;
 import org.apache.lucene.util.mutable.MutableValue;
 import org.apache.lucene.util.mutable.MutableValueFloat;
@@ -136,10 +137,23 @@ public abstract class FunctionValues {
     return Explanation.match(floatVal(doc), toString(doc));
   }
 
+  /**
+   * Yields a {@link Scorer} that matches all documents,
+   * and that which produces scores equal to {@link #floatVal(int)}.
+   */
   public ValueSourceScorer getScorer(IndexReader reader) {
-    return new ValueSourceScorer(reader, this);
+    return new ValueSourceScorer(reader, this) {
+      @Override
+      public boolean matches(int doc) {
+        return true;
+      }
+    };
   }
 
+  /**
+   * Yields a {@link Scorer} that matches documents with values between the specified range,
+   * and that which produces scores equal to {@link #floatVal(int)}.
+   */
   // A RangeValueSource can't easily be a ValueSource that takes another ValueSource
   // because it needs different behavior depending on the type of fields.  There is also
   // a setup cost - parsing and normalizing params, and doing a binary search on the StringIndex.
@@ -165,7 +179,7 @@ public abstract class FunctionValues {
     if (includeLower && includeUpper) {
       return new ValueSourceScorer(reader, this) {
         @Override
-        public boolean matchesValue(int doc) {
+        public boolean matches(int doc) {
           float docVal = floatVal(doc);
           return docVal >= l && docVal <= u;
         }
@@ -174,7 +188,7 @@ public abstract class FunctionValues {
     else if (includeLower && !includeUpper) {
        return new ValueSourceScorer(reader, this) {
         @Override
-        public boolean matchesValue(int doc) {
+        public boolean matches(int doc) {
           float docVal = floatVal(doc);
           return docVal >= l && docVal < u;
         }
@@ -183,7 +197,7 @@ public abstract class FunctionValues {
     else if (!includeLower && includeUpper) {
        return new ValueSourceScorer(reader, this) {
         @Override
-        public boolean matchesValue(int doc) {
+        public boolean matches(int doc) {
           float docVal = floatVal(doc);
           return docVal > l && docVal <= u;
         }
@@ -192,7 +206,7 @@ public abstract class FunctionValues {
     else {
        return new ValueSourceScorer(reader, this) {
         @Override
-        public boolean matchesValue(int doc) {
+        public boolean matches(int doc) {
           float docVal = floatVal(doc);
           return docVal > l && docVal < u;
         }

Modified: lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java (original)
+++ lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java Thu Aug  6 17:47:24 2015
@@ -20,71 +20,74 @@ package org.apache.lucene.queries.functi
 import java.io.IOException;
 
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.MultiFields;
+import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.Scorer;
-import org.apache.lucene.util.Bits;
+import org.apache.lucene.search.TwoPhaseIterator;
 
 /**
  * {@link Scorer} which returns the result of {@link FunctionValues#floatVal(int)} as
- * the score for a document.
+ * the score for a document, and which filters out documents that don't match {@link #matches(int)}.
+ * This Scorer has a {@link TwoPhaseIterator}.  This is similar to {@link FunctionQuery},
+ * but this one has no {@link org.apache.lucene.search.Weight} normalization factors/multipliers
+ * and that one doesn't filter either.
+ * <p>
+ * Note: If the scores are needed, then the underlying value will probably be
+ * fetched/computed twice -- once to filter and next to return the score.  If that's non-trivial then
+ * consider wrapping it in an implementation that will cache the current value.
+ * </p>
+ *
+ * @see FunctionQuery
+ * @lucene.experimental
  */
-public class ValueSourceScorer extends Scorer {
-  protected final IndexReader reader;
-  private int doc = -1;
-  protected final int maxDoc;
+public abstract class ValueSourceScorer extends Scorer {
   protected final FunctionValues values;
-  protected boolean checkDeletes;
-  private final Bits liveDocs;
+  private final TwoPhaseIterator twoPhaseIterator;
+  private final DocIdSetIterator disi;
 
+  //TODO use LeafReaderContext not IndexReader?
   protected ValueSourceScorer(IndexReader reader, FunctionValues values) {
-    super(null);
-    this.reader = reader;
-    this.maxDoc = reader.maxDoc();
+    super(null);//no weight
     this.values = values;
-    setCheckDeletes(true);
-    this.liveDocs = MultiFields.getLiveDocs(reader);
+    this.twoPhaseIterator = new TwoPhaseIterator(DocIdSetIterator.all(reader.maxDoc())) { // no approximation!
+      @Override
+      public boolean matches() throws IOException {
+        return ValueSourceScorer.this.matches(docID());
+      }
+    };
+    this.disi = TwoPhaseIterator.asDocIdSetIterator(twoPhaseIterator);
   }
 
-  public IndexReader getReader() {
-    return reader;
-  }
-
-  public void setCheckDeletes(boolean checkDeletes) {
-    this.checkDeletes = checkDeletes && reader.hasDeletions();
-  }
+  /** Override to decide if this document matches. It's called by {@link TwoPhaseIterator#matches()}. */
+  public abstract boolean matches(int doc);
 
-  public boolean matches(int doc) {
-    return (!checkDeletes || liveDocs.get(doc)) && matchesValue(doc);
-  }
-
-  public boolean matchesValue(int doc) {
-    return true;
+  @Override
+  public TwoPhaseIterator asTwoPhaseIterator() {
+    return twoPhaseIterator;
   }
 
   @Override
   public int docID() {
-    return doc;
+    return disi.docID();
   }
 
   @Override
   public int nextDoc() throws IOException {
-    for (; ;) {
-      doc++;
-      if (doc >= maxDoc) return doc = NO_MORE_DOCS;
-      if (matches(doc)) return doc;
-    }
+    return disi.nextDoc();
   }
 
   @Override
   public int advance(int target) throws IOException {
-    // also works fine when target==NO_MORE_DOCS
-    doc = target - 1;
-    return nextDoc();
+    return disi.advance(target);
   }
 
   @Override
   public float score() throws IOException {
-    return values.floatVal(doc);
+    // (same as FunctionQuery, but no qWeight)  TODO consider adding configurable qWeight
+    float score = values.floatVal(disi.docID());
+    // Current Lucene priority queues can't handle NaN and -Infinity, so
+    // map to -Float.MAX_VALUE. This conditional handles both -infinity
+    // and NaN since comparisons with NaN are always false.
+    return score > Float.NEGATIVE_INFINITY ? score : -Float.MAX_VALUE;
   }
 
   @Override
@@ -94,6 +97,6 @@ public class ValueSourceScorer extends S
 
   @Override
   public long cost() {
-    return maxDoc;
+    return disi.cost();
   }
 }

Modified: lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DocTermsIndexDocValues.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DocTermsIndexDocValues.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DocTermsIndexDocValues.java (original)
+++ lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DocTermsIndexDocValues.java Thu Aug  6 17:47:24 2015
@@ -19,9 +19,9 @@ package org.apache.lucene.queries.functi
 
 import java.io.IOException;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.SortedDocValues;
 import org.apache.lucene.queries.function.FunctionValues;
 import org.apache.lucene.queries.function.ValueSource;
@@ -124,7 +124,7 @@ public abstract class DocTermsIndexDocVa
 
     return new ValueSourceScorer(reader, this) {
       @Override
-      public boolean matchesValue(int doc) {
+      public boolean matches(int doc) {
         int ord = termsIndex.getOrd(doc);
         return ord >= ll && ord <= uu;
       }

Modified: lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DoubleDocValues.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DoubleDocValues.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DoubleDocValues.java (original)
+++ lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/DoubleDocValues.java Thu Aug  6 17:47:24 2015
@@ -106,7 +106,7 @@ public abstract class DoubleDocValues ex
     if (includeLower && includeUpper) {
       return new ValueSourceScorer(reader, this) {
         @Override
-        public boolean matchesValue(int doc) {
+        public boolean matches(int doc) {
           double docVal = doubleVal(doc);
           return docVal >= l && docVal <= u;
         }
@@ -115,7 +115,7 @@ public abstract class DoubleDocValues ex
     else if (includeLower && !includeUpper) {
       return new ValueSourceScorer(reader, this) {
         @Override
-        public boolean matchesValue(int doc) {
+        public boolean matches(int doc) {
           double docVal = doubleVal(doc);
           return docVal >= l && docVal < u;
         }
@@ -124,7 +124,7 @@ public abstract class DoubleDocValues ex
     else if (!includeLower && includeUpper) {
       return new ValueSourceScorer(reader, this) {
         @Override
-        public boolean matchesValue(int doc) {
+        public boolean matches(int doc) {
           double docVal = doubleVal(doc);
           return docVal > l && docVal <= u;
         }
@@ -133,7 +133,7 @@ public abstract class DoubleDocValues ex
     else {
       return new ValueSourceScorer(reader, this) {
         @Override
-        public boolean matchesValue(int doc) {
+        public boolean matches(int doc) {
           double docVal = doubleVal(doc);
           return docVal > l && docVal < u;
         }

Modified: lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/IntDocValues.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/IntDocValues.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/IntDocValues.java (original)
+++ lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/IntDocValues.java Thu Aug  6 17:47:24 2015
@@ -103,7 +103,7 @@ public abstract class IntDocValues exten
 
     return new ValueSourceScorer(reader, this) {
       @Override
-      public boolean matchesValue(int doc) {
+      public boolean matches(int doc) {
         int val = intVal(doc);
         // only check for deleted if it's the default value
         // if (val==0 && reader.isDeleted(doc)) return false;

Modified: lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/LongDocValues.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/LongDocValues.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/LongDocValues.java (original)
+++ lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/docvalues/LongDocValues.java Thu Aug  6 17:47:24 2015
@@ -112,7 +112,7 @@ public abstract class LongDocValues exte
 
     return new ValueSourceScorer(reader, this) {
       @Override
-      public boolean matchesValue(int doc) {
+      public boolean matches(int doc) {
         long val = longVal(doc);
         // only check for deleted if it's the default value
         // if (val==0 && reader.isDeleted(doc)) return false;

Modified: lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/EnumFieldSource.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/EnumFieldSource.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/EnumFieldSource.java (original)
+++ lucene/dev/trunk/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/EnumFieldSource.java Thu Aug  6 17:47:24 2015
@@ -20,9 +20,9 @@ package org.apache.lucene.queries.functi
 import java.io.IOException;
 import java.util.Map;
 
-import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.NumericDocValues;
 import org.apache.lucene.queries.function.FunctionValues;
 import org.apache.lucene.queries.function.ValueSourceScorer;
@@ -143,7 +143,7 @@ public class EnumFieldSource extends Fie
 
         return new ValueSourceScorer(reader, this) {
           @Override
-          public boolean matchesValue(int doc) {
+          public boolean matches(int doc) {
             int val = intVal(doc);
             // only check for deleted if it's the default value
             // if (val==0 && reader.isDeleted(doc)) return false;

Modified: lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java (original)
+++ lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java Thu Aug  6 17:47:24 2015
@@ -20,6 +20,7 @@ package org.apache.lucene.queries.functi
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
 import org.apache.lucene.search.QueryUtils;
 import org.apache.lucene.search.ScoreDoc;
 import org.apache.lucene.search.TopDocs;
@@ -59,7 +60,7 @@ public class TestFieldScoreQuery extends
 
   // Test that FieldScoreQuery returns docs in expected order.
   private void doTestRank (ValueSource valueSource) throws Exception {
-    FunctionQuery functionQuery = new FunctionQuery(valueSource);
+    Query functionQuery = getFunctionQuery(valueSource);
     IndexReader r = DirectoryReader.open(dir);
     IndexSearcher s = newSearcher(r);
     log("test: "+ functionQuery);
@@ -92,7 +93,7 @@ public class TestFieldScoreQuery extends
 
   // Test that FieldScoreQuery returns docs with expected score.
   private void doTestExactScore (ValueSource valueSource) throws Exception {
-    FunctionQuery functionQuery = new FunctionQuery(valueSource);
+    Query functionQuery = getFunctionQuery(valueSource);
     IndexReader r = DirectoryReader.open(dir);
     IndexSearcher s = newSearcher(r);
     TopDocs td = s.search(functionQuery,1000);
@@ -108,4 +109,14 @@ public class TestFieldScoreQuery extends
     r.close();
   }
 
+  protected Query getFunctionQuery(ValueSource valueSource) {
+    if (random().nextBoolean()) {
+      return new FunctionQuery(valueSource);
+    } else {
+      Integer lower = (random().nextBoolean() ? null : 1);//1 is the lowest value
+      Integer upper = (random().nextBoolean() ? null : N_DOCS); // N_DOCS is the highest value
+      return new FunctionRangeQuery(valueSource, lower, upper, true, true);//will match all docs based on the indexed data
+    }
+  }
+
 }

Added: lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java?rev=1694543&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java (added)
+++ lucene/dev/trunk/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java Thu Aug  6 17:47:24 2015
@@ -0,0 +1,114 @@
+package org.apache.lucene.queries.function;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.TopDocs;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestFunctionRangeQuery extends FunctionTestSetup {
+
+  IndexReader indexReader;
+  IndexSearcher indexSearcher;
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    createIndex(true);//doMultiSegment
+  }
+
+  @Before
+  protected void before() throws IOException {
+    indexReader = DirectoryReader.open(dir);
+    indexSearcher = newSearcher(indexReader);
+  }
+
+  @After
+  public void after() throws IOException {
+    indexReader.close();
+  }
+
+  @Test
+  public void testRangeInt() throws IOException {
+    doTestRange(INT_VALUESOURCE);
+  }
+
+  @Test
+  public void testRangeFloat() throws IOException {
+    doTestRange(FLOAT_VALUESOURCE);
+  }
+
+  private void doTestRange(ValueSource valueSource) throws IOException {
+    Query rangeQuery = new FunctionRangeQuery(valueSource, 2, 4, true, false);
+    ScoreDoc[] scoreDocs = indexSearcher.search(rangeQuery, N_DOCS).scoreDocs;
+    expectScores(scoreDocs, 3, 2);
+
+    rangeQuery = new FunctionRangeQuery(valueSource, 2, 4, false, true);
+    scoreDocs = indexSearcher.search(rangeQuery, N_DOCS).scoreDocs;
+    expectScores(scoreDocs, 4, 3);
+  }
+
+  @Test
+  public void testDeleted() throws IOException {
+    // We delete doc with #3. Note we don't commit it to disk; we search using a near eal-time reader.
+    final ValueSource valueSource = INT_VALUESOURCE;
+    IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig(null));
+    try {
+      writer.deleteDocuments(new FunctionRangeQuery(valueSource, 3, 3, true, true));//delete the one with #3
+      assert writer.hasDeletions();
+      try (IndexReader indexReader2 = DirectoryReader.open(writer, true)) {//applyAllDeletes
+        IndexSearcher indexSearcher2 = new IndexSearcher(indexReader2);
+        TopDocs topDocs = indexSearcher2.search(new FunctionRangeQuery(valueSource, 3, 4, true, true), N_DOCS);
+        expectScores(topDocs.scoreDocs, 4);//missing #3 because it's deleted
+      }
+    } finally {
+      writer.rollback();
+      writer.close();
+    }
+  }
+
+  @Test
+  public void testExplain() throws IOException {
+    Query rangeQuery = new FunctionRangeQuery(INT_VALUESOURCE, 2, 2, true, true);
+    ScoreDoc[] scoreDocs = indexSearcher.search(rangeQuery, N_DOCS).scoreDocs;
+    Explanation explain = indexSearcher.explain(rangeQuery, scoreDocs[0].doc);
+    // Just validate it looks reasonable
+    assertEquals(
+            "2.0 = frange(int(" + INT_FIELD + ")):[2 TO 2]\n" +
+            "  2.0 = int(" + INT_FIELD + ")=2\n",
+        explain.toString());
+  }
+
+  private void expectScores(ScoreDoc[] scoreDocs, int... docScores) {
+    assertEquals(docScores.length, scoreDocs.length);
+    for (int i = 0; i < docScores.length; i++) {
+      assertEquals(docScores[i], scoreDocs[i].score, 0.0);
+    }
+  }
+}

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java Thu Aug  6 17:47:24 2015
@@ -25,7 +25,7 @@ import com.spatial4j.core.shape.Shape;
 import org.apache.lucene.document.DoubleField;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.FieldType;
-import org.apache.lucene.queries.function.FunctionQuery;
+import org.apache.lucene.queries.function.FunctionRangeQuery;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanQuery;
@@ -38,8 +38,6 @@ import org.apache.lucene.spatial.Spatial
 import org.apache.lucene.spatial.query.SpatialArgs;
 import org.apache.lucene.spatial.query.SpatialOperation;
 import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
-import org.apache.lucene.spatial.util.CachingDoubleValueSource;
-import org.apache.lucene.spatial.util.ValueSourceFilter;
 
 /**
  * Simple {@link SpatialStrategy} which represents Points in two numeric {@link
@@ -150,80 +148,19 @@ public class PointVectorStrategy extends
     } else if (shape instanceof Circle) {
       Circle circle = (Circle)shape;
       Rectangle bbox = circle.getBoundingBox();
-      ValueSourceFilter vsf = new ValueSourceFilter(
-          new QueryWrapperFilter(makeWithin(bbox)),
-          makeDistanceValueSource(circle.getCenter()),
-          0,
-          circle.getRadius() );
-      return new ConstantScoreQuery(vsf);
+      Query approxQuery = makeWithin(bbox);
+      BooleanQuery.Builder bqBuilder = new BooleanQuery.Builder();
+      FunctionRangeQuery vsRangeQuery =
+          new FunctionRangeQuery(makeDistanceValueSource(circle.getCenter()), 0.0, circle.getRadius(), true, true);
+      bqBuilder.add(approxQuery, BooleanClause.Occur.FILTER);//should have lowest "cost" value; will drive iteration
+      bqBuilder.add(vsRangeQuery, BooleanClause.Occur.FILTER);
+      return new ConstantScoreQuery(bqBuilder.build());
     } else {
       throw new UnsupportedOperationException("Only Rectangles and Circles are currently supported, " +
           "found [" + shape.getClass() + "]");//TODO
     }
   }
 
-  //TODO this is basically old code that hasn't been verified well and should probably be removed
-  public Query makeQueryDistanceScore(SpatialArgs args) {
-    // For starters, just limit the bbox
-    Shape shape = args.getShape();
-    if (!(shape instanceof Rectangle || shape instanceof Circle)) {
-      throw new UnsupportedOperationException("Only Rectangles and Circles are currently supported, " +
-          "found [" + shape.getClass() + "]");//TODO
-    }
-
-    Rectangle bbox = shape.getBoundingBox();
-
-    if (bbox.getCrossesDateLine()) {
-      throw new UnsupportedOperationException( "Crossing dateline not yet supported" );
-    }
-
-    ValueSource valueSource = null;
-
-    Query spatial = null;
-    SpatialOperation op = args.getOperation();
-
-    if( SpatialOperation.is( op,
-        SpatialOperation.BBoxWithin,
-        SpatialOperation.BBoxIntersects ) ) {
-        spatial = makeWithin(bbox);
-    }
-    else if( SpatialOperation.is( op,
-      SpatialOperation.Intersects,
-      SpatialOperation.IsWithin ) ) {
-      spatial = makeWithin(bbox);
-      if( args.getShape() instanceof Circle) {
-        Circle circle = (Circle)args.getShape();
-
-        // Make the ValueSource
-        valueSource = makeDistanceValueSource(shape.getCenter());
-
-        ValueSourceFilter vsf = new ValueSourceFilter(
-            new QueryWrapperFilter( spatial ), valueSource, 0, circle.getRadius() );
-
-        spatial = vsf;
-      }
-    }
-    else if( op == SpatialOperation.IsDisjointTo ) {
-      spatial =  makeDisjoint(bbox);
-    }
-
-    if( spatial == null ) {
-      throw new UnsupportedSpatialOperation(args.getOperation());
-    }
-
-    if( valueSource != null ) {
-      valueSource = new CachingDoubleValueSource(valueSource);
-    }
-    else {
-      valueSource = makeDistanceValueSource(shape.getCenter());
-    }
-    Query spatialRankingQuery = new FunctionQuery(valueSource);
-    BooleanQuery.Builder bq = new BooleanQuery.Builder();
-    bq.add(spatial,BooleanClause.Occur.MUST);
-    bq.add(spatialRankingQuery,BooleanClause.Occur.MUST);
-    return bq.build();
-  }
-
   /**
    * Constructs a query to retrieve documents that fully contain the input envelope.
    */
@@ -252,21 +189,6 @@ public class PointVectorStrategy extends
         true);//inclusive
   }
 
-  /**
-   * Constructs a query to retrieve documents that fully contain the input envelope.
-   */
-  private Query makeDisjoint(Rectangle bbox) {
-    if (bbox.getCrossesDateLine())
-      throw new UnsupportedOperationException("makeDisjoint doesn't handle dateline cross");
-    Query qX = rangeQuery(fieldNameX, bbox.getMinX(), bbox.getMaxX());
-    Query qY = rangeQuery(fieldNameY, bbox.getMinY(), bbox.getMaxY());
-
-    BooleanQuery.Builder bq = new BooleanQuery.Builder();
-    bq.add(qX,BooleanClause.Occur.MUST_NOT);
-    bq.add(qY,BooleanClause.Occur.MUST_NOT);
-    return bq.build();
-  }
-
 }
 
 

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java?rev=1694543&r1=1694542&r2=1694543&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java Thu Aug  6 17:47:24 2015
@@ -17,6 +17,9 @@
 
 package org.apache.solr.search;
 
+import java.io.IOException;
+import java.util.Map;
+
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.queries.function.FunctionValues;
 import org.apache.lucene.queries.function.ValueSource;
@@ -24,9 +27,6 @@ import org.apache.lucene.queries.functio
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.solr.search.function.ValueSourceRangeFilter;
 
-import java.io.IOException;
-import java.util.Map;
-
 // This class works as either a normal constant score query, or as a PostFilter using a collector
 public class FunctionRangeQuery extends SolrConstantScoreQuery implements PostFilter {
   final ValueSourceRangeFilter rangeFilt;
@@ -53,7 +53,8 @@ public class FunctionRangeQuery extends
 
     @Override
     public void collect(int doc) throws IOException {
-      if (doc<maxdoc && scorer.matches(doc)) {
+      assert doc < maxdoc;
+      if (scorer.matches(doc)) {
         leafDelegate.collect(doc);
       }
     }