You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by jp...@apache.org on 2017/01/20 13:46:13 UTC

lucene-solr:branch_6x: LUCENE-7643: Move IndexOrDocValuesQuery to core.

Repository: lucene-solr
Updated Branches:
  refs/heads/branch_6x 3ef9f0e39 -> 20b7dfae4


LUCENE-7643: Move IndexOrDocValuesQuery to core.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/20b7dfae
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/20b7dfae
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/20b7dfae

Branch: refs/heads/branch_6x
Commit: 20b7dfae42810ea4c345355735d732bdbb191150
Parents: 3ef9f0e
Author: Adrien Grand <jp...@gmail.com>
Authored: Thu Jan 19 18:12:04 2017 +0100
Committer: Adrien Grand <jp...@gmail.com>
Committed: Fri Jan 20 14:45:18 2017 +0100

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   7 +
 .../lucene/document/NumericDocValuesField.java  |  48 ++++
 .../lucene/document/SortedDocValuesField.java   |  42 ++++
 .../document/SortedNumericDocValuesField.java   |  54 +++++
 .../SortedNumericDocValuesRangeQuery.java       | 150 ++++++++++++
 .../document/SortedSetDocValuesField.java       |  43 ++++
 .../document/SortedSetDocValuesRangeQuery.java  | 190 +++++++++++++++
 .../lucene/search/IndexOrDocValuesQuery.java    | 177 ++++++++++++++
 .../lucene/search/TestDocValuesQueries.java     | 238 +++++++++++++++++++
 .../search/TestIndexOrDocValuesQuery.java       |  89 +++++++
 .../lucene/search/DocValuesRangeQuery.java      |  12 +-
 .../lucene/search/IndexOrDocValuesQuery.java    | 116 ---------
 .../search/TestIndexOrDocValuesQuery.java       |  89 -------
 .../apache/solr/schema/ICUCollationField.java   |  10 +-
 .../org/apache/solr/schema/CollationField.java  |   3 +-
 .../java/org/apache/solr/schema/EnumField.java  |  20 +-
 .../java/org/apache/solr/schema/FieldType.java  |  16 +-
 .../java/org/apache/solr/schema/TrieField.java  |  45 +++-
 18 files changed, 1110 insertions(+), 239 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index ec75b7f..1e00fbc 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -16,6 +16,9 @@ API Changes
 * LUCENE-7644: FieldComparatorSource.newComparator() and
   SortField.getComparator() no longer throw IOException (Alan Woodward)
 
+* LUCENE-7643: Replaced doc-values queries in lucene/sandbox with factory
+  methods on the *DocValuesField classes. (Adrien Grand)
+
 New Features
 
 * LUCENE-7623: Add FunctionScoreQuery and FunctionMatchQuery (Alan Woodward,
@@ -38,6 +41,10 @@ Improvements
   should be run, eg. using points or doc values depending on costs of other
   parts of the query. (Adrien Grand)
 
+* LUCENE-7643: IndexOrDocValuesQuery allows to execute range queries using
+  either points or doc values depending on which one is more efficient.
+  (Adrien Grand)
+
 Optimizations
 
 * LUCENE-7641: Optimized point range queries to compute documents that do not

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/core/src/java/org/apache/lucene/document/NumericDocValuesField.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/NumericDocValuesField.java b/lucene/core/src/java/org/apache/lucene/document/NumericDocValuesField.java
index 5b6dcc8..2651f92 100644
--- a/lucene/core/src/java/org/apache/lucene/document/NumericDocValuesField.java
+++ b/lucene/core/src/java/org/apache/lucene/document/NumericDocValuesField.java
@@ -17,7 +17,15 @@
 package org.apache.lucene.document;
 
 
+import java.io.IOException;
+
+import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.DocValuesType;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.search.IndexOrDocValuesQuery;
+import org.apache.lucene.search.Query;
 
 /**
  * <p>
@@ -54,4 +62,44 @@ public class NumericDocValuesField extends Field {
     super(name, TYPE);
     fieldsData = Long.valueOf(value);
   }
+
+  /**
+   * Create a range query that matches all documents whose value is between
+   * {@code lowerValue} and {@code upperValue} included.
+   * <p>
+   * You can have half-open ranges (which are in fact &lt;/&le; or &gt;/&ge; queries)
+   * by setting {@code lowerValue = Long.MIN_VALUE} or {@code upperValue = Long.MAX_VALUE}. 
+   * <p>
+   * Ranges are inclusive. For exclusive ranges, pass {@code Math.addExact(lowerValue, 1)}
+   * or {@code Math.addExact(upperValue, -1)}.
+   * <p><b>NOTE</b>: Such queries cannot efficiently advance to the next match,
+   * which makes them slow if they are not ANDed with a selective query. As a
+   * consequence, they are best used wrapped in an {@link IndexOrDocValuesQuery},
+   * alongside a range query that executes on points, such as
+   * {@link LongPoint#newRangeQuery}.
+   */
+  public static Query newRangeQuery(String field, long lowerValue, long upperValue) {
+    return new SortedNumericDocValuesRangeQuery(field, lowerValue, upperValue) {
+      @Override
+      SortedNumericDocValues getValues(LeafReader reader, String field) throws IOException {
+        NumericDocValues values = reader.getNumericDocValues(field);
+        if (values == null) {
+          return null;
+        }
+        return DocValues.singleton(values, reader.getDocsWithField(field));
+      }
+    };
+  }
+
+  /** 
+   * Create a query for matching an exact long value.
+   * <p><b>NOTE</b>: Such queries cannot efficiently advance to the next match,
+   * which makes them slow if they are not ANDed with a selective query. As a
+   * consequence, they are best used wrapped in an {@link IndexOrDocValuesQuery},
+   * alongside a range query that executes on points, such as
+   * {@link LongPoint#newExactQuery}.
+   */
+  public static Query newExactQuery(String field, long value) {
+    return newRangeQuery(field, value, value);
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/core/src/java/org/apache/lucene/document/SortedDocValuesField.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedDocValuesField.java b/lucene/core/src/java/org/apache/lucene/document/SortedDocValuesField.java
index bbfb467..feb7725 100644
--- a/lucene/core/src/java/org/apache/lucene/document/SortedDocValuesField.java
+++ b/lucene/core/src/java/org/apache/lucene/document/SortedDocValuesField.java
@@ -17,7 +17,14 @@
 package org.apache.lucene.document;
 
 
+import java.io.IOException;
+
+import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.DocValuesType;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.search.IndexOrDocValuesQuery;
+import org.apache.lucene.search.Query;
 import org.apache.lucene.util.BytesRef;
 
 /**
@@ -59,4 +66,39 @@ public class SortedDocValuesField extends Field {
     super(name, TYPE);
     fieldsData = bytes;
   }
+
+  /**
+   * Create a range query that matches all documents whose value is between
+   * {@code lowerValue} and {@code upperValue} included.
+   * <p>
+   * You can have half-open ranges by setting {@code lowerValue = null}
+   * or {@code upperValue = null}.
+   * <p><b>NOTE</b>: Such queries cannot efficiently advance to the next match,
+   * which makes them slow if they are not ANDed with a selective query. As a
+   * consequence, they are best used wrapped in an {@link IndexOrDocValuesQuery},
+   * alongside a range query that executes on points, such as
+   * {@link BinaryPoint#newRangeQuery}.
+   */
+  public static Query newRangeQuery(String field,
+      BytesRef lowerValue, BytesRef upperValue,
+      boolean lowerInclusive, boolean upperInclusive) {
+    return new SortedSetDocValuesRangeQuery(field, lowerValue, upperValue, lowerInclusive, upperInclusive) {
+      @Override
+      SortedSetDocValues getValues(LeafReader reader, String field) throws IOException {
+        return DocValues.singleton(DocValues.getSorted(reader, field));
+      }
+    };
+  }
+
+  /** 
+   * Create a query for matching an exact {@link BytesRef} value.
+   * <p><b>NOTE</b>: Such queries cannot efficiently advance to the next match,
+   * which makes them slow if they are not ANDed with a selective query. As a
+   * consequence, they are best used wrapped in an {@link IndexOrDocValuesQuery},
+   * alongside a range query that executes on points, such as
+   * {@link BinaryPoint#newExactQuery}.
+   */
+  public static Query newExactQuery(String field, BytesRef value) {
+    return newRangeQuery(field, value, value, true, true);
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesField.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesField.java b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesField.java
index cbba218..6f9a271 100644
--- a/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesField.java
+++ b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesField.java
@@ -17,7 +17,15 @@
 package org.apache.lucene.document;
 
 
+import java.io.IOException;
+
+import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.DocValuesType;
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.search.IndexOrDocValuesQuery;
+import org.apache.lucene.search.Query;
 
 /**
  * <p>
@@ -63,4 +71,50 @@ public class SortedNumericDocValuesField extends Field {
     super(name, TYPE);
     fieldsData = Long.valueOf(value);
   }
+
+  /**
+   * Create a range query that matches all documents whose value is between
+   * {@code lowerValue} and {@code upperValue} included.
+   * <p>
+   * You can have half-open ranges (which are in fact &lt;/&le; or &gt;/&ge; queries)
+   * by setting {@code lowerValue = Long.MIN_VALUE} or {@code upperValue = Long.MAX_VALUE}. 
+   * <p>
+   * Ranges are inclusive. For exclusive ranges, pass {@code Math.addExact(lowerValue, 1)}
+   * or {@code Math.addExact(upperValue, -1)}.
+   * <p>This query also works with fields that have indexed
+   * {@link NumericDocValuesField}s.
+   * <p><b>NOTE</b>: Such queries cannot efficiently advance to the next match,
+   * which makes them slow if they are not ANDed with a selective query. As a
+   * consequence, they are best used wrapped in an {@link IndexOrDocValuesQuery},
+   * alongside a range query that executes on points, such as
+   * {@link LongPoint#newRangeQuery}.
+   */
+  public static Query newRangeQuery(String field, long lowerValue, long upperValue) {
+    return new SortedNumericDocValuesRangeQuery(field, lowerValue, upperValue) {
+      @Override
+      SortedNumericDocValues getValues(LeafReader reader, String field) throws IOException {
+        FieldInfo info = reader.getFieldInfos().fieldInfo(field);
+        if (info == null) {
+          // Queries have some optimizations when one sub scorer returns null rather
+          // than a scorer that does not match any documents
+          return null;
+        }
+        return DocValues.getSortedNumeric(reader, field);
+      }
+    };
+  }
+
+  /** 
+   * Create a query for matching an exact long value.
+   * <p>This query also works with fields that have indexed
+   * {@link NumericDocValuesField}s.
+   * <p><b>NOTE</b>: Such queries cannot efficiently advance to the next match,
+   * which makes them slow if they are not ANDed with a selective query. As a
+   * consequence, they are best used wrapped in an {@link IndexOrDocValuesQuery},
+   * alongside a range query that executes on points, such as
+   * {@link LongPoint#newExactQuery}.
+   */
+  public static Query newExactQuery(String field, long value) {
+    return newRangeQuery(field, value, value);
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
new file mode 100644
index 0000000..ffe953c
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
@@ -0,0 +1,150 @@
+/*
+ * 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.lucene.document;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.search.ConstantScoreScorer;
+import org.apache.lucene.search.ConstantScoreWeight;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.FieldValueQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TwoPhaseIterator;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.Bits;
+
+abstract class SortedNumericDocValuesRangeQuery extends Query {
+
+  private final String field;
+  private final long lowerValue;
+  private final long upperValue;
+
+  SortedNumericDocValuesRangeQuery(String field, long lowerValue, long upperValue) {
+    this.field = Objects.requireNonNull(field);
+    this.lowerValue = lowerValue;
+    this.upperValue = upperValue;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (sameClassAs(obj) == false) {
+      return false;
+    }
+    SortedNumericDocValuesRangeQuery that = (SortedNumericDocValuesRangeQuery) obj;
+    return Objects.equals(field, that.field)
+        && lowerValue == that.lowerValue
+        && upperValue == that.upperValue;
+  }
+
+  @Override
+  public int hashCode() {
+    int h = classHash();
+    h = 31 * h + field.hashCode();
+    h = 31 * h + Long.hashCode(lowerValue);
+    h = 31 * h + Long.hashCode(upperValue);
+    return h;
+  }
+
+  @Override
+  public String toString(String field) {
+    StringBuilder b = new StringBuilder();
+    if (this.field.equals(field) == false) {
+      b.append(this.field).append(":");
+    }
+    return b
+        .append("[")
+        .append(lowerValue)
+        .append(" TO ")
+        .append(upperValue)
+        .append("]")
+        .toString();
+  }
+
+  @Override
+  public Query rewrite(IndexReader reader) throws IOException {
+    if (lowerValue == Long.MIN_VALUE && upperValue == Long.MAX_VALUE) {
+      return new FieldValueQuery(field);
+    }
+    return super.rewrite(reader);
+  }
+
+  abstract SortedNumericDocValues getValues(LeafReader reader, String field) throws IOException;
+
+  @Override
+  public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+    return new ConstantScoreWeight(this) {
+      @Override
+      public Scorer scorer(LeafReaderContext context) throws IOException {
+        SortedNumericDocValues values = getValues(context.reader(), field);
+        if (values == null) {
+          return null;
+        }
+        final NumericDocValues singleton = DocValues.unwrapSingleton(values);
+        final TwoPhaseIterator iterator;
+        final DocIdSetIterator approximation = DocIdSetIterator.all(context.reader().maxDoc());
+        if (singleton != null) {
+          final Bits docsWithField = DocValues.unwrapSingletonBits(values);
+          iterator = new TwoPhaseIterator(approximation) {
+            @Override
+            public boolean matches() throws IOException {
+              final long value = singleton.get(approximation.docID());
+              return (value != 0 || docsWithField.get(approximation.docID()))
+                  && value >= lowerValue && value <= upperValue;
+            }
+
+            @Override
+            public float matchCost() {
+              return 2; // 2 comparisons
+            }
+          };
+        } else {
+          iterator = new TwoPhaseIterator(approximation) {
+            @Override
+            public boolean matches() throws IOException {
+              values.setDocument(approximation.docID());
+              for (int i = 0, count = values.count(); i < count; ++i) {
+                final long value = values.valueAt(i);
+                if (value < lowerValue) {
+                  continue;
+                }
+                // Values are sorted, so the first value that is >= lowerValue is our best candidate
+                return value <= upperValue;
+              }
+              return false; // all values were < lowerValue
+            }
+
+            @Override
+            public float matchCost() {
+              return 2; // 2 comparisons
+            }
+          };
+        }
+        return new ConstantScoreScorer(this, score(), iterator);
+      }
+    };
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesField.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesField.java b/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesField.java
index 7a273ac..26b1907 100644
--- a/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesField.java
+++ b/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesField.java
@@ -17,7 +17,14 @@
 package org.apache.lucene.document;
 
 
+import java.io.IOException;
+
+import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.DocValuesType;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.search.IndexOrDocValuesQuery;
+import org.apache.lucene.search.Query;
 import org.apache.lucene.util.BytesRef;
 
 /**
@@ -60,4 +67,40 @@ public class SortedSetDocValuesField extends Field {
     super(name, TYPE);
     fieldsData = bytes;
   }
+
+  /**
+   * Create a range query that matches all documents whose value is between
+   * {@code lowerValue} and {@code upperValue}.
+   * <p>This query also works with fields that have indexed
+   * {@link SortedDocValuesField}s.
+   * <p><b>NOTE</b>: Such queries cannot efficiently advance to the next match,
+   * which makes them slow if they are not ANDed with a selective query. As a
+   * consequence, they are best used wrapped in an {@link IndexOrDocValuesQuery},
+   * alongside a range query that executes on points, such as
+   * {@link BinaryPoint#newRangeQuery}.
+   */
+  public static Query newRangeQuery(String field,
+      BytesRef lowerValue, BytesRef upperValue,
+      boolean lowerInclusive, boolean upperInclusive) {
+    return new SortedSetDocValuesRangeQuery(field, lowerValue, upperValue, lowerInclusive, upperInclusive) {
+      @Override
+      SortedSetDocValues getValues(LeafReader reader, String field) throws IOException {
+        return DocValues.getSortedSet(reader, field);
+      }
+    };
+  }
+
+  /** 
+   * Create a query for matching an exact {@link BytesRef} value.
+   * <p>This query also works with fields that have indexed
+   * {@link SortedDocValuesField}s.
+   * <p><b>NOTE</b>: Such queries cannot efficiently advance to the next match,
+   * which makes them slow if they are not ANDed with a selective query. As a
+   * consequence, they are best used wrapped in an {@link IndexOrDocValuesQuery},
+   * alongside a range query that executes on points, such as
+   * {@link BinaryPoint#newExactQuery}.
+   */
+  public static Query newExactQuery(String field, BytesRef value) {
+    return newRangeQuery(field, value, value, true, true);
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java b/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java
new file mode 100644
index 0000000..854eb43
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java
@@ -0,0 +1,190 @@
+/*
+ * 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.lucene.document;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.search.ConstantScoreScorer;
+import org.apache.lucene.search.ConstantScoreWeight;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.FieldValueQuery;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TwoPhaseIterator;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.BytesRef;
+
+abstract class SortedSetDocValuesRangeQuery extends Query {
+
+  private final String field;
+  private final BytesRef lowerValue;
+  private final BytesRef upperValue;
+  private final boolean lowerInclusive;
+  private final boolean upperInclusive;
+
+  SortedSetDocValuesRangeQuery(String field,
+      BytesRef lowerValue, BytesRef upperValue,
+      boolean lowerInclusive, boolean upperInclusive) {
+    this.field = Objects.requireNonNull(field);
+    this.lowerValue = lowerValue;
+    this.upperValue = upperValue;
+    this.lowerInclusive = lowerInclusive && lowerValue != null;
+    this.upperInclusive = upperInclusive && upperValue != null;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (sameClassAs(obj) == false) {
+      return false;
+    }
+    SortedSetDocValuesRangeQuery that = (SortedSetDocValuesRangeQuery) obj;
+    return Objects.equals(field, that.field)
+        && Objects.equals(lowerValue, that.lowerValue)
+        && Objects.equals(upperValue, that.upperValue)
+        && lowerInclusive == that.lowerInclusive
+        && upperInclusive == that.upperInclusive;
+  }
+
+  @Override
+  public int hashCode() {
+    int h = classHash();
+    h = 31 * h + field.hashCode();
+    h = 31 * h + Objects.hashCode(lowerValue);
+    h = 31 * h + Objects.hashCode(upperValue);
+    h = 31 * h + Boolean.hashCode(lowerInclusive);
+    h = 31 * h + Boolean.hashCode(upperInclusive);
+    return h;
+  }
+
+  @Override
+  public String toString(String field) {
+    StringBuilder b = new StringBuilder();
+    if (this.field.equals(field) == false) {
+      b.append(this.field).append(":");
+    }
+    return b
+        .append(lowerInclusive ? "[" : "{")
+        .append(lowerValue == null ? "*" : lowerValue)
+        .append(" TO ")
+        .append(upperValue == null ? "*" : upperValue)
+        .append(upperInclusive ? "]" : "}")
+        .toString();
+  }
+
+  @Override
+  public Query rewrite(IndexReader reader) throws IOException {
+    if (lowerValue == null && upperValue == null) {
+      return new FieldValueQuery(field);
+    }
+    return super.rewrite(reader);
+  }
+
+  abstract SortedSetDocValues getValues(LeafReader reader, String field) throws IOException;
+
+  @Override
+  public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+    return new ConstantScoreWeight(this) {
+      @Override
+      public Scorer scorer(LeafReaderContext context) throws IOException {
+        SortedSetDocValues values = getValues(context.reader(), field);
+        if (values == null) {
+          return null;
+        }
+
+        final long minOrd;
+        if (lowerValue == null) {
+          minOrd = 0;
+        } else {
+          final long ord = values.lookupTerm(lowerValue);
+          if (ord < 0) {
+            minOrd = -1 - ord;
+          } else if (lowerInclusive) {
+            minOrd = ord;
+          } else {
+            minOrd = ord + 1;
+          }
+        }
+
+        final long maxOrd;
+        if (upperValue == null) {
+          maxOrd = values.getValueCount() - 1;
+        } else {
+          final long ord = values.lookupTerm(upperValue);
+          if (ord < 0) {
+            maxOrd = -2 - ord;
+          } else if (upperInclusive) {
+            maxOrd = ord;
+          } else {
+            maxOrd = ord - 1;
+          }
+        }
+
+        if (minOrd > maxOrd) {
+          return null;
+        }
+
+        final SortedDocValues singleton = DocValues.unwrapSingleton(values);
+        final DocIdSetIterator approximation = DocIdSetIterator.all(context.reader().maxDoc());
+        final TwoPhaseIterator iterator;
+        if (singleton != null) {
+          iterator = new TwoPhaseIterator(approximation) {
+            @Override
+            public boolean matches() throws IOException {
+              final long ord = singleton.getOrd(approximation.docID());
+              return ord >= minOrd && ord <= maxOrd;
+            }
+
+            @Override
+            public float matchCost() {
+              return 2; // 2 comparisons
+            }
+          };
+        } else {
+          iterator = new TwoPhaseIterator(approximation) {
+            @Override
+            public boolean matches() throws IOException {
+              values.setDocument(approximation.docID());
+              for (long ord = values.nextOrd(); ord != SortedSetDocValues.NO_MORE_ORDS; ord = values.nextOrd()) {
+                if (ord < minOrd) {
+                  continue;
+                }
+                // Values are sorted, so the first ord that is >= minOrd is our best candidate
+                return ord <= maxOrd;
+              }
+              return false; // all ords were < minOrd
+            }
+
+            @Override
+            public float matchCost() {
+              return 2; // 2 comparisons
+            }
+          };
+        }
+        return new ConstantScoreScorer(this, score(), iterator);
+      }
+    };
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java b/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
new file mode 100644
index 0000000..2b85b18
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
@@ -0,0 +1,177 @@
+/*
+ * 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.lucene.search;
+
+import java.io.IOException;
+import java.util.Set;
+
+import org.apache.lucene.document.LongPoint;
+import org.apache.lucene.document.SortedNumericDocValuesField;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.Term;
+
+/**
+ * A query that uses either an index structure (points or terms) or doc values
+ * in order to run a query, depending which one is more efficient. This is
+ * typically useful for range queries, whose {@link Weight#scorer} is costly
+ * to create since it usually needs to sort large lists of doc ids. For
+ * instance, for a field that both indexed {@link LongPoint}s and
+ * {@link SortedNumericDocValuesField}s with the same values, an efficient
+ * range query could be created by doing:
+ * <pre class="prettyprint">
+ *   String field;
+ *   long minValue, maxValue;
+ *   Query pointQuery = LongPoint.newRangeQuery(field, minValue, maxValue);
+ *   Query dvQuery = SortedNumericDocValuesField.newRangeQuery(field, minValue, maxValue);
+ *   Query query = new IndexOrDocValuesQuery(pointQuery, dvQuery);
+ * </pre>
+ * The above query will be efficient as it will use points in the case that they
+ * perform better, ie. when we need a good lead iterator that will be almost
+ * entirely consumed; and doc values otherwise, ie. in the case that another
+ * part of the query is already leading iteration but we still need the ability
+ * to verify that some documents match.
+ * <p><b>NOTE</b>This query currently only works well with point range/exact
+ * queries and their equivalent doc values queries.
+ * @lucene.experimental
+ */
+public final class IndexOrDocValuesQuery extends Query {
+
+  private final Query indexQuery, dvQuery;
+
+  /**
+   * Create an {@link IndexOrDocValuesQuery}. Both provided queries must match
+   * the same documents and give the same scores.
+   * @param indexQuery a query that has a good iterator but whose scorer may be costly to create
+   * @param dvQuery a query whose scorer is cheap to create that can quickly check whether a given document matches
+   */
+  public IndexOrDocValuesQuery(Query indexQuery, Query dvQuery) {
+    this.indexQuery = indexQuery;
+    this.dvQuery = dvQuery;
+  }
+
+  /** Return the wrapped query that may be costly to initialize but has a good
+   *  iterator. */
+  public Query getIndexQuery() {
+    return indexQuery;
+  }
+
+  /** Return the wrapped query that may be slow at identifying all matching
+   *  documents, but which is cheap to initialize and can efficiently
+   *  verify that some documents match. */
+  public Query getRandomAccessQuery() {
+    return dvQuery;
+  }
+
+  @Override
+  public String toString(String field) {
+    return indexQuery.toString(field);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (sameClassAs(obj) == false) {
+      return false;
+    }
+    IndexOrDocValuesQuery that = (IndexOrDocValuesQuery) obj;
+    return indexQuery.equals(that.indexQuery) && dvQuery.equals(that.dvQuery);
+  }
+
+  @Override
+  public int hashCode() {
+    int h = classHash();
+    h = 31 * h + indexQuery.hashCode();
+    h = 31 * h + dvQuery.hashCode();
+    return h;
+  }
+
+  @Override
+  public Query rewrite(IndexReader reader) throws IOException {
+    Query indexRewrite = indexQuery.rewrite(reader);
+    Query dvRewrite = dvQuery.rewrite(reader);
+    if (indexQuery != indexRewrite || dvQuery != dvRewrite) {
+      return new IndexOrDocValuesQuery(indexRewrite, dvRewrite);
+    }
+    return this;
+  }
+
+  @Override
+  public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+    final Weight indexWeight = indexQuery.createWeight(searcher, needsScores);
+    final Weight dvWeight = dvQuery.createWeight(searcher, needsScores);
+    return new Weight(this) {
+      @Override
+      public void extractTerms(Set<Term> terms) {
+        indexWeight.extractTerms(terms);
+      }
+
+      @Override
+      public void normalize(float norm, float boost) {
+        indexWeight.normalize(norm, boost);
+        dvWeight.normalize(norm, boost);
+      }
+
+      @Override
+      public float getValueForNormalization() throws IOException {
+        return indexWeight.getValueForNormalization();
+      }
+
+      @Override
+      public Explanation explain(LeafReaderContext context, int doc) throws IOException {
+        // We need to check a single doc, so the dv query should perform better
+        return dvWeight.explain(context, doc);
+      }
+
+      @Override
+      public BulkScorer bulkScorer(LeafReaderContext context) throws IOException {
+        // Bulk scorers need to consume the entire set of docs, so using an
+        // index structure should perform better
+        return indexWeight.bulkScorer(context);
+      }
+
+      @Override
+      public ScorerSupplier scorerSupplier(LeafReaderContext context) throws IOException {
+        final ScorerSupplier indexScorerSupplier = indexWeight.scorerSupplier(context);
+        final ScorerSupplier dvScorerSupplier = dvWeight.scorerSupplier(context);
+        if (indexScorerSupplier == null || dvScorerSupplier == null) {
+          return null;
+        }
+        return new ScorerSupplier() {
+          @Override
+          public Scorer get(boolean randomAccess) throws IOException {
+            return (randomAccess ? dvScorerSupplier : indexScorerSupplier).get(randomAccess);
+          }
+
+          @Override
+          public long cost() {
+            return Math.min(indexScorerSupplier.cost(), dvScorerSupplier.cost());
+          }
+        };
+      }
+
+      @Override
+      public Scorer scorer(LeafReaderContext context) throws IOException {
+        ScorerSupplier scorerSupplier = scorerSupplier(context);
+        if (scorerSupplier == null) {
+          return null;
+        }
+        return scorerSupplier.get(false);
+      }
+    };
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/core/src/test/org/apache/lucene/search/TestDocValuesQueries.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestDocValuesQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestDocValuesQueries.java
new file mode 100644
index 0000000..501538f
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/TestDocValuesQueries.java
@@ -0,0 +1,238 @@
+/*
+ * 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.lucene.search;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.LongPoint;
+import org.apache.lucene.document.NumericDocValuesField;
+import org.apache.lucene.document.SortedDocValuesField;
+import org.apache.lucene.document.SortedNumericDocValuesField;
+import org.apache.lucene.document.SortedSetDocValuesField;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.TestUtil;
+
+public class TestDocValuesQueries extends LuceneTestCase {
+
+  public void testDuelPointRangeSortedNumericRangeQuery() throws IOException {
+    doTestDuelPointRangeNumericRangeQuery(true, 1);
+  }
+
+  public void testDuelPointRangeMultivaluedSortedNumericRangeQuery() throws IOException {
+    doTestDuelPointRangeNumericRangeQuery(true, 3);
+  }
+
+  public void testDuelPointRangeNumericRangeQuery() throws IOException {
+    doTestDuelPointRangeNumericRangeQuery(false, 1);
+  }
+
+  private void doTestDuelPointRangeNumericRangeQuery(boolean sortedNumeric, int maxValuesPerDoc) throws IOException {
+    final int iters = atLeast(10);
+    for (int iter = 0; iter < iters; ++iter) {
+      Directory dir = newDirectory();
+      RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
+      final int numDocs = atLeast(100);
+      for (int i = 0; i < numDocs; ++i) {
+        Document doc = new Document();
+        final int numValues = TestUtil.nextInt(random(), 0, maxValuesPerDoc);
+        for (int j = 0; j < numValues; ++j) {
+          final long value = TestUtil.nextLong(random(), -100, 10000);
+          if (sortedNumeric) {
+            doc.add(new SortedNumericDocValuesField("dv", value));
+          } else {
+            doc.add(new NumericDocValuesField("dv", value));
+          }
+          doc.add(new LongPoint("idx", value));
+        }
+        iw.addDocument(doc);
+      }
+      if (random().nextBoolean()) {
+        iw.deleteDocuments(LongPoint.newRangeQuery("idx", 0L, 10L));
+      }
+      final IndexReader reader = iw.getReader();
+      final IndexSearcher searcher = newSearcher(reader, false);
+      iw.close();
+
+      for (int i = 0; i < 100; ++i) {
+        final long min = random().nextBoolean() ? Long.MIN_VALUE : TestUtil.nextLong(random(), -100, 10000);
+        final long max = random().nextBoolean() ? Long.MAX_VALUE : TestUtil.nextLong(random(), -100, 10000);
+        final Query q1 = LongPoint.newRangeQuery("idx", min, max);
+        final Query q2;
+        if (sortedNumeric) {
+          q2 = SortedNumericDocValuesField.newRangeQuery("dv", min, max);
+        } else {
+          q2 = NumericDocValuesField.newRangeQuery("dv", min, max);
+        }
+        assertSameMatches(searcher, q1, q2, false);
+      }
+
+      reader.close();
+      dir.close();
+    }
+  }
+
+  private void doTestDuelPointRangeSortedRangeQuery(boolean sortedSet, int maxValuesPerDoc) throws IOException {
+    final int iters = atLeast(10);
+    for (int iter = 0; iter < iters; ++iter) {
+      Directory dir = newDirectory();
+      RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
+      final int numDocs = atLeast(100);
+      for (int i = 0; i < numDocs; ++i) {
+        Document doc = new Document();
+        final int numValues = TestUtil.nextInt(random(), 0, maxValuesPerDoc);
+        for (int j = 0; j < numValues; ++j) {
+          final long value = TestUtil.nextLong(random(), -100, 10000);
+          byte[] encoded = new byte[Long.BYTES];
+          LongPoint.encodeDimension(value, encoded, 0);
+          if (sortedSet) {
+            doc.add(new SortedSetDocValuesField("dv", new BytesRef(encoded)));
+          } else {
+            doc.add(new SortedDocValuesField("dv", new BytesRef(encoded)));
+          }
+          doc.add(new LongPoint("idx", value));
+        }
+        iw.addDocument(doc);
+      }
+      if (random().nextBoolean()) {
+        iw.deleteDocuments(LongPoint.newRangeQuery("idx", 0L, 10L));
+      }
+      final IndexReader reader = iw.getReader();
+      final IndexSearcher searcher = newSearcher(reader, false);
+      iw.close();
+
+      for (int i = 0; i < 100; ++i) {
+        long min = random().nextBoolean() ? Long.MIN_VALUE : TestUtil.nextLong(random(), -100, 10000);
+        long max = random().nextBoolean() ? Long.MAX_VALUE : TestUtil.nextLong(random(), -100, 10000);
+        byte[] encodedMin = new byte[Long.BYTES];
+        byte[] encodedMax = new byte[Long.BYTES];
+        LongPoint.encodeDimension(min, encodedMin, 0);
+        LongPoint.encodeDimension(max, encodedMax, 0);
+        boolean includeMin = true;
+        boolean includeMax = true;
+        if (random().nextBoolean()) {
+          includeMin = false;
+          min++;
+        }
+        if (random().nextBoolean()) {
+          includeMax = false;
+          max--;
+        }
+        final Query q1 = LongPoint.newRangeQuery("idx", min, max);
+        final Query q2;
+        if (sortedSet) {
+          q2 = SortedSetDocValuesField.newRangeQuery("dv",
+              min == Long.MIN_VALUE && random().nextBoolean() ? null : new BytesRef(encodedMin),
+              max == Long.MAX_VALUE && random().nextBoolean() ? null : new BytesRef(encodedMax),
+              includeMin, includeMax);
+        } else {
+          q2 = SortedDocValuesField.newRangeQuery("dv",
+              min == Long.MIN_VALUE && random().nextBoolean() ? null : new BytesRef(encodedMin),
+              max == Long.MAX_VALUE && random().nextBoolean() ? null : new BytesRef(encodedMax),
+              includeMin, includeMax);
+        }
+        assertSameMatches(searcher, q1, q2, false);
+      }
+
+      reader.close();
+      dir.close();
+    }
+  }
+
+  public void testDuelPointRangeSortedSetRangeQuery() throws IOException {
+    doTestDuelPointRangeSortedRangeQuery(true, 1);
+  }
+
+  public void testDuelPointRangeMultivaluedSortedSetRangeQuery() throws IOException {
+    doTestDuelPointRangeSortedRangeQuery(true, 3);
+  }
+
+  public void testDuelPointRangeSortedRangeQuery() throws IOException {
+    doTestDuelPointRangeSortedRangeQuery(false, 1);
+  }
+
+  private void assertSameMatches(IndexSearcher searcher, Query q1, Query q2, boolean scores) throws IOException {
+    final int maxDoc = searcher.getIndexReader().maxDoc();
+    final TopDocs td1 = searcher.search(q1, maxDoc, scores ? Sort.RELEVANCE : Sort.INDEXORDER);
+    final TopDocs td2 = searcher.search(q2, maxDoc, scores ? Sort.RELEVANCE : Sort.INDEXORDER);
+    assertEquals(td1.totalHits, td2.totalHits);
+    for (int i = 0; i < td1.scoreDocs.length; ++i) {
+      assertEquals(td1.scoreDocs[i].doc, td2.scoreDocs[i].doc);
+      if (scores) {
+        assertEquals(td1.scoreDocs[i].score, td2.scoreDocs[i].score, 10e-7);
+      }
+    }
+  }
+
+  public void testEquals() {
+    Query q1 = SortedNumericDocValuesField.newRangeQuery("foo", 3, 5);
+    QueryUtils.checkEqual(q1, SortedNumericDocValuesField.newRangeQuery("foo", 3, 5));
+    QueryUtils.checkUnequal(q1, SortedNumericDocValuesField.newRangeQuery("foo", 3, 6));
+    QueryUtils.checkUnequal(q1, SortedNumericDocValuesField.newRangeQuery("foo", 4, 5));
+    QueryUtils.checkUnequal(q1, SortedNumericDocValuesField.newRangeQuery("bar", 3, 5));
+
+    Query q2 = SortedSetDocValuesField.newRangeQuery("foo", new BytesRef("bar"), new BytesRef("baz"), true, true);
+    QueryUtils.checkEqual(q2, SortedSetDocValuesField.newRangeQuery("foo", new BytesRef("bar"), new BytesRef("baz"), true, true));
+    QueryUtils.checkUnequal(q2, SortedSetDocValuesField.newRangeQuery("foo", new BytesRef("baz"), new BytesRef("baz"), true, true));
+    QueryUtils.checkUnequal(q2, SortedSetDocValuesField.newRangeQuery("foo", new BytesRef("bar"), new BytesRef("bar"), true, true));
+    QueryUtils.checkUnequal(q2, SortedSetDocValuesField.newRangeQuery("quux", new BytesRef("bar"), new BytesRef("baz"), true, true));
+  }
+
+  public void testToString() {
+    Query q1 = SortedNumericDocValuesField.newRangeQuery("foo", 3, 5);
+    assertEquals("foo:[3 TO 5]", q1.toString());
+    assertEquals("[3 TO 5]", q1.toString("foo"));
+    assertEquals("foo:[3 TO 5]", q1.toString("bar"));
+
+    Query q2 = SortedSetDocValuesField.newRangeQuery("foo", new BytesRef("bar"), new BytesRef("baz"), true, true);
+    assertEquals("foo:[[62 61 72] TO [62 61 7a]]", q2.toString());
+    q2 = SortedSetDocValuesField.newRangeQuery("foo", new BytesRef("bar"), new BytesRef("baz"), false, true);
+    assertEquals("foo:{[62 61 72] TO [62 61 7a]]", q2.toString());
+    q2 = SortedSetDocValuesField.newRangeQuery("foo", new BytesRef("bar"), new BytesRef("baz"), false, false);
+    assertEquals("foo:{[62 61 72] TO [62 61 7a]}", q2.toString());
+    q2 = SortedSetDocValuesField.newRangeQuery("foo", new BytesRef("bar"), null, true, true);
+    assertEquals("foo:[[62 61 72] TO *}", q2.toString());
+    q2 = SortedSetDocValuesField.newRangeQuery("foo", null, new BytesRef("baz"), true, true);
+    assertEquals("foo:{* TO [62 61 7a]]", q2.toString());
+    assertEquals("{* TO [62 61 7a]]", q2.toString("foo"));
+    assertEquals("foo:{* TO [62 61 7a]]", q2.toString("bar"));
+  }
+
+  public void testMissingField() throws IOException {
+    Directory dir = newDirectory();
+    RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
+    iw.addDocument(new Document());
+    IndexReader reader = iw.getReader();
+    iw.close();
+    IndexSearcher searcher = newSearcher(reader);
+    for (Query query : Arrays.asList(
+        NumericDocValuesField.newRangeQuery("foo", 2, 4),
+        SortedNumericDocValuesField.newRangeQuery("foo", 2, 4),
+        SortedDocValuesField.newRangeQuery("foo", new BytesRef("abc"), new BytesRef("bcd"), random().nextBoolean(), random().nextBoolean()),
+        SortedSetDocValuesField.newRangeQuery("foo", new BytesRef("abc"), new BytesRef("bcd"), random().nextBoolean(), random().nextBoolean()))) {
+      Weight w = searcher.createNormalizedWeight(query, random().nextBoolean());
+      assertNull(w.scorer(searcher.getIndexReader().leaves().get(0)));
+    }
+    reader.close();
+    dir.close();
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/core/src/test/org/apache/lucene/search/TestIndexOrDocValuesQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestIndexOrDocValuesQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestIndexOrDocValuesQuery.java
new file mode 100644
index 0000000..8b81822
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/TestIndexOrDocValuesQuery.java
@@ -0,0 +1,89 @@
+/*
+ * 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.lucene.search;
+
+import java.io.IOException;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.LongPoint;
+import org.apache.lucene.document.NumericDocValuesField;
+import org.apache.lucene.document.StringField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.TestUtil;
+
+public class TestIndexOrDocValuesQuery extends LuceneTestCase {
+
+  public void testUseIndexForSelectiveQueries() throws IOException {
+    Directory dir = newDirectory();
+    IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()
+        // relies on costs and PointValues.estimateCost so we need the default codec
+        .setCodec(TestUtil.getDefaultCodec()));
+    for (int i = 0; i < 2000; ++i) {
+      Document doc = new Document();
+      if (i == 42) {
+        doc.add(new StringField("f1", "bar", Store.NO));
+        doc.add(new LongPoint("f2", 42L));
+        doc.add(new NumericDocValuesField("f2", 42L));
+      } else if (i == 100) {
+        doc.add(new StringField("f1", "foo", Store.NO));
+        doc.add(new LongPoint("f2", 2L));
+        doc.add(new NumericDocValuesField("f2", 2L));
+      } else {
+        doc.add(new StringField("f1", "bar", Store.NO));
+        doc.add(new LongPoint("f2", 2L));
+        doc.add(new NumericDocValuesField("f2", 2L));
+      }
+      w.addDocument(doc);
+    }
+    w.forceMerge(1);
+    IndexReader reader = DirectoryReader.open(w);
+    IndexSearcher searcher = newSearcher(reader);
+    searcher.setQueryCache(null);
+
+    // The term query is more selective, so the IndexOrDocValuesQuery should use doc values
+    final Query q1 = new BooleanQuery.Builder()
+        .add(new TermQuery(new Term("f1", "foo")), Occur.MUST)
+        .add(new IndexOrDocValuesQuery(LongPoint.newExactQuery("f2", 2), NumericDocValuesField.newRangeQuery("f2", 2L, 2L)), Occur.MUST)
+        .build();
+
+    final Weight w1 = searcher.createNormalizedWeight(q1, random().nextBoolean());
+    final Scorer s1 = w1.scorer(searcher.getIndexReader().leaves().get(0));
+    assertNotNull(s1.twoPhaseIterator()); // means we use doc values
+
+    // The term query is less selective, so the IndexOrDocValuesQuery should use points
+    final Query q2 = new BooleanQuery.Builder()
+        .add(new TermQuery(new Term("f1", "bar")), Occur.MUST)
+        .add(new IndexOrDocValuesQuery(LongPoint.newExactQuery("f2", 42), NumericDocValuesField.newRangeQuery("f2", 42L, 42L)), Occur.MUST)
+        .build();
+
+    final Weight w2 = searcher.createNormalizedWeight(q2, random().nextBoolean());
+    final Scorer s2 = w2.scorer(searcher.getIndexReader().leaves().get(0));
+    assertNull(s2.twoPhaseIterator()); // means we use points
+
+    reader.close();
+    w.close();
+    dir.close();
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesRangeQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesRangeQuery.java
index b71ebb2..02fbc51 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesRangeQuery.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesRangeQuery.java
@@ -19,6 +19,8 @@ package org.apache.lucene.search;
 import java.io.IOException;
 import java.util.Objects;
 
+import org.apache.lucene.document.SortedNumericDocValuesField;
+import org.apache.lucene.document.SortedSetDocValuesField;
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.DocValuesType;
 import org.apache.lucene.index.IndexReader;
@@ -42,20 +44,24 @@ import org.apache.lucene.util.BytesRef;
  * which allows to run the query on doc values when that would be more
  * efficient, and using an index otherwise.
  *
- * @lucene.experimental
+ * @deprecated Use factory method on doc value field classes
  */
+@Deprecated
 public final class DocValuesRangeQuery extends Query {
 
   /** Create a new numeric range query on a numeric doc-values field. The field
    *  must has been indexed with either {@link DocValuesType#NUMERIC} or
-   *  {@link DocValuesType#SORTED_NUMERIC} doc values. */
+   *  {@link DocValuesType#SORTED_NUMERIC} doc values.
+   *  @deprecated use {@link SortedNumericDocValuesField#newRangeQuery} */
+  @Deprecated
   public static Query newLongRange(String field, Long lowerVal, Long upperVal, boolean includeLower, boolean includeUpper) {
     return new DocValuesRangeQuery(field, lowerVal, upperVal, includeLower, includeUpper);
   }
 
   /** Create a new numeric range query on a numeric doc-values field. The field
    *  must has been indexed with {@link DocValuesType#SORTED} or
-   *  {@link DocValuesType#SORTED_SET} doc values. */
+   *  {@link DocValuesType#SORTED_SET} doc values.
+   *  @deprecated use {@link SortedSetDocValuesField#newRangeQuery} */
   public static Query newBytesRefRange(String field, BytesRef lowerVal, BytesRef upperVal, boolean includeLower, boolean includeUpper) {
     return new DocValuesRangeQuery(field, deepCopyOf(lowerVal), deepCopyOf(upperVal), includeLower, includeUpper);
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/sandbox/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
deleted file mode 100644
index 6ac980a..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.lucene.search;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.LeafReaderContext;
-
-/**
- * A query that uses either an index (points or terms) or doc values in order
- * to run a range query, depending which one is more efficient.
- */
-public final class IndexOrDocValuesQuery extends Query {
-
-  private final Query indexQuery, dvQuery;
-
-  /**
-   * Constructor that takes both a query that executes on an index structure
-   * like the inverted index or the points tree, and another query that
-   * executes on doc values. Both queries must match the same documents and
-   * attribute constant scores.
-   */
-  public IndexOrDocValuesQuery(Query indexQuery, Query dvQuery) {
-    this.indexQuery = indexQuery;
-    this.dvQuery = dvQuery;
-  }
-
-  @Override
-  public String toString(String field) {
-    return indexQuery.toString(field);
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (sameClassAs(obj) == false) {
-      return false;
-    }
-    IndexOrDocValuesQuery that = (IndexOrDocValuesQuery) obj;
-    return indexQuery.equals(that.indexQuery) && dvQuery.equals(that.dvQuery);
-  }
-
-  @Override
-  public int hashCode() {
-    int h = classHash();
-    h = 31 * h + indexQuery.hashCode();
-    h = 31 * h + dvQuery.hashCode();
-    return h;
-  }
-
-  @Override
-  public Query rewrite(IndexReader reader) throws IOException {
-    Query indexRewrite = indexQuery.rewrite(reader);
-    Query dvRewrite = dvQuery.rewrite(reader);
-    if (indexQuery != indexRewrite || dvQuery != dvRewrite) {
-      return new IndexOrDocValuesQuery(indexRewrite, dvRewrite);
-    }
-    return this;
-  }
-
-  @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
-    final Weight indexWeight = indexQuery.createWeight(searcher, needsScores);
-    final Weight dvWeight = dvQuery.createWeight(searcher, needsScores);
-    return new ConstantScoreWeight(this) {
-      @Override
-      public BulkScorer bulkScorer(LeafReaderContext context) throws IOException {
-        return indexWeight.bulkScorer(context);
-      }
-
-      @Override
-      public ScorerSupplier scorerSupplier(LeafReaderContext context) throws IOException {
-        final ScorerSupplier indexScorerSupplier = indexWeight.scorerSupplier(context);
-        final ScorerSupplier dvScorerSupplier = dvWeight.scorerSupplier(context); 
-        if (indexScorerSupplier == null || dvScorerSupplier == null) {
-          return null;
-        }
-        return new ScorerSupplier() {
-          @Override
-          public Scorer get(boolean randomAccess) throws IOException {
-            return (randomAccess ? dvScorerSupplier : indexScorerSupplier).get(randomAccess);
-          }
-
-          @Override
-          public long cost() {
-            return Math.min(indexScorerSupplier.cost(), dvScorerSupplier.cost());
-          }
-        };
-      }
-
-      @Override
-      public Scorer scorer(LeafReaderContext context) throws IOException {
-        ScorerSupplier scorerSupplier = scorerSupplier(context);
-        if (scorerSupplier == null) {
-          return null;
-        }
-        return scorerSupplier.get(false);
-      }
-    };
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/lucene/sandbox/src/test/org/apache/lucene/search/TestIndexOrDocValuesQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestIndexOrDocValuesQuery.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestIndexOrDocValuesQuery.java
deleted file mode 100644
index de289e7..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestIndexOrDocValuesQuery.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.lucene.search;
-
-import java.io.IOException;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field.Store;
-import org.apache.lucene.document.LongPoint;
-import org.apache.lucene.document.NumericDocValuesField;
-import org.apache.lucene.document.StringField;
-import org.apache.lucene.index.DirectoryReader;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.BooleanClause.Occur;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.LuceneTestCase;
-import org.apache.lucene.util.TestUtil;
-
-public class TestIndexOrDocValuesQuery extends LuceneTestCase {
-
-  public void testUseIndexForSelectiveQueries() throws IOException {
-    Directory dir = newDirectory();
-    IndexWriter w = new IndexWriter(dir, newIndexWriterConfig()
-        // relies on costs and PointValues.estimateCost so we need the default codec
-        .setCodec(TestUtil.getDefaultCodec()));
-    for (int i = 0; i < 2000; ++i) {
-      Document doc = new Document();
-      if (i == 42) {
-        doc.add(new StringField("f1", "bar", Store.NO));
-        doc.add(new LongPoint("f2", 42L));
-        doc.add(new NumericDocValuesField("f2", 42L));
-      } else if (i == 100) {
-        doc.add(new StringField("f1", "foo", Store.NO));
-        doc.add(new LongPoint("f2", 2L));
-        doc.add(new NumericDocValuesField("f2", 2L));
-      } else {
-        doc.add(new StringField("f1", "bar", Store.NO));
-        doc.add(new LongPoint("f2", 2L));
-        doc.add(new NumericDocValuesField("f2", 2L));
-      }
-      w.addDocument(doc);
-    }
-    w.forceMerge(1);
-    IndexReader reader = DirectoryReader.open(w);
-    IndexSearcher searcher = newSearcher(reader);
-    searcher.setQueryCache(null);
-
-    // The term query is more selective, so the IndexOrDocValuesQuery should use doc values
-    final Query q1 = new BooleanQuery.Builder()
-        .add(new TermQuery(new Term("f1", "foo")), Occur.MUST)
-        .add(new IndexOrDocValuesQuery(LongPoint.newExactQuery("f2", 2), new DocValuesNumbersQuery("f2", 2L)), Occur.MUST)
-        .build();
-
-    final Weight w1 = searcher.createNormalizedWeight(q1, random().nextBoolean());
-    final Scorer s1 = w1.scorer(searcher.getIndexReader().leaves().get(0));
-    assertNotNull(s1.twoPhaseIterator()); // means we use doc values
-
-    // The term query is less selective, so the IndexOrDocValuesQuery should use points
-    final Query q2 = new BooleanQuery.Builder()
-        .add(new TermQuery(new Term("f1", "bar")), Occur.MUST)
-        .add(new IndexOrDocValuesQuery(LongPoint.newExactQuery("f2", 42), new DocValuesNumbersQuery("f2", 42L)), Occur.MUST)
-        .build();
-
-    final Weight w2 = searcher.createNormalizedWeight(q2, random().nextBoolean());
-    final Scorer s2 = w2.scorer(searcher.getIndexReader().leaves().get(0));
-    assertNull(s2.twoPhaseIterator()); // means we use points
-
-    reader.close();
-    w.close();
-    dir.close();
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/solr/contrib/analysis-extras/src/java/org/apache/solr/schema/ICUCollationField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analysis-extras/src/java/org/apache/solr/schema/ICUCollationField.java b/solr/contrib/analysis-extras/src/java/org/apache/solr/schema/ICUCollationField.java
index 2071163..5152768 100644
--- a/solr/contrib/analysis-extras/src/java/org/apache/solr/schema/ICUCollationField.java
+++ b/solr/contrib/analysis-extras/src/java/org/apache/solr/schema/ICUCollationField.java
@@ -32,7 +32,6 @@ import org.apache.lucene.collation.ICUCollationKeyAnalyzer;
 import org.apache.lucene.document.SortedDocValuesField;
 import org.apache.lucene.document.SortedSetDocValuesField;
 import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.search.DocValuesRangeQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.TermRangeQuery;
@@ -272,13 +271,8 @@ public class ICUCollationField extends FieldType {
     BytesRef low = part1 == null ? null : getCollationKey(f, part1);
     BytesRef high = part2 == null ? null : getCollationKey(f, part2);
     if (!field.indexed() && field.hasDocValues()) {
-      if (field.multiValued()) {
-          return DocValuesRangeQuery.newBytesRefRange(
-              field.getName(), low, high, minInclusive, maxInclusive);
-        } else {
-          return DocValuesRangeQuery.newBytesRefRange(
-              field.getName(), low, high, minInclusive, maxInclusive);
-        } 
+      return SortedSetDocValuesField.newRangeQuery(
+          field.getName(), low, high, minInclusive, maxInclusive);
     } else {
       return new TermRangeQuery(field.getName(), low, high, minInclusive, maxInclusive);
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/solr/core/src/java/org/apache/solr/schema/CollationField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/CollationField.java b/solr/core/src/java/org/apache/solr/schema/CollationField.java
index 998db2a..805e204 100644
--- a/solr/core/src/java/org/apache/solr/schema/CollationField.java
+++ b/solr/core/src/java/org/apache/solr/schema/CollationField.java
@@ -36,7 +36,6 @@ import org.apache.lucene.collation.CollationKeyAnalyzer;
 import org.apache.lucene.document.SortedDocValuesField;
 import org.apache.lucene.document.SortedSetDocValuesField;
 import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.search.DocValuesRangeQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.TermRangeQuery;
@@ -242,7 +241,7 @@ public class CollationField extends FieldType {
     BytesRef low = part1 == null ? null : getCollationKey(f, part1);
     BytesRef high = part2 == null ? null : getCollationKey(f, part2);
     if (!field.indexed() && field.hasDocValues()) {
-      return DocValuesRangeQuery.newBytesRefRange(
+      return SortedSetDocValuesField.newRangeQuery(
           field.getName(), low, high, minInclusive, maxInclusive);
     } else {
       return new TermRangeQuery(field.getName(), low, high, minInclusive, maxInclusive);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/solr/core/src/java/org/apache/solr/schema/EnumField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/EnumField.java b/solr/core/src/java/org/apache/solr/schema/EnumField.java
index 27f3a0a..828274c 100644
--- a/solr/core/src/java/org/apache/solr/schema/EnumField.java
+++ b/solr/core/src/java/org/apache/solr/schema/EnumField.java
@@ -41,7 +41,6 @@ import org.apache.lucene.index.IndexableField;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.EnumFieldSource;
 import org.apache.lucene.search.ConstantScoreQuery;
-import org.apache.lucene.search.DocValuesRangeQuery;
 import org.apache.lucene.search.LegacyNumericRangeQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SortField;
@@ -253,10 +252,21 @@ public class EnumField extends PrimitiveFieldType {
     Query query = null;
     final boolean matchOnly = field.hasDocValues() && !field.indexed();
     if (matchOnly) {
-      query = new ConstantScoreQuery(DocValuesRangeQuery.newLongRange(field.getName(),
-              min == null ? null : minValue.longValue(),
-              max == null ? null : maxValue.longValue(),
-              minInclusive, maxInclusive));
+      long lowerValue = Long.MIN_VALUE;
+      long upperValue = Long.MAX_VALUE;
+      if (minValue != null) {
+        lowerValue = minValue.longValue();
+        if (minInclusive == false) {
+          ++lowerValue;
+        }
+      }
+      if (maxValue != null) {
+        upperValue = maxValue.longValue();
+        if (maxInclusive == false) {
+          --upperValue;
+        }
+      }
+      query = new ConstantScoreQuery(NumericDocValuesField.newRangeQuery(field.getName(), lowerValue, upperValue));
     } else {
       query = LegacyNumericRangeQuery.newIntRange(field.getName(), DEFAULT_PRECISION_STEP,
           min == null ? null : minValue,

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/solr/core/src/java/org/apache/solr/schema/FieldType.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/FieldType.java b/solr/core/src/java/org/apache/solr/schema/FieldType.java
index 4ba4106..accca3b 100644
--- a/solr/core/src/java/org/apache/solr/schema/FieldType.java
+++ b/solr/core/src/java/org/apache/solr/schema/FieldType.java
@@ -37,12 +37,12 @@ import org.apache.lucene.analysis.util.TokenFilterFactory;
 import org.apache.lucene.analysis.util.TokenizerFactory;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.index.IndexOptions;
+import org.apache.lucene.document.SortedSetDocValuesField;
 import org.apache.lucene.index.IndexableField;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.DocValuesRangeQuery;
 import org.apache.lucene.search.DocValuesRewriteMethod;
 import org.apache.lucene.search.MultiTermQuery;
 import org.apache.lucene.search.PrefixQuery;
@@ -728,17 +728,17 @@ public abstract class FieldType extends FieldProperties {
    */
   public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive, boolean maxInclusive) {
     // TODO: change these all to use readableToIndexed/bytes instead (e.g. for unicode collation)
+    final BytesRef miValue = part1 == null ? null : new BytesRef(toInternal(part1));
+    final BytesRef maxValue = part2 == null ? null : new BytesRef(toInternal(part2));
     if (field.hasDocValues() && !field.indexed()) {
-      return DocValuesRangeQuery.newBytesRefRange(
-          field.getName(),
-          part1 == null ? null : new BytesRef(toInternal(part1)),
-          part2 == null ? null : new BytesRef(toInternal(part2)),
-          minInclusive, maxInclusive);
+      return SortedSetDocValuesField.newRangeQuery(
+            field.getName(),
+            miValue, maxValue,
+            minInclusive, maxInclusive);
     } else {
       SolrRangeQuery rangeQuery = new SolrRangeQuery(
             field.getName(),
-            part1 == null ? null : new BytesRef(toInternal(part1)),
-            part2 == null ? null : new BytesRef(toInternal(part2)),
+            miValue, maxValue,
             minInclusive, maxInclusive);
       return rangeQuery;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/20b7dfae/solr/core/src/java/org/apache/solr/schema/TrieField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/TrieField.java b/solr/core/src/java/org/apache/solr/schema/TrieField.java
index b7bac1c..21a35bb 100644
--- a/solr/core/src/java/org/apache/solr/schema/TrieField.java
+++ b/solr/core/src/java/org/apache/solr/schema/TrieField.java
@@ -42,8 +42,8 @@ import org.apache.lucene.queries.function.valuesource.DoubleFieldSource;
 import org.apache.lucene.queries.function.valuesource.FloatFieldSource;
 import org.apache.lucene.queries.function.valuesource.IntFieldSource;
 import org.apache.lucene.queries.function.valuesource.LongFieldSource;
-import org.apache.lucene.search.DocValuesRangeQuery;
 import org.apache.lucene.search.LegacyNumericRangeQuery;
+import org.apache.lucene.search.MatchNoDocsQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.SortedSetSelector;
@@ -377,9 +377,9 @@ public class TrieField extends PrimitiveFieldType {
     switch (type) {
       case INTEGER:
         if (matchOnly) {
-          query = DocValuesRangeQuery.newLongRange(field.getName(),
-                min == null ? null : (long) Integer.parseInt(min),
-                max == null ? null : (long) Integer.parseInt(max),
+          query = numericDocValuesRangeQuery(field.getName(),
+                min == null ? null : Integer.parseInt(min),
+                max == null ? null : Integer.parseInt(max),
                 minInclusive, maxInclusive);
         } else {
           query = LegacyNumericRangeQuery.newIntRange(field.getName(), ps,
@@ -400,7 +400,7 @@ public class TrieField extends PrimitiveFieldType {
         break;
       case LONG:
         if (matchOnly) {
-          query = DocValuesRangeQuery.newLongRange(field.getName(),
+          query = numericDocValuesRangeQuery(field.getName(),
                 min == null ? null : Long.parseLong(min),
                 max == null ? null : Long.parseLong(max),
                 minInclusive, maxInclusive);
@@ -423,7 +423,7 @@ public class TrieField extends PrimitiveFieldType {
         break;
       case DATE:
         if (matchOnly) {
-          query = DocValuesRangeQuery.newLongRange(field.getName(),
+          query = numericDocValuesRangeQuery(field.getName(),
                 min == null ? null : DateMathParser.parseMath(null, min).getTime(),
                 max == null ? null : DateMathParser.parseMath(null, max).getTime(),
                 minInclusive, maxInclusive);
@@ -441,6 +441,35 @@ public class TrieField extends PrimitiveFieldType {
     return query;
   }
 
+  private static Query numericDocValuesRangeQuery(
+      String field,
+      Number lowerValue, Number upperValue,
+      boolean lowerInclusive, boolean upperInclusive) {
+
+    long actualLowerValue = Long.MIN_VALUE;
+    if (lowerValue != null) {
+      actualLowerValue = lowerValue.longValue();
+      if (lowerInclusive == false) {
+        if (actualLowerValue == Long.MAX_VALUE) {
+          return new MatchNoDocsQuery();
+        }
+        ++actualLowerValue;
+      }
+    }
+
+    long actualUpperValue = Long.MAX_VALUE;
+    if (upperValue != null) {
+      actualUpperValue = upperValue.longValue();
+      if (upperInclusive == false) {
+        if (actualUpperValue == Long.MIN_VALUE) {
+          return new MatchNoDocsQuery();
+        }
+        --actualUpperValue;
+      }
+    }
+    return NumericDocValuesField.newRangeQuery(field, actualLowerValue, actualUpperValue);
+  }
+
   private static long FLOAT_NEGATIVE_INFINITY_BITS = (long)Float.floatToIntBits(Float.NEGATIVE_INFINITY);
   private static long DOUBLE_NEGATIVE_INFINITY_BITS = Double.doubleToLongBits(Double.NEGATIVE_INFINITY);
   private static long FLOAT_POSITIVE_INFINITY_BITS = (long)Float.floatToIntBits(Float.POSITIVE_INFINITY);
@@ -477,10 +506,10 @@ public class TrieField extends PrimitiveFieldType {
     } else { // If both max and min are negative (or -0d), then issue range query with max and min reversed
       if ((minVal == null || minVal.doubleValue() < 0d || minBits == minusZeroBits) &&
           (maxVal != null && (maxVal.doubleValue() < 0d || maxBits == minusZeroBits))) {
-        query = DocValuesRangeQuery.newLongRange
+        query = numericDocValuesRangeQuery
             (fieldName, maxBits, (min == null ? negativeInfinityBits : minBits), maxInclusive, minInclusive);
       } else { // If both max and min are positive, then issue range query
-        query = DocValuesRangeQuery.newLongRange
+        query = numericDocValuesRangeQuery
             (fieldName, minBits, (max == null ? positiveInfinityBits : maxBits), minInclusive, maxInclusive);
       }
     }