You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by da...@apache.org on 2017/03/14 02:19:37 UTC

[01/23] lucene-solr:jira/solr-9835: SOLR-10257: Add logarithm StreamEvaluator

Repository: lucene-solr
Updated Branches:
  refs/heads/jira/solr-9835 f32977db6 -> 2f64575dc


SOLR-10257: Add logarithm StreamEvaluator


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

Branch: refs/heads/jira/solr-9835
Commit: d945a246f6071699790119f07a66fb4c5505cee2
Parents: c680f45
Author: Joel Bernstein <jb...@apache.org>
Authored: Thu Mar 9 20:48:14 2017 -0500
Committer: Joel Bernstein <jb...@apache.org>
Committed: Thu Mar 9 21:01:24 2017 -0500

----------------------------------------------------------------------
 .../org/apache/solr/handler/StreamHandler.java  |  3 +-
 .../solrj/io/eval/NaturalLogEvaluator.java      | 60 ++++++++++++
 .../io/stream/eval/NaturalLogEvaluatorTest.java | 98 ++++++++++++++++++++
 3 files changed, 160 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d945a246/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/StreamHandler.java b/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
index 06e59b6..e69f52b 100644
--- a/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
@@ -42,6 +42,7 @@ import org.apache.solr.client.solrj.io.eval.IfThenElseEvaluator;
 import org.apache.solr.client.solrj.io.eval.LessThanEqualToEvaluator;
 import org.apache.solr.client.solrj.io.eval.LessThanEvaluator;
 import org.apache.solr.client.solrj.io.eval.MultiplyEvaluator;
+import org.apache.solr.client.solrj.io.eval.NaturalLogEvaluator;
 import org.apache.solr.client.solrj.io.eval.NotEvaluator;
 import org.apache.solr.client.solrj.io.eval.OrEvaluator;
 import org.apache.solr.client.solrj.io.eval.RawValueEvaluator;
@@ -197,7 +198,7 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
       .withFunctionName("div", DivideEvaluator.class)
       .withFunctionName("mult", MultiplyEvaluator.class)
       .withFunctionName("sub", SubtractEvaluator.class)
-      
+      .withFunctionName("log", NaturalLogEvaluator.class)
       // Conditional Stream Evaluators
       .withFunctionName("if", IfThenElseEvaluator.class)
       ;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d945a246/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/NaturalLogEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/NaturalLogEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/NaturalLogEvaluator.java
new file mode 100644
index 0000000..19709e6
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/NaturalLogEvaluator.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ */
+package org.apache.solr.client.solrj.io.eval;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.solr.client.solrj.io.Tuple;
+import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
+import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
+
+public class NaturalLogEvaluator extends NumberEvaluator {
+  protected static final long serialVersionUID = 1L;
+
+  public NaturalLogEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
+    super(expression, factory);
+
+    if(1 != subEvaluators.size()){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting one value but found %d",expression,subEvaluators.size()));
+    }
+  }
+
+  @Override
+  public Number evaluate(Tuple tuple) throws IOException {
+
+    List<BigDecimal> results = evaluateAll(tuple);
+
+    // we're still doing these checks because if we ever add an array-flatten evaluator,
+    // one found in the constructor could become != 1
+    if(1 != results.size()){
+      throw new IOException(String.format(Locale.ROOT,"%s(...) only works with a 1 value but %d were provided", constructingFactory.getFunctionName(getClass()), results.size()));
+    }
+
+    if(null == results.get(0)){
+      return null;
+    }
+
+    return Math.log(results.get(0).doubleValue());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d945a246/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/NaturalLogEvaluatorTest.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/NaturalLogEvaluatorTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/NaturalLogEvaluatorTest.java
new file mode 100644
index 0000000..c4ae127
--- /dev/null
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/NaturalLogEvaluatorTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.solrj.io.stream.eval;
+
+import java.io.IOException;
+import java.util.Map;
+
+import junit.framework.Assert;
+
+import org.apache.commons.collections.map.HashedMap;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.solr.client.solrj.io.Tuple;
+import org.apache.solr.client.solrj.io.eval.AddEvaluator;
+import org.apache.solr.client.solrj.io.eval.NaturalLogEvaluator;
+import org.apache.solr.client.solrj.io.eval.StreamEvaluator;
+import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
+import org.junit.Test;
+
+public class NaturalLogEvaluatorTest extends LuceneTestCase {
+
+  StreamFactory factory;
+  Map<String, Object> values;
+
+  public NaturalLogEvaluatorTest() {
+    super();
+
+    factory = new StreamFactory()
+        .withFunctionName("log", NaturalLogEvaluator.class).withFunctionName("add", AddEvaluator.class);
+    values = new HashedMap();
+  }
+
+  @Test
+  public void logOneField() throws Exception{
+    StreamEvaluator evaluator = factory.constructEvaluator("log(a)");
+    Object result;
+
+    values.clear();
+    values.put("a", 100);
+    result = evaluator.evaluate(new Tuple(values));
+    Assert.assertTrue(result instanceof Double);
+    Assert.assertTrue(result.equals(Math.log(100)));
+
+  }
+
+  @Test
+  public void logNestedField() throws Exception{
+    StreamEvaluator evaluator = factory.constructEvaluator("log(add(50,50))");
+    Object result;
+
+    values.clear();
+    result = evaluator.evaluate(new Tuple(values));
+    Assert.assertTrue(result instanceof Double);
+    Assert.assertTrue(result.equals(Math.log(100)));
+
+  }
+
+  @Test(expected = IOException.class)
+  public void logNoField() throws Exception{
+    factory.constructEvaluator("log()");
+  }
+
+  @Test(expected = IOException.class)
+  public void logTwoFields() throws Exception{
+    factory.constructEvaluator("log(a,b)");
+  }
+
+  @Test
+  public void logNoValue() throws Exception{
+    StreamEvaluator evaluator = factory.constructEvaluator("log(a)");
+
+    values.clear();
+    Object result = evaluator.evaluate(new Tuple(values));
+    assertNull(result);
+  }
+  @Test
+  public void logNullValue() throws Exception{
+    StreamEvaluator evaluator = factory.constructEvaluator("log(a)");
+
+    values.clear();
+    values.put("a", null);
+    Object result = evaluator.evaluate(new Tuple(values));
+    assertNull(result);
+  }
+}


[20/23] lucene-solr:jira/solr-9835: SOLR-10269 MetricHandler JSON output was incorrect.

Posted by da...@apache.org.
SOLR-10269 MetricHandler JSON output was incorrect.


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

Branch: refs/heads/jira/solr-9835
Commit: e3a0b428fd7dd8747a6b48ef165300ebb23b3198
Parents: c8bad8c
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Mon Mar 13 20:29:22 2017 +0100
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Mon Mar 13 20:30:27 2017 +0100

----------------------------------------------------------------------
 solr/CHANGES.txt                                         |  2 ++
 .../src/java/org/apache/solr/util/stats/MetricUtils.java |  7 ++++---
 .../apache/solr/handler/admin/MetricsHandlerTest.java    | 11 +++++++----
 3 files changed, 13 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e3a0b428/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 1469d3e..63424dd 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -235,6 +235,8 @@ Bug Fixes
 
 * SOLR-9838: "inc" atomic update doesn't respect default field value (hoss, Amrit Sarkar, Ishan Chattopadhyaya)
 
+* SOLR-10269: MetricsHandler JSON output incorrect. (ab)
+
 Optimizations
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e3a0b428/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java b/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
index 5a7c680..70fd467 100644
--- a/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
+++ b/solr/core/src/java/org/apache/solr/util/stats/MetricUtils.java
@@ -38,6 +38,7 @@ import com.codahale.metrics.Snapshot;
 import com.codahale.metrics.Timer;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.metrics.AggregateMetric;
 
 /**
@@ -117,12 +118,12 @@ public class MetricUtils {
                                       MetricFilter mustMatchFilter, boolean skipHistograms,
                                       boolean skipAggregateValues,
                                       Map<String, Object> metadata) {
-    NamedList result = new NamedList();
+    NamedList result = new SimpleOrderedMap();
     toNamedMaps(registry, shouldMatchFilters, mustMatchFilter, skipHistograms, skipAggregateValues, (k, v) -> {
-      result.add(k, new NamedList(v));
+      result.add(k, v);
     });
     if (metadata != null && !metadata.isEmpty()) {
-      result.add("_metadata_", new NamedList(metadata));
+      result.add("_metadata_", metadata);
     }
     return result;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e3a0b428/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
index a1b29db..2fd7e9c 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/MetricsHandlerTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.solr.handler.admin;
 
+import java.util.Map;
+
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.util.NamedList;
@@ -48,13 +50,14 @@ public class MetricsHandlerTest extends SolrTestCaseJ4 {
     NamedList nl = (NamedList) values.get("solr.core.collection1");
     assertNotNull(nl);
     assertNotNull(nl.get("SEARCHER.new.errors")); // counter type
-    assertNotNull(((NamedList) nl.get("SEARCHER.new.errors")).get("count"));
-    assertEquals(0L, ((NamedList) nl.get("SEARCHER.new.errors")).get("count"));
+    assertNotNull(((Map) nl.get("SEARCHER.new.errors")).get("count"));
+    // response wasn't serialized so we get here whatever MetricUtils produced instead of NamedList
+    assertEquals(0L, ((Map) nl.get("SEARCHER.new.errors")).get("count"));
     nl = (NamedList) values.get("solr.node");
     assertNotNull(nl.get("CONTAINER.cores.loaded")); // int gauge
-    assertEquals(1, ((NamedList) nl.get("CONTAINER.cores.loaded")).get("value"));
+    assertEquals(1, ((Map) nl.get("CONTAINER.cores.loaded")).get("value"));
     assertNotNull(nl.get("ADMIN./admin/authorization.clientErrors")); // timer type
-    assertEquals(5, ((NamedList) nl.get("ADMIN./admin/authorization.clientErrors")).size());
+    assertEquals(5, ((Map) nl.get("ADMIN./admin/authorization.clientErrors")).size());
 
     resp = new SolrQueryResponse();
     handler.handleRequestBody(req(CommonParams.QT, "/admin/metrics", CommonParams.WT, "json", "group", "jvm,jetty"), resp);


[07/23] lucene-solr:jira/solr-9835: LUCENE-7449: fix CROSSES queries so they don't match all docs when internal nodes are equal

Posted by da...@apache.org.
LUCENE-7449: fix CROSSES queries so they don't match all docs when internal nodes are equal


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

Branch: refs/heads/jira/solr-9835
Commit: f3ba7f41057227555992c1534a8265d37bfe7c23
Parents: a3f4896
Author: Nicholas Knize <nk...@gmail.com>
Authored: Sat Mar 11 18:40:55 2017 -0600
Committer: Nicholas Knize <nk...@gmail.com>
Committed: Sat Mar 11 18:41:18 2017 -0600

----------------------------------------------------------------------
 .../src/java/org/apache/lucene/document/RangeFieldQuery.java     | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f3ba7f41/lucene/sandbox/src/java/org/apache/lucene/document/RangeFieldQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/RangeFieldQuery.java b/lucene/sandbox/src/java/org/apache/lucene/document/RangeFieldQuery.java
index 41e64cf..10f10fa 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/document/RangeFieldQuery.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/RangeFieldQuery.java
@@ -171,8 +171,8 @@ abstract class RangeFieldQuery extends Query {
           // if query crosses, docs need to be further scrutinized
           byte[] range = getInternalRange(values.getMinPackedValue(), values.getMaxPackedValue());
           // if the internal node is not equal and not contained by the query, all docs do not match
-          if (!Arrays.equals(ranges, range)
-              && (!target.contains(range) || queryType != QueryType.WITHIN)) {
+          if (queryType == QueryType.CROSSES || (!Arrays.equals(ranges, range)
+              && (target.contains(range) == false || queryType != QueryType.WITHIN))) {
             allDocsMatch = false;
           }
         } else {


[17/23] lucene-solr:jira/solr-9835: SOLR-9838: 'inc' atomic update doesn't respect default field value

Posted by da...@apache.org.
SOLR-9838: 'inc' atomic update doesn't respect default field value


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

Branch: refs/heads/jira/solr-9835
Commit: a06c39f3e50a1616cd7c48f4454dd77be17c7278
Parents: d5181ec
Author: Ishan Chattopadhyaya <is...@apache.org>
Authored: Mon Mar 13 18:46:08 2017 +0530
Committer: Ishan Chattopadhyaya <is...@apache.org>
Committed: Mon Mar 13 18:46:08 2017 +0530

----------------------------------------------------------------------
 solr/CHANGES.txt                                         |  2 ++
 .../update/processor/AtomicUpdateDocumentMerger.java     | 11 ++++++-----
 .../apache/solr/update/processor/AtomicUpdatesTest.java  |  3 +--
 3 files changed, 9 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a06c39f3/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 7bf679f..6e96cbb 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -227,6 +227,8 @@ Bug Fixes
 
 * SOLR-10226: add back "totalTime" metric to all handlers. See also the back-compat note. (ab)
 
+* SOLR-9838: "inc" atomic update doesn't respect default field value (hoss, Amrit Sarkar, Ishan Chattopadhyaya)
+
 Optimizations
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a06c39f3/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java b/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
index 093149a..9061235 100644
--- a/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
+++ b/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
@@ -316,12 +316,11 @@ public class AtomicUpdateDocumentMerger {
 
   protected void doInc(SolrInputDocument toDoc, SolrInputField sif, Object fieldVal) {
     SolrInputField numericField = toDoc.get(sif.getName());
-    if (numericField == null) {
-      toDoc.setField(sif.getName(),  fieldVal);
-    } else {
+    SchemaField sf = schema.getField(sif.getName());
+    if (numericField != null || sf.getDefaultValue() != null) {
       // TODO: fieldtype needs externalToObject?
-      String oldValS = numericField.getFirstValue().toString();
-      SchemaField sf = schema.getField(sif.getName());
+      String oldValS = (numericField != null) ?
+          numericField.getFirstValue().toString(): sf.getDefaultValue().toString();
       BytesRefBuilder term = new BytesRefBuilder();
       sf.getType().readableToIndexed(oldValS, term);
       Object oldVal = sf.getType().toObject(sf, term.get());
@@ -340,6 +339,8 @@ public class AtomicUpdateDocumentMerger {
       }
 
       toDoc.setField(sif.getName(),  result);
+    } else {
+      toDoc.setField(sif.getName(), fieldVal);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a06c39f3/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java b/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
index 7bae2c9..bfcf015 100644
--- a/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
+++ b/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
@@ -1204,7 +1204,6 @@ public class AtomicUpdatesTest extends SolrTestCaseJ4 {
     
   }
 
-  @AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/SOLR-9838")
   public void testAtomicUpdateOfFieldsWithDefaultValue() {
     // both fields have the same default value (42)
     for (String fieldToUpdate : Arrays.asList("intDefault", "intDvoDefault")) {
@@ -1254,7 +1253,7 @@ public class AtomicUpdatesTest extends SolrTestCaseJ4 {
               , "count(//doc/*)=6"
               );
       // do atomic update
-      assertU(adoc(sdoc("id", "7", fieldToUpdate, ImmutableMap.of("inc", -555))));
+      assertU(adoc(sdoc("id", "8", fieldToUpdate, ImmutableMap.of("inc", -555))));
       assertQ(fieldToUpdate + ": RTG after atomic update"
               , req("qt", "/get", "id", "8")
               , "count(//doc)=1"


[18/23] lucene-solr:jira/solr-9835: SOLR-10250: CloudSolrClient can now return versions for documents added or deleted when versions=true is passed

Posted by da...@apache.org.
SOLR-10250: CloudSolrClient can now return versions for documents added or deleted when versions=true is passed


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

Branch: refs/heads/jira/solr-9835
Commit: ceffbf98445065220dcc9380e2731fd26a467f5d
Parents: a06c39f
Author: Ishan Chattopadhyaya <is...@apache.org>
Authored: Mon Mar 13 19:53:22 2017 +0530
Committer: Ishan Chattopadhyaya <is...@apache.org>
Committed: Mon Mar 13 19:53:22 2017 +0530

----------------------------------------------------------------------
 solr/CHANGES.txt                                |  4 ++
 .../solr/client/solrj/impl/CloudSolrClient.java | 17 ++++++-
 .../client/solrj/impl/CloudSolrClientTest.java  | 48 ++++++++++++++++++++
 3 files changed, 68 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ceffbf98/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 6e96cbb..5d6d9d7 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -169,6 +169,10 @@ New Features
 * SOLR-10039: New LatLonPointSpatialField replacement for LatLonType (and some uses of RPT).  Multi-value capable
   indexed geo lat-lon points, query by rect or circle.  Efficient distance sorting/boosting too. (David Smiley)
 
+* SOLR-10250: CloudSolrClient can now return versions for documents added or deleted when "versions=true" is passed.
+  However, if there is a leader election while this request is in transit, the versions may not be returned from that
+  shard. (Boris Naguet, Ishan Chattopadhyaya)
+
 Bug Fixes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ceffbf98/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
index d3938c8..6941a77 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
@@ -903,7 +903,10 @@ public class CloudSolrClient extends SolrClient {
     // TolerantUpdateProcessor
     List<SimpleOrderedMap<String>> toleratedErrors = null; 
     int maxToleratedErrors = Integer.MAX_VALUE;
-      
+
+    // For "adds", "deletes", "deleteByQuery" etc.
+    Map<String, NamedList> versions = new HashMap<>();
+
     for(int i=0; i<response.size(); i++) {
       NamedList shardResponse = (NamedList)response.getVal(i);
       NamedList header = (NamedList)shardResponse.get("responseHeader");      
@@ -937,6 +940,15 @@ public class CloudSolrClient extends SolrClient {
           toleratedErrors.add(err);
         }
       }
+      for (String updateType: Arrays.asList("adds", "deletes", "deleteByQuery")) {
+        Object obj = shardResponse.get(updateType);
+        if (obj instanceof NamedList) {
+          NamedList versionsList = versions.containsKey(updateType) ?
+              versions.get(updateType): new NamedList();
+          versionsList.addAll((NamedList)obj);
+          versions.put(updateType, versionsList);
+        }
+      }
     }
 
     NamedList cheader = new NamedList();
@@ -971,6 +983,9 @@ public class CloudSolrClient extends SolrClient {
         throw toThrow;
       }
     }
+    for (String updateType: versions.keySet()) {
+      condensed.add(updateType, versions.get(updateType));
+    }
     condensed.add("responseHeader", cheader);
     return condensed;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ceffbf98/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
index dd0dd16..d22b37c 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/impl/CloudSolrClientTest.java
@@ -49,6 +49,7 @@ import org.apache.solr.client.solrj.response.RequestStatusState;
 import org.apache.solr.client.solrj.response.UpdateResponse;
 import org.apache.solr.cloud.AbstractDistribZkTestBase;
 import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
@@ -61,6 +62,7 @@ import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.ShardParams;
+import org.apache.solr.common.params.UpdateParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.handler.admin.CollectionsHandler;
@@ -633,6 +635,52 @@ public class CloudSolrClientTest extends SolrCloudTestCase {
     }
   }
 
+  @Test
+  public void testVersionsAreReturned() throws Exception {
+    
+    // assert that "adds" are returned
+    UpdateRequest updateRequest = new UpdateRequest()
+        .add("id", "1", "a_t", "hello1")
+        .add("id", "2", "a_t", "hello2");
+    updateRequest.setParam(UpdateParams.VERSIONS, Boolean.TRUE.toString());
+
+    NamedList<Object> response = updateRequest.commit(cluster.getSolrClient(), COLLECTION).getResponse();
+    Object addsObject = response.get("adds");
+    
+    assertNotNull("There must be a adds parameter", addsObject);
+    assertTrue(addsObject instanceof NamedList<?>);
+    NamedList<?> adds = (NamedList<?>) addsObject;
+    assertEquals("There must be 2 versions (one per doc)", 2, adds.size());
+
+    Map<String, Long> versions = new HashMap<>();
+    Object object = adds.get("1");
+    assertNotNull("There must be a version for id 1", object);
+    assertTrue("Version for id 1 must be a long", object instanceof Long);
+    versions.put("1", (Long) object);
+
+    object = adds.get("2");
+    assertNotNull("There must be a version for id 2", object);
+    assertTrue("Version for id 2 must be a long", object instanceof Long);
+    versions.put("2", (Long) object);
+
+    QueryResponse resp = cluster.getSolrClient().query(COLLECTION, new SolrQuery("*:*"));
+    assertEquals("There should be one document because overwrite=true", 2, resp.getResults().getNumFound());
+
+    for (SolrDocument doc : resp.getResults()) {
+      Long version = versions.get(doc.getFieldValue("id"));
+      assertEquals("Version on add must match _version_ field", version, doc.getFieldValue("_version_"));
+    }
+
+    // assert that "deletes" are returned
+    UpdateRequest deleteRequest = new UpdateRequest().deleteById("1");
+    deleteRequest.setParam(UpdateParams.VERSIONS, Boolean.TRUE.toString());
+    response = deleteRequest.commit(cluster.getSolrClient(), COLLECTION).getResponse();
+    Object deletesObject = response.get("deletes");
+    assertNotNull("There must be a deletes parameter", deletesObject);
+    NamedList deletes = (NamedList) deletesObject;
+    assertEquals("There must be 1 version", 1, deletes.size());
+  }
+  
   private static void checkSingleServer(NamedList<Object> response) {
     final CloudSolrClient.RouteResponse rr = (CloudSolrClient.RouteResponse) response;
     final Map<String,LBHttpSolrClient.Req> routes = rr.getRoutes();


[09/23] lucene-solr:jira/solr-9835: SOLR-10039: New LatLonPointSpatialField

Posted by da...@apache.org.
SOLR-10039: New LatLonPointSpatialField


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

Branch: refs/heads/jira/solr-9835
Commit: 182c20c4e55c39362f6d46d388eb2b8e0a9052e6
Parents: 1745b03
Author: David Smiley <ds...@apache.org>
Authored: Sat Mar 11 20:48:01 2017 -0500
Committer: David Smiley <ds...@apache.org>
Committed: Sat Mar 11 20:48:01 2017 -0500

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   3 +
 .../solr/schema/AbstractSpatialFieldType.java   |   2 +-
 .../solr/schema/LatLonPointSpatialField.java    | 272 +++++++++++++++++++
 .../solr/collection1/conf/schema-spatial.xml    |   4 +
 .../apache/solr/search/TestSolr4Spatial.java    |  61 ++++-
 .../basic_configs/conf/managed-schema           |   7 +-
 .../conf/managed-schema                         |   7 +-
 .../conf/managed-schema                         |   7 +-
 8 files changed, 338 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/182c20c4/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index b164405..7bf679f 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -166,6 +166,9 @@ New Features
 
 * SOLR-8045: Deploy V2 API at /v2 instead of /solr/v2 (Cao Manh Dat, Noble Paul)
 
+* SOLR-10039: New LatLonPointSpatialField replacement for LatLonType (and some uses of RPT).  Multi-value capable
+  indexed geo lat-lon points, query by rect or circle.  Efficient distance sorting/boosting too. (David Smiley)
+
 Bug Fixes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/182c20c4/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java b/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java
index 4e2829e..2106205 100644
--- a/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java
+++ b/solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java
@@ -225,7 +225,7 @@ public abstract class AbstractSpatialFieldType<T extends SpatialStrategy> extend
     }
 
     List<IndexableField> result = new ArrayList<>();
-    if (field.indexed()) {
+    if (field.indexed() || field.hasDocValues()) {
       T strategy = getStrategy(field.getName());
       result.addAll(Arrays.asList(strategy.createIndexableFields(shape)));
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/182c20c4/solr/core/src/java/org/apache/solr/schema/LatLonPointSpatialField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/LatLonPointSpatialField.java b/solr/core/src/java/org/apache/solr/schema/LatLonPointSpatialField.java
new file mode 100644
index 0000000..c09856a
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/schema/LatLonPointSpatialField.java
@@ -0,0 +1,272 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.schema;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.LatLonDocValuesField;
+import org.apache.lucene.document.LatLonPoint;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
+import org.apache.lucene.search.FieldComparator;
+import org.apache.lucene.search.IndexOrDocValuesQuery;
+import org.apache.lucene.search.LeafFieldComparator;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.spatial.SpatialStrategy;
+import org.apache.lucene.spatial.query.SpatialArgs;
+import org.apache.lucene.spatial.query.SpatialOperation;
+import org.apache.lucene.spatial.query.UnsupportedSpatialOperation;
+import org.apache.solr.common.SolrException;
+import org.locationtech.spatial4j.context.SpatialContext;
+import org.locationtech.spatial4j.distance.DistanceUtils;
+import org.locationtech.spatial4j.shape.Circle;
+import org.locationtech.spatial4j.shape.Point;
+import org.locationtech.spatial4j.shape.Rectangle;
+import org.locationtech.spatial4j.shape.Shape;
+
+/**
+ * A spatial implementation based on Lucene's {@code LatLonPoint} and {@code LatLonDocValuesField}. The
+ * first is based on Lucene's "Points" API, which is a BKD Index.  This field type is strictly limited to
+ * coordinates in lat/lon decimal degrees.  The accuracy is about a centimeter.
+ */
+// TODO once LLP & LLDVF are out of Lucene Sandbox, we should be able to javadoc reference them.
+public class LatLonPointSpatialField extends AbstractSpatialFieldType implements SchemaAware {
+  private IndexSchema schema;
+
+  // TODO handle polygons
+
+  @Override
+  public void checkSchemaField(SchemaField field) {
+    // override because if we didn't, FieldType will complain about docValues not being supported (we do support it)
+  }
+
+  @Override
+  public void inform(IndexSchema schema) {
+    this.schema = schema;
+  }
+
+  @Override
+  protected SpatialStrategy newSpatialStrategy(String fieldName) {
+    SchemaField schemaField = schema.getField(fieldName); // TODO change AbstractSpatialFieldType so we get schemaField?
+    return new LatLonPointSpatialStrategy(ctx, fieldName, schemaField.indexed(), schemaField.hasDocValues());
+  }
+
+  // TODO move to Lucene-spatial-extras once LatLonPoint & LatLonDocValuesField moves out of sandbox
+  public static class LatLonPointSpatialStrategy extends SpatialStrategy {
+
+    private final boolean indexed; // for query/filter
+    private final boolean docValues; // for sort. Can be used to query/filter.
+
+    public LatLonPointSpatialStrategy(SpatialContext ctx, String fieldName, boolean indexed, boolean docValues) {
+      super(ctx, fieldName);
+      if (!ctx.isGeo()) {
+        throw new IllegalArgumentException("ctx must be geo=true: " + ctx);
+      }
+      this.indexed = indexed;
+      this.docValues = docValues;
+    }
+
+    @Override
+    public Field[] createIndexableFields(Shape shape) {
+      if (!(shape instanceof Point)) {
+        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+            getClass().getSimpleName() + " only supports indexing points; got: " + shape);
+      }
+      Point point = (Point) shape;
+
+      int fieldsLen = (indexed ? 1 : 0) + (docValues ? 1 : 0);
+      Field[] fields = new Field[fieldsLen];
+      int fieldsIdx = 0;
+      if (indexed) {
+        fields[fieldsIdx++] = new LatLonPoint(getFieldName(), point.getY(), point.getX());
+      }
+      if (docValues) {
+        fields[fieldsIdx++] = new LatLonDocValuesField(getFieldName(), point.getY(), point.getX());
+      }
+      return fields;
+    }
+
+    @Override
+    public Query makeQuery(SpatialArgs args) {
+      if (args.getOperation() != SpatialOperation.Intersects) {
+        throw new UnsupportedSpatialOperation(args.getOperation());
+      }
+      Shape shape = args.getShape();
+      if (indexed && docValues) {
+        return new IndexOrDocValuesQuery(makeQueryFromIndex(shape), makeQueryFromDocValues(shape));
+      } else if (indexed) {
+        return makeQueryFromIndex(shape);
+      } else if (docValues) {
+        return makeQueryFromDocValues(shape);
+      } else {
+        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+            getFieldName() + " needs indexed (preferred) or docValues to support search");
+      }
+    }
+
+    // Uses LatLonPoint
+    protected Query makeQueryFromIndex(Shape shape) {
+      // note: latitude then longitude order for LLP's methods
+      if (shape instanceof Circle) {
+        Circle circle = (Circle) shape;
+        double radiusMeters = circle.getRadius() * DistanceUtils.DEG_TO_KM * 1000;
+        return LatLonPoint.newDistanceQuery(getFieldName(),
+            circle.getCenter().getY(), circle.getCenter().getX(),
+            radiusMeters);
+      } else if (shape instanceof Rectangle) {
+        Rectangle rect = (Rectangle) shape;
+        return LatLonPoint.newBoxQuery(getFieldName(),
+            rect.getMinY(), rect.getMaxY(), rect.getMinX(), rect.getMaxX());
+      } else if (shape instanceof Point) {
+        Point point = (Point) shape;
+        return LatLonPoint.newDistanceQuery(getFieldName(),
+            point.getY(), point.getX(), 0);
+      } else {
+        throw new UnsupportedOperationException("Shape " + shape.getClass() + " is not supported by " + getClass());
+      }
+//      } else if (shape instanceof LucenePolygonShape) {
+//        // TODO support multi-polygon
+//        Polygon poly = ((LucenePolygonShape)shape).lucenePolygon;
+//        return LatLonPoint.newPolygonQuery(getFieldName(), poly);
+    }
+
+    // Uses DocValuesField  (otherwise identical to above)
+    protected Query makeQueryFromDocValues(Shape shape) {
+      // note: latitude then longitude order for LLP's methods
+      if (shape instanceof Circle) {
+        Circle circle = (Circle) shape;
+        double radiusMeters = circle.getRadius() * DistanceUtils.DEG_TO_KM * 1000;
+        return LatLonDocValuesField.newDistanceQuery(getFieldName(),
+            circle.getCenter().getY(), circle.getCenter().getX(),
+            radiusMeters);
+      } else if (shape instanceof Rectangle) {
+        Rectangle rect = (Rectangle) shape;
+        return LatLonDocValuesField.newBoxQuery(getFieldName(),
+            rect.getMinY(), rect.getMaxY(), rect.getMinX(), rect.getMaxX());
+      } else if (shape instanceof Point) {
+        Point point = (Point) shape;
+        return LatLonDocValuesField.newDistanceQuery(getFieldName(),
+            point.getY(), point.getX(), 0);
+      } else {
+        throw new UnsupportedOperationException("Shape " + shape.getClass() + " is not supported by " + getClass());
+      }
+//      } else if (shape instanceof LucenePolygonShape) {
+//        // TODO support multi-polygon
+//        Polygon poly = ((LucenePolygonShape)shape).lucenePolygon;
+//        return LatLonPoint.newPolygonQuery(getFieldName(), poly);
+    }
+
+    @Override
+    public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) {
+      if (!docValues) {
+        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+            getFieldName() + " must have docValues enabled to support this feature");
+      }
+      // Internally, the distance from LatLonPointSortField/Comparator is in meters. So we must also go from meters to
+      //  degrees, which is what Lucene spatial-extras is oriented around.
+      return new DistanceSortValueSource(getFieldName(), queryPoint,
+          DistanceUtils.KM_TO_DEG / 1000.0 * multiplier);
+    }
+
+    /**
+     * A {@link ValueSource} based around {@code LatLonDocValuesField#newDistanceSort(String, double, double)}.
+     */
+    private static class DistanceSortValueSource extends ValueSource {
+      private final String fieldName;
+      private final Point queryPoint;
+      private final double multiplier;
+
+      DistanceSortValueSource(String fieldName, Point queryPoint, double multiplier) {
+        this.fieldName = fieldName;
+        this.queryPoint = queryPoint;
+        this.multiplier = multiplier;
+      }
+
+      @Override
+      public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        DistanceSortValueSource that = (DistanceSortValueSource) o;
+        return Double.compare(that.multiplier, multiplier) == 0 &&
+            Objects.equals(fieldName, that.fieldName) &&
+            Objects.equals(queryPoint, that.queryPoint);
+      }
+
+      @Override
+      public int hashCode() {
+        return Objects.hash(fieldName, queryPoint, multiplier);
+      }
+
+      @Override
+      public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+        return new DoubleDocValues(this) {
+          @SuppressWarnings("unchecked")
+          final FieldComparator<Double> comparator =
+              (FieldComparator<Double>) getSortField(false).getComparator(1, 1);
+          final LeafFieldComparator leafComparator = comparator.getLeafComparator(readerContext);
+          final double mult = multiplier; // so it's a local field
+
+          // Since this computation is expensive, it's worth caching it just in case.
+          double cacheDoc = -1;
+          double cacheVal = Double.POSITIVE_INFINITY;
+
+          @Override
+          public double doubleVal(int doc) {
+            if (cacheDoc != doc) {
+              try {
+                leafComparator.copy(0, doc);
+                cacheVal = comparator.value(0) * mult;
+                cacheDoc = doc;
+              } catch (IOException e) {
+                throw new RuntimeException(e);
+              }
+            }
+            return cacheVal;
+          }
+
+          @Override
+          public boolean exists(int doc) {
+            return !Double.isInfinite(doubleVal(doc));
+          }
+        };
+      }
+
+      @Override
+      public String description() {
+        return "distSort(" + fieldName + ", " + queryPoint + ", mult:" + multiplier + ")";
+      }
+
+      @Override
+      public SortField getSortField(boolean reverse) {
+        if (reverse) {
+          return super.getSortField(true); // will use an impl that calls getValues
+        }
+        return LatLonDocValuesField.newDistanceSort(fieldName, queryPoint.getY(), queryPoint.getX());
+      }
+
+    }
+
+  }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/182c20c4/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml b/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml
index 254f58b..9c7a36f 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml
@@ -54,6 +54,7 @@
   <fieldType name="bbox" class="solr.BBoxField"
              numberType="tdoubleDV" distanceUnits="degrees" storeSubFields="false"/>
 
+  <fieldType name="llp" class="solr.LatLonPointSpatialField" distanceUnits="degrees" multiValued="true" />
 
   <field name="id" type="string" required="true"/>
 
@@ -64,6 +65,9 @@
   <field name="pointvector" type="pointvector"/>
   <field name="srptgeom" type="srptgeom"/>
   <field name="bbox" type="bbox"/>
+  <field name="llp" type="llp" indexed="true" docValues="true" />
+  <field name="llp_idx" type="llp" indexed="true" docValues="false" />
+  <field name="llp_dv" type="llp" indexed="false" docValues="true" />
 
   <dynamicField name="bboxD_*" type="bbox" indexed="true"/>
   <dynamicField name="str_*" type="string" indexed="true" stored="true"/>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/182c20c4/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial.java b/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial.java
index 2fe3740..8cd96ae 100644
--- a/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial.java
+++ b/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial.java
@@ -19,7 +19,6 @@ package org.apache.solr.search;
 import java.text.ParseException;
 import java.util.Arrays;
 
-import com.carrotsearch.randomizedtesting.RandomizedTest;
 import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
 import org.locationtech.spatial4j.context.SpatialContext;
 import org.locationtech.spatial4j.distance.DistanceUtils;
@@ -43,16 +42,18 @@ import org.junit.Test;
  */
 public class TestSolr4Spatial extends SolrTestCaseJ4 {
 
-  private String fieldName;
+  private final String fieldName;
+  private final boolean canCalcDistance;
 
   public TestSolr4Spatial(String fieldName) {
     this.fieldName = fieldName;
+    this.canCalcDistance = !fieldName.equals("llp_idx");
   }
 
   @ParametersFactory
   public static Iterable<Object[]> parameters() {
     return Arrays.asList(new Object[][]{
-        {"srpt_geohash"}, {"srpt_quad"}, {"srpt_packedquad"}, {"stqpt_geohash"}, {"pointvector"}, {"bbox"}
+        {"llp"}, {"llp_idx"}, {"llp_dv"}, {"srpt_geohash"}, {"srpt_quad"}, {"srpt_packedquad"}, {"stqpt_geohash"}, {"pointvector"}, {"bbox"}
     });
   }
 
@@ -105,6 +106,10 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 {
     assertU(adoc("id", "11", fieldName, "89.9,-130"));
     assertU(adoc("id", "12", fieldName, "-89.9,50"));
     assertU(adoc("id", "13", fieldName, "-89.9,-130"));
+    if (random().nextBoolean()) {
+      assertU(commit());
+    }
+    assertU(adoc("id", "99"));//blank
     assertU(commit());
   }
 
@@ -192,7 +197,7 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 {
     //Test using the Lucene spatial syntax
     {
       //never actually need the score but lets test
-      String score = new String[]{null, "none","distance","recipDistance"}[random().nextInt(4)];
+      String score = randomScoreMode();
 
       double distDEG = DistanceUtils.dist2Degrees(distKM, DistanceUtils.EARTH_MEAN_RADIUS_KM);
       Point point = SpatialUtils.parsePoint(ptStr, SpatialContext.GEO);
@@ -225,6 +230,10 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 {
 
   }
 
+  private String randomScoreMode() {
+    return canCalcDistance ? new String[]{null, "none","distance","recipDistance"}[random().nextInt(4)] : "none";
+  }
+
   @Test
   public void testRangeSyntax() {
     setupDocs();
@@ -232,10 +241,10 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 {
     int docId = 1;
     int count = 1;
 
-    String score = random().nextBoolean() ? "none" : "distance";//never actually need the score but lets test
+    String score = randomScoreMode();//never actually need the score but lets test
     assertQ(req(
         "fl", "id", "q","*:*", "rows", "1000",    // testing quotes in range too
-        "fq", "{! score="+score+" df="+fieldName+"}[32,-80 TO \"33 , -79\"]"),//lower-left to upper-right
+        "fq", "{! "+(score==null?"":" score="+score)+" df="+fieldName+"}[32,-80 TO \"33 , -79\"]"),//lower-left to upper-right
 
         "//result/doc/*[@name='id'][.='" + docId + "']",
         "*[count(//doc)=" + count + "]");
@@ -243,13 +252,46 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 {
 
   @Test
   public void testSort() throws Exception {
+    assumeTrue("dist sorting not supported on field " + fieldName, canCalcDistance);
     assertU(adoc("id", "100", fieldName, "1,2"));
     assertU(adoc("id", "101", fieldName, "4,-1"));
-    assertU(adoc("id", "999", fieldName, "70,70"));//far away from these queries
+    if (random().nextBoolean()) {
+      assertU(commit()); // new segment
+    }
+    if (random().nextBoolean()) {
+      assertU(adoc("id", "999", fieldName, "70,70"));//far away from these queries; we filter it out
+    } else {
+      assertU(adoc("id", "999")); // no data
+    }
     assertU(commit());
 
-    //test absence of score=distance means it doesn't score
 
+    // geodist asc
+    assertJQ(req(
+        "q", radiusQuery(3, 4, 9, null, null),
+        "fl","id",
+        "sort","geodist() asc",
+        "sfield", fieldName, "pt", "3,4")
+        , 1e-3
+        , "/response/docs/[0]/id=='100'"
+        , "/response/docs/[1]/id=='101'"
+    );
+    // geodist desc  (simply reverse the assertions)
+    assertJQ(req(
+        "q", radiusQuery(3, 4, 9, null, null),
+        "fl","id",
+        "sort","geodist() desc", // DESC
+        "sfield", fieldName, "pt", "3,4")
+        , 1e-3
+        , "/response/docs/[0]/id=='101'" // FLIPPED
+        , "/response/docs/[1]/id=='100'" // FLIPPED
+    );
+
+    //
+    //  NOTE: the rest work via the score of the spatial query. Generally, you should use geodist() instead.
+    //
+
+    //test absence of score=distance means it doesn't score
     assertJQ(req(
         "q", radiusQuery(3, 4, 9, null, null),
         "fl","id,score")
@@ -345,7 +387,8 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 {
 
   @Test
   public void testSortMultiVal() throws Exception {
-    RandomizedTest.assumeFalse("Multivalue not supported for this field",
+    assumeTrue("dist sorting not supported on field " + fieldName, canCalcDistance);
+    assumeFalse("Multivalue not supported for this field",
         fieldName.equals("pointvector") || fieldName.equals("bbox"));
 
     assertU(adoc("id", "100", fieldName, "1,2"));//1 point

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/182c20c4/solr/server/solr/configsets/basic_configs/conf/managed-schema
----------------------------------------------------------------------
diff --git a/solr/server/solr/configsets/basic_configs/conf/managed-schema b/solr/server/solr/configsets/basic_configs/conf/managed-schema
index 60a0e98..22553d8 100644
--- a/solr/server/solr/configsets/basic_configs/conf/managed-schema
+++ b/solr/server/solr/configsets/basic_configs/conf/managed-schema
@@ -148,9 +148,6 @@
     <dynamicField name="*_d"  type="double" indexed="true"  stored="true"/>
     <dynamicField name="*_ds" type="doubles" indexed="true"  stored="true"/>
 
-    <!-- Type used to index the lat and lon components for the "location" FieldType -->
-    <dynamicField name="*_coordinate"  type="tdouble" indexed="true"  stored="false" useDocValuesAsStored="false" />
-
     <dynamicField name="*_dt"  type="date"    indexed="true"  stored="true"/>
     <dynamicField name="*_dts" type="date"    indexed="true"  stored="true" multiValued="true"/>
     <dynamicField name="*_p"  type="location" indexed="true" stored="true"/>
@@ -551,8 +548,8 @@
     <dynamicField name="*_point" type="point"  indexed="true"  stored="true"/>
     <fieldType name="point" class="solr.PointType" dimension="2" subFieldSuffix="_d"/>
 
-    <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. -->
-    <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
+    <!-- A specialized field for geospatial search filters and distance sorting. -->
+    <fieldType name="location" class="solr.LatLonPointSpatialField" docValues="true"/>
 
     <!-- An alternative geospatial field type new to Solr 4.  It supports multiValued and polygon shapes.
       For more information about this and other Spatial fields new to Solr 4, see:

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/182c20c4/solr/server/solr/configsets/data_driven_schema_configs/conf/managed-schema
----------------------------------------------------------------------
diff --git a/solr/server/solr/configsets/data_driven_schema_configs/conf/managed-schema b/solr/server/solr/configsets/data_driven_schema_configs/conf/managed-schema
index b1373d8..558c05e 100644
--- a/solr/server/solr/configsets/data_driven_schema_configs/conf/managed-schema
+++ b/solr/server/solr/configsets/data_driven_schema_configs/conf/managed-schema
@@ -148,9 +148,6 @@
     <dynamicField name="*_d"  type="double" indexed="true"  stored="true"/>
     <dynamicField name="*_ds" type="doubles" indexed="true"  stored="true"/>
 
-    <!-- Type used to index the lat and lon components for the "location" FieldType -->
-    <dynamicField name="*_coordinate"  type="tdouble" indexed="true"  stored="false" useDocValuesAsStored="false" />
-
     <dynamicField name="*_dt"  type="date"    indexed="true"  stored="true"/>
     <dynamicField name="*_dts" type="date"    indexed="true"  stored="true" multiValued="true"/>
     <dynamicField name="*_p"  type="location" indexed="true" stored="true"/>
@@ -551,8 +548,8 @@
     <dynamicField name="*_point" type="point"  indexed="true"  stored="true"/>
     <fieldType name="point" class="solr.PointType" dimension="2" subFieldSuffix="_d"/>
 
-    <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. -->
-    <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
+    <!-- A specialized field for geospatial search filters and distance sorting. -->
+    <fieldType name="location" class="solr.LatLonPointSpatialField" docValues="true"/>
 
     <!-- An alternative geospatial field type new to Solr 4.  It supports multiValued and polygon shapes.
       For more information about this and other Spatial fields new to Solr 4, see:

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/182c20c4/solr/server/solr/configsets/sample_techproducts_configs/conf/managed-schema
----------------------------------------------------------------------
diff --git a/solr/server/solr/configsets/sample_techproducts_configs/conf/managed-schema b/solr/server/solr/configsets/sample_techproducts_configs/conf/managed-schema
index 4980540..bd292a0 100644
--- a/solr/server/solr/configsets/sample_techproducts_configs/conf/managed-schema
+++ b/solr/server/solr/configsets/sample_techproducts_configs/conf/managed-schema
@@ -212,9 +212,6 @@
    <dynamicField name="*_d"  type="double" indexed="true"  stored="true"/>
    <dynamicField name="*_ds" type="double" indexed="true"  stored="true"  multiValued="true"/>
 
-   <!-- Type used to index the lat and lon components for the "location" FieldType -->
-   <dynamicField name="*_coordinate"  type="tdouble" indexed="true"  stored="false" useDocValuesAsStored="false" />
-
    <dynamicField name="*_dt"  type="date"    indexed="true"  stored="true"/>
    <dynamicField name="*_dts" type="date"    indexed="true"  stored="true" multiValued="true"/>
    <dynamicField name="*_p"  type="location" indexed="true" stored="true"/>
@@ -696,8 +693,8 @@
      -->
     <fieldType name="point" class="solr.PointType" dimension="2" subFieldSuffix="_d"/>
 
-    <!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. -->
-    <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
+    <!-- A specialized field for geospatial search filters and distance sorting. -->
+    <fieldType name="location" class="solr.LatLonPointSpatialField" docValues="true"/>
 
     <!-- An alternative geospatial field type new to Solr 4.  It supports multiValued and polygon shapes.
       For more information about this and other Spatial fields new to Solr 4, see:


[13/23] lucene-solr:jira/solr-9835: LUCENE-7740: Refactor Range Fields to remove Field suffix (e.g., DoubleRange), move InetAddressRange and InetAddressPoint from sandbox to misc module, and refactor all other range fields from sandbox to core.

Posted by da...@apache.org.
LUCENE-7740: Refactor Range Fields to remove Field suffix (e.g., DoubleRange),
move InetAddressRange and InetAddressPoint from sandbox to misc module, and
refactor all other range fields from sandbox 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/d34d81f9
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/d34d81f9
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/d34d81f9

Branch: refs/heads/jira/solr-9835
Commit: d34d81f9af89657fdd4fe0b3174459142955215b
Parents: 182c20c
Author: Nicholas Knize <nk...@gmail.com>
Authored: Mon Mar 13 01:59:04 2017 -0500
Committer: Nicholas Knize <nk...@gmail.com>
Committed: Mon Mar 13 02:22:29 2017 -0500

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   8 +-
 .../org/apache/lucene/document/DoubleRange.java | 271 +++++++++++++++
 .../org/apache/lucene/document/FloatRange.java  | 271 +++++++++++++++
 .../org/apache/lucene/document/IntRange.java    | 276 +++++++++++++++
 .../org/apache/lucene/document/LongRange.java   | 269 ++++++++++++++
 .../apache/lucene/document/RangeFieldQuery.java | 340 ++++++++++++++++++
 .../org/apache/lucene/index/PointValues.java    |   2 +-
 .../search/TestDoubleRangeFieldQueries.java     | 251 ++++++++++++++
 .../search/TestFloatRangeFieldQueries.java      | 251 ++++++++++++++
 .../lucene/search/TestIntRangeFieldQueries.java | 251 ++++++++++++++
 .../search/TestLongRangeFieldQueries.java       | 251 ++++++++++++++
 .../lucene/document/InetAddressPoint.java       | 313 +++++++++++++++++
 .../lucene/document/InetAddressRange.java       | 168 +++++++++
 .../lucene/document/TestInetAddressPoint.java   | 176 ++++++++++
 .../search/TestInetAddressRangeQueries.java     | 215 ++++++++++++
 .../lucene/document/DoubleRangeField.java       | 282 ---------------
 .../apache/lucene/document/FloatRangeField.java | 282 ---------------
 .../lucene/document/InetAddressPoint.java       | 313 -----------------
 .../lucene/document/InetAddressRangeField.java  | 168 ---------
 .../apache/lucene/document/IntRangeField.java   | 282 ---------------
 .../apache/lucene/document/LongRangeField.java  | 280 ---------------
 .../apache/lucene/document/RangeFieldQuery.java | 340 ------------------
 .../org/apache/lucene/document/package.html     |   3 +-
 .../lucene/document/TestDoubleRangeField.java   |  10 +-
 .../lucene/document/TestInetAddressPoint.java   | 176 ----------
 .../search/BaseRangeFieldQueryTestCase.java     | 344 ------------------
 .../search/TestDoubleRangeFieldQueries.java     | 251 --------------
 .../search/TestFloatRangeFieldQueries.java      | 251 --------------
 .../lucene/search/TestIntRangeFieldQueries.java | 251 --------------
 .../lucene/search/TestIpRangeFieldQueries.java  | 220 ------------
 .../search/TestLongRangeFieldQueries.java       | 251 --------------
 .../search/BaseRangeFieldQueryTestCase.java     | 346 +++++++++++++++++++
 32 files changed, 3662 insertions(+), 3701 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index c2fe191..e14ab53 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -84,6 +84,10 @@ Other
 
 API Changes
 
+* LUCENE-7740: Refactor Range Fields to remove Field suffix (e.g., DoubleRange),
+  move InetAddressRange and InetAddressPoint from sandbox to misc module, and
+  refactor all other range fields from sandbox to core. (Nick Knize)
+
 * LUCENE-7624: TermsQuery has been renamed as TermInSetQuery and moved to core.
   (Alan Woodward)
 
@@ -131,8 +135,8 @@ API Changes
 
 New Features
 
-* LUCENE-7738: Add new InetAddressRangeField for indexing and querying
-  InetAddress ranges. (Nick Knize)
+* LUCENE-7738: Add new InetAddressRange for indexing and querying InetAddress
+  ranges. (Nick Knize)
 
 * LUCENE-7449: Add CROSSES relation support to RangeFieldQuery. (Nick Knize)
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/java/org/apache/lucene/document/DoubleRange.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/DoubleRange.java b/lucene/core/src/java/org/apache/lucene/document/DoubleRange.java
new file mode 100644
index 0000000..90a8eb9
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/document/DoubleRange.java
@@ -0,0 +1,271 @@
+/*
+ * 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 org.apache.lucene.document.RangeFieldQuery.QueryType;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+
+/**
+ * An indexed Double Range field.
+ * <p>
+ * This field indexes dimensional ranges defined as min/max pairs. It supports
+ * up to a maximum of 4 dimensions (indexed as 8 numeric values). With 1 dimension representing a single double range,
+ * 2 dimensions representing a bounding box, 3 dimensions a bounding cube, and 4 dimensions a tesseract.
+ * <p>
+ * Multiple values for the same field in one document is supported, and open ended ranges can be defined using
+ * {@code Double.NEGATIVE_INFINITY} and {@code Double.POSITIVE_INFINITY}.
+ *
+ * <p>
+ * This field defines the following static factory methods for common search operations over double ranges:
+ * <ul>
+ *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ranges that intersect the defined search range.
+ *   <li>{@link #newWithinQuery newWithinQuery()} matches ranges that are within the defined search range.
+ *   <li>{@link #newContainsQuery newContainsQuery()} matches ranges that contain the defined search range.
+ * </ul>
+ */
+public class DoubleRange extends Field {
+  /** stores double values so number of bytes is 8 */
+  public static final int BYTES = Double.BYTES;
+
+  /**
+   * Create a new DoubleRange type, from min/max parallel arrays
+   *
+   * @param name field name. must not be null.
+   * @param min range min values; each entry is the min value for the dimension
+   * @param max range max values; each entry is the max value for the dimension
+   */
+  public DoubleRange(String name, final double[] min, final double[] max) {
+    super(name, getType(min.length));
+    setRangeValues(min, max);
+  }
+
+  /** set the field type */
+  private static FieldType getType(int dimensions) {
+    if (dimensions > 4) {
+      throw new IllegalArgumentException("DoubleRange does not support greater than 4 dimensions");
+    }
+
+    FieldType ft = new FieldType();
+    // dimensions is set as 2*dimension size (min/max per dimension)
+    ft.setDimensions(dimensions*2, BYTES);
+    ft.freeze();
+    return ft;
+  }
+
+  /**
+   * Changes the values of the field.
+   * @param min array of min values. (accepts {@code Double.NEGATIVE_INFINITY})
+   * @param max array of max values. (accepts {@code Double.POSITIVE_INFINITY})
+   * @throws IllegalArgumentException if {@code min} or {@code max} is invalid
+   */
+  public void setRangeValues(double[] min, double[] max) {
+    checkArgs(min, max);
+    if (min.length*2 != type.pointDimensionCount() || max.length*2 != type.pointDimensionCount()) {
+      throw new IllegalArgumentException("field (name=" + name + ") uses " + type.pointDimensionCount()/2
+          + " dimensions; cannot change to (incoming) " + min.length + " dimensions");
+    }
+
+    final byte[] bytes;
+    if (fieldsData == null) {
+      bytes = new byte[BYTES*2*min.length];
+      fieldsData = new BytesRef(bytes);
+    } else {
+      bytes = ((BytesRef)fieldsData).bytes;
+    }
+    verifyAndEncode(min, max, bytes);
+  }
+
+  /** validate the arguments */
+  private static void checkArgs(final double[] min, final double[] max) {
+    if (min == null || max == null || min.length == 0 || max.length == 0) {
+      throw new IllegalArgumentException("min/max range values cannot be null or empty");
+    }
+    if (min.length != max.length) {
+      throw new IllegalArgumentException("min/max ranges must agree");
+    }
+    if (min.length > 4) {
+      throw new IllegalArgumentException("DoubleRange does not support greater than 4 dimensions");
+    }
+  }
+
+  /**
+   * Encodes the min, max ranges into a byte array
+   */
+  private static byte[] encode(double[] min, double[] max) {
+    checkArgs(min, max);
+    byte[] b = new byte[BYTES*2*min.length];
+    verifyAndEncode(min, max, b);
+    return b;
+  }
+
+  /**
+   * encode the ranges into a sortable byte array ({@code Double.NaN} not allowed)
+   * <p>
+   * example for 4 dimensions (8 bytes per dimension value):
+   * minD1 ... minD4 | maxD1 ... maxD4
+   */
+  static void verifyAndEncode(double[] min, double[] max, byte[] bytes) {
+    for (int d=0,i=0,j=min.length*BYTES; d<min.length; ++d, i+=BYTES, j+=BYTES) {
+      if (Double.isNaN(min[d])) {
+        throw new IllegalArgumentException("invalid min value (" + Double.NaN + ")" + " in DoubleRange");
+      }
+      if (Double.isNaN(max[d])) {
+        throw new IllegalArgumentException("invalid max value (" + Double.NaN + ")" + " in DoubleRange");
+      }
+      if (min[d] > max[d]) {
+        throw new IllegalArgumentException("min value (" + min[d] + ") is greater than max value (" + max[d] + ")");
+      }
+      encode(min[d], bytes, i);
+      encode(max[d], bytes, j);
+    }
+  }
+
+  /** encode the given value into the byte array at the defined offset */
+  private static void encode(double val, byte[] bytes, int offset) {
+    NumericUtils.longToSortableBytes(NumericUtils.doubleToSortableLong(val), bytes, offset);
+  }
+
+  /**
+   * Get the min value for the given dimension
+   * @param dimension the dimension, always positive
+   * @return the decoded min value
+   */
+  public double getMin(int dimension) {
+    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
+      throw new IllegalArgumentException("dimension request (" + dimension +
+          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
+    }
+    return decodeMin(((BytesRef)fieldsData).bytes, dimension);
+  }
+
+  /**
+   * Get the max value for the given dimension
+   * @param dimension the dimension, always positive
+   * @return the decoded max value
+   */
+  public double getMax(int dimension) {
+    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
+      throw new IllegalArgumentException("dimension request (" + dimension +
+          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
+    }
+    return decodeMax(((BytesRef)fieldsData).bytes, dimension);
+  }
+
+  /** decodes the min value (for the defined dimension) from the encoded input byte array */
+  static double decodeMin(byte[] b, int dimension) {
+    int offset = dimension*BYTES;
+    return NumericUtils.sortableLongToDouble(NumericUtils.sortableBytesToLong(b, offset));
+  }
+
+  /** decodes the max value (for the defined dimension) from the encoded input byte array */
+  static double decodeMax(byte[] b, int dimension) {
+    int offset = b.length/2 + dimension*BYTES;
+    return NumericUtils.sortableLongToDouble(NumericUtils.sortableBytesToLong(b, offset));
+  }
+
+  /**
+   * Create a query for matching indexed ranges that intersect the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Double.NEGATIVE_INFINITY})
+   * @param max array of max values. (accepts {@code Double.POSITIVE_INFINITY})
+   * @return query for matching intersecting ranges (overlap, within, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newIntersectsQuery(String field, final double[] min, final double[] max) {
+    return newRelationQuery(field, min, max, QueryType.INTERSECTS);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that contain the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Double.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Double.MAX_VALUE})
+   * @return query for matching ranges that contain the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newContainsQuery(String field, final double[] min, final double[] max) {
+    return newRelationQuery(field, min, max, QueryType.CONTAINS);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that are within the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Double.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Double.MAX_VALUE})
+   * @return query for matching ranges within the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newWithinQuery(String field, final double[] min, final double[] max) {
+    return newRelationQuery(field, min, max, QueryType.WITHIN);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that cross the defined range.
+   * A CROSSES is defined as any set of ranges that are not disjoint and not wholly contained by
+   * the query. Effectively, its the complement of union(WITHIN, DISJOINT).
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Double.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Double.MAX_VALUE})
+   * @return query for matching ranges within the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newCrossesQuery(String field, final double[] min, final double[] max) {
+    return newRelationQuery(field, min, max, QueryType.CROSSES);
+  }
+
+  /** helper method for creating the desired relational query */
+  private static Query newRelationQuery(String field, final double[] min, final double[] max, QueryType relation) {
+    checkArgs(min, max);
+    return new RangeFieldQuery(field, encode(min, max), min.length, relation) {
+      @Override
+      protected String toString(byte[] ranges, int dimension) {
+        return DoubleRange.toString(ranges, dimension);
+      }
+    };
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(getClass().getSimpleName());
+    sb.append(" <");
+    sb.append(name);
+    sb.append(':');
+    byte[] b = ((BytesRef)fieldsData).bytes;
+    toString(b, 0);
+    for (int d=1; d<type.pointDimensionCount(); ++d) {
+      sb.append(' ');
+      toString(b, d);
+    }
+    sb.append('>');
+
+    return sb.toString();
+  }
+
+  /**
+   * Returns the String representation for the range at the given dimension
+   * @param ranges the encoded ranges, never null
+   * @param dimension the dimension of interest
+   * @return The string representation for the range at the provided dimension
+   */
+  private static String toString(byte[] ranges, int dimension) {
+    return "[" + Double.toString(decodeMin(ranges, dimension)) + " : "
+        + Double.toString(decodeMax(ranges, dimension)) + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/java/org/apache/lucene/document/FloatRange.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/FloatRange.java b/lucene/core/src/java/org/apache/lucene/document/FloatRange.java
new file mode 100644
index 0000000..8b40538
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/document/FloatRange.java
@@ -0,0 +1,271 @@
+/*
+ * 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 org.apache.lucene.document.RangeFieldQuery.QueryType;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+
+/**
+ * An indexed Float Range field.
+ * <p>
+ * This field indexes dimensional ranges defined as min/max pairs. It supports
+ * up to a maximum of 4 dimensions (indexed as 8 numeric values). With 1 dimension representing a single float range,
+ * 2 dimensions representing a bounding box, 3 dimensions a bounding cube, and 4 dimensions a tesseract.
+ * <p>
+ * Multiple values for the same field in one document is supported, and open ended ranges can be defined using
+ * {@code Float.NEGATIVE_INFINITY} and {@code Float.POSITIVE_INFINITY}.
+ *
+ * <p>
+ * This field defines the following static factory methods for common search operations over float ranges:
+ * <ul>
+ *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ranges that intersect the defined search range.
+ *   <li>{@link #newWithinQuery newWithinQuery()} matches ranges that are within the defined search range.
+ *   <li>{@link #newContainsQuery newContainsQuery()} matches ranges that contain the defined search range.
+ * </ul>
+ */
+public class FloatRange extends Field {
+  /** stores float values so number of bytes is 4 */
+  public static final int BYTES = Float.BYTES;
+
+  /**
+   * Create a new FloatRange type, from min/max parallel arrays
+   *
+   * @param name field name. must not be null.
+   * @param min range min values; each entry is the min value for the dimension
+   * @param max range max values; each entry is the max value for the dimension
+   */
+  public FloatRange(String name, final float[] min, final float[] max) {
+    super(name, getType(min.length));
+    setRangeValues(min, max);
+  }
+
+  /** set the field type */
+  private static FieldType getType(int dimensions) {
+    if (dimensions > 4) {
+      throw new IllegalArgumentException("FloatRange does not support greater than 4 dimensions");
+    }
+
+    FieldType ft = new FieldType();
+    // dimensions is set as 2*dimension size (min/max per dimension)
+    ft.setDimensions(dimensions*2, BYTES);
+    ft.freeze();
+    return ft;
+  }
+
+  /**
+   * Changes the values of the field.
+   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
+   * @param max array of max values. (accepts {@code Float.POSITIVE_INFINITY})
+   * @throws IllegalArgumentException if {@code min} or {@code max} is invalid
+   */
+  public void setRangeValues(float[] min, float[] max) {
+    checkArgs(min, max);
+    if (min.length*2 != type.pointDimensionCount() || max.length*2 != type.pointDimensionCount()) {
+      throw new IllegalArgumentException("field (name=" + name + ") uses " + type.pointDimensionCount()/2
+          + " dimensions; cannot change to (incoming) " + min.length + " dimensions");
+    }
+
+    final byte[] bytes;
+    if (fieldsData == null) {
+      bytes = new byte[BYTES*2*min.length];
+      fieldsData = new BytesRef(bytes);
+    } else {
+      bytes = ((BytesRef)fieldsData).bytes;
+    }
+    verifyAndEncode(min, max, bytes);
+  }
+
+  /** validate the arguments */
+  private static void checkArgs(final float[] min, final float[] max) {
+    if (min == null || max == null || min.length == 0 || max.length == 0) {
+      throw new IllegalArgumentException("min/max range values cannot be null or empty");
+    }
+    if (min.length != max.length) {
+      throw new IllegalArgumentException("min/max ranges must agree");
+    }
+    if (min.length > 4) {
+      throw new IllegalArgumentException("FloatRange does not support greater than 4 dimensions");
+    }
+  }
+
+  /**
+   * Encodes the min, max ranges into a byte array
+   */
+  private static byte[] encode(float[] min, float[] max) {
+    checkArgs(min, max);
+    byte[] b = new byte[BYTES*2*min.length];
+    verifyAndEncode(min, max, b);
+    return b;
+  }
+
+  /**
+   * encode the ranges into a sortable byte array ({@code Float.NaN} not allowed)
+   * <p>
+   * example for 4 dimensions (8 bytes per dimension value):
+   * minD1 ... minD4 | maxD1 ... maxD4
+   */
+  static void verifyAndEncode(float[] min, float[] max, byte[] bytes) {
+    for (int d=0,i=0,j=min.length*BYTES; d<min.length; ++d, i+=BYTES, j+=BYTES) {
+      if (Double.isNaN(min[d])) {
+        throw new IllegalArgumentException("invalid min value (" + Float.NaN + ")" + " in FloatRange");
+      }
+      if (Double.isNaN(max[d])) {
+        throw new IllegalArgumentException("invalid max value (" + Float.NaN + ")" + " in FloatRange");
+      }
+      if (min[d] > max[d]) {
+        throw new IllegalArgumentException("min value (" + min[d] + ") is greater than max value (" + max[d] + ")");
+      }
+      encode(min[d], bytes, i);
+      encode(max[d], bytes, j);
+    }
+  }
+
+  /** encode the given value into the byte array at the defined offset */
+  private static void encode(float val, byte[] bytes, int offset) {
+    NumericUtils.intToSortableBytes(NumericUtils.floatToSortableInt(val), bytes, offset);
+  }
+
+  /**
+   * Get the min value for the given dimension
+   * @param dimension the dimension, always positive
+   * @return the decoded min value
+   */
+  public float getMin(int dimension) {
+    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
+      throw new IllegalArgumentException("dimension request (" + dimension +
+          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
+    }
+    return decodeMin(((BytesRef)fieldsData).bytes, dimension);
+  }
+
+  /**
+   * Get the max value for the given dimension
+   * @param dimension the dimension, always positive
+   * @return the decoded max value
+   */
+  public float getMax(int dimension) {
+    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
+      throw new IllegalArgumentException("dimension request (" + dimension +
+          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
+    }
+    return decodeMax(((BytesRef)fieldsData).bytes, dimension);
+  }
+
+  /** decodes the min value (for the defined dimension) from the encoded input byte array */
+  static float decodeMin(byte[] b, int dimension) {
+    int offset = dimension*BYTES;
+    return NumericUtils.sortableIntToFloat(NumericUtils.sortableBytesToInt(b, offset));
+  }
+
+  /** decodes the max value (for the defined dimension) from the encoded input byte array */
+  static float decodeMax(byte[] b, int dimension) {
+    int offset = b.length/2 + dimension*BYTES;
+    return NumericUtils.sortableIntToFloat(NumericUtils.sortableBytesToInt(b, offset));
+  }
+
+  /**
+   * Create a query for matching indexed ranges that intersect the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
+   * @param max array of max values. (accepts {@code Float.MAX_VALUE})
+   * @return query for matching intersecting ranges (overlap, within, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newIntersectsQuery(String field, final float[] min, final float[] max) {
+    return newRelationQuery(field, min, max, QueryType.INTERSECTS);
+  }
+
+  /**
+   * Create a query for matching indexed float ranges that contain the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
+   * @param max array of max values. (accepts {@code Float.POSITIVE_INFINITY})
+   * @return query for matching ranges that contain the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newContainsQuery(String field, final float[] min, final float[] max) {
+    return newRelationQuery(field, min, max, QueryType.CONTAINS);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that are within the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
+   * @param max array of max values. (accepts {@code Float.POSITIVE_INFINITY})
+   * @return query for matching ranges within the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newWithinQuery(String field, final float[] min, final float[] max) {
+    return newRelationQuery(field, min, max, QueryType.WITHIN);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that cross the defined range.
+   * A CROSSES is defined as any set of ranges that are not disjoint and not wholly contained by
+   * the query. Effectively, its the complement of union(WITHIN, DISJOINT).
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
+   * @param max array of max values. (accepts {@code Float.POSITIVE_INFINITY})
+   * @return query for matching ranges within the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newCrossesQuery(String field, final float[] min, final float[] max) {
+    return newRelationQuery(field, min, max, QueryType.CROSSES);
+  }
+
+  /** helper method for creating the desired relational query */
+  private static Query newRelationQuery(String field, final float[] min, final float[] max, QueryType relation) {
+    checkArgs(min, max);
+    return new RangeFieldQuery(field, encode(min, max), min.length, relation) {
+      @Override
+      protected String toString(byte[] ranges, int dimension) {
+        return FloatRange.toString(ranges, dimension);
+      }
+    };
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(getClass().getSimpleName());
+    sb.append(" <");
+    sb.append(name);
+    sb.append(':');
+    byte[] b = ((BytesRef)fieldsData).bytes;
+    toString(b, 0);
+    for (int d=1; d<type.pointDimensionCount(); ++d) {
+      sb.append(' ');
+      toString(b, d);
+    }
+    sb.append('>');
+
+    return sb.toString();
+  }
+
+  /**
+   * Returns the String representation for the range at the given dimension
+   * @param ranges the encoded ranges, never null
+   * @param dimension the dimension of interest
+   * @return The string representation for the range at the provided dimension
+   */
+  private static String toString(byte[] ranges, int dimension) {
+    return "[" + Float.toString(decodeMin(ranges, dimension)) + " : "
+        + Float.toString(decodeMax(ranges, dimension)) + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/java/org/apache/lucene/document/IntRange.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/IntRange.java b/lucene/core/src/java/org/apache/lucene/document/IntRange.java
new file mode 100644
index 0000000..2618f14
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/document/IntRange.java
@@ -0,0 +1,276 @@
+/*
+ * 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 org.apache.lucene.document.RangeFieldQuery.QueryType;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+
+/**
+ * An indexed Integer Range field.
+ * <p>
+ * This field indexes dimensional ranges defined as min/max pairs. It supports
+ * up to a maximum of 4 dimensions (indexed as 8 numeric values). With 1 dimension representing a single integer range,
+ * 2 dimensions representing a bounding box, 3 dimensions a bounding cube, and 4 dimensions a tesseract.
+ * <p>
+ * Multiple values for the same field in one document is supported, and open ended ranges can be defined using
+ * {@code Integer.MIN_VALUE} and {@code Integer.MAX_VALUE}.
+ *
+ * <p>
+ * This field defines the following static factory methods for common search operations over integer ranges:
+ * <ul>
+ *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ranges that intersect the defined search range.
+ *   <li>{@link #newWithinQuery newWithinQuery()} matches ranges that are within the defined search range.
+ *   <li>{@link #newContainsQuery newContainsQuery()} matches ranges that contain the defined search range.
+ * </ul>
+ */
+public class IntRange extends Field {
+  /** stores integer values so number of bytes is 4 */
+  public static final int BYTES = Integer.BYTES;
+
+  /**
+   * Create a new IntRange type, from min/max parallel arrays
+   *
+   * @param name field name. must not be null.
+   * @param min range min values; each entry is the min value for the dimension
+   * @param max range max values; each entry is the max value for the dimension
+   */
+  public IntRange(String name, final int[] min, final int[] max) {
+    super(name, getType(min.length));
+    setRangeValues(min, max);
+  }
+
+  /** set the field type */
+  private static FieldType getType(int dimensions) {
+    if (dimensions > 4) {
+      throw new IllegalArgumentException("IntRange does not support greater than 4 dimensions");
+    }
+
+    FieldType ft = new FieldType();
+    // dimensions is set as 2*dimension size (min/max per dimension)
+    ft.setDimensions(dimensions*2, BYTES);
+    ft.freeze();
+    return ft;
+  }
+
+  /**
+   * Changes the values of the field.
+   * @param min array of min values. (accepts {@code Integer.NEGATIVE_INFINITY})
+   * @param max array of max values. (accepts {@code Integer.POSITIVE_INFINITY})
+   * @throws IllegalArgumentException if {@code min} or {@code max} is invalid
+   */
+  public void setRangeValues(int[] min, int[] max) {
+    checkArgs(min, max);
+    if (min.length*2 != type.pointDimensionCount() || max.length*2 != type.pointDimensionCount()) {
+      throw new IllegalArgumentException("field (name=" + name + ") uses " + type.pointDimensionCount()/2
+          + " dimensions; cannot change to (incoming) " + min.length + " dimensions");
+    }
+
+    final byte[] bytes;
+    if (fieldsData == null) {
+      bytes = new byte[BYTES*2*min.length];
+      fieldsData = new BytesRef(bytes);
+    } else {
+      bytes = ((BytesRef)fieldsData).bytes;
+    }
+    verifyAndEncode(min, max, bytes);
+  }
+
+  /** validate the arguments */
+  private static void checkArgs(final int[] min, final int[] max) {
+    if (min == null || max == null || min.length == 0 || max.length == 0) {
+      throw new IllegalArgumentException("min/max range values cannot be null or empty");
+    }
+    if (min.length != max.length) {
+      throw new IllegalArgumentException("min/max ranges must agree");
+    }
+    if (min.length > 4) {
+      throw new IllegalArgumentException("IntRange does not support greater than 4 dimensions");
+    }
+  }
+
+  /**
+   * Encodes the min, max ranges into a byte array
+   */
+  private static byte[] encode(int[] min, int[] max) {
+    checkArgs(min, max);
+    byte[] b = new byte[BYTES*2*min.length];
+    verifyAndEncode(min, max, b);
+    return b;
+  }
+
+  /**
+   * encode the ranges into a sortable byte array ({@code Double.NaN} not allowed)
+   * <p>
+   * example for 4 dimensions (8 bytes per dimension value):
+   * minD1 ... minD4 | maxD1 ... maxD4
+   */
+  static void verifyAndEncode(int[] min, int[] max, byte[] bytes) {
+    for (int d=0,i=0,j=min.length*BYTES; d<min.length; ++d, i+=BYTES, j+=BYTES) {
+      if (Double.isNaN(min[d])) {
+        throw new IllegalArgumentException("invalid min value (" + Double.NaN + ")" + " in IntRange");
+      }
+      if (Double.isNaN(max[d])) {
+        throw new IllegalArgumentException("invalid max value (" + Double.NaN + ")" + " in IntRange");
+      }
+      if (min[d] > max[d]) {
+        throw new IllegalArgumentException("min value (" + min[d] + ") is greater than max value (" + max[d] + ")");
+      }
+      encode(min[d], bytes, i);
+      encode(max[d], bytes, j);
+    }
+  }
+
+  /** encode the given value into the byte array at the defined offset */
+  private static void encode(int val, byte[] bytes, int offset) {
+    NumericUtils.intToSortableBytes(val, bytes, offset);
+  }
+
+  /**
+   * Get the min value for the given dimension
+   * @param dimension the dimension, always positive
+   * @return the decoded min value
+   */
+  public int getMin(int dimension) {
+    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
+      throw new IllegalArgumentException("dimension request (" + dimension +
+          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
+    }
+    return decodeMin(((BytesRef)fieldsData).bytes, dimension);
+  }
+
+  /**
+   * Get the max value for the given dimension
+   * @param dimension the dimension, always positive
+   * @return the decoded max value
+   */
+  public int getMax(int dimension) {
+    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
+      throw new IllegalArgumentException("dimension request (" + dimension +
+          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
+    }
+    return decodeMax(((BytesRef)fieldsData).bytes, dimension);
+  }
+
+  /** decodes the min value (for the defined dimension) from the encoded input byte array */
+  static int decodeMin(byte[] b, int dimension) {
+    int offset = dimension*BYTES;
+    return NumericUtils.sortableBytesToInt(b, offset);
+  }
+
+  /** decodes the max value (for the defined dimension) from the encoded input byte array */
+  static int decodeMax(byte[] b, int dimension) {
+    int offset = b.length/2 + dimension*BYTES;
+    return NumericUtils.sortableBytesToInt(b, offset);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that intersect the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Integer.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Integer.MAX_VALUE})
+   * @return query for matching intersecting ranges (overlap, within, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newIntersectsQuery(String field, final int[] min, final int[] max) {
+    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.INTERSECTS) {
+      @Override
+      protected String toString(byte[] ranges, int dimension) {
+        return IntRange.toString(ranges, dimension);
+      }
+    };
+  }
+
+  /**
+   * Create a query for matching indexed ranges that contain the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Integer.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Integer.MAX_VALUE})
+   * @return query for matching ranges that contain the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newContainsQuery(String field, final int[] min, final int[] max) {
+    return newRelationQuery(field, min, max, QueryType.CONTAINS);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that are within the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Integer.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Integer.MAX_VALUE})
+   * @return query for matching ranges within the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newWithinQuery(String field, final int[] min, final int[] max) {
+    return newRelationQuery(field, min, max, QueryType.WITHIN);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that cross the defined range.
+   * A CROSSES is defined as any set of ranges that are not disjoint and not wholly contained by
+   * the query. Effectively, its the complement of union(WITHIN, DISJOINT).
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Integer.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Integer.MAX_VALUE})
+   * @return query for matching ranges within the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newCrossesQuery(String field, final int[] min, final int[] max) {
+    return newRelationQuery(field, min, max, QueryType.CROSSES);
+  }
+
+  /** helper method for creating the desired relational query */
+  private static Query newRelationQuery(String field, final int[] min, final int[] max, QueryType relation) {
+    checkArgs(min, max);
+    return new RangeFieldQuery(field, encode(min, max), min.length, relation) {
+      @Override
+      protected String toString(byte[] ranges, int dimension) {
+        return IntRange.toString(ranges, dimension);
+      }
+    };
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(getClass().getSimpleName());
+    sb.append(" <");
+    sb.append(name);
+    sb.append(':');
+    byte[] b = ((BytesRef)fieldsData).bytes;
+    toString(b, 0);
+    for (int d=1; d<type.pointDimensionCount(); ++d) {
+      sb.append(' ');
+      toString(b, d);
+    }
+    sb.append('>');
+
+    return sb.toString();
+  }
+
+  /**
+   * Returns the String representation for the range at the given dimension
+   * @param ranges the encoded ranges, never null
+   * @param dimension the dimension of interest
+   * @return The string representation for the range at the provided dimension
+   */
+  private static String toString(byte[] ranges, int dimension) {
+    return "[" + Integer.toString(decodeMin(ranges, dimension)) + " : "
+        + Integer.toString(decodeMax(ranges, dimension)) + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/java/org/apache/lucene/document/LongRange.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/LongRange.java b/lucene/core/src/java/org/apache/lucene/document/LongRange.java
new file mode 100644
index 0000000..009f4a1
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/document/LongRange.java
@@ -0,0 +1,269 @@
+/*
+ * 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 org.apache.lucene.document.RangeFieldQuery.QueryType;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+
+/**
+ * An indexed Long Range field.
+ * <p>
+ * This field indexes dimensional ranges defined as min/max pairs. It supports
+ * up to a maximum of 4 dimensions (indexed as 8 numeric values). With 1 dimension representing a single long range,
+ * 2 dimensions representing a bounding box, 3 dimensions a bounding cube, and 4 dimensions a tesseract.
+ * <p>
+ * Multiple values for the same field in one document is supported, and open ended ranges can be defined using
+ * {@code Long.MIN_VALUE} and {@code Long.MAX_VALUE}.
+ *
+ * <p>
+ * This field defines the following static factory methods for common search operations over long ranges:
+ * <ul>
+ *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ranges that intersect the defined search range.
+ *   <li>{@link #newWithinQuery newWithinQuery()} matches ranges that are within the defined search range.
+ *   <li>{@link #newContainsQuery newContainsQuery()} matches ranges that contain the defined search range.
+ * </ul>
+ */
+public class LongRange extends Field {
+  /** stores long values so number of bytes is 8 */
+  public static final int BYTES = Long.BYTES;
+
+  /**
+   * Create a new LongRange type, from min/max parallel arrays
+   *
+   * @param name field name. must not be null.
+   * @param min range min values; each entry is the min value for the dimension
+   * @param max range max values; each entry is the max value for the dimension
+   */
+  public LongRange(String name, final long[] min, final long[] max) {
+    super(name, getType(min.length));
+    setRangeValues(min, max);
+  }
+
+  /** set the field type */
+  private static FieldType getType(int dimensions) {
+    if (dimensions > 4) {
+      throw new IllegalArgumentException("LongRange does not support greater than 4 dimensions");
+    }
+
+    FieldType ft = new FieldType();
+    // dimensions is set as 2*dimension size (min/max per dimension)
+    ft.setDimensions(dimensions*2, BYTES);
+    ft.freeze();
+    return ft;
+  }
+
+  /**
+   * Changes the values of the field.
+   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
+   * @throws IllegalArgumentException if {@code min} or {@code max} is invalid
+   */
+  public void setRangeValues(long[] min, long[] max) {
+    checkArgs(min, max);
+    if (min.length*2 != type.pointDimensionCount() || max.length*2 != type.pointDimensionCount()) {
+      throw new IllegalArgumentException("field (name=" + name + ") uses " + type.pointDimensionCount()/2
+          + " dimensions; cannot change to (incoming) " + min.length + " dimensions");
+    }
+
+    final byte[] bytes;
+    if (fieldsData == null) {
+      bytes = new byte[BYTES*2*min.length];
+      fieldsData = new BytesRef(bytes);
+    } else {
+      bytes = ((BytesRef)fieldsData).bytes;
+    }
+    verifyAndEncode(min, max, bytes);
+  }
+
+  /** validate the arguments */
+  private static void checkArgs(final long[] min, final long[] max) {
+    if (min == null || max == null || min.length == 0 || max.length == 0) {
+      throw new IllegalArgumentException("min/max range values cannot be null or empty");
+    }
+    if (min.length != max.length) {
+      throw new IllegalArgumentException("min/max ranges must agree");
+    }
+    if (min.length > 4) {
+      throw new IllegalArgumentException("LongRange does not support greater than 4 dimensions");
+    }
+  }
+
+  /** Encodes the min, max ranges into a byte array */
+  private static byte[] encode(long[] min, long[] max) {
+    checkArgs(min, max);
+    byte[] b = new byte[BYTES*2*min.length];
+    verifyAndEncode(min, max, b);
+    return b;
+  }
+
+  /**
+   * encode the ranges into a sortable byte array ({@code Double.NaN} not allowed)
+   * <p>
+   * example for 4 dimensions (8 bytes per dimension value):
+   * minD1 ... minD4 | maxD1 ... maxD4
+   */
+  static void verifyAndEncode(long[] min, long[] max, byte[] bytes) {
+    for (int d=0,i=0,j=min.length*BYTES; d<min.length; ++d, i+=BYTES, j+=BYTES) {
+      if (Double.isNaN(min[d])) {
+        throw new IllegalArgumentException("invalid min value (" + Double.NaN + ")" + " in LongRange");
+      }
+      if (Double.isNaN(max[d])) {
+        throw new IllegalArgumentException("invalid max value (" + Double.NaN + ")" + " in LongRange");
+      }
+      if (min[d] > max[d]) {
+        throw new IllegalArgumentException("min value (" + min[d] + ") is greater than max value (" + max[d] + ")");
+      }
+      encode(min[d], bytes, i);
+      encode(max[d], bytes, j);
+    }
+  }
+
+  /** encode the given value into the byte array at the defined offset */
+  private static void encode(long val, byte[] bytes, int offset) {
+    NumericUtils.longToSortableBytes(val, bytes, offset);
+  }
+
+  /**
+   * Get the min value for the given dimension
+   * @param dimension the dimension, always positive
+   * @return the decoded min value
+   */
+  public long getMin(int dimension) {
+    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
+      throw new IllegalArgumentException("dimension request (" + dimension +
+          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
+    }
+    return decodeMin(((BytesRef)fieldsData).bytes, dimension);
+  }
+
+  /**
+   * Get the max value for the given dimension
+   * @param dimension the dimension, always positive
+   * @return the decoded max value
+   */
+  public long getMax(int dimension) {
+    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
+      throw new IllegalArgumentException("dimension request (" + dimension +
+          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
+    }
+    return decodeMax(((BytesRef)fieldsData).bytes, dimension);
+  }
+
+  /** decodes the min value (for the defined dimension) from the encoded input byte array */
+  static long decodeMin(byte[] b, int dimension) {
+    int offset = dimension*BYTES;
+    return NumericUtils.sortableBytesToLong(b, offset);
+  }
+
+  /** decodes the max value (for the defined dimension) from the encoded input byte array */
+  static long decodeMax(byte[] b, int dimension) {
+    int offset = b.length/2 + dimension*BYTES;
+    return NumericUtils.sortableBytesToLong(b, offset);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that intersect the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
+   * @return query for matching intersecting ranges (overlap, within, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newIntersectsQuery(String field, final long[] min, final long[] max) {
+    return newRelationQuery(field, min, max, QueryType.INTERSECTS);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that contain the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
+   * @return query for matching ranges that contain the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newContainsQuery(String field, final long[] min, final long[] max) {
+    return newRelationQuery(field, min, max, QueryType.CONTAINS);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that are within the defined range.
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
+   * @return query for matching ranges within the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newWithinQuery(String field, final long[] min, final long[] max) {
+    return newRelationQuery(field, min, max, QueryType.WITHIN);
+  }
+
+  /**
+   * Create a query for matching indexed ranges that cross the defined range.
+   * A CROSSES is defined as any set of ranges that are not disjoint and not wholly contained by
+   * the query. Effectively, its the complement of union(WITHIN, DISJOINT).
+   * @param field field name. must not be null.
+   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
+   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
+   * @return query for matching ranges within the defined range
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newCrossesQuery(String field, final long[] min, final long[] max) {
+    return newRelationQuery(field, min, max, QueryType.CROSSES);
+  }
+
+  /** helper method for creating the desired relational query */
+  private static Query newRelationQuery(String field, final long[] min, final long[] max, QueryType relation) {
+    checkArgs(min, max);
+    return new RangeFieldQuery(field, encode(min, max), min.length, relation) {
+      @Override
+      protected String toString(byte[] ranges, int dimension) {
+        return LongRange.toString(ranges, dimension);
+      }
+    };
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(getClass().getSimpleName());
+    sb.append(" <");
+    sb.append(name);
+    sb.append(':');
+    byte[] b = ((BytesRef)fieldsData).bytes;
+    toString(b, 0);
+    for (int d=1; d<type.pointDimensionCount(); ++d) {
+      sb.append(' ');
+      toString(b, d);
+    }
+    sb.append('>');
+
+    return sb.toString();
+  }
+
+  /**
+   * Returns the String representation for the range at the given dimension
+   * @param ranges the encoded ranges, never null
+   * @param dimension the dimension of interest
+   * @return The string representation for the range at the provided dimension
+   */
+  private static String toString(byte[] ranges, int dimension) {
+    return "[" + Long.toString(decodeMin(ranges, dimension)) + " : "
+        + Long.toString(decodeMax(ranges, dimension)) + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java b/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java
new file mode 100644
index 0000000..10f10fa
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java
@@ -0,0 +1,340 @@
+/*
+ * 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.Arrays;
+import java.util.Objects;
+import java.util.function.IntPredicate;
+import java.util.function.Predicate;
+
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.PointValues;
+import org.apache.lucene.index.PointValues.Relation;
+import org.apache.lucene.index.PointValues.IntersectVisitor;
+import org.apache.lucene.search.ConstantScoreScorer;
+import org.apache.lucene.search.ConstantScoreWeight;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.DocIdSetBuilder;
+import org.apache.lucene.util.StringHelper;
+
+/**
+ * Query class for searching {@code RangeField} types by a defined {@link Relation}.
+ */
+abstract class RangeFieldQuery extends Query {
+  /** field name */
+  final String field;
+  /** query relation
+   * intersects: {@code CELL_CROSSES_QUERY},
+   * contains: {@code CELL_CONTAINS_QUERY},
+   * within: {@code CELL_WITHIN_QUERY} */
+  final QueryType queryType;
+  /** number of dimensions - max 4 */
+  final int numDims;
+  /** ranges encoded as a sortable byte array */
+  final byte[] ranges;
+  /** number of bytes per dimension */
+  final int bytesPerDim;
+
+  /** Used by {@code RangeFieldQuery} to check how each internal or leaf node relates to the query. */
+  enum QueryType {
+    /** Use this for intersects queries. */
+    INTERSECTS,
+    /** Use this for within queries. */
+    WITHIN,
+    /** Use this for contains */
+    CONTAINS,
+    /** Use this for crosses queries */
+    CROSSES
+  }
+
+  /**
+   * Create a query for searching indexed ranges that match the provided relation.
+   * @param field field name. must not be null.
+   * @param ranges encoded range values; this is done by the {@code RangeField} implementation
+   * @param queryType the query relation
+   */
+  RangeFieldQuery(String field, final byte[] ranges, final int numDims, final QueryType queryType) {
+    checkArgs(field, ranges, numDims);
+    if (queryType == null) {
+      throw new IllegalArgumentException("Query type cannot be null");
+    }
+    this.field = field;
+    this.queryType = queryType;
+    this.numDims = numDims;
+    this.ranges = ranges;
+    this.bytesPerDim = ranges.length / (2*numDims);
+  }
+
+  /** check input arguments */
+  private static void checkArgs(String field, final byte[] ranges, final int numDims) {
+    if (field == null) {
+      throw new IllegalArgumentException("field must not be null");
+    }
+    if (numDims > 4) {
+      throw new IllegalArgumentException("dimension size cannot be greater than 4");
+    }
+    if (ranges == null || ranges.length == 0) {
+      throw new IllegalArgumentException("encoded ranges cannot be null or empty");
+    }
+  }
+
+  /** Check indexed field info against the provided query data. */
+  private void checkFieldInfo(FieldInfo fieldInfo) {
+    if (fieldInfo.getPointDimensionCount()/2 != numDims) {
+      throw new IllegalArgumentException("field=\"" + field + "\" was indexed with numDims="
+          + fieldInfo.getPointDimensionCount()/2 + " but this query has numDims=" + numDims);
+    }
+  }
+
+  @Override
+  public final Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
+    return new ConstantScoreWeight(this, boost) {
+      final RangeFieldComparator target = new RangeFieldComparator();
+      private DocIdSet buildMatchingDocIdSet(LeafReader reader, PointValues values) throws IOException {
+        DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc(), values, field);
+        values.intersect(
+            new IntersectVisitor() {
+              DocIdSetBuilder.BulkAdder adder;
+              @Override
+              public void grow(int count) {
+                adder = result.grow(count);
+              }
+              @Override
+              public void visit(int docID) throws IOException {
+                adder.add(docID);
+              }
+              @Override
+              public void visit(int docID, byte[] leaf) throws IOException {
+                if (target.matches(leaf)) {
+                  adder.add(docID);
+                }
+              }
+              @Override
+              public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
+                byte[] node = getInternalRange(minPackedValue, maxPackedValue);
+                // compute range relation for BKD traversal
+                if (target.intersects(node) == false) {
+                  return Relation.CELL_OUTSIDE_QUERY;
+                } else if (target.within(node)) {
+                  // target within cell; continue traversing:
+                  return Relation.CELL_CROSSES_QUERY;
+                } else if (target.contains(node)) {
+                  // target contains cell; add iff queryType is not a CONTAINS or CROSSES query:
+                  return (queryType == QueryType.CONTAINS || queryType == QueryType.CROSSES) ?
+                      Relation.CELL_OUTSIDE_QUERY : Relation.CELL_INSIDE_QUERY;
+                }
+                // target intersects cell; continue traversing:
+                return Relation.CELL_CROSSES_QUERY;
+              }
+            });
+        return result.build();
+      }
+
+      @Override
+      public Scorer scorer(LeafReaderContext context) throws IOException {
+        LeafReader reader = context.reader();
+        PointValues values = reader.getPointValues(field);
+        if (values == null) {
+          // no docs in this segment indexed any ranges
+          return null;
+        }
+        FieldInfo fieldInfo = reader.getFieldInfos().fieldInfo(field);
+        if (fieldInfo == null) {
+          // no docs in this segment indexed this field
+          return null;
+        }
+        checkFieldInfo(fieldInfo);
+        boolean allDocsMatch = true;
+        if (values.getDocCount() == reader.maxDoc()) {
+          // if query crosses, docs need to be further scrutinized
+          byte[] range = getInternalRange(values.getMinPackedValue(), values.getMaxPackedValue());
+          // if the internal node is not equal and not contained by the query, all docs do not match
+          if (queryType == QueryType.CROSSES || (!Arrays.equals(ranges, range)
+              && (target.contains(range) == false || queryType != QueryType.WITHIN))) {
+            allDocsMatch = false;
+          }
+        } else {
+          allDocsMatch = false;
+        }
+
+        DocIdSetIterator iterator = allDocsMatch == true ?
+            DocIdSetIterator.all(reader.maxDoc()) : buildMatchingDocIdSet(reader, values).iterator();
+        return new ConstantScoreScorer(this, score(), iterator);
+      }
+
+      /** get an encoded byte representation of the internal node; this is
+       *  the lower half of the min array and the upper half of the max array */
+      private byte[] getInternalRange(byte[] min, byte[] max) {
+        byte[] range = new byte[min.length];
+        final int dimSize = numDims * bytesPerDim;
+        System.arraycopy(min, 0, range, 0, dimSize);
+        System.arraycopy(max, dimSize, range, dimSize, dimSize);
+        return range;
+      }
+    };
+  }
+
+  /**
+   * RangeFieldComparator class provides the core comparison logic for accepting or rejecting indexed
+   * {@code RangeField} types based on the defined query range and relation.
+   */
+  class RangeFieldComparator {
+    final Predicate<byte[]> predicate;
+
+    /** constructs the comparator based on the query type */
+    RangeFieldComparator() {
+      switch (queryType) {
+        case INTERSECTS:
+          predicate = this::intersects;
+          break;
+        case WITHIN:
+          predicate = this::contains;
+          break;
+        case CONTAINS:
+          predicate = this::within;
+          break;
+        case CROSSES:
+          // crosses first checks intersection (disjoint automatic fails),
+          // then ensures the query doesn't wholly contain the leaf:
+          predicate = (byte[] leaf) -> this.intersects(leaf)
+              && this.contains(leaf) == false;
+          break;
+        default:
+          throw new IllegalArgumentException("invalid queryType [" + queryType + "] found.");
+      }
+    }
+
+    /** determines if the candidate range matches the query request */
+    private boolean matches(final byte[] candidate) {
+      return (Arrays.equals(ranges, candidate) && queryType != QueryType.CROSSES)
+          || predicate.test(candidate);
+    }
+
+    /** check if query intersects candidate range */
+    private boolean intersects(final byte[] candidate) {
+      return relate((int d) -> compareMinMax(candidate, d) > 0 || compareMaxMin(candidate, d) < 0);
+    }
+
+    /** check if query is within candidate range */
+    private boolean within(final byte[] candidate) {
+      return relate((int d) -> compareMinMin(candidate, d) < 0 || compareMaxMax(candidate, d) > 0);
+    }
+
+    /** check if query contains candidate range */
+    private boolean contains(final byte[] candidate) {
+      return relate((int d) -> compareMinMin(candidate, d) > 0 || compareMaxMax(candidate, d) < 0);
+    }
+
+    /** internal method used by each relation method to test range relation logic */
+    private boolean relate(IntPredicate predicate) {
+      for (int d=0; d<numDims; ++d) {
+        if (predicate.test(d)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /** compare the encoded min value (for the defined query dimension) with the encoded min value in the byte array */
+    private int compareMinMin(byte[] b, int dimension) {
+      // convert dimension to offset:
+      dimension *= bytesPerDim;
+      return StringHelper.compare(bytesPerDim, ranges, dimension, b, dimension);
+    }
+
+    /** compare the encoded min value (for the defined query dimension) with the encoded max value in the byte array */
+    private int compareMinMax(byte[] b, int dimension) {
+      // convert dimension to offset:
+      dimension *= bytesPerDim;
+      return StringHelper.compare(bytesPerDim, ranges, dimension, b, numDims * bytesPerDim + dimension);
+    }
+
+    /** compare the encoded max value (for the defined query dimension) with the encoded min value in the byte array */
+    private int compareMaxMin(byte[] b, int dimension) {
+      // convert dimension to offset:
+      dimension *= bytesPerDim;
+      return StringHelper.compare(bytesPerDim, ranges, numDims * bytesPerDim + dimension, b, dimension);
+    }
+
+    /** compare the encoded max value (for the defined query dimension) with the encoded max value in the byte array */
+    private int compareMaxMax(byte[] b, int dimension) {
+      // convert dimension to max offset:
+      dimension = numDims * bytesPerDim + dimension * bytesPerDim;
+      return StringHelper.compare(bytesPerDim, ranges, dimension, b, dimension);
+    }
+  }
+
+  @Override
+  public int hashCode() {
+    int hash = classHash();
+    hash = 31 * hash + field.hashCode();
+    hash = 31 * hash + numDims;
+    hash = 31 * hash + queryType.hashCode();
+    hash = 31 * hash + Arrays.hashCode(ranges);
+
+    return hash;
+  }
+
+  @Override
+  public final boolean equals(Object o) {
+    return sameClassAs(o) &&
+        equalsTo(getClass().cast(o));
+  }
+
+  protected boolean equalsTo(RangeFieldQuery other) {
+    return Objects.equals(field, other.field) &&
+        numDims == other.numDims &&
+        Arrays.equals(ranges, other.ranges) &&
+        other.queryType == queryType;
+  }
+
+  @Override
+  public String toString(String field) {
+    StringBuilder sb = new StringBuilder();
+    if (this.field.equals(field) == false) {
+      sb.append(this.field);
+      sb.append(':');
+    }
+    sb.append("<ranges:");
+    sb.append(toString(ranges, 0));
+    for (int d=1; d<numDims; ++d) {
+      sb.append(' ');
+      sb.append(toString(ranges, d));
+    }
+    sb.append('>');
+
+    return sb.toString();
+  }
+
+  /**
+   * Returns a string of a single value in a human-readable format for debugging.
+   * This is used by {@link #toString()}.
+   *
+   * @param dimension dimension of the particular value
+   * @param ranges encoded ranges, never null
+   * @return human readable value for debugging
+   */
+  protected abstract String toString(byte[] ranges, int dimension);
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/java/org/apache/lucene/index/PointValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/PointValues.java b/lucene/core/src/java/org/apache/lucene/index/PointValues.java
index dab9140..186dbd5 100644
--- a/lucene/core/src/java/org/apache/lucene/index/PointValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/PointValues.java
@@ -46,7 +46,7 @@ import org.apache.lucene.util.bkd.BKDWriter;
  *   <tr><td>{@code double}</td><td>{@link DoublePoint}</td></tr>
  *   <tr><td>{@code byte[]}</td><td>{@link BinaryPoint}</td></tr>
  *   <tr><td>{@link BigInteger}</td><td><a href="{@docRoot}/../sandbox/org/apache/lucene/document/BigIntegerPoint.html">BigIntegerPoint</a>*</td></tr>
- *   <tr><td>{@link InetAddress}</td><td><a href="{@docRoot}/../sandbox/org/apache/lucene/document/InetAddressPoint.html">InetAddressPoint</a>*</td></tr>
+ *   <tr><td>{@link InetAddress}</td><td><a href="{@docRoot}/../misc/org/apache/lucene/document/InetAddressPoint.html">InetAddressPoint</a>*</td></tr>
  * </table>
  * * in the <i>lucene-sandbox</i> jar<br>
  * <p>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java
new file mode 100644
index 0000000..49ca710
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java
@@ -0,0 +1,251 @@
+/*
+ * 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.util.Arrays;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.DoubleRange;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+
+/**
+ * Random testing for RangeFieldQueries.
+ */
+public class TestDoubleRangeFieldQueries extends BaseRangeFieldQueryTestCase {
+  private static final String FIELD_NAME = "doubleRangeField";
+
+  private double nextDoubleInternal() {
+    if (rarely()) {
+      return random().nextBoolean() ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
+    }
+    double max = Double.MAX_VALUE / 2;
+    return (max + max) * random().nextDouble() - max;
+  }
+
+  @Override
+  protected Range nextRange(int dimensions) throws Exception {
+    double[] min = new double[dimensions];
+    double[] max = new double[dimensions];
+
+    double minV, maxV;
+    for (int d=0; d<dimensions; ++d) {
+      minV = nextDoubleInternal();
+      maxV = nextDoubleInternal();
+      min[d] = Math.min(minV, maxV);
+      max[d] = Math.max(minV, maxV);
+    }
+    return new DoubleTestRange(min, max);
+  }
+
+  @Override
+  protected DoubleRange newRangeField(Range r) {
+    return new DoubleRange(FIELD_NAME, ((DoubleTestRange)r).min, ((DoubleTestRange)r).max);
+  }
+
+  @Override
+  protected Query newIntersectsQuery(Range r) {
+    return DoubleRange.newIntersectsQuery(FIELD_NAME, ((DoubleTestRange)r).min, ((DoubleTestRange)r).max);
+  }
+
+  @Override
+  protected Query newContainsQuery(Range r) {
+    return DoubleRange.newContainsQuery(FIELD_NAME, ((DoubleTestRange)r).min, ((DoubleTestRange)r).max);
+  }
+
+  @Override
+  protected Query newWithinQuery(Range r) {
+    return DoubleRange.newWithinQuery(FIELD_NAME, ((DoubleTestRange)r).min, ((DoubleTestRange)r).max);
+  }
+
+  @Override
+  protected Query newCrossesQuery(Range r) {
+    return DoubleRange.newCrossesQuery(FIELD_NAME, ((DoubleTestRange)r).min, ((DoubleTestRange)r).max);
+  }
+
+  /** Basic test */
+  public void testBasics() throws Exception {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+
+    // intersects (within)
+    Document document = new Document();
+    document.add(new DoubleRange(FIELD_NAME, new double[] {-10.0, -10.0}, new double[] {9.1, 10.1}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new DoubleRange(FIELD_NAME, new double[] {10.0, -10.0}, new double[] {20.0, 10.0}));
+    writer.addDocument(document);
+
+    // intersects (contains, crosses)
+    document = new Document();
+    document.add(new DoubleRange(FIELD_NAME, new double[] {-20.0, -20.0}, new double[] {30.0, 30.1}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new DoubleRange(FIELD_NAME, new double[] {-11.1, -11.2}, new double[] {1.23, 11.5}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new DoubleRange(FIELD_NAME, new double[] {12.33, 1.2}, new double[] {15.1, 29.9}));
+    writer.addDocument(document);
+
+    // disjoint
+    document = new Document();
+    document.add(new DoubleRange(FIELD_NAME, new double[] {-122.33, 1.2}, new double[] {-115.1, 29.9}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new DoubleRange(FIELD_NAME, new double[] {Double.NEGATIVE_INFINITY, 1.2}, new double[] {-11.0, 29.9}));
+    writer.addDocument(document);
+
+    // equal (within, contains, intersects)
+    document = new Document();
+    document.add(new DoubleRange(FIELD_NAME, new double[] {-11, -15}, new double[] {15, 20}));
+    writer.addDocument(document);
+
+    // search
+    IndexReader reader = writer.getReader();
+    IndexSearcher searcher = newSearcher(reader);
+    assertEquals(7, searcher.count(DoubleRange.newIntersectsQuery(FIELD_NAME,
+        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
+    assertEquals(2, searcher.count(DoubleRange.newWithinQuery(FIELD_NAME,
+        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
+    assertEquals(2, searcher.count(DoubleRange.newContainsQuery(FIELD_NAME,
+        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
+    assertEquals(5, searcher.count(DoubleRange.newCrossesQuery(FIELD_NAME,
+        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
+
+    reader.close();
+    writer.close();
+    dir.close();
+  }
+
+  /** DoubleRange test class implementation - use to validate DoubleRange */
+  private class DoubleTestRange extends Range {
+    double[] min;
+    double[] max;
+
+    DoubleTestRange(double[] min, double[] max) {
+      assert min != null && max != null && min.length > 0 && max.length > 0
+          : "test box: min/max cannot be null or empty";
+      assert min.length == max.length : "test box: min/max length do not agree";
+      this.min = min;
+      this.max = max;
+    }
+
+    @Override
+    protected int numDimensions() {
+      return min.length;
+    }
+
+    @Override
+    protected Double getMin(int dim) {
+      return min[dim];
+    }
+
+    @Override
+    protected void setMin(int dim, Object val) {
+      double v = (Double)val;
+      if (min[dim] < v) {
+        max[dim] = v;
+      } else {
+        min[dim] = v;
+      }
+    }
+
+    @Override
+    protected Double getMax(int dim) {
+      return max[dim];
+    }
+
+    @Override
+    protected void setMax(int dim, Object val) {
+      double v = (Double)val;
+      if (max[dim] > v) {
+        min[dim] = v;
+      } else {
+        max[dim] = v;
+      }
+    }
+
+    @Override
+    protected boolean isEqual(Range other) {
+      DoubleTestRange o = (DoubleTestRange)other;
+      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
+    }
+
+    @Override
+    protected boolean isDisjoint(Range o) {
+      DoubleTestRange other = (DoubleTestRange)o;
+      for (int d=0; d<this.min.length; ++d) {
+        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
+          // disjoint:
+          return true;
+        }
+      }
+      return false;
+    }
+
+    @Override
+    protected boolean isWithin(Range o) {
+      DoubleTestRange other = (DoubleTestRange)o;
+      for (int d=0; d<this.min.length; ++d) {
+        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
+          // not within:
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    protected boolean contains(Range o) {
+      DoubleTestRange other = (DoubleTestRange) o;
+      for (int d=0; d<this.min.length; ++d) {
+        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
+          // not contains:
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder b = new StringBuilder();
+      b.append("Box(");
+      b.append(min[0]);
+      b.append(" TO ");
+      b.append(max[0]);
+      for (int d=1; d<min.length; ++d) {
+        b.append(", ");
+        b.append(min[d]);
+        b.append(" TO ");
+        b.append(max[d]);
+      }
+      b.append(")");
+
+      return b.toString();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java
new file mode 100644
index 0000000..6dc5907
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java
@@ -0,0 +1,251 @@
+/*
+ * 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.util.Arrays;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FloatRange;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+
+/**
+ * Random testing for FloatRange Queries.
+ */
+public class TestFloatRangeFieldQueries extends BaseRangeFieldQueryTestCase {
+  private static final String FIELD_NAME = "floatRangeField";
+
+  private float nextFloatInternal() {
+    if (rarely()) {
+      return random().nextBoolean() ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
+    }
+    float max = Float.MAX_VALUE / 2;
+    return (max + max) * random().nextFloat() - max;
+  }
+
+  @Override
+  protected Range nextRange(int dimensions) throws Exception {
+    float[] min = new float[dimensions];
+    float[] max = new float[dimensions];
+
+    float minV, maxV;
+    for (int d=0; d<dimensions; ++d) {
+      minV = nextFloatInternal();
+      maxV = nextFloatInternal();
+      min[d] = Math.min(minV, maxV);
+      max[d] = Math.max(minV, maxV);
+    }
+    return new FloatTestRange(min, max);
+  }
+
+  @Override
+  protected FloatRange newRangeField(Range r) {
+    return new FloatRange(FIELD_NAME, ((FloatTestRange)r).min, ((FloatTestRange)r).max);
+  }
+
+  @Override
+  protected Query newIntersectsQuery(Range r) {
+    return FloatRange.newIntersectsQuery(FIELD_NAME, ((FloatTestRange)r).min, ((FloatTestRange)r).max);
+  }
+
+  @Override
+  protected Query newContainsQuery(Range r) {
+    return FloatRange.newContainsQuery(FIELD_NAME, ((FloatTestRange)r).min, ((FloatTestRange)r).max);
+  }
+
+  @Override
+  protected Query newWithinQuery(Range r) {
+    return FloatRange.newWithinQuery(FIELD_NAME, ((FloatTestRange)r).min, ((FloatTestRange)r).max);
+  }
+
+  @Override
+  protected Query newCrossesQuery(Range r) {
+    return FloatRange.newCrossesQuery(FIELD_NAME, ((FloatTestRange)r).min, ((FloatTestRange)r).max);
+  }
+
+  /** Basic test */
+  public void testBasics() throws Exception {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+
+    // intersects (within)
+    Document document = new Document();
+    document.add(new FloatRange(FIELD_NAME, new float[] {-10.0f, -10.0f}, new float[] {9.1f, 10.1f}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new FloatRange(FIELD_NAME, new float[] {10.0f, -10.0f}, new float[] {20.0f, 10.0f}));
+    writer.addDocument(document);
+
+    // intersects (contains, crosses)
+    document = new Document();
+    document.add(new FloatRange(FIELD_NAME, new float[] {-20.0f, -20.0f}, new float[] {30.0f, 30.1f}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new FloatRange(FIELD_NAME, new float[] {-11.1f, -11.2f}, new float[] {1.23f, 11.5f}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new FloatRange(FIELD_NAME, new float[] {12.33f, 1.2f}, new float[] {15.1f, 29.9f}));
+    writer.addDocument(document);
+
+    // disjoint
+    document = new Document();
+    document.add(new FloatRange(FIELD_NAME, new float[] {-122.33f, 1.2f}, new float[] {-115.1f, 29.9f}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new FloatRange(FIELD_NAME, new float[] {Float.NEGATIVE_INFINITY, 1.2f}, new float[] {-11.0f, 29.9f}));
+    writer.addDocument(document);
+
+    // equal (within, contains, intersects)
+    document = new Document();
+    document.add(new FloatRange(FIELD_NAME, new float[] {-11f, -15f}, new float[] {15f, 20f}));
+    writer.addDocument(document);
+
+    // search
+    IndexReader reader = writer.getReader();
+    IndexSearcher searcher = newSearcher(reader);
+    assertEquals(7, searcher.count(FloatRange.newIntersectsQuery(FIELD_NAME,
+        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
+    assertEquals(2, searcher.count(FloatRange.newWithinQuery(FIELD_NAME,
+        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
+    assertEquals(2, searcher.count(FloatRange.newContainsQuery(FIELD_NAME,
+        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
+    assertEquals(5, searcher.count(FloatRange.newCrossesQuery(FIELD_NAME,
+        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
+
+    reader.close();
+    writer.close();
+    dir.close();
+  }
+
+  /** FloatRange test class implementation - use to validate FloatRange */
+  private class FloatTestRange extends Range {
+    float[] min;
+    float[] max;
+
+    FloatTestRange(float[] min, float[] max) {
+      assert min != null && max != null && min.length > 0 && max.length > 0
+          : "test box: min/max cannot be null or empty";
+      assert min.length == max.length : "test box: min/max length do not agree";
+      this.min = min;
+      this.max = max;
+    }
+
+    @Override
+    protected int numDimensions() {
+      return min.length;
+    }
+
+    @Override
+    protected Float getMin(int dim) {
+      return min[dim];
+    }
+
+    @Override
+    protected void setMin(int dim, Object val) {
+      float v = (Float)val;
+      if (min[dim] < v) {
+        max[dim] = v;
+      } else {
+        min[dim] = v;
+      }
+    }
+
+    @Override
+    protected Float getMax(int dim) {
+      return max[dim];
+    }
+
+    @Override
+    protected void setMax(int dim, Object val) {
+      float v = (Float)val;
+      if (max[dim] > v) {
+        min[dim] = v;
+      } else {
+        max[dim] = v;
+      }
+    }
+
+    @Override
+    protected boolean isEqual(Range other) {
+      FloatTestRange o = (FloatTestRange)other;
+      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
+    }
+
+    @Override
+    protected boolean isDisjoint(Range o) {
+      FloatTestRange other = (FloatTestRange)o;
+      for (int d=0; d<this.min.length; ++d) {
+        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
+          // disjoint:
+          return true;
+        }
+      }
+      return false;
+    }
+
+    @Override
+    protected boolean isWithin(Range o) {
+      FloatTestRange other = (FloatTestRange)o;
+      for (int d=0; d<this.min.length; ++d) {
+        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
+          // not within:
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    protected boolean contains(Range o) {
+      FloatTestRange other = (FloatTestRange) o;
+      for (int d=0; d<this.min.length; ++d) {
+        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
+          // not contains:
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder b = new StringBuilder();
+      b.append("Box(");
+      b.append(min[0]);
+      b.append(" TO ");
+      b.append(max[0]);
+      for (int d=1; d<min.length; ++d) {
+        b.append(", ");
+        b.append(min[d]);
+        b.append(" TO ");
+        b.append(max[d]);
+      }
+      b.append(")");
+
+      return b.toString();
+    }
+  }
+}


[10/23] lucene-solr:jira/solr-9835: LUCENE-7740: Refactor Range Fields to remove Field suffix (e.g., DoubleRange), move InetAddressRange and InetAddressPoint from sandbox to misc module, and refactor all other range fields from sandbox to core.

Posted by da...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java
deleted file mode 100644
index 43630e3..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestDoubleRangeFieldQueries.java
+++ /dev/null
@@ -1,251 +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.util.Arrays;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.DoubleRangeField;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.store.Directory;
-
-/**
- * Random testing for RangeFieldQueries.
- */
-public class TestDoubleRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "doubleRangeField";
-
-  private double nextDoubleInternal() {
-    if (rarely()) {
-      return random().nextBoolean() ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
-    }
-    double max = Double.MAX_VALUE / 2;
-    return (max + max) * random().nextDouble() - max;
-  }
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    double[] min = new double[dimensions];
-    double[] max = new double[dimensions];
-
-    double minV, maxV;
-    for (int d=0; d<dimensions; ++d) {
-      minV = nextDoubleInternal();
-      maxV = nextDoubleInternal();
-      min[d] = Math.min(minV, maxV);
-      max[d] = Math.max(minV, maxV);
-    }
-    return new DoubleRange(min, max);
-  }
-
-  @Override
-  protected DoubleRangeField newRangeField(Range r) {
-    return new DoubleRangeField(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return DoubleRangeField.newIntersectsQuery(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return DoubleRangeField.newContainsQuery(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return DoubleRangeField.newWithinQuery(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return DoubleRangeField.newCrossesQuery(FIELD_NAME, ((DoubleRange)r).min, ((DoubleRange)r).max);
-  }
-
-  /** Basic test */
-  public void testBasics() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // intersects (within)
-    Document document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-10.0, -10.0}, new double[] {9.1, 10.1}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {10.0, -10.0}, new double[] {20.0, 10.0}));
-    writer.addDocument(document);
-
-    // intersects (contains, crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-20.0, -20.0}, new double[] {30.0, 30.1}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-11.1, -11.2}, new double[] {1.23, 11.5}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {12.33, 1.2}, new double[] {15.1, 29.9}));
-    writer.addDocument(document);
-
-    // disjoint
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-122.33, 1.2}, new double[] {-115.1, 29.9}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {Double.NEGATIVE_INFINITY, 1.2}, new double[] {-11.0, 29.9}));
-    writer.addDocument(document);
-
-    // equal (within, contains, intersects)
-    document = new Document();
-    document.add(new DoubleRangeField(FIELD_NAME, new double[] {-11, -15}, new double[] {15, 20}));
-    writer.addDocument(document);
-
-    // search
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(7, searcher.count(DoubleRangeField.newIntersectsQuery(FIELD_NAME,
-        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
-    assertEquals(2, searcher.count(DoubleRangeField.newWithinQuery(FIELD_NAME,
-        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
-    assertEquals(2, searcher.count(DoubleRangeField.newContainsQuery(FIELD_NAME,
-        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
-    assertEquals(5, searcher.count(DoubleRangeField.newCrossesQuery(FIELD_NAME,
-        new double[] {-11.0, -15.0}, new double[] {15.0, 20.0})));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-
-  /** DoubleRange test class implementation - use to validate DoubleRangeField */
-  private class DoubleRange extends Range {
-    double[] min;
-    double[] max;
-
-    DoubleRange(double[] min, double[] max) {
-      assert min != null && max != null && min.length > 0 && max.length > 0
-          : "test box: min/max cannot be null or empty";
-      assert min.length == max.length : "test box: min/max length do not agree";
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return min.length;
-    }
-
-    @Override
-    protected Double getMin(int dim) {
-      return min[dim];
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      double v = (Double)val;
-      if (min[dim] < v) {
-        max[dim] = v;
-      } else {
-        min[dim] = v;
-      }
-    }
-
-    @Override
-    protected Double getMax(int dim) {
-      return max[dim];
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      double v = (Double)val;
-      if (max[dim] > v) {
-        min[dim] = v;
-      } else {
-        max[dim] = v;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range other) {
-      DoubleRange o = (DoubleRange)other;
-      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      DoubleRange other = (DoubleRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
-          // disjoint:
-          return true;
-        }
-      }
-      return false;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      DoubleRange other = (DoubleRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
-          // not within:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      DoubleRange other = (DoubleRange) o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
-          // not contains:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min[0]);
-      b.append(" TO ");
-      b.append(max[0]);
-      for (int d=1; d<min.length; ++d) {
-        b.append(", ");
-        b.append(min[d]);
-        b.append(" TO ");
-        b.append(max[d]);
-      }
-      b.append(")");
-
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java
deleted file mode 100644
index 3509e35..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestFloatRangeFieldQueries.java
+++ /dev/null
@@ -1,251 +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.util.Arrays;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.FloatRangeField;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.store.Directory;
-
-/**
- * Random testing for FloatRangeField Queries.
- */
-public class TestFloatRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "floatRangeField";
-
-  private float nextFloatInternal() {
-    if (rarely()) {
-      return random().nextBoolean() ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
-    }
-    float max = Float.MAX_VALUE / 2;
-    return (max + max) * random().nextFloat() - max;
-  }
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    float[] min = new float[dimensions];
-    float[] max = new float[dimensions];
-
-    float minV, maxV;
-    for (int d=0; d<dimensions; ++d) {
-      minV = nextFloatInternal();
-      maxV = nextFloatInternal();
-      min[d] = Math.min(minV, maxV);
-      max[d] = Math.max(minV, maxV);
-    }
-    return new FloatRange(min, max);
-  }
-
-  @Override
-  protected FloatRangeField newRangeField(Range r) {
-    return new FloatRangeField(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return FloatRangeField.newIntersectsQuery(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return FloatRangeField.newContainsQuery(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return FloatRangeField.newWithinQuery(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return FloatRangeField.newCrossesQuery(FIELD_NAME, ((FloatRange)r).min, ((FloatRange)r).max);
-  }
-
-  /** Basic test */
-  public void testBasics() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // intersects (within)
-    Document document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-10.0f, -10.0f}, new float[] {9.1f, 10.1f}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {10.0f, -10.0f}, new float[] {20.0f, 10.0f}));
-    writer.addDocument(document);
-
-    // intersects (contains, crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-20.0f, -20.0f}, new float[] {30.0f, 30.1f}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-11.1f, -11.2f}, new float[] {1.23f, 11.5f}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {12.33f, 1.2f}, new float[] {15.1f, 29.9f}));
-    writer.addDocument(document);
-
-    // disjoint
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-122.33f, 1.2f}, new float[] {-115.1f, 29.9f}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {Float.NEGATIVE_INFINITY, 1.2f}, new float[] {-11.0f, 29.9f}));
-    writer.addDocument(document);
-
-    // equal (within, contains, intersects)
-    document = new Document();
-    document.add(new FloatRangeField(FIELD_NAME, new float[] {-11f, -15f}, new float[] {15f, 20f}));
-    writer.addDocument(document);
-
-    // search
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(7, searcher.count(FloatRangeField.newIntersectsQuery(FIELD_NAME,
-        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
-    assertEquals(2, searcher.count(FloatRangeField.newWithinQuery(FIELD_NAME,
-        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
-    assertEquals(2, searcher.count(FloatRangeField.newContainsQuery(FIELD_NAME,
-        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
-    assertEquals(5, searcher.count(FloatRangeField.newCrossesQuery(FIELD_NAME,
-        new float[] {-11.0f, -15.0f}, new float[] {15.0f, 20.0f})));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-
-  /** FloatRange test class implementation - use to validate FloatRangeField */
-  private class FloatRange extends Range {
-    float[] min;
-    float[] max;
-
-    FloatRange(float[] min, float[] max) {
-      assert min != null && max != null && min.length > 0 && max.length > 0
-          : "test box: min/max cannot be null or empty";
-      assert min.length == max.length : "test box: min/max length do not agree";
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return min.length;
-    }
-
-    @Override
-    protected Float getMin(int dim) {
-      return min[dim];
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      float v = (Float)val;
-      if (min[dim] < v) {
-        max[dim] = v;
-      } else {
-        min[dim] = v;
-      }
-    }
-
-    @Override
-    protected Float getMax(int dim) {
-      return max[dim];
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      float v = (Float)val;
-      if (max[dim] > v) {
-        min[dim] = v;
-      } else {
-        max[dim] = v;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range other) {
-      FloatRange o = (FloatRange)other;
-      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      FloatRange other = (FloatRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
-          // disjoint:
-          return true;
-        }
-      }
-      return false;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      FloatRange other = (FloatRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
-          // not within:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      FloatRange other = (FloatRange) o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
-          // not contains:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min[0]);
-      b.append(" TO ");
-      b.append(max[0]);
-      for (int d=1; d<min.length; ++d) {
-        b.append(", ");
-        b.append(min[d]);
-        b.append(" TO ");
-        b.append(max[d]);
-      }
-      b.append(")");
-
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java
deleted file mode 100644
index 0bb782e..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java
+++ /dev/null
@@ -1,251 +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.util.Arrays;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.IntRangeField;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.store.Directory;
-
-/**
- * Random testing for IntRangeField Queries.
- */
-public class TestIntRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "intRangeField";
-
-  private int nextIntInternal() {
-    if (rarely()) {
-      return random().nextBoolean() ? Integer.MAX_VALUE : Integer.MIN_VALUE;
-    }
-    int max = Integer.MAX_VALUE / 2;
-    return (max + max) * random().nextInt() - max;
-  }
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    int[] min = new int[dimensions];
-    int[] max = new int[dimensions];
-
-    int minV, maxV;
-    for (int d=0; d<dimensions; ++d) {
-      minV = nextIntInternal();
-      maxV = nextIntInternal();
-      min[d] = Math.min(minV, maxV);
-      max[d] = Math.max(minV, maxV);
-    }
-    return new IntRange(min, max);
-  }
-
-  @Override
-  protected IntRangeField newRangeField(Range r) {
-    return new IntRangeField(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return IntRangeField.newIntersectsQuery(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return IntRangeField.newContainsQuery(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return IntRangeField.newWithinQuery(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return IntRangeField.newCrossesQuery(FIELD_NAME, ((IntRange)r).min, ((IntRange)r).max);
-  }
-
-  /** Basic test */
-  public void testBasics() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // intersects (within)
-    Document document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-10, -10}, new int[] {9, 10}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {10, -10}, new int[] {20, 10}));
-    writer.addDocument(document);
-
-    // intersects (contains / crosses)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-20, -20}, new int[] {30, 30}));
-    writer.addDocument(document);
-
-    // intersects (within)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-11, -11}, new int[] {1, 11}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {12, 1}, new int[] {15, 29}));
-    writer.addDocument(document);
-
-    // disjoint
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-122, 1}, new int[] {-115, 29}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {Integer.MIN_VALUE, 1}, new int[] {-11, 29}));
-    writer.addDocument(document);
-
-    // equal (within, contains, intersects)
-    document = new Document();
-    document.add(new IntRangeField(FIELD_NAME, new int[] {-11, -15}, new int[] {15, 20}));
-    writer.addDocument(document);
-
-    // search
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(7, searcher.count(IntRangeField.newIntersectsQuery(FIELD_NAME,
-        new int[] {-11, -15}, new int[] {15, 20})));
-    assertEquals(3, searcher.count(IntRangeField.newWithinQuery(FIELD_NAME,
-        new int[] {-11, -15}, new int[] {15, 20})));
-    assertEquals(2, searcher.count(IntRangeField.newContainsQuery(FIELD_NAME,
-        new int[] {-11, -15}, new int[] {15, 20})));
-    assertEquals(4, searcher.count(IntRangeField.newCrossesQuery(FIELD_NAME,
-        new int[] {-11, -15}, new int[] {15, 20})));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-
-  /** IntRange test class implementation - use to validate IntRangeField */
-  private class IntRange extends Range {
-    int[] min;
-    int[] max;
-
-    IntRange(int[] min, int[] max) {
-      assert min != null && max != null && min.length > 0 && max.length > 0
-          : "test box: min/max cannot be null or empty";
-      assert min.length == max.length : "test box: min/max length do not agree";
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return min.length;
-    }
-
-    @Override
-    protected Integer getMin(int dim) {
-      return min[dim];
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      int v = (Integer)val;
-      if (min[dim] < v) {
-        max[dim] = v;
-      } else {
-        min[dim] = v;
-      }
-    }
-
-    @Override
-    protected Integer getMax(int dim) {
-      return max[dim];
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      int v = (Integer)val;
-      if (max[dim] > v) {
-        min[dim] = v;
-      } else {
-        max[dim] = v;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range other) {
-      IntRange o = (IntRange)other;
-      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      IntRange other = (IntRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
-          // disjoint:
-          return true;
-        }
-      }
-      return false;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      IntRange other = (IntRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
-          // not within:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      IntRange other = (IntRange) o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
-          // not contains:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min[0]);
-      b.append(" TO ");
-      b.append(max[0]);
-      for (int d=1; d<min.length; ++d) {
-        b.append(", ");
-        b.append(min[d]);
-        b.append(" TO ");
-        b.append(max[d]);
-      }
-      b.append(")");
-
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java
deleted file mode 100644
index 1563584..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java
+++ /dev/null
@@ -1,220 +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.net.InetAddress;
-import java.net.UnknownHostException;
-
-import org.apache.lucene.document.InetAddressRangeField;
-import org.apache.lucene.util.StringHelper;
-
-/**
- * Random testing for {@link InetAddressRangeField}
- */
-public class TestIpRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "ipRangeField";
-
-  private IPVersion ipVersion;
-
-  private enum IPVersion {IPv4, IPv6}
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    try {
-      InetAddress min = nextInetaddress();
-      byte[] bMin = min.getAddress();
-      InetAddress max = nextInetaddress();
-      byte[] bMax = max.getAddress();
-      if (StringHelper.compare(bMin.length, bMin, 0, bMax, 0) > 0) {
-        return new IpRange(max, min);
-      }
-      return new IpRange(min, max);
-    } catch (UnknownHostException e) {
-      e.printStackTrace();
-    }
-    return null;
-  }
-
-  /** return random IPv4 or IPv6 address */
-  private InetAddress nextInetaddress() throws UnknownHostException {
-    byte[] b;
-    switch (ipVersion) {
-      case IPv4:
-        b = new byte[4];
-        break;
-      case IPv6:
-        b = new byte[16];
-        break;
-      default:
-        throw new IllegalArgumentException("incorrect IP version: " + ipVersion);
-    }
-    random().nextBytes(b);
-    return InetAddress.getByAddress(b);
-  }
-
-  /** randomly select version across tests */
-  private IPVersion ipVersion() {
-    return random().nextBoolean() ? IPVersion.IPv4 : IPVersion.IPv6;
-  }
-
-  @Override
-  public void testRandomTiny() throws Exception {
-    ipVersion = ipVersion();
-    super.testRandomTiny();
-  }
-
-  @Override
-  public void testMultiValued() throws Exception {
-    ipVersion = ipVersion();
-    super.testRandomMedium();
-  }
-
-  @Override
-  public void testRandomMedium() throws Exception {
-    ipVersion = ipVersion();
-    super.testMultiValued();
-  }
-
-  @Nightly
-  @Override
-  public void testRandomBig() throws Exception {
-    ipVersion = ipVersion();
-    super.testRandomBig();
-  }
-
-  /** return random range */
-  @Override
-  protected InetAddressRangeField newRangeField(Range r) {
-    return new InetAddressRangeField(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** return random intersects query */
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return InetAddressRangeField.newIntersectsQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** return random contains query */
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return InetAddressRangeField.newContainsQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** return random within query */
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return InetAddressRangeField.newWithinQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** return random crosses query */
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return InetAddressRangeField.newCrossesQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
-  }
-
-  /** encapsulated IpRange for test validation */
-  private class IpRange extends Range {
-    InetAddress min;
-    InetAddress max;
-
-    IpRange(InetAddress min, InetAddress max) {
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return 1;
-    }
-
-    @Override
-    protected InetAddress getMin(int dim) {
-      return min;
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      byte[] v = ((InetAddress)val).getAddress();
-
-      if (StringHelper.compare(v.length, min.getAddress(), 0, v, 0) < 0) {
-        max = (InetAddress)val;
-      } else {
-        min = (InetAddress) val;
-      }
-    }
-
-    @Override
-    protected InetAddress getMax(int dim) {
-      return max;
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      byte[] v = ((InetAddress)val).getAddress();
-
-      if (StringHelper.compare(v.length, max.getAddress(), 0, v, 0) > 0) {
-        min = (InetAddress)val;
-      } else {
-        max = (InetAddress) val;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range o) {
-      IpRange other = (IpRange)o;
-      return this.min.equals(other.min) && this.max.equals(other.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      IpRange other = (IpRange)o;
-      byte[] bMin = min.getAddress();
-      byte[] bMax = max.getAddress();
-      return StringHelper.compare(bMin.length, bMin, 0, other.max.getAddress(), 0) > 0 ||
-          StringHelper.compare(bMax.length, bMax, 0, other.min.getAddress(), 0) < 0;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      IpRange other = (IpRange)o;
-      byte[] bMin = min.getAddress();
-      byte[] bMax = max.getAddress();
-      return StringHelper.compare(bMin.length, bMin, 0, other.min.getAddress(), 0) >= 0 &&
-          StringHelper.compare(bMax.length, bMax, 0, other.max.getAddress(), 0) <= 0;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      IpRange other = (IpRange)o;
-      byte[] bMin = min.getAddress();
-      byte[] bMax = max.getAddress();
-      return StringHelper.compare(bMin.length, bMin, 0, other.min.getAddress(), 0) <= 0 &&
-          StringHelper.compare(bMax.length, bMax, 0, other.max.getAddress(), 0) >= 0;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min.getHostAddress());
-      b.append(" TO ");
-      b.append(max.getHostAddress());
-      b.append(")");
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java
deleted file mode 100644
index fc21a64..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java
+++ /dev/null
@@ -1,251 +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.util.Arrays;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.LongRangeField;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.store.Directory;
-
-/**
- * Random testing for LongRangeField Queries.
- */
-public class TestLongRangeFieldQueries extends BaseRangeFieldQueryTestCase {
-  private static final String FIELD_NAME = "longRangeField";
-
-  private long nextLongInternal() {
-    if (rarely()) {
-      return random().nextBoolean() ? Long.MAX_VALUE : Long.MIN_VALUE;
-    }
-    long max = Long.MAX_VALUE / 2;
-    return (max + max) * random().nextLong() - max;
-  }
-
-  @Override
-  protected Range nextRange(int dimensions) {
-    long[] min = new long[dimensions];
-    long[] max = new long[dimensions];
-
-    long minV, maxV;
-    for (int d=0; d<dimensions; ++d) {
-      minV = nextLongInternal();
-      maxV = nextLongInternal();
-      min[d] = Math.min(minV, maxV);
-      max[d] = Math.max(minV, maxV);
-    }
-    return new LongRange(min, max);
-  }
-
-  @Override
-  protected LongRangeField newRangeField(Range r) {
-    return new LongRangeField(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  @Override
-  protected Query newIntersectsQuery(Range r) {
-    return LongRangeField.newIntersectsQuery(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  @Override
-  protected Query newContainsQuery(Range r) {
-    return LongRangeField.newContainsQuery(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  @Override
-  protected Query newWithinQuery(Range r) {
-    return LongRangeField.newWithinQuery(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  @Override
-  protected Query newCrossesQuery(Range r) {
-    return LongRangeField.newCrossesQuery(FIELD_NAME, ((LongRange)r).min, ((LongRange)r).max);
-  }
-
-  /** Basic test */
-  public void testBasics() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // intersects (within)
-    Document document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-10, -10}, new long[] {9, 10}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {10, -10}, new long[] {20, 10}));
-    writer.addDocument(document);
-
-    // intersects (contains, crosses)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-20, -20}, new long[] {30, 30}));
-    writer.addDocument(document);
-
-    // intersects (within)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-11, -11}, new long[] {1, 11}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {12, 1}, new long[] {15, 29}));
-    writer.addDocument(document);
-
-    // disjoint
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-122, 1}, new long[] {-115, 29}));
-    writer.addDocument(document);
-
-    // intersects (crosses)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {Long.MIN_VALUE, 1}, new long[] {-11, 29}));
-    writer.addDocument(document);
-
-    // equal (within, contains, intersects)
-    document = new Document();
-    document.add(new LongRangeField(FIELD_NAME, new long[] {-11, -15}, new long[] {15, 20}));
-    writer.addDocument(document);
-
-    // search
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(7, searcher.count(LongRangeField.newIntersectsQuery(FIELD_NAME,
-        new long[] {-11, -15}, new long[] {15, 20})));
-    assertEquals(3, searcher.count(LongRangeField.newWithinQuery(FIELD_NAME,
-        new long[] {-11, -15}, new long[] {15, 20})));
-    assertEquals(2, searcher.count(LongRangeField.newContainsQuery(FIELD_NAME,
-        new long[] {-11, -15}, new long[] {15, 20})));
-    assertEquals(4, searcher.count(LongRangeField.newCrossesQuery(FIELD_NAME,
-        new long[] {-11, -15}, new long[] {15, 20})));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-
-  /** LongRange test class implementation - use to validate LongRangeField */
-  private class LongRange extends Range {
-    long[] min;
-    long[] max;
-
-    LongRange(long[] min, long[] max) {
-      assert min != null && max != null && min.length > 0 && max.length > 0
-          : "test box: min/max cannot be null or empty";
-      assert min.length == max.length : "test box: min/max length do not agree";
-      this.min = min;
-      this.max = max;
-    }
-
-    @Override
-    protected int numDimensions() {
-      return min.length;
-    }
-
-    @Override
-    protected Long getMin(int dim) {
-      return min[dim];
-    }
-
-    @Override
-    protected void setMin(int dim, Object val) {
-      long v = (Long)val;
-      if (min[dim] < v) {
-        max[dim] = v;
-      } else {
-        min[dim] = v;
-      }
-    }
-
-    @Override
-    protected Long getMax(int dim) {
-      return max[dim];
-    }
-
-    @Override
-    protected void setMax(int dim, Object val) {
-      long v = (Long)val;
-      if (max[dim] > v) {
-        min[dim] = v;
-      } else {
-        max[dim] = v;
-      }
-    }
-
-    @Override
-    protected boolean isEqual(Range other) {
-      LongRange o = (LongRange)other;
-      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
-    }
-
-    @Override
-    protected boolean isDisjoint(Range o) {
-      LongRange other = (LongRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
-          // disjoint:
-          return true;
-        }
-      }
-      return false;
-    }
-
-    @Override
-    protected boolean isWithin(Range o) {
-      LongRange other = (LongRange)o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
-          // not within:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    protected boolean contains(Range o) {
-      LongRange other = (LongRange) o;
-      for (int d=0; d<this.min.length; ++d) {
-        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
-          // not contains:
-          return false;
-        }
-      }
-      return true;
-    }
-
-    @Override
-    public String toString() {
-      StringBuilder b = new StringBuilder();
-      b.append("Box(");
-      b.append(min[0]);
-      b.append(" TO ");
-      b.append(max[0]);
-      for (int d=1; d<min.length; ++d) {
-        b.append(", ");
-        b.append(min[d]);
-        b.append(" TO ");
-        b.append(max[d]);
-      }
-      b.append(")");
-
-      return b.toString();
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/test-framework/src/java/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java
new file mode 100644
index 0000000..76de732
--- /dev/null
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java
@@ -0,0 +1,346 @@
+/*
+ * 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.HashSet;
+import java.util.Set;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.NumericDocValuesField;
+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.index.LeafReaderContext;
+import org.apache.lucene.index.MultiDocValues;
+import org.apache.lucene.index.MultiFields;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.index.SerialMergeScheduler;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.FixedBitSet;
+import org.apache.lucene.util.IOUtils;
+import org.apache.lucene.util.LuceneTestCase;
+
+/**
+ * Abstract class to do basic tests for a RangeField query. Testing rigor inspired by {@code BaseGeoPointTestCase}
+ */
+public abstract class BaseRangeFieldQueryTestCase extends LuceneTestCase {
+  protected abstract Field newRangeField(Range box);
+
+  protected abstract Query newIntersectsQuery(Range box);
+
+  protected abstract Query newContainsQuery(Range box);
+
+  protected abstract Query newWithinQuery(Range box);
+
+  protected abstract Query newCrossesQuery(Range box);
+
+  protected abstract Range nextRange(int dimensions) throws Exception;
+
+  protected int dimension() {
+    return random().nextInt(4) + 1;
+  }
+
+  public void testRandomTiny() throws Exception {
+    // Make sure single-leaf-node case is OK:
+    doTestRandom(10, false);
+  }
+
+  public void testRandomMedium() throws Exception {
+    doTestRandom(10000, false);
+  }
+
+  @Nightly
+  public void testRandomBig() throws Exception {
+    doTestRandom(200000, false);
+  }
+
+  public void testMultiValued() throws Exception {
+    doTestRandom(10000, true);
+  }
+
+  private void doTestRandom(int count, boolean multiValued) throws Exception {
+    int numDocs = atLeast(count);
+    int dimensions = dimension();
+
+    if (VERBOSE) {
+      System.out.println("TEST: numDocs=" + numDocs);
+    }
+
+    Range[][] ranges = new Range[numDocs][];
+
+    boolean haveRealDoc = true;
+
+    nextdoc: for (int id=0; id<numDocs; ++id) {
+      int x = random().nextInt(20);
+      if (ranges[id] == null) {
+        ranges[id] = new Range[] {nextRange(dimensions)};
+      }
+      if (x == 17) {
+        // some docs don't have a box:
+        ranges[id][0].isMissing = true;
+        if (VERBOSE) {
+          System.out.println("  id=" + id + " is missing");
+        }
+        continue;
+      }
+
+      if (multiValued == true && random().nextBoolean()) {
+        // randomly add multi valued documents (up to 2 fields)
+        int n = random().nextInt(2) + 1;
+        ranges[id] = new Range[n];
+        for (int i=0; i<n; ++i) {
+          ranges[id][i] = nextRange(dimensions);
+        }
+      }
+
+      if (id > 0 && x < 9 && haveRealDoc) {
+        int oldID;
+        int i=0;
+        // don't step on missing ranges:
+        while (true) {
+          oldID = random().nextInt(id);
+          if (ranges[oldID][0].isMissing == false) {
+            break;
+          } else if (++i > id) {
+            continue nextdoc;
+          }
+        }
+
+        if (x == dimensions*2) {
+          // Fully identical box (use first box in case current is multivalued but old is not)
+          for (int d=0; d<dimensions; ++d) {
+            ranges[id][0].setMin(d, ranges[oldID][0].getMin(d));
+            ranges[id][0].setMax(d, ranges[oldID][0].getMax(d));
+          }
+          if (VERBOSE) {
+            System.out.println("  id=" + id + " box=" + ranges[id] + " (same box as doc=" + oldID + ")");
+          }
+        } else {
+          for (int m = 0, even = dimensions % 2; m < dimensions * 2; ++m) {
+            if (x == m) {
+              int d = (int)Math.floor(m/2);
+              // current could be multivalue but old may not be, so use first box
+              if (even == 0) {
+                ranges[id][0].setMin(d, ranges[oldID][0].getMin(d));
+                if (VERBOSE) {
+                  System.out.println("  id=" + id + " box=" + ranges[id] + " (same min[" + d + "] as doc=" + oldID + ")");
+                }
+              } else {
+                ranges[id][0].setMax(d, ranges[oldID][0].getMax(d));
+                if (VERBOSE) {
+                  System.out.println("  id=" + id + " box=" + ranges[id] + " (same max[" + d + "] as doc=" + oldID + ")");
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+    verify(ranges);
+  }
+
+  private void verify(Range[][] ranges) throws Exception {
+    IndexWriterConfig iwc = newIndexWriterConfig();
+    // Else seeds may not reproduce:
+    iwc.setMergeScheduler(new SerialMergeScheduler());
+    // Else we can get O(N^2) merging
+    int mbd = iwc.getMaxBufferedDocs();
+    if (mbd != -1 && mbd < ranges.length/100) {
+      iwc.setMaxBufferedDocs(ranges.length/100);
+    }
+    Directory dir;
+    if (ranges.length > 50000) {
+      dir = newFSDirectory(createTempDir(getClass().getSimpleName()));
+    } else {
+      dir = newDirectory();
+    }
+
+    Set<Integer> deleted = new HashSet<>();
+    IndexWriter w = new IndexWriter(dir, iwc);
+    for (int id=0; id < ranges.length; ++id) {
+      Document doc = new Document();
+      doc.add(newStringField("id", ""+id, Field.Store.NO));
+      doc.add(new NumericDocValuesField("id", id));
+      if (ranges[id][0].isMissing == false) {
+        for (int n=0; n<ranges[id].length; ++n) {
+          doc.add(newRangeField(ranges[id][n]));
+        }
+      }
+      w.addDocument(doc);
+      if (id > 0 && random().nextInt(100) == 1) {
+        int idToDelete = random().nextInt(id);
+        w.deleteDocuments(new Term("id", ""+idToDelete));
+        deleted.add(idToDelete);
+        if (VERBOSE) {
+          System.out.println("  delete id=" + idToDelete);
+        }
+      }
+    }
+
+    if (random().nextBoolean()) {
+      w.forceMerge(1);
+    }
+    final IndexReader r = DirectoryReader.open(w);
+    w.close();
+    IndexSearcher s = newSearcher(r);
+
+    int dimensions = ranges[0][0].numDimensions();
+    int iters = atLeast(25);
+    Bits liveDocs = MultiFields.getLiveDocs(s.getIndexReader());
+    int maxDoc = s.getIndexReader().maxDoc();
+
+    for (int iter=0; iter<iters; ++iter) {
+      if (VERBOSE) {
+        System.out.println("\nTEST: iter=" + iter + " s=" + s);
+      }
+
+      // occasionally test open ended bounding ranges
+      Range queryRange = nextRange(dimensions);
+      int rv = random().nextInt(4);
+      Query query;
+      Range.QueryType queryType;
+      if (rv == 0) {
+        queryType = Range.QueryType.INTERSECTS;
+        query = newIntersectsQuery(queryRange);
+      } else if (rv == 1)  {
+        queryType = Range.QueryType.CONTAINS;
+        query = newContainsQuery(queryRange);
+      } else if (rv == 2) {
+        queryType = Range.QueryType.WITHIN;
+        query = newWithinQuery(queryRange);
+      } else {
+        queryType = Range.QueryType.CROSSES;
+        query = newCrossesQuery(queryRange);
+      }
+
+      if (VERBOSE) {
+        System.out.println("  query=" + query);
+      }
+
+      final FixedBitSet hits = new FixedBitSet(maxDoc);
+      s.search(query, new SimpleCollector() {
+        private int docBase;
+
+        @Override
+        public void collect(int doc) {
+          hits.set(docBase + doc);
+        }
+
+        @Override
+        protected void doSetNextReader(LeafReaderContext context) throws IOException {
+          docBase = context.docBase;
+        }
+
+        @Override
+        public boolean needsScores() { return false; }
+      });
+
+      NumericDocValues docIDToID = MultiDocValues.getNumericValues(r, "id");
+      for (int docID=0; docID<maxDoc; ++docID) {
+        assertEquals(docID, docIDToID.nextDoc());
+        int id = (int) docIDToID.longValue();
+        boolean expected;
+        if (liveDocs != null && liveDocs.get(docID) == false) {
+          // document is deleted
+          expected = false;
+        } else if (ranges[id][0].isMissing) {
+          expected = false;
+        } else {
+          expected = expectedResult(queryRange, ranges[id], queryType);
+        }
+
+        if (hits.get(docID) != expected) {
+          StringBuilder b = new StringBuilder();
+          b.append("FAIL (iter " + iter + "): ");
+          if (expected == true) {
+            b.append("id=" + id + (ranges[id].length > 1 ? " (MultiValue) " : " ") + "should match but did not\n");
+          } else {
+            b.append("id=" + id + " should not match but did\n");
+          }
+          b.append(" queryRange=" + queryRange + "\n");
+          b.append(" box" + ((ranges[id].length > 1) ? "es=" : "=" ) + ranges[id][0]);
+          for (int n=1; n<ranges[id].length; ++n) {
+            b.append(", ");
+            b.append(ranges[id][n]);
+          }
+          b.append("\n queryType=" + queryType + "\n");
+          b.append(" deleted?=" + (liveDocs != null && liveDocs.get(docID) == false));
+          fail("wrong hit (first of possibly more):\n\n" + b);
+        }
+      }
+    }
+    IOUtils.close(r, dir);
+  }
+
+  protected boolean expectedResult(Range queryRange, Range[] range, Range.QueryType queryType) {
+    for (int i=0; i<range.length; ++i) {
+      if (expectedBBoxQueryResult(queryRange, range[i], queryType) == true) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  protected boolean expectedBBoxQueryResult(Range queryRange, Range range, Range.QueryType queryType) {
+    if (queryRange.isEqual(range) && queryType != Range.QueryType.CROSSES) {
+      return true;
+    }
+    Range.QueryType relation = range.relate(queryRange);
+    if (queryType == Range.QueryType.INTERSECTS) {
+      return relation != null;
+    } else if (queryType == Range.QueryType.CROSSES) {
+      // by definition, RangeFields that CONTAIN the query are also considered to cross
+      return relation == queryType || relation == Range.QueryType.CONTAINS;
+    }
+    return relation == queryType;
+  }
+
+  /** base class for range verification */
+  protected abstract static class Range {
+    protected boolean isMissing = false;
+
+    /** supported query relations */
+    protected enum QueryType { INTERSECTS, WITHIN, CONTAINS, CROSSES }
+
+    protected abstract int numDimensions();
+    protected abstract Object getMin(int dim);
+    protected abstract void setMin(int dim, Object val);
+    protected abstract Object getMax(int dim);
+    protected abstract void setMax(int dim, Object val);
+    protected abstract boolean isEqual(Range other);
+    protected abstract boolean isDisjoint(Range other);
+    protected abstract boolean isWithin(Range other);
+    protected abstract boolean contains(Range other);
+
+    protected QueryType relate(Range other) {
+      if (isDisjoint(other)) {
+        // if disjoint; return null:
+        return null;
+      } else if (isWithin(other)) {
+        return QueryType.WITHIN;
+      } else if (contains(other)) {
+        return QueryType.CONTAINS;
+      }
+      return QueryType.CROSSES;
+    }
+  }
+}


[05/23] lucene-solr:jira/solr-9835: SOLR-8045: Deploy V2 API at /v2 instead of /solr/v2

Posted by da...@apache.org.
SOLR-8045: Deploy V2 API at /v2 instead of /solr/v2


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

Branch: refs/heads/jira/solr-9835
Commit: 0fb386a864ff5b7d32af3bef3f7eeca4d009acc1
Parents: 6415d91
Author: Cao Manh Dat <da...@apache.org>
Authored: Sat Mar 11 10:30:52 2017 +0700
Committer: Cao Manh Dat <da...@apache.org>
Committed: Sat Mar 11 10:30:52 2017 +0700

----------------------------------------------------------------------
 dev-tools/scripts/smokeTestRelease.py           |  4 ++
 solr/CHANGES.txt                                |  2 +
 .../java/org/apache/solr/api/V2HttpCall.java    |  2 +-
 .../java/org/apache/solr/core/PluginBag.java    |  2 +-
 .../apache/solr/servlet/SolrDispatchFilter.java |  2 +-
 .../conf/solrconfig-managed-schema.xml          |  2 +-
 .../org/apache/solr/cloud/rule/RulesTest.java   |  2 +-
 .../apache/solr/core/TestDynamicLoading.java    |  2 +-
 .../apache/solr/core/TestSolrConfigHandler.java | 14 ++---
 .../apache/solr/handler/TestReqParamsAPI.java   |  3 ++
 .../solr/handler/V2ApiIntegrationTest.java      |  4 +-
 .../solr/rest/schema/TestBulkSchemaAPI.java     |  2 +-
 .../solr/security/BasicAuthIntegrationTest.java |  4 +-
 solr/server/etc/jetty.xml                       | 54 +++++++++++---------
 .../conf/solrconfig.xml                         |  2 +-
 .../solr/client/solrj/impl/CloudSolrClient.java |  4 +-
 .../solrj/embedded/SolrExampleJettyTest.java    |  2 +-
 17 files changed, 62 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/dev-tools/scripts/smokeTestRelease.py
----------------------------------------------------------------------
diff --git a/dev-tools/scripts/smokeTestRelease.py b/dev-tools/scripts/smokeTestRelease.py
index f9c3499..b0e76e9 100644
--- a/dev-tools/scripts/smokeTestRelease.py
+++ b/dev-tools/scripts/smokeTestRelease.py
@@ -855,6 +855,10 @@ def testSolrExample(unpackPath, javaPath, isSrc):
     if s.find('<result name="response" numFound="3" start="0">') == -1:
       print('FAILED: response is:\n%s' % s)
       raise RuntimeError('query on solr example instance failed')
+    s = load('http://localhost:8983/v2/cores')
+    if s.find('"responseHeader":{"status":0,"QTime":1}') == -1:
+      print('FAILED: response is:\n%s' % s)
+      raise RuntimeError('query api v2 on solr example instance failed')
   finally:
     # Stop server:
     print('      stop server using: bin/solr stop -p 8983')

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 7285e4f..b164405 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -164,6 +164,8 @@ New Features
 
 * SOLR-9986: Implement DatePointField (Cao Manh Dat, Tom�s Fern�ndez L�bbe)
 
+* SOLR-8045: Deploy V2 API at /v2 instead of /solr/v2 (Cao Manh Dat, Noble Paul)
+
 Bug Fixes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
index c996b25..fb4aa56 100644
--- a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
+++ b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
@@ -75,7 +75,7 @@ public class V2HttpCall extends HttpSolrCall {
 
   protected void init() throws Exception {
     String path = this.path;
-    String fullPath = path = path.substring(3);//strip off '/v2'
+    String fullPath = path = path.substring(7);//strip off '/____v2'
     try {
       pieces = getPathSegments(path);
       if (pieces.size() == 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/java/org/apache/solr/core/PluginBag.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/PluginBag.java b/solr/core/src/java/org/apache/solr/core/PluginBag.java
index ad8bdec..65978f3 100644
--- a/solr/core/src/java/org/apache/solr/core/PluginBag.java
+++ b/solr/core/src/java/org/apache/solr/core/PluginBag.java
@@ -194,7 +194,7 @@ public class PluginBag<T> implements AutoCloseable {
       String registerAt = plugin.pluginInfo.attributes.get("registerPath");
       if (registerAt != null) {
         List<String> strs = StrUtils.splitSmart(registerAt, ',');
-        disableHandler = !strs.contains("/");
+        disableHandler = !strs.contains("/solr");
         registerApi = strs.contains("/v2");
       }
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
index ce65069..ff0db9b 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
@@ -388,7 +388,7 @@ public class SolrDispatchFilter extends BaseSolrFilter {
       path += request.getPathInfo();
     }
 
-    if (isV2Enabled && (path.startsWith("/v2/") || path.equals("/v2"))) {
+    if (isV2Enabled && (path.startsWith("/____v2/") || path.equals("/____v2"))) {
       return new V2HttpCall(this, cores, request, response, false);
     } else {
       return new HttpSolrCall(this, cores, request, response, retry);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/test-files/solr/collection1/conf/solrconfig-managed-schema.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/solrconfig-managed-schema.xml b/solr/core/src/test-files/solr/collection1/conf/solrconfig-managed-schema.xml
index 31bbbb3..abd4fbe 100644
--- a/solr/core/src/test-files/solr/collection1/conf/solrconfig-managed-schema.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/solrconfig-managed-schema.xml
@@ -40,7 +40,7 @@
   </requestHandler>
 
 
-  <requestHandler name="/dump" class="DumpRequestHandler" initParams="a" registerPath="/,/v2">
+  <requestHandler name="/dump" class="DumpRequestHandler" initParams="a" registerPath="/solr,/v2">
     <lst name="defaults">
       <str name="a">${my.custom.variable.a:A}</str>
       <str name="b">${my.custom.variable.b:B}</str>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java b/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
index 13649e1..d4a72bf 100644
--- a/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/rule/RulesTest.java
@@ -168,7 +168,7 @@ public class RulesTest extends SolrCloudTestCase {
   public void testInvokeApi() throws Exception {
     JettySolrRunner jetty = cluster.getRandomJetty(random());
     try (SolrClient client = getHttpSolrClient(jetty.getBaseUrl().toString())) {
-      GenericSolrRequest req =  new GenericSolrRequest(GET, "/v2/node/invoke", new ModifiableSolrParams()
+      GenericSolrRequest req =  new GenericSolrRequest(GET, "/____v2/node/invoke", new ModifiableSolrParams()
           .add("class", ImplicitSnitch.class.getName())
           .add("cores", "1")
           .add("freedisk", "1")

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java b/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java
index 8479ae4..306b4b2 100644
--- a/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java
+++ b/solr/core/src/test/org/apache/solr/core/TestDynamicLoading.java
@@ -88,7 +88,7 @@ public class TestDynamicLoading extends AbstractFullDistribZkTestBase {
 
 
     payload = "{\n" +
-        "'create-requesthandler' : { 'name' : '/test1', 'class': 'org.apache.solr.core.BlobStoreTestRequestHandler' ,registerPath: '/,/v2',  'runtimeLib' : true }\n" +
+        "'create-requesthandler' : { 'name' : '/test1', 'class': 'org.apache.solr.core.BlobStoreTestRequestHandler' ,registerPath: '/solr,/v2',  'runtimeLib' : true }\n" +
         "}";
 
     client = restTestHarnesses.get(random().nextInt(restTestHarnesses.size()));

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
index 3f85a79..ec81c25 100644
--- a/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
+++ b/solr/core/src/test/org/apache/solr/core/TestSolrConfigHandler.java
@@ -86,7 +86,7 @@ public class TestSolrConfigHandler extends RestTestBase {
         "/solr", true, extraServlets);
     if (random().nextBoolean()) {
       log.info("These tests are run with V2 API");
-      restTestHarness.setServerProvider(() -> jetty.getBaseUrl().toString() + "/v2/cores/" + DEFAULT_TEST_CORENAME);
+      restTestHarness.setServerProvider(() -> jetty.getBaseUrl().toString() + "/____v2/cores/" + DEFAULT_TEST_CORENAME);
     }
   }
 
@@ -201,7 +201,7 @@ public class TestSolrConfigHandler extends RestTestBase {
         10);
 
     payload = "{\n" +
-        "'update-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' ,registerPath :'/,/v2', " +
+        "'update-requesthandler' : { 'name' : '/x', 'class': 'org.apache.solr.handler.DumpRequestHandler' ,registerPath :'/solr,/v2', " +
         " 'startup' : 'lazy' , 'a':'b' , 'defaults': {'def_a':'def A val', 'multival':['a','b','c']}}\n" +
         "}";
     runConfigCommand(writeHarness, "/config?wt=json", payload);
@@ -442,7 +442,7 @@ public class TestSolrConfigHandler extends RestTestBase {
     payload = "{\n" +
         "    'add-requesthandler': {\n" +
         "        name : '/dump100',\n" +
-        "       registerPath :'/,/v2',"+
+        "       registerPath :'/solr,/v2',"+
     "        class : 'org.apache.solr.handler.DumpRequestHandler'," +
         "        suggester: [{name: s1,lookupImpl: FuzzyLookupFactory, dictionaryImpl : DocumentDictionaryFactory}," +
         "                    {name: s2,lookupImpl: FuzzyLookupFactory , dictionaryImpl : DocumentExpressionDictionaryFactory}]" +
@@ -467,7 +467,7 @@ public class TestSolrConfigHandler extends RestTestBase {
     payload = "{\n" +
         "'add-requesthandler' : { 'name' : '/dump101', 'class': " +
         "'" + CacheTest.class.getName() + "', " +
-        "    registerPath :'/,/v2'"+
+        "    registerPath :'/solr,/v2'"+
         ", 'startup' : 'lazy'}\n" +
         "}";
     runConfigCommand(writeHarness, "/config?wt=json", payload);
@@ -589,7 +589,7 @@ public class TestSolrConfigHandler extends RestTestBase {
         10);
 
     payload = "{\n" +
-        "'create-requesthandler' : { 'name' : '/d', registerPath :'/,/v2' , 'class': 'org.apache.solr.handler.DumpRequestHandler' }\n" +
+        "'create-requesthandler' : { 'name' : '/d', registerPath :'/solr,/v2' , 'class': 'org.apache.solr.handler.DumpRequestHandler' }\n" +
         "}";
 
     TestSolrConfigHandler.runConfigCommand(harness, "/config?wt=json", payload);
@@ -619,7 +619,7 @@ public class TestSolrConfigHandler extends RestTestBase {
         5);
 
     payload = "{\n" +
-        "'create-requesthandler' : { 'name' : '/dump1', registerPath :'/,/v2' , 'class': 'org.apache.solr.handler.DumpRequestHandler', 'useParams':'x' }\n" +
+        "'create-requesthandler' : { 'name' : '/dump1', registerPath :'/solr,/v2' , 'class': 'org.apache.solr.handler.DumpRequestHandler', 'useParams':'x' }\n" +
         "}";
 
     TestSolrConfigHandler.runConfigCommand(harness, "/config?wt=json", payload);
@@ -794,7 +794,7 @@ public class TestSolrConfigHandler extends RestTestBase {
         "org.apache.solr.handler.DumpRequestHandler",
         10);
     RESTfulServerProvider oldProvider = restTestHarness.getServerProvider();
-    restTestHarness.setServerProvider(() -> jetty.getBaseUrl().toString() + "/v2/cores/" + DEFAULT_TEST_CORENAME);
+    restTestHarness.setServerProvider(() -> jetty.getBaseUrl().toString() + "/____v2/cores/" + DEFAULT_TEST_CORENAME);
 
     Map rsp = TestSolrConfigHandler.testForResponseElement(
         harness,

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java b/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java
index 3912011..de4a27a 100644
--- a/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java
+++ b/solr/core/src/test/org/apache/solr/handler/TestReqParamsAPI.java
@@ -50,6 +50,9 @@ public class TestReqParamsAPI extends SolrCloudTestCase {
   private void setupHarnesses() {
     for (final JettySolrRunner jettySolrRunner : cluster.getJettySolrRunners()) {
       RestTestHarness harness = new RestTestHarness(() -> jettySolrRunner.getBaseUrl().toString() + "/" + COLL_NAME);
+      if (true) {
+        harness.setServerProvider(() -> jettySolrRunner.getBaseUrl().toString() + "/____v2/c/" + COLL_NAME);
+      }
       restTestHarnesses.add(harness);
     }
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java b/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java
index 1af5d93..4eb3de2 100644
--- a/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/V2ApiIntegrationTest.java
@@ -86,10 +86,10 @@ public class V2ApiIntegrationTest extends SolrCloudTestCase {
   private void testApis() throws Exception {
     RestTestHarness restHarness = restTestHarnesses.get(0);
     ServerProvider serverProvider = (ServerProvider) restHarness.getServerProvider();
-    serverProvider.baseurl = serverProvider.jettySolrRunner.getBaseUrl()+"/v2/c/"+ COLL_NAME;
+    serverProvider.baseurl = serverProvider.jettySolrRunner.getBaseUrl()+"/____v2/c/"+ COLL_NAME;
     Map result = TestSolrConfigHandler.getRespMap("/get/_introspect", restHarness);
     assertEquals("/c/collection1/get", Utils.getObjectByPath(result, true, "/spec[0]/url/paths[0]"));
-    serverProvider.baseurl = serverProvider.jettySolrRunner.getBaseUrl()+"/v2/collections/"+ COLL_NAME;
+    serverProvider.baseurl = serverProvider.jettySolrRunner.getBaseUrl()+"/____v2/collections/"+ COLL_NAME;
     result = TestSolrConfigHandler.getRespMap("/get/_introspect", restHarness);
     assertEquals("/collections/collection1/get", Utils.getObjectByPath(result, true, "/spec[0]/url/paths[0]"));
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/test/org/apache/solr/rest/schema/TestBulkSchemaAPI.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/rest/schema/TestBulkSchemaAPI.java b/solr/core/src/test/org/apache/solr/rest/schema/TestBulkSchemaAPI.java
index ea8fd7b..e2dc2bf 100644
--- a/solr/core/src/test/org/apache/solr/rest/schema/TestBulkSchemaAPI.java
+++ b/solr/core/src/test/org/apache/solr/rest/schema/TestBulkSchemaAPI.java
@@ -69,7 +69,7 @@ public class TestBulkSchemaAPI extends RestTestBase {
       restTestHarness.setServerProvider(new RESTfulServerProvider() {
         @Override
         public String getBaseURL() {
-          return jetty.getBaseUrl().toString() + "/v2/cores/" + DEFAULT_TEST_CORENAME;
+          return jetty.getBaseUrl().toString() + "/____v2/cores/" + DEFAULT_TEST_CORENAME;
         }
       });
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java b/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
index 397f4e8..5231dd8 100644
--- a/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/security/BasicAuthIntegrationTest.java
@@ -86,8 +86,8 @@ public class BasicAuthIntegrationTest extends SolrCloudTestCase {
     String authcPrefix = "/admin/authentication";
     String authzPrefix = "/admin/authorization";
     if(random().nextBoolean()){
-      authcPrefix = "/v2/cluster/security/authentication";
-      authzPrefix = "/v2/cluster/security/authorization";
+      authcPrefix = "/____v2/cluster/security/authentication";
+      authzPrefix = "/____v2/cluster/security/authorization";
     }
 
     NamedList<Object> rsp;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/server/etc/jetty.xml
----------------------------------------------------------------------
diff --git a/solr/server/etc/jetty.xml b/solr/server/etc/jetty.xml
index 8cb8223..b512c51 100644
--- a/solr/server/etc/jetty.xml
+++ b/solr/server/etc/jetty.xml
@@ -97,35 +97,43 @@
           </New>
         </Arg>
       </Call>
+       <Call name="addRule">
+         <Arg>
+           <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
+             <Set name="pattern">/v2/*</Set>
+             <Set name="replacement">/solr/____v2</Set>
+           </New>
+         </Arg>
+       </Call>
+       <Set name="handler">
+         <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
+           <Set name="handlers">
+             <Array type="org.eclipse.jetty.server.Handler">
+               <Item>
+                 <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
+               </Item>
+               <Item>
+                 <New id="InstrumentedHandler" class="com.codahale.metrics.jetty9.InstrumentedHandler">
+                   <Arg><Ref refid="solrJettyMetricRegistry"/></Arg>
+                   <Set name="handler">
+                     <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
+                   </Set>
+                 </New>
+               </Item>
+               <Item>
+                 <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
+               </Item>
+             </Array>
+           </Set>
+         </New>
+       </Set>
      </New>
 
     <!-- =========================================================== -->
     <!-- Set handler Collection Structure                            -->
     <!-- =========================================================== -->
     <Set name="handler">
-      <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
-        <Set name="handlers">
-         <Array type="org.eclipse.jetty.server.Handler">
-           <Item>
-             <Ref id="RewriteHandler"/>
-           </Item>
-           <Item>
-             <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
-           </Item>
-           <Item>
-             <New id="InstrumentedHandler" class="com.codahale.metrics.jetty9.InstrumentedHandler">
-               <Arg><Ref refid="solrJettyMetricRegistry"/></Arg>
-               <Set name="handler">
-                 <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
-               </Set>
-             </New>
-           </Item>
-           <Item>
-             <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
-           </Item>
-         </Array>
-        </Set>
-      </New>
+      <Ref id="RewriteHandler"/>
     </Set>
     
     <!-- =========================================================== -->

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
----------------------------------------------------------------------
diff --git a/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml b/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
index a9ddb25..3ff89c0 100644
--- a/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
+++ b/solr/server/solr/configsets/sample_techproducts_configs/conf/solrconfig.xml
@@ -850,7 +850,7 @@
     </requestHandler>
 
   <!-- A request handler that returns indented JSON by default -->
-  <requestHandler name="/query" class="solr.SearchHandler" registerPath="/,/v2">
+  <requestHandler name="/query" class="solr.SearchHandler" registerPath="/solr,/v2">
      <lst name="defaults">
        <str name="echoParams">explicit</str>
        <str name="wt">json</str>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
index 3147d4e..d3938c8 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/impl/CloudSolrClient.java
@@ -1047,8 +1047,8 @@ public class CloudSolrClient extends SolrClient {
       CONFIGSETS_HANDLER_PATH,
       AUTHC_PATH,
       AUTHZ_PATH,
-      "/v2/cluster/security/authentication",
-      "/v2/cluster/security/authorization"
+      "/____v2/cluster/security/authentication",
+      "/____v2/cluster/security/authorization"
       ));
 
   /**

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0fb386a8/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleJettyTest.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleJettyTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleJettyTest.java
index b7ac7de..cb4ba50 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleJettyTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/SolrExampleJettyTest.java
@@ -98,7 +98,7 @@ public class SolrExampleJettyTest extends SolrExampleTests {
   private String getUri(HttpSolrClient client) {
     String baseURL = client.getBaseURL();
     return random().nextBoolean() ?
-        baseURL.replace("/collection1", "/v2/cores/collection1/update") :
+        baseURL.replace("/collection1", "/____v2/cores/collection1/update") :
         baseURL + "/update/json/docs";
   }
 }


[16/23] lucene-solr:jira/solr-9835: SOLR-10079: Increasing threadpool termination to 15s, clearIndex() to do clean replica indexes as well

Posted by da...@apache.org.
SOLR-10079: Increasing threadpool termination to 15s, clearIndex() to do clean replica indexes as well


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

Branch: refs/heads/jira/solr-9835
Commit: d5181ec8e5bbc8932f606caaf281ff6913a1537d
Parents: b64382b
Author: Ishan Chattopadhyaya <is...@apache.org>
Authored: Mon Mar 13 16:55:50 2017 +0530
Committer: Ishan Chattopadhyaya <is...@apache.org>
Committed: Mon Mar 13 16:56:29 2017 +0530

----------------------------------------------------------------------
 .../solr/update/TestInPlaceUpdatesDistrib.java  | 89 +++++++++++++++-----
 1 file changed, 68 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5181ec8/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
index 4c90bc6..bb0ab9a 100644
--- a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
+++ b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
@@ -151,15 +151,18 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
         "docValues",Boolean.TRUE));
 
     // Do the tests now:
-    reorderedDBQIndividualReplicaTest();
-    testDBQUsingUpdatedFieldFromDroppedUpdate();
-    outOfOrderDBQsTest();
     docValuesUpdateTest();
     ensureRtgWorksWithPartialUpdatesTest();
-    delayedReorderingFetchesMissingUpdateFromLeaderTest();
     outOfOrderUpdatesIndividualReplicaTest();
-    outOfOrderDeleteUpdatesIndividualReplicaTest();
-    reorderedDBQsWithInPlaceUpdatesShouldNotThrowReplicaInLIRTest();
+    delayedReorderingFetchesMissingUpdateFromLeaderTest();
+    updatingDVsInAVeryOldSegment();
+
+    // TODO Should we combine all/some of these into a single test, so as to cut down on execution time?
+    reorderedDBQIndividualReplicaTest();
+    reorderedDeletesTest();
+    reorderedDBQsSimpleTest();
+    reorderedDBQsResurrectionTest();
+    reorderedDBQsUsingUpdatedValueFromADroppedUpdate();
   }
   
   private void mapReplicasToClients() throws KeeperException, InterruptedException {
@@ -195,7 +198,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
   final int NUM_RETRIES = 100, WAIT_TIME = 10;
 
   // The following should work: full update to doc 0, in-place update for doc 0, delete doc 0
-  private void outOfOrderDBQsTest() throws Exception {
+  private void reorderedDBQsSimpleTest() throws Exception {
     
     clearIndex();
     commit();
@@ -243,7 +246,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
     }
     
     threadpool.shutdown();
-    assertTrue("Thread pool didn't terminate within 10 secs", threadpool.awaitTermination(10, TimeUnit.SECONDS));
+    assertTrue("Thread pool didn't terminate within 15 secs", threadpool.awaitTermination(15, TimeUnit.SECONDS));
     
     // assert all requests were successful
     for (Future<UpdateResponse> resp: updateResponses) {
@@ -256,7 +259,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
       assertNull("This doc was supposed to have been deleted, but was: " + doc, doc);
     }
 
-    log.info("outOfOrderDeleteUpdatesIndividualReplicaTest: This test passed fine...");
+    log.info("reorderedDBQsSimpleTest: This test passed fine...");
     clearIndex();
     commit();
   }
@@ -294,7 +297,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
     }
 
     threadpool.shutdown();
-    assertTrue("Thread pool didn't terminate within 10 secs", threadpool.awaitTermination(10, TimeUnit.SECONDS));
+    assertTrue("Thread pool didn't terminate within 15 secs", threadpool.awaitTermination(15, TimeUnit.SECONDS));
 
     // assert all requests were successful
     for (Future<UpdateResponse> resp: updateResponses) {
@@ -391,6 +394,36 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
   }
 
   /**
+   * Ingest many documents, keep committing. Then update a document from a very old segment.
+   */
+  private void updatingDVsInAVeryOldSegment() throws Exception {
+    clearIndex();
+    commit();
+
+    String id = String.valueOf(Integer.MAX_VALUE);
+    index("id", id, "inplace_updatable_float", "1", "title_s", "newtitle");
+
+    // create 10 more segments
+    for (int i=0; i<10; i++) {
+      buildRandomIndex(101.0F, Collections.emptyList());
+    }
+
+    index("id", id, "inplace_updatable_float", map("inc", "1"));
+
+    for (SolrClient client: new SolrClient[] {LEADER, NONLEADERS.get(0), NONLEADERS.get(1)}) {
+      assertEquals("newtitle", client.getById(id).get("title_s"));
+      assertEquals(2.0f, client.getById(id).get("inplace_updatable_float"));
+    }
+    commit();
+    for (SolrClient client: new SolrClient[] {LEADER, NONLEADERS.get(0), NONLEADERS.get(1)}) {
+      assertEquals("newtitle", client.getById(id).get("title_s"));
+      assertEquals(2.0f, client.getById(id).get("inplace_updatable_float"));
+    }
+
+    log.info("updatingDVsInAVeryOldSegment: This test passed fine...");
+  }
+
+  /**
    * Retries the specified 'req' against each SolrClient in "clients" untill the expected number of 
    * results are returned, at which point the results are verified using assertDocIdsAndValuesInResults
    *
@@ -610,7 +643,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
     }
     
     threadpool.shutdown();
-    assertTrue("Thread pool didn't terminate within 10 secs", threadpool.awaitTermination(10, TimeUnit.SECONDS));
+    assertTrue("Thread pool didn't terminate within 15 secs", threadpool.awaitTermination(15, TimeUnit.SECONDS));
 
     // assert all requests were successful
     for (Future<UpdateResponse> resp: updateResponses) {
@@ -633,7 +666,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
   }
   
   // The following should work: full update to doc 0, in-place update for doc 0, delete doc 0
-  private void outOfOrderDeleteUpdatesIndividualReplicaTest() throws Exception {
+  private void reorderedDeletesTest() throws Exception {
     
     clearIndex();
     commit();
@@ -680,7 +713,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
     }
     
     threadpool.shutdown();
-    assertTrue("Thread pool didn't terminate within 10 secs", threadpool.awaitTermination(10, TimeUnit.SECONDS));
+    assertTrue("Thread pool didn't terminate within 15 secs", threadpool.awaitTermination(15, TimeUnit.SECONDS));
 
     // assert all requests were successful
     for (Future<UpdateResponse> resp: updateResponses) {
@@ -693,7 +726,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
       assertNull("This doc was supposed to have been deleted, but was: " + doc, doc);
     }
 
-    log.info("outOfOrderDeleteUpdatesIndividualReplicaTest: This test passed fine...");
+    log.info("reorderedDeletesTest: This test passed fine...");
     clearIndex();
     commit();
   }
@@ -707,7 +740,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
         DBQ(q=val:10, v=4)
         DV(id=x, val=5, ver=3)
    */
-  private void reorderedDBQsWithInPlaceUpdatesShouldNotThrowReplicaInLIRTest() throws Exception {
+  private void reorderedDBQsResurrectionTest() throws Exception {
     clearIndex();
     commit();
 
@@ -754,7 +787,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
     }
     
     threadpool.shutdown();
-    assertTrue("Thread pool didn't terminate within 10 secs", threadpool.awaitTermination(10, TimeUnit.SECONDS));
+    assertTrue("Thread pool didn't terminate within 15 secs", threadpool.awaitTermination(15, TimeUnit.SECONDS));
 
     int successful = 0;
     for (Future<UpdateResponse> resp: updateResponses) {
@@ -794,7 +827,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
       assertEquals("Client: "+((HttpSolrClient)client).getBaseURL(), 5, doc.getFieldValue(field));
     }
 
-    log.info("reorderedDBQsWithInPlaceUpdatesShouldNotThrowReplicaInLIRTest: This test passed fine...");
+    log.info("reorderedDBQsResurrectionTest: This test passed fine...");
     clearIndex();
     commit();
   }
@@ -829,7 +862,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
     }
 
     threadpool.shutdown();
-    assertTrue("Thread pool didn't terminate within 10 secs", threadpool.awaitTermination(15, TimeUnit.SECONDS));
+    assertTrue("Thread pool didn't terminate within 15 secs", threadpool.awaitTermination(15, TimeUnit.SECONDS));
 
     commit();
 
@@ -1104,7 +1137,7 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
    * inp(id=1,inpfield=14,prevVersion=2,version=3) // will wait till timeout, and then fetch a "not found" from leader
    * dbq("inp:14",version=4)
    */
-  private void testDBQUsingUpdatedFieldFromDroppedUpdate() throws Exception {
+  private void reorderedDBQsUsingUpdatedValueFromADroppedUpdate() throws Exception {
     clearIndex();
     commit();
     
@@ -1161,7 +1194,21 @@ public class TestInPlaceUpdatesDistrib extends AbstractFullDistribZkTestBase {
       assertNull(client.getById("1", params("distrib", "false")));
     }
 
-    log.info("testDBQUsingUpdatedFieldFromDroppedUpdate: This test passed fine...");
+    log.info("reorderedDBQsUsingUpdatedValueFromADroppedUpdate: This test passed fine...");
+  }
+
+  @Override
+  public void clearIndex() {
+    super.clearIndex();
+    try {
+      for (SolrClient client: new SolrClient[] {LEADER, NONLEADERS.get(0), NONLEADERS.get(1)}) {
+        if (client != null) {
+          client.request(simulatedDeleteRequest("*:*", -Long.MAX_VALUE));
+          client.commit();
+        }
+      }
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
   }
-  
 }


[08/23] lucene-solr:jira/solr-9835: LUCENE-7738: Add new InetAddressRangeField for indexing and querying InetAddress ranges.

Posted by da...@apache.org.
LUCENE-7738: Add new InetAddressRangeField for indexing and querying InetAddress ranges.


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

Branch: refs/heads/jira/solr-9835
Commit: 1745b0338e822db43f292f7ad495789b21c6634a
Parents: f3ba7f4
Author: Nicholas Knize <nk...@gmail.com>
Authored: Fri Mar 10 15:05:43 2017 -0600
Committer: Nicholas Knize <nk...@gmail.com>
Committed: Sat Mar 11 18:51:43 2017 -0600

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   3 +
 .../lucene/document/InetAddressRangeField.java  | 168 ++++++++++++++
 .../lucene/search/TestIpRangeFieldQueries.java  | 220 +++++++++++++++++++
 3 files changed, 391 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/1745b033/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 9407dfa..c2fe191 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -131,6 +131,9 @@ API Changes
 
 New Features
 
+* LUCENE-7738: Add new InetAddressRangeField for indexing and querying
+  InetAddress ranges. (Nick Knize)
+
 * LUCENE-7449: Add CROSSES relation support to RangeFieldQuery. (Nick Knize)
 
 * LUCENE-7623: Add FunctionScoreQuery and FunctionMatchQuery (Alan Woodward,

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/1745b033/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressRangeField.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressRangeField.java b/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressRangeField.java
new file mode 100644
index 0000000..c6ebc83
--- /dev/null
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressRangeField.java
@@ -0,0 +1,168 @@
+/*
+ * 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.net.InetAddress;
+
+import org.apache.lucene.document.RangeFieldQuery.QueryType;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.StringHelper;
+
+/**
+ * An indexed InetAddress Range Field
+ * <p>
+ * This field indexes an {@code InetAddress} range defined as a min/max pairs. It is single
+ * dimension only (indexed as two 16 byte paired values).
+ * <p>
+ * Multiple values are supported.
+ *
+ * <p>
+ * This field defines the following static factory methods for common search operations over Ip Ranges
+ * <ul>
+ *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ip ranges that intersect the defined search range.
+ *   <li>{@link #newWithinQuery newWithinQuery()} matches ip ranges that are within the defined search range.
+ *   <li>{@link #newContainsQuery newContainsQuery()} matches ip ranges that contain the defined search range.
+ *   <li>{@link #newCrossesQuery newCrossesQuery()} matches ip ranges that cross the defined search range
+ * </ul>
+ */
+public class InetAddressRangeField extends Field {
+  /** The number of bytes per dimension : sync w/ {@code InetAddressPoint} */
+  public static final int BYTES = InetAddressPoint.BYTES;
+
+  private static final FieldType TYPE;
+  static {
+    TYPE = new FieldType();
+    TYPE.setDimensions(2, BYTES);
+    TYPE.freeze();
+  }
+
+  /**
+   * Create a new InetAddressRangeField from min/max value
+   * @param name field name. must not be null.
+   * @param min range min value; defined as an {@code InetAddress}
+   * @param max range max value; defined as an {@code InetAddress}
+   */
+  public InetAddressRangeField(String name, final InetAddress min, final InetAddress max) {
+    super(name, TYPE);
+    setRangeValues(min, max);
+  }
+
+  /**
+   * Change (or set) the min/max values of the field.
+   * @param min range min value; defined as an {@code InetAddress}
+   * @param max range max value; defined as an {@code InetAddress}
+   */
+  public void setRangeValues(InetAddress min, InetAddress max) {
+    if (StringHelper.compare(BYTES, min.getAddress(), 0, max.getAddress(), 0) > 0) {
+      throw new IllegalArgumentException("min value cannot be greater than max value for range field (name=" + name + ")");
+    }
+    final byte[] bytes;
+    if (fieldsData == null) {
+      bytes = new byte[BYTES*2];
+      fieldsData = new BytesRef(bytes);
+    } else {
+      bytes = ((BytesRef)fieldsData).bytes;
+    }
+    encode(min, max, bytes);
+  }
+
+  /** encode the min/max range into the provided byte array */
+  private static void encode(final InetAddress min, final InetAddress max, final byte[] bytes) {
+    System.arraycopy(InetAddressPoint.encode(min), 0, bytes, 0, BYTES);
+    System.arraycopy(InetAddressPoint.encode(max), 0, bytes, BYTES, BYTES);
+  }
+
+  /** encode the min/max range and return the byte array */
+  private static byte[] encode(InetAddress min, InetAddress max) {
+    byte[] b = new byte[BYTES*2];
+    encode(min, max, b);
+    return b;
+  }
+
+  /**
+   * Create a query for matching indexed ip ranges that {@code INTERSECT} the defined range.
+   * @param field field name. must not be null.
+   * @param min range min value; provided as an {@code InetAddress}
+   * @param max range max value; provided as an {@code InetAddress}
+   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newIntersectsQuery(String field, final InetAddress min, final InetAddress max) {
+    return newRelationQuery(field, min, max, QueryType.INTERSECTS);
+  }
+
+  /**
+   * Create a query for matching indexed ip ranges that {@code CONTAINS} the defined range.
+   * @param field field name. must not be null.
+   * @param min range min value; provided as an {@code InetAddress}
+   * @param max range max value; provided as an {@code InetAddress}
+   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newContainsQuery(String field, final InetAddress min, final InetAddress max) {
+    return newRelationQuery(field, min, max, QueryType.CONTAINS);
+  }
+
+  /**
+   * Create a query for matching indexed ip ranges that are {@code WITHIN} the defined range.
+   * @param field field name. must not be null.
+   * @param min range min value; provided as an {@code InetAddress}
+   * @param max range max value; provided as an {@code InetAddress}
+   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newWithinQuery(String field, final InetAddress min, final InetAddress max) {
+    return newRelationQuery(field, min, max, QueryType.WITHIN);
+  }
+
+  /**
+   * Create a query for matching indexed ip ranges that {@code CROSS} the defined range.
+   * @param field field name. must not be null.
+   * @param min range min value; provided as an {@code InetAddress}
+   * @param max range max value; provided as an {@code InetAddress}
+   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newCrossesQuery(String field, final InetAddress min, final InetAddress max) {
+    return newRelationQuery(field, min, max, QueryType.CROSSES);
+  }
+
+  /** helper method for creating the desired relational query */
+  private static Query newRelationQuery(String field, final InetAddress min, final InetAddress max, QueryType relation) {
+    return new RangeFieldQuery(field, encode(min, max), 1, relation) {
+      @Override
+      protected String toString(byte[] ranges, int dimension) {
+        return InetAddressRangeField.toString(ranges, dimension);
+      }
+    };
+  }
+
+  /**
+   * Returns the String representation for the range at the given dimension
+   * @param ranges the encoded ranges, never null
+   * @param dimension the dimension of interest (not used for this field)
+   * @return The string representation for the range at the provided dimension
+   */
+  private static String toString(byte[] ranges, int dimension) {
+    byte[] min = new byte[BYTES];
+    System.arraycopy(ranges, 0, min, 0, BYTES);
+    byte[] max = new byte[BYTES];
+    System.arraycopy(ranges, BYTES, max, 0, BYTES);
+    return "[" + InetAddressPoint.decode(min) + " : " + InetAddressPoint.decode(max) + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/1745b033/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java
new file mode 100644
index 0000000..1563584
--- /dev/null
+++ b/lucene/sandbox/src/test/org/apache/lucene/search/TestIpRangeFieldQueries.java
@@ -0,0 +1,220 @@
+/*
+ * 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.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.apache.lucene.document.InetAddressRangeField;
+import org.apache.lucene.util.StringHelper;
+
+/**
+ * Random testing for {@link InetAddressRangeField}
+ */
+public class TestIpRangeFieldQueries extends BaseRangeFieldQueryTestCase {
+  private static final String FIELD_NAME = "ipRangeField";
+
+  private IPVersion ipVersion;
+
+  private enum IPVersion {IPv4, IPv6}
+
+  @Override
+  protected Range nextRange(int dimensions) {
+    try {
+      InetAddress min = nextInetaddress();
+      byte[] bMin = min.getAddress();
+      InetAddress max = nextInetaddress();
+      byte[] bMax = max.getAddress();
+      if (StringHelper.compare(bMin.length, bMin, 0, bMax, 0) > 0) {
+        return new IpRange(max, min);
+      }
+      return new IpRange(min, max);
+    } catch (UnknownHostException e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /** return random IPv4 or IPv6 address */
+  private InetAddress nextInetaddress() throws UnknownHostException {
+    byte[] b;
+    switch (ipVersion) {
+      case IPv4:
+        b = new byte[4];
+        break;
+      case IPv6:
+        b = new byte[16];
+        break;
+      default:
+        throw new IllegalArgumentException("incorrect IP version: " + ipVersion);
+    }
+    random().nextBytes(b);
+    return InetAddress.getByAddress(b);
+  }
+
+  /** randomly select version across tests */
+  private IPVersion ipVersion() {
+    return random().nextBoolean() ? IPVersion.IPv4 : IPVersion.IPv6;
+  }
+
+  @Override
+  public void testRandomTiny() throws Exception {
+    ipVersion = ipVersion();
+    super.testRandomTiny();
+  }
+
+  @Override
+  public void testMultiValued() throws Exception {
+    ipVersion = ipVersion();
+    super.testRandomMedium();
+  }
+
+  @Override
+  public void testRandomMedium() throws Exception {
+    ipVersion = ipVersion();
+    super.testMultiValued();
+  }
+
+  @Nightly
+  @Override
+  public void testRandomBig() throws Exception {
+    ipVersion = ipVersion();
+    super.testRandomBig();
+  }
+
+  /** return random range */
+  @Override
+  protected InetAddressRangeField newRangeField(Range r) {
+    return new InetAddressRangeField(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** return random intersects query */
+  @Override
+  protected Query newIntersectsQuery(Range r) {
+    return InetAddressRangeField.newIntersectsQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** return random contains query */
+  @Override
+  protected Query newContainsQuery(Range r) {
+    return InetAddressRangeField.newContainsQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** return random within query */
+  @Override
+  protected Query newWithinQuery(Range r) {
+    return InetAddressRangeField.newWithinQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** return random crosses query */
+  @Override
+  protected Query newCrossesQuery(Range r) {
+    return InetAddressRangeField.newCrossesQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** encapsulated IpRange for test validation */
+  private class IpRange extends Range {
+    InetAddress min;
+    InetAddress max;
+
+    IpRange(InetAddress min, InetAddress max) {
+      this.min = min;
+      this.max = max;
+    }
+
+    @Override
+    protected int numDimensions() {
+      return 1;
+    }
+
+    @Override
+    protected InetAddress getMin(int dim) {
+      return min;
+    }
+
+    @Override
+    protected void setMin(int dim, Object val) {
+      byte[] v = ((InetAddress)val).getAddress();
+
+      if (StringHelper.compare(v.length, min.getAddress(), 0, v, 0) < 0) {
+        max = (InetAddress)val;
+      } else {
+        min = (InetAddress) val;
+      }
+    }
+
+    @Override
+    protected InetAddress getMax(int dim) {
+      return max;
+    }
+
+    @Override
+    protected void setMax(int dim, Object val) {
+      byte[] v = ((InetAddress)val).getAddress();
+
+      if (StringHelper.compare(v.length, max.getAddress(), 0, v, 0) > 0) {
+        min = (InetAddress)val;
+      } else {
+        max = (InetAddress) val;
+      }
+    }
+
+    @Override
+    protected boolean isEqual(Range o) {
+      IpRange other = (IpRange)o;
+      return this.min.equals(other.min) && this.max.equals(other.max);
+    }
+
+    @Override
+    protected boolean isDisjoint(Range o) {
+      IpRange other = (IpRange)o;
+      byte[] bMin = min.getAddress();
+      byte[] bMax = max.getAddress();
+      return StringHelper.compare(bMin.length, bMin, 0, other.max.getAddress(), 0) > 0 ||
+          StringHelper.compare(bMax.length, bMax, 0, other.min.getAddress(), 0) < 0;
+    }
+
+    @Override
+    protected boolean isWithin(Range o) {
+      IpRange other = (IpRange)o;
+      byte[] bMin = min.getAddress();
+      byte[] bMax = max.getAddress();
+      return StringHelper.compare(bMin.length, bMin, 0, other.min.getAddress(), 0) >= 0 &&
+          StringHelper.compare(bMax.length, bMax, 0, other.max.getAddress(), 0) <= 0;
+    }
+
+    @Override
+    protected boolean contains(Range o) {
+      IpRange other = (IpRange)o;
+      byte[] bMin = min.getAddress();
+      byte[] bMax = max.getAddress();
+      return StringHelper.compare(bMin.length, bMin, 0, other.min.getAddress(), 0) <= 0 &&
+          StringHelper.compare(bMax.length, bMax, 0, other.max.getAddress(), 0) >= 0;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder b = new StringBuilder();
+      b.append("Box(");
+      b.append(min.getHostAddress());
+      b.append(" TO ");
+      b.append(max.getHostAddress());
+      b.append(")");
+      return b.toString();
+    }
+  }
+}


[19/23] lucene-solr:jira/solr-9835: SOLR-9045: Make RecoveryStrategy settings configurable.

Posted by da...@apache.org.
SOLR-9045: Make RecoveryStrategy settings configurable.


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

Branch: refs/heads/jira/solr-9835
Commit: c8bad8c10ac52d89318932636b1e1401c314b5e4
Parents: ceffbf9
Author: Christine Poerschke <cp...@apache.org>
Authored: Mon Mar 13 15:49:01 2017 +0000
Committer: Christine Poerschke <cp...@apache.org>
Committed: Mon Mar 13 15:49:01 2017 +0000

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   2 +
 .../org/apache/solr/cloud/RecoveryStrategy.java | 106 ++++++++++++++----
 .../java/org/apache/solr/core/SolrConfig.java   |   2 +
 .../src/java/org/apache/solr/core/SolrCore.java |  22 +++-
 .../solr/update/DefaultSolrCoreState.java       |  15 ++-
 .../org/apache/solr/update/SolrCoreState.java   |   6 +
 .../solrconfig-configurerecoverystrategy.xml    |  28 +++++
 .../conf/solrconfig-customrecoverystrategy.xml  |  32 ++++++
 .../core/ConfigureRecoveryStrategyTest.java     | 111 +++++++++++++++++++
 9 files changed, 300 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c8bad8c1/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 5d6d9d7..1469d3e 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -173,6 +173,8 @@ New Features
   However, if there is a leader election while this request is in transit, the versions may not be returned from that
   shard. (Boris Naguet, Ishan Chattopadhyaya)
 
+* SOLR-9045: Make RecoveryStrategy settings configurable. (Christine Poerschke)
+
 Bug Fixes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c8bad8c1/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java b/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
index 3bd2e74..8865c08 100644
--- a/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
+++ b/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
@@ -46,6 +46,7 @@ import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.cloud.ZooKeeperException;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.UpdateParams;
+import org.apache.solr.common.util.NamedList;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.CoreDescriptor;
 import org.apache.solr.core.DirectoryFactory.DirContext;
@@ -62,17 +63,43 @@ import org.apache.solr.update.UpdateLog;
 import org.apache.solr.update.UpdateLog.RecoveryInfo;
 import org.apache.solr.update.processor.DistributedUpdateProcessor;
 import org.apache.solr.util.RefCounted;
+import org.apache.solr.util.SolrPluginUtils;
+import org.apache.solr.util.plugin.NamedListInitializedPlugin;
 import org.apache.zookeeper.KeeperException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+/**
+ * This class may change in future and customisations are not supported
+ * between versions in terms of API or back compat behaviour.
+ * @lucene.experimental
+ */
 public class RecoveryStrategy extends Thread implements Closeable {
 
+  public static class Builder implements NamedListInitializedPlugin {
+    private NamedList args;
+    @Override
+    public void init(NamedList args) {
+      this.args = args;
+    }
+    // this should only be used from SolrCoreState
+    public RecoveryStrategy create(CoreContainer cc, CoreDescriptor cd,
+        RecoveryStrategy.RecoveryListener recoveryListener) {
+      final RecoveryStrategy recoveryStrategy = newRecoveryStrategy(cc, cd, recoveryListener);
+      SolrPluginUtils.invokeSetters(recoveryStrategy, args);
+      return recoveryStrategy;
+    }
+    protected RecoveryStrategy newRecoveryStrategy(CoreContainer cc, CoreDescriptor cd,
+        RecoveryStrategy.RecoveryListener recoveryListener) {
+      return new RecoveryStrategy(cc, cd, recoveryListener);
+    }
+  }
+
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  private static final int WAIT_FOR_UPDATES_WITH_STALE_STATE_PAUSE = Integer.getInteger("solr.cloud.wait-for-updates-with-stale-state-pause", 2500);
-  private static final int MAX_RETRIES = 500;
-  private static final int STARTING_RECOVERY_DELAY = 5000;
+  private int waitForUpdatesWithStaleStatePauseMilliSeconds = Integer.getInteger("solr.cloud.wait-for-updates-with-stale-state-pause", 2500);
+  private int maxRetries = 500;
+  private int startingRecoveryDelayMilliSeconds = 5000;
 
   public static interface RecoveryListener {
     public void recovered();
@@ -92,8 +119,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
   private CoreContainer cc;
   private volatile HttpUriRequest prevSendPreRecoveryHttpUriRequest;
   
-  // this should only be used from SolrCoreState
-  public RecoveryStrategy(CoreContainer cc, CoreDescriptor cd, RecoveryListener recoveryListener) {
+  protected RecoveryStrategy(CoreContainer cc, CoreDescriptor cd, RecoveryListener recoveryListener) {
     this.cc = cc;
     this.coreName = cd.getName();
     this.recoveryListener = recoveryListener;
@@ -104,13 +130,41 @@ public class RecoveryStrategy extends Thread implements Closeable {
     coreZkNodeName = cd.getCloudDescriptor().getCoreNodeName();
   }
 
-  public void setRecoveringAfterStartup(boolean recoveringAfterStartup) {
+  final public int getWaitForUpdatesWithStaleStatePauseMilliSeconds() {
+    return waitForUpdatesWithStaleStatePauseMilliSeconds;
+  }
+
+  final public void setWaitForUpdatesWithStaleStatePauseMilliSeconds(int waitForUpdatesWithStaleStatePauseMilliSeconds) {
+    this.waitForUpdatesWithStaleStatePauseMilliSeconds = waitForUpdatesWithStaleStatePauseMilliSeconds;
+  }
+
+  final public int getMaxRetries() {
+    return maxRetries;
+  }
+
+  final public void setMaxRetries(int maxRetries) {
+    this.maxRetries = maxRetries;
+  }
+
+  final public int getStartingRecoveryDelayMilliSeconds() {
+    return startingRecoveryDelayMilliSeconds;
+  }
+
+  final public void setStartingRecoveryDelayMilliSeconds(int startingRecoveryDelayMilliSeconds) {
+    this.startingRecoveryDelayMilliSeconds = startingRecoveryDelayMilliSeconds;
+  }
+
+  final public boolean getRecoveringAfterStartup() {
+    return recoveringAfterStartup;
+  }
+
+  final public void setRecoveringAfterStartup(boolean recoveringAfterStartup) {
     this.recoveringAfterStartup = recoveringAfterStartup;
   }
 
   // make sure any threads stop retrying
   @Override
-  public void close() {
+  final public void close() {
     close = true;
     if (prevSendPreRecoveryHttpUriRequest != null) {
       prevSendPreRecoveryHttpUriRequest.abort();
@@ -118,7 +172,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
     LOG.warn("Stopping recovery for core=[{}] coreNodeName=[{}]", coreName, coreZkNodeName);
   }
 
-  private void recoveryFailed(final SolrCore core,
+  final private void recoveryFailed(final SolrCore core,
       final ZkController zkController, final String baseUrl,
       final String shardZkNodeName, final CoreDescriptor cd) throws KeeperException, InterruptedException {
     SolrException.log(LOG, "Recovery failed - I give up.");
@@ -130,11 +184,19 @@ public class RecoveryStrategy extends Thread implements Closeable {
     }
   }
   
-  private void replicate(String nodeName, SolrCore core, ZkNodeProps leaderprops)
+  /**
+   * This method may change in future and customisations are not supported
+   * between versions in terms of API or back compat behaviour.
+   * @lucene.experimental
+   */
+  protected String getReplicateLeaderUrl(ZkNodeProps leaderprops) {
+    return new ZkCoreNodeProps(leaderprops).getCoreUrl();
+  }
+
+  final private void replicate(String nodeName, SolrCore core, ZkNodeProps leaderprops)
       throws SolrServerException, IOException {
 
-    ZkCoreNodeProps leaderCNodeProps = new ZkCoreNodeProps(leaderprops);
-    String leaderUrl = leaderCNodeProps.getCoreUrl();
+    final String leaderUrl = getReplicateLeaderUrl(leaderprops);
     
     LOG.info("Attempting to replicate from [{}].", leaderUrl);
     
@@ -191,7 +253,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
 
   }
 
-  private void commitOnLeader(String leaderUrl) throws SolrServerException,
+  final private void commitOnLeader(String leaderUrl) throws SolrServerException,
       IOException {
     try (HttpSolrClient client = new HttpSolrClient.Builder(leaderUrl).build()) {
       client.setConnectionTimeout(30000);
@@ -205,7 +267,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
   }
 
   @Override
-  public void run() {
+  final public void run() {
 
     // set request info for logging
     try (SolrCore core = cc.getCore(coreName)) {
@@ -234,7 +296,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
   }
 
   // TODO: perhaps make this grab a new core each time through the loop to handle core reloads?
-  public void doRecovery(SolrCore core) throws KeeperException, InterruptedException {
+  final public void doRecovery(SolrCore core) throws KeeperException, InterruptedException {
     boolean replayed = false;
     boolean successfulRecovery = false;
 
@@ -360,7 +422,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
         // are sure to have finished (see SOLR-7141 for
         // discussion around current value)
         try {
-          Thread.sleep(WAIT_FOR_UPDATES_WITH_STALE_STATE_PAUSE);
+          Thread.sleep(waitForUpdatesWithStaleStatePauseMilliSeconds);
         } catch (InterruptedException e) {
           Thread.currentThread().interrupt();
         }
@@ -479,7 +541,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
           LOG.error("Recovery failed - trying again... (" + retries + ")");
           
           retries++;
-          if (retries >= MAX_RETRIES) {
+          if (retries >= maxRetries) {
             SolrException.log(LOG, "Recovery failed - max retries exceeded (" + retries + ").");
             try {
               recoveryFailed(core, zkController, baseUrl, coreZkNodeName, core.getCoreDescriptor());
@@ -504,7 +566,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
               LOG.info("RecoveryStrategy has been closed");
               break; // check if someone closed us
             }
-            Thread.sleep(STARTING_RECOVERY_DELAY);
+            Thread.sleep(startingRecoveryDelayMilliSeconds);
           }
         } catch (InterruptedException e) {
           Thread.currentThread().interrupt();
@@ -525,7 +587,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
     LOG.info("Finished recovery process, successful=[{}]", Boolean.toString(successfulRecovery));
   }
 
-  private Future<RecoveryInfo> replay(SolrCore core)
+  final private Future<RecoveryInfo> replay(SolrCore core)
       throws InterruptedException, ExecutionException {
     Future<RecoveryInfo> future = core.getUpdateHandler().getUpdateLog().applyBufferedUpdates();
     if (future == null) {
@@ -547,7 +609,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
     return future;
   }
   
-  private void cloudDebugLog(SolrCore core, String op) {
+  final private void cloudDebugLog(SolrCore core, String op) {
     if (!LOG.isDebugEnabled()) {
       return;
     }
@@ -566,11 +628,11 @@ public class RecoveryStrategy extends Thread implements Closeable {
     }
   }
 
-  public boolean isClosed() {
+  final public boolean isClosed() {
     return close;
   }
   
-  private void sendPrepRecoveryCmd(String leaderBaseUrl, String leaderCoreName, Slice slice)
+  final private void sendPrepRecoveryCmd(String leaderBaseUrl, String leaderCoreName, Slice slice)
       throws SolrServerException, IOException, InterruptedException, ExecutionException {
 
     WaitForState prepCmd = new WaitForState();
@@ -603,7 +665,7 @@ public class RecoveryStrategy extends Thread implements Closeable {
     }
   }
 
-  private void sendPrepRecoveryCmd(String leaderBaseUrl, WaitForState prepCmd)
+  final private void sendPrepRecoveryCmd(String leaderBaseUrl, WaitForState prepCmd)
       throws SolrServerException, IOException, InterruptedException, ExecutionException {
     try (HttpSolrClient client = new HttpSolrClient.Builder(leaderBaseUrl).build()) {
       client.setConnectionTimeout(10000);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c8bad8c1/solr/core/src/java/org/apache/solr/core/SolrConfig.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/SolrConfig.java b/solr/core/src/java/org/apache/solr/core/SolrConfig.java
index bd98075..a244420 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrConfig.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrConfig.java
@@ -58,6 +58,7 @@ import org.apache.lucene.index.IndexDeletionPolicy;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.util.Version;
 import org.apache.solr.client.solrj.io.stream.expr.Expressible;
+import org.apache.solr.cloud.RecoveryStrategy;
 import org.apache.solr.cloud.ZkSolrResourceLoader;
 import org.apache.solr.common.MapSerializable;
 import org.apache.solr.common.SolrException;
@@ -357,6 +358,7 @@ public class SolrConfig extends Config implements MapSerializable {
       .add(new SolrPluginInfo(SolrEventListener.class, "//listener", REQUIRE_CLASS, MULTI_OK, REQUIRE_NAME_IN_OVERLAY))
 
       .add(new SolrPluginInfo(DirectoryFactory.class, "directoryFactory", REQUIRE_CLASS))
+      .add(new SolrPluginInfo(RecoveryStrategy.Builder.class, "recoveryStrategy"))
       .add(new SolrPluginInfo(IndexDeletionPolicy.class, "indexConfig/deletionPolicy", REQUIRE_CLASS))
       .add(new SolrPluginInfo(CodecFactory.class, "codecFactory", REQUIRE_CLASS))
       .add(new SolrPluginInfo(IndexReaderFactory.class, "indexReaderFactory", REQUIRE_CLASS))

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c8bad8c1/solr/core/src/java/org/apache/solr/core/SolrCore.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java
index 13c3bdd..70203d4 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -74,6 +74,7 @@ import org.apache.lucene.store.IndexOutput;
 import org.apache.lucene.store.LockObtainFailedException;
 import org.apache.solr.client.solrj.impl.BinaryResponseParser;
 import org.apache.solr.cloud.CloudDescriptor;
+import org.apache.solr.cloud.RecoveryStrategy;
 import org.apache.solr.cloud.ZkSolrResourceLoader;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.cloud.ClusterState;
@@ -203,6 +204,7 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
   private final IndexDeletionPolicyWrapper solrDelPolicy;
   private final SolrSnapshotMetaDataManager snapshotMgr;
   private final DirectoryFactory directoryFactory;
+  private final RecoveryStrategy.Builder recoveryStrategyBuilder;
   private IndexReaderFactory indexReaderFactory;
   private final Codec codec;
   private final MemClassLoader memClassLoader;
@@ -657,6 +659,22 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
     return DirectoryFactory.loadDirectoryFactory(solrConfig, getCoreDescriptor().getCoreContainer(), coreMetricManager.getRegistryName());
   }
 
+  private RecoveryStrategy.Builder initRecoveryStrategyBuilder() {
+    final PluginInfo info = solrConfig.getPluginInfo(RecoveryStrategy.Builder.class.getName());
+    final RecoveryStrategy.Builder rsBuilder;
+    if (info != null && info.className != null) {
+      log.info(info.className);
+      rsBuilder = getResourceLoader().newInstance(info.className, RecoveryStrategy.Builder.class);
+    } else {
+      log.info("solr.RecoveryStrategy.Builder");
+      rsBuilder = new RecoveryStrategy.Builder();
+    }
+    if (info != null) {
+      rsBuilder.init(info.initArgs);
+    }
+    return rsBuilder;
+  }
+
   private void initIndexReaderFactory() {
     IndexReaderFactory indexReaderFactory;
     PluginInfo info = solrConfig.getPluginInfo(IndexReaderFactory.class.getName());
@@ -864,10 +882,12 @@ public final class SolrCore implements SolrInfoMBean, Closeable {
 
     if (updateHandler == null) {
       directoryFactory = initDirectoryFactory();
-      solrCoreState = new DefaultSolrCoreState(directoryFactory);
+      recoveryStrategyBuilder = initRecoveryStrategyBuilder();
+      solrCoreState = new DefaultSolrCoreState(directoryFactory, recoveryStrategyBuilder);
     } else {
       solrCoreState = updateHandler.getSolrCoreState();
       directoryFactory = solrCoreState.getDirectoryFactory();
+      recoveryStrategyBuilder = solrCoreState.getRecoveryStrategyBuilder();
       isReloaded = true;
     }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c8bad8c1/solr/core/src/java/org/apache/solr/update/DefaultSolrCoreState.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/update/DefaultSolrCoreState.java b/solr/core/src/java/org/apache/solr/update/DefaultSolrCoreState.java
index 59e35f0..d0daebb 100644
--- a/solr/core/src/java/org/apache/solr/update/DefaultSolrCoreState.java
+++ b/solr/core/src/java/org/apache/solr/update/DefaultSolrCoreState.java
@@ -63,6 +63,7 @@ public final class DefaultSolrCoreState extends SolrCoreState implements Recover
 
   private SolrIndexWriter indexWriter = null;
   private DirectoryFactory directoryFactory;
+  private final RecoveryStrategy.Builder recoveryStrategyBuilder;
 
   private volatile RecoveryStrategy recoveryStrat;
 
@@ -76,8 +77,15 @@ public final class DefaultSolrCoreState extends SolrCoreState implements Recover
   
   protected final ReentrantLock commitLock = new ReentrantLock();
 
+  @Deprecated
   public DefaultSolrCoreState(DirectoryFactory directoryFactory) {
+    this(directoryFactory, new RecoveryStrategy.Builder());
+  }
+
+  public DefaultSolrCoreState(DirectoryFactory directoryFactory,
+      RecoveryStrategy.Builder recoveryStrategyBuilder) {
     this.directoryFactory = directoryFactory;
+    this.recoveryStrategyBuilder = recoveryStrategyBuilder;
   }
   
   private void closeIndexWriter(IndexWriterCloser closer) {
@@ -263,6 +271,11 @@ public final class DefaultSolrCoreState extends SolrCoreState implements Recover
   }
 
   @Override
+  public RecoveryStrategy.Builder getRecoveryStrategyBuilder() {
+    return recoveryStrategyBuilder;
+  }
+
+  @Override
   public void doRecovery(CoreContainer cc, CoreDescriptor cd) {
     
     Thread thread = new Thread() {
@@ -310,7 +323,7 @@ public final class DefaultSolrCoreState extends SolrCoreState implements Recover
               recoveryThrottle.minimumWaitBetweenActions();
               recoveryThrottle.markAttemptingAction();
               
-              recoveryStrat = new RecoveryStrategy(cc, cd, DefaultSolrCoreState.this);
+              recoveryStrat = recoveryStrategyBuilder.create(cc, cd, DefaultSolrCoreState.this);
               recoveryStrat.setRecoveringAfterStartup(recoveringAfterStartup);
               Future<?> future = cc.getUpdateShardHandler().getRecoveryExecutor().submit(recoveryStrat);
               try {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c8bad8c1/solr/core/src/java/org/apache/solr/update/SolrCoreState.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/update/SolrCoreState.java b/solr/core/src/java/org/apache/solr/update/SolrCoreState.java
index f775b72..31dd66a 100644
--- a/solr/core/src/java/org/apache/solr/update/SolrCoreState.java
+++ b/solr/core/src/java/org/apache/solr/update/SolrCoreState.java
@@ -23,6 +23,7 @@ import java.util.concurrent.locks.Lock;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.search.Sort;
 import org.apache.solr.cloud.ActionThrottle;
+import org.apache.solr.cloud.RecoveryStrategy;
 import org.apache.solr.core.CoreContainer;
 import org.apache.solr.core.CoreDescriptor;
 import org.apache.solr.core.DirectoryFactory;
@@ -144,6 +145,11 @@ public abstract class SolrCoreState {
    */
   public abstract DirectoryFactory getDirectoryFactory();
 
+  /**
+   * @return the {@link org.apache.solr.cloud.RecoveryStrategy.Builder} that should be used.
+   */
+  public abstract RecoveryStrategy.Builder getRecoveryStrategyBuilder();
+
 
   public interface IndexWriterCloser {
     void closeWriter(IndexWriter writer) throws IOException;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c8bad8c1/solr/core/src/test-files/solr/collection1/conf/solrconfig-configurerecoverystrategy.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/solrconfig-configurerecoverystrategy.xml b/solr/core/src/test-files/solr/collection1/conf/solrconfig-configurerecoverystrategy.xml
new file mode 100644
index 0000000..62e671d
--- /dev/null
+++ b/solr/core/src/test-files/solr/collection1/conf/solrconfig-configurerecoverystrategy.xml
@@ -0,0 +1,28 @@
+<?xml version="1.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.
+-->
+
+<config>
+  <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
+  <xi:include href="solrconfig.snippet.randomindexconfig.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+  <requestHandler name="standard" class="solr.StandardRequestHandler"></requestHandler>
+  <recoveryStrategy>
+    <int name="maxRetries">250</int>
+  </recoveryStrategy>
+  <schemaFactory class="ClassicIndexSchemaFactory"/>
+</config>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c8bad8c1/solr/core/src/test-files/solr/collection1/conf/solrconfig-customrecoverystrategy.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/solrconfig-customrecoverystrategy.xml b/solr/core/src/test-files/solr/collection1/conf/solrconfig-customrecoverystrategy.xml
new file mode 100644
index 0000000..d43ed29
--- /dev/null
+++ b/solr/core/src/test-files/solr/collection1/conf/solrconfig-customrecoverystrategy.xml
@@ -0,0 +1,32 @@
+<?xml version="1.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.
+-->
+
+<config>
+  <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
+  <xi:include href="solrconfig.snippet.randomindexconfig.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+  <requestHandler name="standard" class="solr.StandardRequestHandler"></requestHandler>
+  <!--
+    The RecoveryStrategy and RecoveryStrategy.Builder classes may change in future and customisations
+    are not supported between versions in terms of API or back compat behaviour.
+  -->
+  <recoveryStrategy class="org.apache.solr.core.ConfigureRecoveryStrategyTest$CustomRecoveryStrategyBuilder">
+    <str name="alternativeBaseUrlProp">recovery_base_url</str>
+  </recoveryStrategy>
+  <schemaFactory class="ClassicIndexSchemaFactory"/>
+</config>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c8bad8c1/solr/core/src/test/org/apache/solr/core/ConfigureRecoveryStrategyTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/core/ConfigureRecoveryStrategyTest.java b/solr/core/src/test/org/apache/solr/core/ConfigureRecoveryStrategyTest.java
new file mode 100644
index 0000000..80032af
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/core/ConfigureRecoveryStrategyTest.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.core;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.cloud.RecoveryStrategy;
+import org.apache.solr.common.cloud.ZkCoreNodeProps;
+import org.apache.solr.common.cloud.ZkNodeProps;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.junit.BeforeClass;
+
+/**
+ * test that configs can override the RecoveryStrategy
+ */
+public class ConfigureRecoveryStrategyTest extends SolrTestCaseJ4 {
+
+  private static final String solrConfigFileNameConfigure = "solrconfig-configurerecoverystrategy.xml";
+  private static final String solrConfigFileNameCustom = "solrconfig-customrecoverystrategy.xml";
+
+  private static String solrConfigFileName;
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    solrConfigFileName = (random().nextBoolean()
+        ? solrConfigFileNameConfigure : solrConfigFileNameCustom);
+    initCore(solrConfigFileName, "schema.xml");
+  }
+
+  public void testBuilder() throws Exception {
+    final RecoveryStrategy.Builder recoveryStrategyBuilder =
+        h.getCore().getSolrCoreState().getRecoveryStrategyBuilder();
+    assertNotNull("recoveryStrategyBuilder is null", recoveryStrategyBuilder);
+
+    final String expectedClassName;
+
+    if (solrConfigFileName.equals(solrConfigFileNameConfigure)) {
+      expectedClassName = RecoveryStrategy.Builder.class.getName();
+    } else if (solrConfigFileName.equals(solrConfigFileNameCustom)) {
+      assertTrue("recoveryStrategyBuilder is wrong class (instanceof)",
+          recoveryStrategyBuilder instanceof CustomRecoveryStrategyBuilder);
+      expectedClassName = ConfigureRecoveryStrategyTest.CustomRecoveryStrategyBuilder.class.getName();
+    } else {
+      expectedClassName = null;
+    }
+
+    assertEquals("recoveryStrategyBuilder is wrong class (name)",
+        expectedClassName, recoveryStrategyBuilder.getClass().getName());
+  }
+
+  public void testAlmostAllMethodsAreFinal() throws Exception {
+    for (Method m : RecoveryStrategy.class.getDeclaredMethods()) {
+      final String methodName = m.getName();
+      if ("getReplicateLeaderUrl".equals(methodName)) {
+        assertFalse(m.toString(), Modifier.isFinal(m.getModifiers()));
+      } else {
+        assertTrue(m.toString(), Modifier.isFinal(m.getModifiers()));
+      }
+    }
+  }
+
+  static public class CustomRecoveryStrategy extends RecoveryStrategy {
+
+    private String alternativeBaseUrlProp;
+
+    public String getAlternativeBaseUrlProp() {
+      return alternativeBaseUrlProp;
+    }
+
+    public void setAlternativeBaseUrlProp(String alternativeBaseUrlProp) {
+      this.alternativeBaseUrlProp = alternativeBaseUrlProp;
+    }
+
+    public CustomRecoveryStrategy(CoreContainer cc, CoreDescriptor cd,
+        RecoveryStrategy.RecoveryListener recoveryListener) {
+      super(cc, cd, recoveryListener);
+    }
+
+    @Override
+    protected String getReplicateLeaderUrl(ZkNodeProps leaderprops) {
+      return ZkCoreNodeProps.getCoreUrl(
+          leaderprops.getStr(alternativeBaseUrlProp),
+          leaderprops.getStr(ZkStateReader.CORE_NAME_PROP));
+    }
+  }
+
+  static public class CustomRecoveryStrategyBuilder extends RecoveryStrategy.Builder {
+    @Override
+    protected RecoveryStrategy newRecoveryStrategy(CoreContainer cc, CoreDescriptor cd,
+        RecoveryStrategy.RecoveryListener recoveryListener) {
+      return new CustomRecoveryStrategy(cc, cd, recoveryListener);
+    }
+  }
+
+}


[23/23] lucene-solr:jira/solr-9835: Merge branch 'master' into jira/solr-9835

Posted by da...@apache.org.
Merge branch 'master' into jira/solr-9835


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

Branch: refs/heads/jira/solr-9835
Commit: 2f64575dc9064353d32ce7cc8a811ad94d9fd7c7
Parents: f32977d faeb1fe
Author: Cao Manh Dat <da...@apache.org>
Authored: Tue Mar 14 09:18:06 2017 +0700
Committer: Cao Manh Dat <da...@apache.org>
Committed: Tue Mar 14 09:18:06 2017 +0700

----------------------------------------------------------------------
 dev-tools/scripts/smokeTestRelease.py           |   4 +
 lucene/CHANGES.txt                              |  19 +
 .../org/apache/lucene/document/DoubleRange.java | 271 +++++++++++++++
 .../org/apache/lucene/document/FieldType.java   |   6 +-
 .../org/apache/lucene/document/FloatRange.java  | 271 +++++++++++++++
 .../org/apache/lucene/document/IntRange.java    | 271 +++++++++++++++
 .../org/apache/lucene/document/LongRange.java   | 269 ++++++++++++++
 .../apache/lucene/document/RangeFieldQuery.java | 340 ++++++++++++++++++
 .../lucene/index/ConcurrentMergeScheduler.java  |  75 ++--
 .../org/apache/lucene/index/IndexWriter.java    |  94 ++---
 .../org/apache/lucene/index/MergePolicy.java    | 184 +++++++++-
 .../apache/lucene/index/MergeRateLimiter.java   | 177 ++++------
 .../org/apache/lucene/index/MergeScheduler.java |  12 +
 .../apache/lucene/index/NoMergeScheduler.java   |   7 +
 .../org/apache/lucene/index/PointValues.java    |   2 +-
 .../org/apache/lucene/search/BooleanQuery.java  |   1 -
 .../lucene/index/TestMergeRateLimiter.java      |   4 +-
 .../search/TestDoubleRangeFieldQueries.java     | 251 ++++++++++++++
 .../search/TestFloatRangeFieldQueries.java      | 251 ++++++++++++++
 .../lucene/search/TestIntRangeFieldQueries.java | 251 ++++++++++++++
 .../search/TestLongRangeFieldQueries.java       | 251 ++++++++++++++
 .../lucene/document/InetAddressPoint.java       | 313 +++++++++++++++++
 .../lucene/document/InetAddressRange.java       | 168 +++++++++
 .../lucene/document/TestInetAddressPoint.java   | 176 ++++++++++
 .../search/TestInetAddressRangeQueries.java     | 215 ++++++++++++
 .../lucene/document/DoubleRangeField.java       | 282 ---------------
 .../apache/lucene/document/FloatRangeField.java | 282 ---------------
 .../lucene/document/InetAddressPoint.java       | 313 -----------------
 .../apache/lucene/document/IntRangeField.java   | 282 ---------------
 .../apache/lucene/document/LongRangeField.java  | 280 ---------------
 .../apache/lucene/document/RangeFieldQuery.java | 340 ------------------
 .../org/apache/lucene/document/package.html     |   3 +-
 .../lucene/document/TestDoubleRangeField.java   |  10 +-
 .../lucene/document/TestInetAddressPoint.java   | 176 ----------
 .../search/BaseRangeFieldQueryTestCase.java     | 344 ------------------
 .../search/TestDoubleRangeFieldQueries.java     | 251 --------------
 .../search/TestFloatRangeFieldQueries.java      | 251 --------------
 .../lucene/search/TestIntRangeFieldQueries.java | 251 --------------
 .../search/TestLongRangeFieldQueries.java       | 251 --------------
 .../search/BaseRangeFieldQueryTestCase.java     | 346 +++++++++++++++++++
 solr/CHANGES.txt                                |  19 +
 .../java/org/apache/solr/api/V2HttpCall.java    |   2 +-
 .../org/apache/solr/cloud/RecoveryStrategy.java | 106 ++++--
 .../java/org/apache/solr/core/PluginBag.java    |   2 +-
 .../java/org/apache/solr/core/SolrConfig.java   |   2 +
 .../src/java/org/apache/solr/core/SolrCore.java |  22 +-
 .../org/apache/solr/handler/StreamHandler.java  |   3 +-
 .../solr/schema/AbstractSpatialFieldType.java   |   2 +-
 .../org/apache/solr/schema/DatePointField.java  |  11 -
 .../apache/solr/schema/DoublePointField.java    |  12 -
 .../java/org/apache/solr/schema/EnumField.java  |   9 -
 .../java/org/apache/solr/schema/FieldType.java  |  11 -
 .../org/apache/solr/schema/FloatPointField.java |  13 -
 .../org/apache/solr/schema/IntPointField.java   |  11 -
 .../solr/schema/LatLonPointSpatialField.java    | 272 +++++++++++++++
 .../org/apache/solr/schema/LongPointField.java  |  11 -
 .../schema/SpatialPointVectorFieldType.java     |   7 -
 .../java/org/apache/solr/schema/TrieField.java  |  17 -
 .../apache/solr/servlet/SolrDispatchFilter.java |   2 +-
 .../solr/update/DefaultSolrCoreState.java       |  15 +-
 .../org/apache/solr/update/SolrCoreState.java   |   6 +
 .../processor/AtomicUpdateDocumentMerger.java   |  11 +-
 .../org/apache/solr/util/stats/MetricUtils.java |   7 +-
 .../solr/collection1/conf/schema-spatial.xml    |   4 +
 .../solrconfig-configurerecoverystrategy.xml    |  28 ++
 .../conf/solrconfig-customrecoverystrategy.xml  |  32 ++
 .../conf/solrconfig-managed-schema.xml          |   2 +-
 .../org/apache/solr/cloud/rule/RulesTest.java   |   2 +-
 .../core/ConfigureRecoveryStrategyTest.java     | 111 ++++++
 .../apache/solr/core/TestDynamicLoading.java    |   2 +-
 .../apache/solr/core/TestSolrConfigHandler.java |  14 +-
 .../apache/solr/handler/TestReqParamsAPI.java   |   3 +
 .../solr/handler/V2ApiIntegrationTest.java      |   4 +-
 .../solr/handler/admin/MetricsHandlerTest.java  |  11 +-
 .../solr/rest/schema/TestBulkSchemaAPI.java     |   2 +-
 .../apache/solr/search/TestSolr4Spatial.java    |  61 +++-
 .../solr/security/BasicAuthIntegrationTest.java |   4 +-
 .../solr/update/TestInPlaceUpdatesDistrib.java  |  89 +++--
 .../update/TestInPlaceUpdatesStandalone.java    |  27 ++
 .../update/processor/AtomicUpdatesTest.java     |   3 +-
 solr/server/etc/jetty.xml                       |  54 +--
 .../basic_configs/conf/managed-schema           |   7 +-
 .../conf/managed-schema                         |   7 +-
 .../conf/managed-schema                         |   7 +-
 .../conf/solrconfig.xml                         |   2 +-
 .../solr/client/solrj/impl/CloudSolrClient.java |  21 +-
 .../solrj/io/eval/NaturalLogEvaluator.java      |  60 ++++
 .../solrj/embedded/SolrExampleJettyTest.java    |   2 +-
 .../client/solrj/impl/CloudSolrClientTest.java  |  48 +++
 .../io/stream/eval/NaturalLogEvaluatorTest.java |  98 ++++++
 90 files changed, 5074 insertions(+), 3749 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f64575d/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
----------------------------------------------------------------------
diff --cc solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
index 52efff9,8865c08..cb6c69c
--- a/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
+++ b/solr/core/src/java/org/apache/solr/cloud/RecoveryStrategy.java
@@@ -91,10 -118,8 +118,9 @@@ public class RecoveryStrategy extends T
    private boolean recoveringAfterStartup;
    private CoreContainer cc;
    private volatile HttpUriRequest prevSendPreRecoveryHttpUriRequest;
 -  
 +  private boolean onlyLeaderIndexes;
 +
-   // this should only be used from SolrCoreState
-   public RecoveryStrategy(CoreContainer cc, CoreDescriptor cd, RecoveryListener recoveryListener) {
+   protected RecoveryStrategy(CoreContainer cc, CoreDescriptor cd, RecoveryListener recoveryListener) {
      this.cc = cc;
      this.coreName = cd.getName();
      this.recoveryListener = recoveryListener;
@@@ -103,11 -128,37 +129,39 @@@
      zkStateReader = zkController.getZkStateReader();
      baseUrl = zkController.getBaseUrl();
      coreZkNodeName = cd.getCloudDescriptor().getCoreNodeName();
 +    String collection = cd.getCloudDescriptor().getCollectionName();
 +    onlyLeaderIndexes = zkStateReader.getClusterState().getCollection(collection).getRealtimeReplicas() == 1;
    }
  
-   public void setRecoveringAfterStartup(boolean recoveringAfterStartup) {
+   final public int getWaitForUpdatesWithStaleStatePauseMilliSeconds() {
+     return waitForUpdatesWithStaleStatePauseMilliSeconds;
+   }
+ 
+   final public void setWaitForUpdatesWithStaleStatePauseMilliSeconds(int waitForUpdatesWithStaleStatePauseMilliSeconds) {
+     this.waitForUpdatesWithStaleStatePauseMilliSeconds = waitForUpdatesWithStaleStatePauseMilliSeconds;
+   }
+ 
+   final public int getMaxRetries() {
+     return maxRetries;
+   }
+ 
+   final public void setMaxRetries(int maxRetries) {
+     this.maxRetries = maxRetries;
+   }
+ 
+   final public int getStartingRecoveryDelayMilliSeconds() {
+     return startingRecoveryDelayMilliSeconds;
+   }
+ 
+   final public void setStartingRecoveryDelayMilliSeconds(int startingRecoveryDelayMilliSeconds) {
+     this.startingRecoveryDelayMilliSeconds = startingRecoveryDelayMilliSeconds;
+   }
+ 
+   final public boolean getRecoveringAfterStartup() {
+     return recoveringAfterStartup;
+   }
+ 
+   final public void setRecoveringAfterStartup(boolean recoveringAfterStartup) {
      this.recoveringAfterStartup = recoveringAfterStartup;
    }
  
@@@ -536,20 -587,8 +598,20 @@@
      LOG.info("Finished recovery process, successful=[{}]", Boolean.toString(successfulRecovery));
    }
  
 +  public static Runnable testing_beforeReplayBufferingUpdates;
 +
-   private Future<RecoveryInfo> replay(SolrCore core)
+   final private Future<RecoveryInfo> replay(SolrCore core)
        throws InterruptedException, ExecutionException {
 +    if (testing_beforeReplayBufferingUpdates != null) {
 +      testing_beforeReplayBufferingUpdates.run();
 +    }
 +    if (onlyLeaderIndexes) {
 +      // roll over all updates during buffering to new tlog, make RTG available
 +      SolrQueryRequest req = new LocalSolrQueryRequest(core,
 +          new ModifiableSolrParams());
 +      core.getUpdateHandler().getUpdateLog().copyOverBufferingUpdates(new CommitUpdateCommand(req, false));
 +      return null;
 +    }
      Future<RecoveryInfo> future = core.getUpdateHandler().getUpdateLog().applyBufferedUpdates();
      if (future == null) {
        // no replay needed\

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2f64575d/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
----------------------------------------------------------------------
diff --cc solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
index 6ae5476,bb0ab9a..7a4fa86
--- a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
+++ b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesDistrib.java
@@@ -716,11 -740,7 +749,11 @@@ public class TestInPlaceUpdatesDistrib 
          DBQ(q=val:10, v=4)
          DV(id=x, val=5, ver=3)
     */
-   private void reorderedDBQsWithInPlaceUpdatesShouldNotThrowReplicaInLIRTest() throws Exception {
+   private void reorderedDBQsResurrectionTest() throws Exception {
 +    if (onlyLeaderIndexes) {
 +      log.info("RTG with DBQs are not working in append replicas");
 +      return;
 +    }
      clearIndex();
      commit();
  
@@@ -1117,11 -1137,7 +1150,11 @@@
     * inp(id=1,inpfield=14,prevVersion=2,version=3) // will wait till timeout, and then fetch a "not found" from leader
     * dbq("inp:14",version=4)
     */
-   private void testDBQUsingUpdatedFieldFromDroppedUpdate() throws Exception {
+   private void reorderedDBQsUsingUpdatedValueFromADroppedUpdate() throws Exception {
 +    if (onlyLeaderIndexes) {
 +      log.info("RTG with DBQs are not working in append replicas");
 +      return;
 +    }
      clearIndex();
      commit();
      


[02/23] lucene-solr:jira/solr-9835: LUCENE-7734: FieldType copy constructor widened to IndexableFieldType

Posted by da...@apache.org.
LUCENE-7734: FieldType copy constructor widened to IndexableFieldType


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

Branch: refs/heads/jira/solr-9835
Commit: d2bf30d58fbfc9279bed663500400153b4d4df44
Parents: d945a24
Author: David Smiley <ds...@apache.org>
Authored: Thu Mar 9 23:12:45 2017 -0500
Committer: David Smiley <ds...@apache.org>
Committed: Thu Mar 9 23:12:45 2017 -0500

----------------------------------------------------------------------
 lucene/CHANGES.txt                                             | 3 +++
 lucene/core/src/java/org/apache/lucene/document/FieldType.java | 6 +++---
 2 files changed, 6 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d2bf30d5/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index a8f7ee4..4040945 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -117,6 +117,9 @@ API Changes
   instead of once all shard responses are present. (Simon Willnauer,
   Mike McCandless)
 
+* LUCENE-7734: FieldType's copy constructor was widened to accept any IndexableFieldType.
+  (David Smiley)
+
 New Features
 
 * LUCENE-7449: Add CROSSES relation support to RangeFieldQuery. (Nick Knize)

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d2bf30d5/lucene/core/src/java/org/apache/lucene/document/FieldType.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/FieldType.java b/lucene/core/src/java/org/apache/lucene/document/FieldType.java
index 6f206a4..3c7d276 100644
--- a/lucene/core/src/java/org/apache/lucene/document/FieldType.java
+++ b/lucene/core/src/java/org/apache/lucene/document/FieldType.java
@@ -44,7 +44,7 @@ public class FieldType implements IndexableFieldType  {
   /**
    * Create a new mutable FieldType with all of the properties from <code>ref</code>
    */
-  public FieldType(FieldType ref) {
+  public FieldType(IndexableFieldType ref) {
     this.stored = ref.stored();
     this.tokenized = ref.tokenized();
     this.storeTermVectors = ref.storeTermVectors();
@@ -54,8 +54,8 @@ public class FieldType implements IndexableFieldType  {
     this.omitNorms = ref.omitNorms();
     this.indexOptions = ref.indexOptions();
     this.docValuesType = ref.docValuesType();
-    this.dimensionCount = ref.dimensionCount;
-    this.dimensionNumBytes = ref.dimensionNumBytes;
+    this.dimensionCount = ref.pointDimensionCount();
+    this.dimensionNumBytes = ref.pointNumBytes();
     // Do not copy frozen!
   }
   


[04/23] lucene-solr:jira/solr-9835: LUCENE-7734: move to 7x section; won't do 6x backport

Posted by da...@apache.org.
LUCENE-7734: move to 7x section; won't do 6x backport


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

Branch: refs/heads/jira/solr-9835
Commit: 6415d912ca370c47ac9bd138d719b0ade71893a1
Parents: 9540bc3
Author: David Smiley <ds...@apache.org>
Authored: Fri Mar 10 21:35:53 2017 -0500
Committer: David Smiley <ds...@apache.org>
Committed: Fri Mar 10 21:35:53 2017 -0500

----------------------------------------------------------------------
 lucene/CHANGES.txt | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6415d912/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index b6ee4b8..9407dfa 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -41,6 +41,9 @@ API Changes
   index-time scoring factors should be indexed into a doc value field and
   combined at query time using eg. FunctionScoreQuery. (Adrien Grand)
 
+* LUCENE-7734: FieldType's copy constructor was widened to accept any IndexableFieldType.
+  (David Smiley)
+
 Bug Fixes
 
 * LUCENE-7626: IndexWriter will no longer accept broken token offsets


[11/23] lucene-solr:jira/solr-9835: LUCENE-7740: Refactor Range Fields to remove Field suffix (e.g., DoubleRange), move InetAddressRange and InetAddressPoint from sandbox to misc module, and refactor all other range fields from sandbox to core.

Posted by da...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressPoint.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressPoint.java b/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressPoint.java
deleted file mode 100644
index 5cda742..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressPoint.java
+++ /dev/null
@@ -1,313 +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.document;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Arrays;
-import java.util.Comparator;
-
-import org.apache.lucene.index.PointValues;
-import org.apache.lucene.search.PointInSetQuery;
-import org.apache.lucene.search.PointRangeQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.NumericUtils;
-import org.apache.lucene.util.StringHelper;
-
-/** 
- * An indexed 128-bit {@code InetAddress} field.
- * <p>
- * Finding all documents within a range at search time is
- * efficient.  Multiple values for the same field in one document
- * is allowed. 
- * <p>
- * This field defines static factory methods for creating common queries:
- * <ul>
- *   <li>{@link #newExactQuery(String, InetAddress)} for matching an exact network address.
- *   <li>{@link #newPrefixQuery(String, InetAddress, int)} for matching a network based on CIDR prefix.
- *   <li>{@link #newRangeQuery(String, InetAddress, InetAddress)} for matching arbitrary network address ranges.
- *   <li>{@link #newSetQuery(String, InetAddress...)} for matching a set of network addresses.
- * </ul>
- * <p>
- * This field supports both IPv4 and IPv6 addresses: IPv4 addresses are converted
- * to <a href="https://tools.ietf.org/html/rfc4291#section-2.5.5">IPv4-Mapped IPv6 Addresses</a>:
- * indexing {@code 1.2.3.4} is the same as indexing {@code ::FFFF:1.2.3.4}.
- * @see PointValues
- */
-public class InetAddressPoint extends Field {
-
-  // implementation note: we convert all addresses to IPv6: we expect prefix compression of values,
-  // so its not wasteful, but allows one field to handle both IPv4 and IPv6.
-  /** The number of bytes per dimension: 128 bits */
-  public static final int BYTES = 16;
-  
-  // rfc4291 prefix
-  static final byte[] IPV4_PREFIX = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1 }; 
-
-  private static final FieldType TYPE;
-  static {
-    TYPE = new FieldType();
-    TYPE.setDimensions(1, BYTES);
-    TYPE.freeze();
-  }
-
-  /** The minimum value that an ip address can hold. */
-  public static final InetAddress MIN_VALUE;
-  /** The maximum value that an ip address can hold. */
-  public static final InetAddress MAX_VALUE;
-  static {
-    MIN_VALUE = decode(new byte[BYTES]);
-    byte[] maxValueBytes = new byte[BYTES];
-    Arrays.fill(maxValueBytes, (byte) 0xFF);
-    MAX_VALUE = decode(maxValueBytes);
-  }
-
-  /**
-   * Return the {@link InetAddress} that compares immediately greater than
-   * {@code address}.
-   * @throws ArithmeticException if the provided address is the
-   *              {@link #MAX_VALUE maximum ip address}
-   */
-  public static InetAddress nextUp(InetAddress address) {
-    if (address.equals(MAX_VALUE)) {
-      throw new ArithmeticException("Overflow: there is no greater InetAddress than "
-          + address.getHostAddress());
-    }
-    byte[] delta = new byte[BYTES];
-    delta[BYTES-1] = 1;
-    byte[] nextUpBytes = new byte[InetAddressPoint.BYTES];
-    NumericUtils.add(InetAddressPoint.BYTES, 0, encode(address), delta, nextUpBytes);
-    return decode(nextUpBytes);
-  }
-
-  /**
-   * Return the {@link InetAddress} that compares immediately less than
-   * {@code address}.
-   * @throws ArithmeticException if the provided address is the
-   *              {@link #MIN_VALUE minimum ip address}
-   */
-  public static InetAddress nextDown(InetAddress address) {
-    if (address.equals(MIN_VALUE)) {
-      throw new ArithmeticException("Underflow: there is no smaller InetAddress than "
-          + address.getHostAddress());
-    }
-    byte[] delta = new byte[BYTES];
-    delta[BYTES-1] = 1;
-    byte[] nextDownBytes = new byte[InetAddressPoint.BYTES];
-    NumericUtils.subtract(InetAddressPoint.BYTES, 0, encode(address), delta, nextDownBytes);
-    return decode(nextDownBytes);
-  }
-
-  /** Change the values of this field */
-  public void setInetAddressValue(InetAddress value) {
-    if (value == null) {
-      throw new IllegalArgumentException("point must not be null");
-    }
-    fieldsData = new BytesRef(encode(value));
-  }
-
-  @Override
-  public void setBytesValue(BytesRef bytes) {
-    throw new IllegalArgumentException("cannot change value type from InetAddress to BytesRef");
-  }
-
-  /** Creates a new InetAddressPoint, indexing the
-   *  provided address.
-   *
-   *  @param name field name
-   *  @param point InetAddress value
-   *  @throws IllegalArgumentException if the field name or value is null.
-   */
-  public InetAddressPoint(String name, InetAddress point) {
-    super(name, TYPE);
-    setInetAddressValue(point);
-  }
-  
-  @Override
-  public String toString() {
-    StringBuilder result = new StringBuilder();
-    result.append(getClass().getSimpleName());
-    result.append(" <");
-    result.append(name);
-    result.append(':');
-
-    // IPv6 addresses are bracketed, to not cause confusion with historic field:value representation
-    BytesRef bytes = (BytesRef) fieldsData;
-    InetAddress address = decode(BytesRef.deepCopyOf(bytes).bytes);
-    if (address.getAddress().length == 16) {
-      result.append('[');
-      result.append(address.getHostAddress());
-      result.append(']');
-    } else {
-      result.append(address.getHostAddress());
-    }
-
-    result.append('>');
-    return result.toString();
-  }
-  
-  // public helper methods (e.g. for queries)
-
-  /** Encode InetAddress value into binary encoding */
-  public static byte[] encode(InetAddress value) {
-    byte[] address = value.getAddress();
-    if (address.length == 4) {
-      byte[] mapped = new byte[16];
-      System.arraycopy(IPV4_PREFIX, 0, mapped, 0, IPV4_PREFIX.length);
-      System.arraycopy(address, 0, mapped, IPV4_PREFIX.length, address.length);
-      address = mapped;
-    } else if (address.length != 16) {
-      // more of an assertion, how did you create such an InetAddress :)
-      throw new UnsupportedOperationException("Only IPv4 and IPv6 addresses are supported");
-    }
-    return address;
-  }
-  
-  /** Decodes InetAddress value from binary encoding */
-  public static InetAddress decode(byte value[]) {
-    try {
-      return InetAddress.getByAddress(value);
-    } catch (UnknownHostException e) {
-      // this only happens if value.length != 4 or 16, strange exception class
-      throw new IllegalArgumentException("encoded bytes are of incorrect length", e);
-    }
-  }
-
-  // static methods for generating queries
-
-  /** 
-   * Create a query for matching a network address.
-   *
-   * @param field field name. must not be {@code null}.
-   * @param value exact value
-   * @throws IllegalArgumentException if {@code field} is null.
-   * @return a query matching documents with this exact value
-   */
-  public static Query newExactQuery(String field, InetAddress value) {
-    return newRangeQuery(field, value, value);
-  }
-  
-  /** 
-   * Create a prefix query for matching a CIDR network range.
-   *
-   * @param field field name. must not be {@code null}.
-   * @param value any host address
-   * @param prefixLength the network prefix length for this address. This is also known as the subnet mask in the context of IPv4 addresses.
-   * @throws IllegalArgumentException if {@code field} is null, or prefixLength is invalid.
-   * @return a query matching documents with addresses contained within this network
-   */
-  public static Query newPrefixQuery(String field, InetAddress value, int prefixLength) {
-    if (value == null) {
-      throw new IllegalArgumentException("InetAddress must not be null");
-    }
-    if (prefixLength < 0 || prefixLength > 8 * value.getAddress().length) {
-      throw new IllegalArgumentException("illegal prefixLength '" + prefixLength + "'. Must be 0-32 for IPv4 ranges, 0-128 for IPv6 ranges");
-    }
-    // create the lower value by zeroing out the host portion, upper value by filling it with all ones.
-    byte lower[] = value.getAddress();
-    byte upper[] = value.getAddress();
-    for (int i = prefixLength; i < 8 * lower.length; i++) {
-      int m = 1 << (7 - (i & 7));
-      lower[i >> 3] &= ~m;
-      upper[i >> 3] |= m;
-    }
-    try {
-      return newRangeQuery(field, InetAddress.getByAddress(lower), InetAddress.getByAddress(upper));
-    } catch (UnknownHostException e) {
-      throw new AssertionError(e); // values are coming from InetAddress
-    }
-  }
-
-  /** 
-   * Create a range query for network addresses.
-   * <p>
-   * You can have half-open ranges (which are in fact &lt;/&le; or &gt;/&ge; queries)
-   * by setting {@code lowerValue = InetAddressPoint.MIN_VALUE} or
-   * {@code upperValue = InetAddressPoint.MAX_VALUE}.
-   * <p> Ranges are inclusive. For exclusive ranges, pass {@code InetAddressPoint#nextUp(lowerValue)}
-   * or {@code InetAddressPoint#nexDown(upperValue)}.
-   *
-   * @param field field name. must not be {@code null}.
-   * @param lowerValue lower portion of the range (inclusive). must not be null.
-   * @param upperValue upper portion of the range (inclusive). must not be null.
-   * @throws IllegalArgumentException if {@code field} is null, {@code lowerValue} is null, 
-   *                                  or {@code upperValue} is null
-   * @return a query matching documents within this range.
-   */
-  public static Query newRangeQuery(String field, InetAddress lowerValue, InetAddress upperValue) {
-    PointRangeQuery.checkArgs(field, lowerValue, upperValue);
-    return new PointRangeQuery(field, encode(lowerValue), encode(upperValue), 1) {
-      @Override
-      protected String toString(int dimension, byte[] value) {
-        return decode(value).getHostAddress(); // for ranges, the range itself is already bracketed
-      }
-    };
-  }
-
-  /**
-   * Create a query matching any of the specified 1D values.  This is the points equivalent of {@code TermsQuery}.
-   * 
-   * @param field field name. must not be {@code null}.
-   * @param values all values to match
-   */
-  public static Query newSetQuery(String field, InetAddress... values) {
-
-    // We must compare the encoded form (InetAddress doesn't implement Comparable, and even if it
-    // did, we do our own thing with ipv4 addresses):
-
-    // NOTE: we could instead convert-per-comparison and save this extra array, at cost of slower sort:
-    byte[][] sortedValues = new byte[values.length][];
-    for(int i=0;i<values.length;i++) {
-      sortedValues[i] = encode(values[i]);
-    }
-
-    Arrays.sort(sortedValues,
-                new Comparator<byte[]>() {
-                  @Override
-                  public int compare(byte[] a, byte[] b) {
-                    return StringHelper.compare(BYTES, a, 0, b, 0);
-                  }
-                });
-
-    final BytesRef encoded = new BytesRef(new byte[BYTES]);
-
-    return new PointInSetQuery(field, 1, BYTES,
-                               new PointInSetQuery.Stream() {
-
-                                 int upto;
-
-                                 @Override
-                                 public BytesRef next() {
-                                   if (upto == sortedValues.length) {
-                                     return null;
-                                   } else {
-                                     encoded.bytes = sortedValues[upto];
-                                     assert encoded.bytes.length == encoded.length;
-                                     upto++;
-                                     return encoded;
-                                   }
-                                 }
-                               }) {
-      @Override
-      protected String toString(byte[] value) {
-        assert value.length == BYTES;
-        return decode(value).getHostAddress();
-      }
-    };
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressRangeField.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressRangeField.java b/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressRangeField.java
deleted file mode 100644
index c6ebc83..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressRangeField.java
+++ /dev/null
@@ -1,168 +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.document;
-
-import java.net.InetAddress;
-
-import org.apache.lucene.document.RangeFieldQuery.QueryType;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.StringHelper;
-
-/**
- * An indexed InetAddress Range Field
- * <p>
- * This field indexes an {@code InetAddress} range defined as a min/max pairs. It is single
- * dimension only (indexed as two 16 byte paired values).
- * <p>
- * Multiple values are supported.
- *
- * <p>
- * This field defines the following static factory methods for common search operations over Ip Ranges
- * <ul>
- *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ip ranges that intersect the defined search range.
- *   <li>{@link #newWithinQuery newWithinQuery()} matches ip ranges that are within the defined search range.
- *   <li>{@link #newContainsQuery newContainsQuery()} matches ip ranges that contain the defined search range.
- *   <li>{@link #newCrossesQuery newCrossesQuery()} matches ip ranges that cross the defined search range
- * </ul>
- */
-public class InetAddressRangeField extends Field {
-  /** The number of bytes per dimension : sync w/ {@code InetAddressPoint} */
-  public static final int BYTES = InetAddressPoint.BYTES;
-
-  private static final FieldType TYPE;
-  static {
-    TYPE = new FieldType();
-    TYPE.setDimensions(2, BYTES);
-    TYPE.freeze();
-  }
-
-  /**
-   * Create a new InetAddressRangeField from min/max value
-   * @param name field name. must not be null.
-   * @param min range min value; defined as an {@code InetAddress}
-   * @param max range max value; defined as an {@code InetAddress}
-   */
-  public InetAddressRangeField(String name, final InetAddress min, final InetAddress max) {
-    super(name, TYPE);
-    setRangeValues(min, max);
-  }
-
-  /**
-   * Change (or set) the min/max values of the field.
-   * @param min range min value; defined as an {@code InetAddress}
-   * @param max range max value; defined as an {@code InetAddress}
-   */
-  public void setRangeValues(InetAddress min, InetAddress max) {
-    if (StringHelper.compare(BYTES, min.getAddress(), 0, max.getAddress(), 0) > 0) {
-      throw new IllegalArgumentException("min value cannot be greater than max value for range field (name=" + name + ")");
-    }
-    final byte[] bytes;
-    if (fieldsData == null) {
-      bytes = new byte[BYTES*2];
-      fieldsData = new BytesRef(bytes);
-    } else {
-      bytes = ((BytesRef)fieldsData).bytes;
-    }
-    encode(min, max, bytes);
-  }
-
-  /** encode the min/max range into the provided byte array */
-  private static void encode(final InetAddress min, final InetAddress max, final byte[] bytes) {
-    System.arraycopy(InetAddressPoint.encode(min), 0, bytes, 0, BYTES);
-    System.arraycopy(InetAddressPoint.encode(max), 0, bytes, BYTES, BYTES);
-  }
-
-  /** encode the min/max range and return the byte array */
-  private static byte[] encode(InetAddress min, InetAddress max) {
-    byte[] b = new byte[BYTES*2];
-    encode(min, max, b);
-    return b;
-  }
-
-  /**
-   * Create a query for matching indexed ip ranges that {@code INTERSECT} the defined range.
-   * @param field field name. must not be null.
-   * @param min range min value; provided as an {@code InetAddress}
-   * @param max range max value; provided as an {@code InetAddress}
-   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newIntersectsQuery(String field, final InetAddress min, final InetAddress max) {
-    return newRelationQuery(field, min, max, QueryType.INTERSECTS);
-  }
-
-  /**
-   * Create a query for matching indexed ip ranges that {@code CONTAINS} the defined range.
-   * @param field field name. must not be null.
-   * @param min range min value; provided as an {@code InetAddress}
-   * @param max range max value; provided as an {@code InetAddress}
-   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newContainsQuery(String field, final InetAddress min, final InetAddress max) {
-    return newRelationQuery(field, min, max, QueryType.CONTAINS);
-  }
-
-  /**
-   * Create a query for matching indexed ip ranges that are {@code WITHIN} the defined range.
-   * @param field field name. must not be null.
-   * @param min range min value; provided as an {@code InetAddress}
-   * @param max range max value; provided as an {@code InetAddress}
-   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newWithinQuery(String field, final InetAddress min, final InetAddress max) {
-    return newRelationQuery(field, min, max, QueryType.WITHIN);
-  }
-
-  /**
-   * Create a query for matching indexed ip ranges that {@code CROSS} the defined range.
-   * @param field field name. must not be null.
-   * @param min range min value; provided as an {@code InetAddress}
-   * @param max range max value; provided as an {@code InetAddress}
-   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newCrossesQuery(String field, final InetAddress min, final InetAddress max) {
-    return newRelationQuery(field, min, max, QueryType.CROSSES);
-  }
-
-  /** helper method for creating the desired relational query */
-  private static Query newRelationQuery(String field, final InetAddress min, final InetAddress max, QueryType relation) {
-    return new RangeFieldQuery(field, encode(min, max), 1, relation) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return InetAddressRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Returns the String representation for the range at the given dimension
-   * @param ranges the encoded ranges, never null
-   * @param dimension the dimension of interest (not used for this field)
-   * @return The string representation for the range at the provided dimension
-   */
-  private static String toString(byte[] ranges, int dimension) {
-    byte[] min = new byte[BYTES];
-    System.arraycopy(ranges, 0, min, 0, BYTES);
-    byte[] max = new byte[BYTES];
-    System.arraycopy(ranges, BYTES, max, 0, BYTES);
-    return "[" + InetAddressPoint.decode(min) + " : " + InetAddressPoint.decode(max) + "]";
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/java/org/apache/lucene/document/IntRangeField.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/IntRangeField.java b/lucene/sandbox/src/java/org/apache/lucene/document/IntRangeField.java
deleted file mode 100644
index 53a3311..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/IntRangeField.java
+++ /dev/null
@@ -1,282 +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.document;
-
-import org.apache.lucene.document.RangeFieldQuery.QueryType;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.NumericUtils;
-
-/**
- * An indexed Integer Range field.
- * <p>
- * This field indexes dimensional ranges defined as min/max pairs. It supports
- * up to a maximum of 4 dimensions (indexed as 8 numeric values). With 1 dimension representing a single integer range,
- * 2 dimensions representing a bounding box, 3 dimensions a bounding cube, and 4 dimensions a tesseract.
- * <p>
- * Multiple values for the same field in one document is supported, and open ended ranges can be defined using
- * {@code Integer.MIN_VALUE} and {@code Integer.MAX_VALUE}.
- *
- * <p>
- * This field defines the following static factory methods for common search operations over integer ranges:
- * <ul>
- *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ranges that intersect the defined search range.
- *   <li>{@link #newWithinQuery newWithinQuery()} matches ranges that are within the defined search range.
- *   <li>{@link #newContainsQuery newContainsQuery()} matches ranges that contain the defined search range.
- * </ul>
- */
-public class IntRangeField extends Field {
-  /** stores integer values so number of bytes is 4 */
-  public static final int BYTES = Integer.BYTES;
-
-  /**
-   * Create a new IntRangeField type, from min/max parallel arrays
-   *
-   * @param name field name. must not be null.
-   * @param min range min values; each entry is the min value for the dimension
-   * @param max range max values; each entry is the max value for the dimension
-   */
-  public IntRangeField(String name, final int[] min, final int[] max) {
-    super(name, getType(min.length));
-    setRangeValues(min, max);
-  }
-
-  /** set the field type */
-  private static FieldType getType(int dimensions) {
-    if (dimensions > 4) {
-      throw new IllegalArgumentException("IntRangeField does not support greater than 4 dimensions");
-    }
-
-    FieldType ft = new FieldType();
-    // dimensions is set as 2*dimension size (min/max per dimension)
-    ft.setDimensions(dimensions*2, BYTES);
-    ft.freeze();
-    return ft;
-  }
-
-  /**
-   * Changes the values of the field.
-   * @param min array of min values. (accepts {@code Integer.NEGATIVE_INFINITY})
-   * @param max array of max values. (accepts {@code Integer.POSITIVE_INFINITY})
-   * @throws IllegalArgumentException if {@code min} or {@code max} is invalid
-   */
-  public void setRangeValues(int[] min, int[] max) {
-    checkArgs(min, max);
-    if (min.length*2 != type.pointDimensionCount() || max.length*2 != type.pointDimensionCount()) {
-      throw new IllegalArgumentException("field (name=" + name + ") uses " + type.pointDimensionCount()/2
-          + " dimensions; cannot change to (incoming) " + min.length + " dimensions");
-    }
-
-    final byte[] bytes;
-    if (fieldsData == null) {
-      bytes = new byte[BYTES*2*min.length];
-      fieldsData = new BytesRef(bytes);
-    } else {
-      bytes = ((BytesRef)fieldsData).bytes;
-    }
-    verifyAndEncode(min, max, bytes);
-  }
-
-  /** validate the arguments */
-  private static void checkArgs(final int[] min, final int[] max) {
-    if (min == null || max == null || min.length == 0 || max.length == 0) {
-      throw new IllegalArgumentException("min/max range values cannot be null or empty");
-    }
-    if (min.length != max.length) {
-      throw new IllegalArgumentException("min/max ranges must agree");
-    }
-    if (min.length > 4) {
-      throw new IllegalArgumentException("IntRangeField does not support greater than 4 dimensions");
-    }
-  }
-
-  /**
-   * Encodes the min, max ranges into a byte array
-   */
-  private static byte[] encode(int[] min, int[] max) {
-    checkArgs(min, max);
-    byte[] b = new byte[BYTES*2*min.length];
-    verifyAndEncode(min, max, b);
-    return b;
-  }
-
-  /**
-   * encode the ranges into a sortable byte array ({@code Double.NaN} not allowed)
-   * <p>
-   * example for 4 dimensions (8 bytes per dimension value):
-   * minD1 ... minD4 | maxD1 ... maxD4
-   */
-  static void verifyAndEncode(int[] min, int[] max, byte[] bytes) {
-    for (int d=0,i=0,j=min.length*BYTES; d<min.length; ++d, i+=BYTES, j+=BYTES) {
-      if (Double.isNaN(min[d])) {
-        throw new IllegalArgumentException("invalid min value (" + Double.NaN + ")" + " in IntRangeField");
-      }
-      if (Double.isNaN(max[d])) {
-        throw new IllegalArgumentException("invalid max value (" + Double.NaN + ")" + " in IntRangeField");
-      }
-      if (min[d] > max[d]) {
-        throw new IllegalArgumentException("min value (" + min[d] + ") is greater than max value (" + max[d] + ")");
-      }
-      encode(min[d], bytes, i);
-      encode(max[d], bytes, j);
-    }
-  }
-
-  /** encode the given value into the byte array at the defined offset */
-  private static void encode(int val, byte[] bytes, int offset) {
-    NumericUtils.intToSortableBytes(val, bytes, offset);
-  }
-
-  /**
-   * Get the min value for the given dimension
-   * @param dimension the dimension, always positive
-   * @return the decoded min value
-   */
-  public int getMin(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
-    return decodeMin(((BytesRef)fieldsData).bytes, dimension);
-  }
-
-  /**
-   * Get the max value for the given dimension
-   * @param dimension the dimension, always positive
-   * @return the decoded max value
-   */
-  public int getMax(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
-    return decodeMax(((BytesRef)fieldsData).bytes, dimension);
-  }
-
-  /** decodes the min value (for the defined dimension) from the encoded input byte array */
-  static int decodeMin(byte[] b, int dimension) {
-    int offset = dimension*BYTES;
-    return NumericUtils.sortableBytesToInt(b, offset);
-  }
-
-  /** decodes the max value (for the defined dimension) from the encoded input byte array */
-  static int decodeMax(byte[] b, int dimension) {
-    int offset = b.length/2 + dimension*BYTES;
-    return NumericUtils.sortableBytesToInt(b, offset);
-  }
-
-  /**
-   * Create a query for matching indexed ranges that intersect the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Integer.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Integer.MAX_VALUE})
-   * @return query for matching intersecting ranges (overlap, within, or contains)
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newIntersectsQuery(String field, final int[] min, final int[] max) {
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.INTERSECTS) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return IntRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that contain the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Integer.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Integer.MAX_VALUE})
-   * @return query for matching ranges that contain the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newContainsQuery(String field, final int[] min, final int[] max) {
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.CONTAINS) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return IntRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that are within the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Integer.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Integer.MAX_VALUE})
-   * @return query for matching ranges within the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newWithinQuery(String field, final int[] min, final int[] max) {
-    checkArgs(min, max);
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.WITHIN) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return IntRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that cross the defined range.
-   * A CROSSES is defined as any set of ranges that are not disjoint and not wholly contained by
-   * the query. Effectively, its the complement of union(WITHIN, DISJOINT).
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Integer.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Integer.MAX_VALUE})
-   * @return query for matching ranges within the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newCrossesQuery(String field, final int[] min, final int[] max) {
-    checkArgs(min, max);
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.CROSSES) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return IntRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder();
-    sb.append(getClass().getSimpleName());
-    sb.append(" <");
-    sb.append(name);
-    sb.append(':');
-    byte[] b = ((BytesRef)fieldsData).bytes;
-    toString(b, 0);
-    for (int d=1; d<type.pointDimensionCount(); ++d) {
-      sb.append(' ');
-      toString(b, d);
-    }
-    sb.append('>');
-
-    return sb.toString();
-  }
-
-  /**
-   * Returns the String representation for the range at the given dimension
-   * @param ranges the encoded ranges, never null
-   * @param dimension the dimension of interest
-   * @return The string representation for the range at the provided dimension
-   */
-  private static String toString(byte[] ranges, int dimension) {
-    return "[" + Integer.toString(decodeMin(ranges, dimension)) + " : "
-        + Integer.toString(decodeMax(ranges, dimension)) + "]";
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/java/org/apache/lucene/document/LongRangeField.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LongRangeField.java b/lucene/sandbox/src/java/org/apache/lucene/document/LongRangeField.java
deleted file mode 100644
index 7addaf5..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LongRangeField.java
+++ /dev/null
@@ -1,280 +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.document;
-
-import org.apache.lucene.document.RangeFieldQuery.QueryType;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.NumericUtils;
-
-/**
- * An indexed Long Range field.
- * <p>
- * This field indexes dimensional ranges defined as min/max pairs. It supports
- * up to a maximum of 4 dimensions (indexed as 8 numeric values). With 1 dimension representing a single long range,
- * 2 dimensions representing a bounding box, 3 dimensions a bounding cube, and 4 dimensions a tesseract.
- * <p>
- * Multiple values for the same field in one document is supported, and open ended ranges can be defined using
- * {@code Long.MIN_VALUE} and {@code Long.MAX_VALUE}.
- *
- * <p>
- * This field defines the following static factory methods for common search operations over long ranges:
- * <ul>
- *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ranges that intersect the defined search range.
- *   <li>{@link #newWithinQuery newWithinQuery()} matches ranges that are within the defined search range.
- *   <li>{@link #newContainsQuery newContainsQuery()} matches ranges that contain the defined search range.
- * </ul>
- */
-public class LongRangeField extends Field {
-  /** stores long values so number of bytes is 8 */
-  public static final int BYTES = Long.BYTES;
-
-  /**
-   * Create a new LongRangeField type, from min/max parallel arrays
-   *
-   * @param name field name. must not be null.
-   * @param min range min values; each entry is the min value for the dimension
-   * @param max range max values; each entry is the max value for the dimension
-   */
-  public LongRangeField(String name, final long[] min, final long[] max) {
-    super(name, getType(min.length));
-    setRangeValues(min, max);
-  }
-
-  /** set the field type */
-  private static FieldType getType(int dimensions) {
-    if (dimensions > 4) {
-      throw new IllegalArgumentException("LongRangeField does not support greater than 4 dimensions");
-    }
-
-    FieldType ft = new FieldType();
-    // dimensions is set as 2*dimension size (min/max per dimension)
-    ft.setDimensions(dimensions*2, BYTES);
-    ft.freeze();
-    return ft;
-  }
-
-  /**
-   * Changes the values of the field.
-   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
-   * @throws IllegalArgumentException if {@code min} or {@code max} is invalid
-   */
-  public void setRangeValues(long[] min, long[] max) {
-    checkArgs(min, max);
-    if (min.length*2 != type.pointDimensionCount() || max.length*2 != type.pointDimensionCount()) {
-      throw new IllegalArgumentException("field (name=" + name + ") uses " + type.pointDimensionCount()/2
-          + " dimensions; cannot change to (incoming) " + min.length + " dimensions");
-    }
-
-    final byte[] bytes;
-    if (fieldsData == null) {
-      bytes = new byte[BYTES*2*min.length];
-      fieldsData = new BytesRef(bytes);
-    } else {
-      bytes = ((BytesRef)fieldsData).bytes;
-    }
-    verifyAndEncode(min, max, bytes);
-  }
-
-  /** validate the arguments */
-  private static void checkArgs(final long[] min, final long[] max) {
-    if (min == null || max == null || min.length == 0 || max.length == 0) {
-      throw new IllegalArgumentException("min/max range values cannot be null or empty");
-    }
-    if (min.length != max.length) {
-      throw new IllegalArgumentException("min/max ranges must agree");
-    }
-    if (min.length > 4) {
-      throw new IllegalArgumentException("LongRangeField does not support greater than 4 dimensions");
-    }
-  }
-
-  /** Encodes the min, max ranges into a byte array */
-  private static byte[] encode(long[] min, long[] max) {
-    checkArgs(min, max);
-    byte[] b = new byte[BYTES*2*min.length];
-    verifyAndEncode(min, max, b);
-    return b;
-  }
-
-  /**
-   * encode the ranges into a sortable byte array ({@code Double.NaN} not allowed)
-   * <p>
-   * example for 4 dimensions (8 bytes per dimension value):
-   * minD1 ... minD4 | maxD1 ... maxD4
-   */
-  static void verifyAndEncode(long[] min, long[] max, byte[] bytes) {
-    for (int d=0,i=0,j=min.length*BYTES; d<min.length; ++d, i+=BYTES, j+=BYTES) {
-      if (Double.isNaN(min[d])) {
-        throw new IllegalArgumentException("invalid min value (" + Double.NaN + ")" + " in IntRangeField");
-      }
-      if (Double.isNaN(max[d])) {
-        throw new IllegalArgumentException("invalid max value (" + Double.NaN + ")" + " in IntRangeField");
-      }
-      if (min[d] > max[d]) {
-        throw new IllegalArgumentException("min value (" + min[d] + ") is greater than max value (" + max[d] + ")");
-      }
-      encode(min[d], bytes, i);
-      encode(max[d], bytes, j);
-    }
-  }
-
-  /** encode the given value into the byte array at the defined offset */
-  private static void encode(long val, byte[] bytes, int offset) {
-    NumericUtils.longToSortableBytes(val, bytes, offset);
-  }
-
-  /**
-   * Get the min value for the given dimension
-   * @param dimension the dimension, always positive
-   * @return the decoded min value
-   */
-  public long getMin(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
-    return decodeMin(((BytesRef)fieldsData).bytes, dimension);
-  }
-
-  /**
-   * Get the max value for the given dimension
-   * @param dimension the dimension, always positive
-   * @return the decoded max value
-   */
-  public long getMax(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
-    return decodeMax(((BytesRef)fieldsData).bytes, dimension);
-  }
-
-  /** decodes the min value (for the defined dimension) from the encoded input byte array */
-  static long decodeMin(byte[] b, int dimension) {
-    int offset = dimension*BYTES;
-    return NumericUtils.sortableBytesToLong(b, offset);
-  }
-
-  /** decodes the max value (for the defined dimension) from the encoded input byte array */
-  static long decodeMax(byte[] b, int dimension) {
-    int offset = b.length/2 + dimension*BYTES;
-    return NumericUtils.sortableBytesToLong(b, offset);
-  }
-
-  /**
-   * Create a query for matching indexed ranges that intersect the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
-   * @return query for matching intersecting ranges (overlap, within, or contains)
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newIntersectsQuery(String field, final long[] min, final long[] max) {
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.INTERSECTS) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return LongRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that contain the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
-   * @return query for matching ranges that contain the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newContainsQuery(String field, final long[] min, final long[] max) {
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.CONTAINS) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return LongRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that are within the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
-   * @return query for matching ranges within the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newWithinQuery(String field, final long[] min, final long[] max) {
-    checkArgs(min, max);
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.WITHIN) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return LongRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that cross the defined range.
-   * A CROSSES is defined as any set of ranges that are not disjoint and not wholly contained by
-   * the query. Effectively, its the complement of union(WITHIN, DISJOINT).
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Long.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Long.MAX_VALUE})
-   * @return query for matching ranges within the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newCrossesQuery(String field, final long[] min, final long[] max) {
-    checkArgs(min, max);
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.CROSSES) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return LongRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder();
-    sb.append(getClass().getSimpleName());
-    sb.append(" <");
-    sb.append(name);
-    sb.append(':');
-    byte[] b = ((BytesRef)fieldsData).bytes;
-    toString(b, 0);
-    for (int d=1; d<type.pointDimensionCount(); ++d) {
-      sb.append(' ');
-      toString(b, d);
-    }
-    sb.append('>');
-
-    return sb.toString();
-  }
-
-  /**
-   * Returns the String representation for the range at the given dimension
-   * @param ranges the encoded ranges, never null
-   * @param dimension the dimension of interest
-   * @return The string representation for the range at the provided dimension
-   */
-  private static String toString(byte[] ranges, int dimension) {
-    return "[" + Long.toString(decodeMin(ranges, dimension)) + " : "
-        + Long.toString(decodeMax(ranges, dimension)) + "]";
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/java/org/apache/lucene/document/RangeFieldQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/RangeFieldQuery.java b/lucene/sandbox/src/java/org/apache/lucene/document/RangeFieldQuery.java
deleted file mode 100644
index 10f10fa..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/RangeFieldQuery.java
+++ /dev/null
@@ -1,340 +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.document;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.function.IntPredicate;
-import java.util.function.Predicate;
-
-import org.apache.lucene.index.FieldInfo;
-import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.PointValues;
-import org.apache.lucene.index.PointValues.Relation;
-import org.apache.lucene.index.PointValues.IntersectVisitor;
-import org.apache.lucene.search.ConstantScoreScorer;
-import org.apache.lucene.search.ConstantScoreWeight;
-import org.apache.lucene.search.DocIdSet;
-import org.apache.lucene.search.DocIdSetIterator;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.Weight;
-import org.apache.lucene.util.DocIdSetBuilder;
-import org.apache.lucene.util.StringHelper;
-
-/**
- * Query class for searching {@code RangeField} types by a defined {@link Relation}.
- */
-abstract class RangeFieldQuery extends Query {
-  /** field name */
-  final String field;
-  /** query relation
-   * intersects: {@code CELL_CROSSES_QUERY},
-   * contains: {@code CELL_CONTAINS_QUERY},
-   * within: {@code CELL_WITHIN_QUERY} */
-  final QueryType queryType;
-  /** number of dimensions - max 4 */
-  final int numDims;
-  /** ranges encoded as a sortable byte array */
-  final byte[] ranges;
-  /** number of bytes per dimension */
-  final int bytesPerDim;
-
-  /** Used by {@code RangeFieldQuery} to check how each internal or leaf node relates to the query. */
-  enum QueryType {
-    /** Use this for intersects queries. */
-    INTERSECTS,
-    /** Use this for within queries. */
-    WITHIN,
-    /** Use this for contains */
-    CONTAINS,
-    /** Use this for crosses queries */
-    CROSSES
-  }
-
-  /**
-   * Create a query for searching indexed ranges that match the provided relation.
-   * @param field field name. must not be null.
-   * @param ranges encoded range values; this is done by the {@code RangeField} implementation
-   * @param queryType the query relation
-   */
-  RangeFieldQuery(String field, final byte[] ranges, final int numDims, final QueryType queryType) {
-    checkArgs(field, ranges, numDims);
-    if (queryType == null) {
-      throw new IllegalArgumentException("Query type cannot be null");
-    }
-    this.field = field;
-    this.queryType = queryType;
-    this.numDims = numDims;
-    this.ranges = ranges;
-    this.bytesPerDim = ranges.length / (2*numDims);
-  }
-
-  /** check input arguments */
-  private static void checkArgs(String field, final byte[] ranges, final int numDims) {
-    if (field == null) {
-      throw new IllegalArgumentException("field must not be null");
-    }
-    if (numDims > 4) {
-      throw new IllegalArgumentException("dimension size cannot be greater than 4");
-    }
-    if (ranges == null || ranges.length == 0) {
-      throw new IllegalArgumentException("encoded ranges cannot be null or empty");
-    }
-  }
-
-  /** Check indexed field info against the provided query data. */
-  private void checkFieldInfo(FieldInfo fieldInfo) {
-    if (fieldInfo.getPointDimensionCount()/2 != numDims) {
-      throw new IllegalArgumentException("field=\"" + field + "\" was indexed with numDims="
-          + fieldInfo.getPointDimensionCount()/2 + " but this query has numDims=" + numDims);
-    }
-  }
-
-  @Override
-  public final Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
-    return new ConstantScoreWeight(this, boost) {
-      final RangeFieldComparator target = new RangeFieldComparator();
-      private DocIdSet buildMatchingDocIdSet(LeafReader reader, PointValues values) throws IOException {
-        DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc(), values, field);
-        values.intersect(
-            new IntersectVisitor() {
-              DocIdSetBuilder.BulkAdder adder;
-              @Override
-              public void grow(int count) {
-                adder = result.grow(count);
-              }
-              @Override
-              public void visit(int docID) throws IOException {
-                adder.add(docID);
-              }
-              @Override
-              public void visit(int docID, byte[] leaf) throws IOException {
-                if (target.matches(leaf)) {
-                  adder.add(docID);
-                }
-              }
-              @Override
-              public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
-                byte[] node = getInternalRange(minPackedValue, maxPackedValue);
-                // compute range relation for BKD traversal
-                if (target.intersects(node) == false) {
-                  return Relation.CELL_OUTSIDE_QUERY;
-                } else if (target.within(node)) {
-                  // target within cell; continue traversing:
-                  return Relation.CELL_CROSSES_QUERY;
-                } else if (target.contains(node)) {
-                  // target contains cell; add iff queryType is not a CONTAINS or CROSSES query:
-                  return (queryType == QueryType.CONTAINS || queryType == QueryType.CROSSES) ?
-                      Relation.CELL_OUTSIDE_QUERY : Relation.CELL_INSIDE_QUERY;
-                }
-                // target intersects cell; continue traversing:
-                return Relation.CELL_CROSSES_QUERY;
-              }
-            });
-        return result.build();
-      }
-
-      @Override
-      public Scorer scorer(LeafReaderContext context) throws IOException {
-        LeafReader reader = context.reader();
-        PointValues values = reader.getPointValues(field);
-        if (values == null) {
-          // no docs in this segment indexed any ranges
-          return null;
-        }
-        FieldInfo fieldInfo = reader.getFieldInfos().fieldInfo(field);
-        if (fieldInfo == null) {
-          // no docs in this segment indexed this field
-          return null;
-        }
-        checkFieldInfo(fieldInfo);
-        boolean allDocsMatch = true;
-        if (values.getDocCount() == reader.maxDoc()) {
-          // if query crosses, docs need to be further scrutinized
-          byte[] range = getInternalRange(values.getMinPackedValue(), values.getMaxPackedValue());
-          // if the internal node is not equal and not contained by the query, all docs do not match
-          if (queryType == QueryType.CROSSES || (!Arrays.equals(ranges, range)
-              && (target.contains(range) == false || queryType != QueryType.WITHIN))) {
-            allDocsMatch = false;
-          }
-        } else {
-          allDocsMatch = false;
-        }
-
-        DocIdSetIterator iterator = allDocsMatch == true ?
-            DocIdSetIterator.all(reader.maxDoc()) : buildMatchingDocIdSet(reader, values).iterator();
-        return new ConstantScoreScorer(this, score(), iterator);
-      }
-
-      /** get an encoded byte representation of the internal node; this is
-       *  the lower half of the min array and the upper half of the max array */
-      private byte[] getInternalRange(byte[] min, byte[] max) {
-        byte[] range = new byte[min.length];
-        final int dimSize = numDims * bytesPerDim;
-        System.arraycopy(min, 0, range, 0, dimSize);
-        System.arraycopy(max, dimSize, range, dimSize, dimSize);
-        return range;
-      }
-    };
-  }
-
-  /**
-   * RangeFieldComparator class provides the core comparison logic for accepting or rejecting indexed
-   * {@code RangeField} types based on the defined query range and relation.
-   */
-  class RangeFieldComparator {
-    final Predicate<byte[]> predicate;
-
-    /** constructs the comparator based on the query type */
-    RangeFieldComparator() {
-      switch (queryType) {
-        case INTERSECTS:
-          predicate = this::intersects;
-          break;
-        case WITHIN:
-          predicate = this::contains;
-          break;
-        case CONTAINS:
-          predicate = this::within;
-          break;
-        case CROSSES:
-          // crosses first checks intersection (disjoint automatic fails),
-          // then ensures the query doesn't wholly contain the leaf:
-          predicate = (byte[] leaf) -> this.intersects(leaf)
-              && this.contains(leaf) == false;
-          break;
-        default:
-          throw new IllegalArgumentException("invalid queryType [" + queryType + "] found.");
-      }
-    }
-
-    /** determines if the candidate range matches the query request */
-    private boolean matches(final byte[] candidate) {
-      return (Arrays.equals(ranges, candidate) && queryType != QueryType.CROSSES)
-          || predicate.test(candidate);
-    }
-
-    /** check if query intersects candidate range */
-    private boolean intersects(final byte[] candidate) {
-      return relate((int d) -> compareMinMax(candidate, d) > 0 || compareMaxMin(candidate, d) < 0);
-    }
-
-    /** check if query is within candidate range */
-    private boolean within(final byte[] candidate) {
-      return relate((int d) -> compareMinMin(candidate, d) < 0 || compareMaxMax(candidate, d) > 0);
-    }
-
-    /** check if query contains candidate range */
-    private boolean contains(final byte[] candidate) {
-      return relate((int d) -> compareMinMin(candidate, d) > 0 || compareMaxMax(candidate, d) < 0);
-    }
-
-    /** internal method used by each relation method to test range relation logic */
-    private boolean relate(IntPredicate predicate) {
-      for (int d=0; d<numDims; ++d) {
-        if (predicate.test(d)) {
-          return false;
-        }
-      }
-      return true;
-    }
-
-    /** compare the encoded min value (for the defined query dimension) with the encoded min value in the byte array */
-    private int compareMinMin(byte[] b, int dimension) {
-      // convert dimension to offset:
-      dimension *= bytesPerDim;
-      return StringHelper.compare(bytesPerDim, ranges, dimension, b, dimension);
-    }
-
-    /** compare the encoded min value (for the defined query dimension) with the encoded max value in the byte array */
-    private int compareMinMax(byte[] b, int dimension) {
-      // convert dimension to offset:
-      dimension *= bytesPerDim;
-      return StringHelper.compare(bytesPerDim, ranges, dimension, b, numDims * bytesPerDim + dimension);
-    }
-
-    /** compare the encoded max value (for the defined query dimension) with the encoded min value in the byte array */
-    private int compareMaxMin(byte[] b, int dimension) {
-      // convert dimension to offset:
-      dimension *= bytesPerDim;
-      return StringHelper.compare(bytesPerDim, ranges, numDims * bytesPerDim + dimension, b, dimension);
-    }
-
-    /** compare the encoded max value (for the defined query dimension) with the encoded max value in the byte array */
-    private int compareMaxMax(byte[] b, int dimension) {
-      // convert dimension to max offset:
-      dimension = numDims * bytesPerDim + dimension * bytesPerDim;
-      return StringHelper.compare(bytesPerDim, ranges, dimension, b, dimension);
-    }
-  }
-
-  @Override
-  public int hashCode() {
-    int hash = classHash();
-    hash = 31 * hash + field.hashCode();
-    hash = 31 * hash + numDims;
-    hash = 31 * hash + queryType.hashCode();
-    hash = 31 * hash + Arrays.hashCode(ranges);
-
-    return hash;
-  }
-
-  @Override
-  public final boolean equals(Object o) {
-    return sameClassAs(o) &&
-        equalsTo(getClass().cast(o));
-  }
-
-  protected boolean equalsTo(RangeFieldQuery other) {
-    return Objects.equals(field, other.field) &&
-        numDims == other.numDims &&
-        Arrays.equals(ranges, other.ranges) &&
-        other.queryType == queryType;
-  }
-
-  @Override
-  public String toString(String field) {
-    StringBuilder sb = new StringBuilder();
-    if (this.field.equals(field) == false) {
-      sb.append(this.field);
-      sb.append(':');
-    }
-    sb.append("<ranges:");
-    sb.append(toString(ranges, 0));
-    for (int d=1; d<numDims; ++d) {
-      sb.append(' ');
-      sb.append(toString(ranges, d));
-    }
-    sb.append('>');
-
-    return sb.toString();
-  }
-
-  /**
-   * Returns a string of a single value in a human-readable format for debugging.
-   * This is used by {@link #toString()}.
-   *
-   * @param dimension dimension of the particular value
-   * @param ranges encoded ranges, never null
-   * @return human readable value for debugging
-   */
-  protected abstract String toString(byte[] ranges, int dimension);
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/java/org/apache/lucene/document/package.html
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/package.html b/lucene/sandbox/src/java/org/apache/lucene/document/package.html
index b6a077e..4783b5e 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/document/package.html
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/package.html
@@ -26,8 +26,7 @@
 This package contains several point types:
 <ul>
    <li>{@link org.apache.lucene.document.BigIntegerPoint BigIntegerPoint} for 128-bit integers</li>
-   <li>{@link org.apache.lucene.document.InetAddressPoint InetAddressPoint} for IPv4 and IPv6 network addresses</li>
-   <li>{@link org.apache.lucene.document.LatLonPoint LatLonPoint} for latitude/longitude geospatial points</li> 
+   <li>{@link org.apache.lucene.document.LatLonPoint LatLonPoint} for latitude/longitude geospatial points</li>
 </ul>
 </body>
 </html>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/document/TestDoubleRangeField.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestDoubleRangeField.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestDoubleRangeField.java
index 188aab6..8a8130b 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestDoubleRangeField.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestDoubleRangeField.java
@@ -30,11 +30,11 @@ public class TestDoubleRangeField extends LuceneTestCase {
     IllegalArgumentException expected;
 
     expected = expectThrows(IllegalArgumentException.class, () ->
-        doc.add(new DoubleRangeField(FIELD_NAME, new double[] {Double.NaN}, new double[] {5})));
+        doc.add(new DoubleRange(FIELD_NAME, new double[] {Double.NaN}, new double[] {5})));
     assertTrue(expected.getMessage().contains("invalid min value"));
 
     expected = expectThrows(IllegalArgumentException.class, () ->
-        doc.add(new DoubleRangeField(FIELD_NAME, new double[] {5}, new double[] {Double.NaN})));
+        doc.add(new DoubleRange(FIELD_NAME, new double[] {5}, new double[] {Double.NaN})));
     assertTrue(expected.getMessage().contains("invalid max value"));
   }
 
@@ -43,7 +43,7 @@ public class TestDoubleRangeField extends LuceneTestCase {
     Document doc = new Document();
     IllegalArgumentException expected;
     expected = expectThrows(IllegalArgumentException.class, () ->
-        doc.add(new DoubleRangeField(FIELD_NAME, new double[] {5, 6}, new double[] {5})));
+        doc.add(new DoubleRange(FIELD_NAME, new double[] {5, 6}, new double[] {5})));
     assertTrue(expected.getMessage().contains("min/max ranges must agree"));
   }
 
@@ -52,7 +52,7 @@ public class TestDoubleRangeField extends LuceneTestCase {
     Document doc = new Document();
     IllegalArgumentException expected;
     expected = expectThrows(IllegalArgumentException.class, () ->
-        doc.add(new DoubleRangeField(FIELD_NAME, new double[] {1, 2, 3, 4, 5}, new double[] {5})));
+        doc.add(new DoubleRange(FIELD_NAME, new double[] {1, 2, 3, 4, 5}, new double[] {5})));
     assertTrue(expected.getMessage().contains("does not support greater than 4 dimensions"));
   }
 
@@ -61,7 +61,7 @@ public class TestDoubleRangeField extends LuceneTestCase {
     Document doc = new Document();
     IllegalArgumentException expected;
     expected = expectThrows(IllegalArgumentException.class, () ->
-      doc.add(new DoubleRangeField(FIELD_NAME, new double[] {3, 4}, new double[] {1, 2})));
+      doc.add(new DoubleRange(FIELD_NAME, new double[] {3, 4}, new double[] {1, 2})));
     assertTrue(expected.getMessage().contains("is greater than max value"));
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/document/TestInetAddressPoint.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestInetAddressPoint.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestInetAddressPoint.java
deleted file mode 100644
index 0e0901b..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestInetAddressPoint.java
+++ /dev/null
@@ -1,176 +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.document;
-
-import java.net.InetAddress;
-
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.LuceneTestCase;
-
-/** Simple tests for {@link InetAddressPoint} */
-public class TestInetAddressPoint extends LuceneTestCase {
-
-  /** Add a single address and search for it */
-  public void testBasics() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // add a doc with an address
-    Document document = new Document();
-    InetAddress address = InetAddress.getByName("1.2.3.4");
-    document.add(new InetAddressPoint("field", address));
-    writer.addDocument(document);
-    
-    // search and verify we found our doc
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(1, searcher.count(InetAddressPoint.newExactQuery("field", address)));
-    assertEquals(1, searcher.count(InetAddressPoint.newPrefixQuery("field", address, 24)));
-    assertEquals(1, searcher.count(InetAddressPoint.newRangeQuery("field", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"))));
-    assertEquals(1, searcher.count(InetAddressPoint.newSetQuery("field", InetAddress.getByName("1.2.3.4"))));
-    assertEquals(1, searcher.count(InetAddressPoint.newSetQuery("field", InetAddress.getByName("1.2.3.4"), InetAddress.getByName("1.2.3.5"))));
-    assertEquals(0, searcher.count(InetAddressPoint.newSetQuery("field", InetAddress.getByName("1.2.3.3"))));
-    assertEquals(0, searcher.count(InetAddressPoint.newSetQuery("field")));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-  
-  /** Add a single address and search for it */
-  public void testBasicsV6() throws Exception {
-    Directory dir = newDirectory();
-    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
-
-    // add a doc with an address
-    Document document = new Document();
-    InetAddress address = InetAddress.getByName("fec0::f66d");
-    document.add(new InetAddressPoint("field", address));
-    writer.addDocument(document);
-    
-    // search and verify we found our doc
-    IndexReader reader = writer.getReader();
-    IndexSearcher searcher = newSearcher(reader);
-    assertEquals(1, searcher.count(InetAddressPoint.newExactQuery("field", address)));
-    assertEquals(1, searcher.count(InetAddressPoint.newPrefixQuery("field", address, 64)));
-    assertEquals(1, searcher.count(InetAddressPoint.newRangeQuery("field", InetAddress.getByName("fec0::f66c"), InetAddress.getByName("fec0::f66e"))));
-
-    reader.close();
-    writer.close();
-    dir.close();
-  }
-    
-  public void testToString() throws Exception {
-    assertEquals("InetAddressPoint <field:1.2.3.4>", new InetAddressPoint("field", InetAddress.getByName("1.2.3.4")).toString());
-    assertEquals("InetAddressPoint <field:1.2.3.4>", new InetAddressPoint("field", InetAddress.getByName("::FFFF:1.2.3.4")).toString());
-    assertEquals("InetAddressPoint <field:[fdc8:57ed:f042:ad1:f66d:4ff:fe90:ce0c]>", new InetAddressPoint("field", InetAddress.getByName("fdc8:57ed:f042:0ad1:f66d:4ff:fe90:ce0c")).toString());
-    
-    assertEquals("field:[1.2.3.4 TO 1.2.3.4]", InetAddressPoint.newExactQuery("field", InetAddress.getByName("1.2.3.4")).toString());
-    assertEquals("field:[0:0:0:0:0:0:0:1 TO 0:0:0:0:0:0:0:1]", InetAddressPoint.newExactQuery("field", InetAddress.getByName("::1")).toString());
-    
-    assertEquals("field:[1.2.3.0 TO 1.2.3.255]", InetAddressPoint.newPrefixQuery("field", InetAddress.getByName("1.2.3.4"), 24).toString());
-    assertEquals("field:[fdc8:57ed:f042:ad1:0:0:0:0 TO fdc8:57ed:f042:ad1:ffff:ffff:ffff:ffff]", InetAddressPoint.newPrefixQuery("field", InetAddress.getByName("fdc8:57ed:f042:0ad1:f66d:4ff:fe90:ce0c"), 64).toString());
-    assertEquals("field:{fdc8:57ed:f042:ad1:f66d:4ff:fe90:ce0c}", InetAddressPoint.newSetQuery("field", InetAddress.getByName("fdc8:57ed:f042:0ad1:f66d:4ff:fe90:ce0c")).toString());
-  }
-
-  public void testQueryEquals() throws Exception {
-    Query q1, q2;
-    q1 = InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
-    q2 = InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
-    assertEquals(q1, q2);
-    assertEquals(q1.hashCode(), q2.hashCode());
-    assertFalse(q1.equals(InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.7"))));
-    assertFalse(q1.equals(InetAddressPoint.newRangeQuery("b", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"))));
-
-    q1 = InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.3"), 16);
-    q2 = InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.3"), 16);
-    assertEquals(q1, q2);
-    assertEquals(q1.hashCode(), q2.hashCode());
-    assertFalse(q1.equals(InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.1.3.5"), 16)));
-    assertFalse(q1.equals(InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.5"), 24)));
-
-    q1 = InetAddressPoint.newExactQuery("a", InetAddress.getByName("1.2.3.3"));
-    q2 = InetAddressPoint.newExactQuery("a", InetAddress.getByName("1.2.3.3"));
-    assertEquals(q1, q2);
-    assertEquals(q1.hashCode(), q2.hashCode());
-    assertFalse(q1.equals(InetAddressPoint.newExactQuery("a", InetAddress.getByName("1.2.3.5"))));
-
-    q1 = InetAddressPoint.newSetQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
-    q2 = InetAddressPoint.newSetQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
-    assertEquals(q1, q2);
-    assertEquals(q1.hashCode(), q2.hashCode());
-    assertFalse(q1.equals(InetAddressPoint.newSetQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.7"))));
-  }
-
-  public void testPrefixQuery() throws Exception {
-    assertEquals(
-        InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.0"), InetAddress.getByName("1.2.3.255")),
-        InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.127"), 24));
-    assertEquals(
-        InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.128"), InetAddress.getByName("1.2.3.255")),
-        InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.213"), 25));
-    assertEquals(
-        InetAddressPoint.newRangeQuery("a", InetAddress.getByName("2001::a000:0"), InetAddress.getByName("2001::afff:ffff")),
-        InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("2001::a6bd:fc80"), 100));
-  }
-
-  public void testNextUp() throws Exception {
-    assertEquals(InetAddress.getByName("::1"),
-        InetAddressPoint.nextUp(InetAddress.getByName("::")));
-
-    assertEquals(InetAddress.getByName("::1:0"),
-        InetAddressPoint.nextUp(InetAddress.getByName("::ffff")));
-
-    assertEquals(InetAddress.getByName("1.2.4.0"),
-        InetAddressPoint.nextUp(InetAddress.getByName("1.2.3.255")));
-
-    assertEquals(InetAddress.getByName("0.0.0.0"),
-        InetAddressPoint.nextUp(InetAddress.getByName("::fffe:ffff:ffff")));
-
-    assertEquals(InetAddress.getByName("::1:0:0:0"),
-        InetAddressPoint.nextUp(InetAddress.getByName("255.255.255.255")));
-
-    ArithmeticException e = expectThrows(ArithmeticException.class,
-        () -> InetAddressPoint.nextUp(InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
-    assertEquals("Overflow: there is no greater InetAddress than ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", e.getMessage());
-  }
-
-  public void testNextDown() throws Exception {
-    assertEquals(InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"),
-        InetAddressPoint.nextDown(InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
-
-    assertEquals(InetAddress.getByName("::ffff"),
-        InetAddressPoint.nextDown(InetAddress.getByName("::1:0")));
-
-    assertEquals(InetAddress.getByName("1.2.3.255"),
-        InetAddressPoint.nextDown(InetAddress.getByName("1.2.4.0")));
-
-    assertEquals(InetAddress.getByName("::fffe:ffff:ffff"),
-        InetAddressPoint.nextDown(InetAddress.getByName("0.0.0.0")));
-
-    assertEquals(InetAddress.getByName("255.255.255.255"),
-        InetAddressPoint.nextDown(InetAddress.getByName("::1:0:0:0")));
-
-    ArithmeticException e = expectThrows(ArithmeticException.class,
-        () -> InetAddressPoint.nextDown(InetAddress.getByName("::")));
-    assertEquals("Underflow: there is no smaller InetAddress than 0:0:0:0:0:0:0:0", e.getMessage());
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/test/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java b/lucene/sandbox/src/test/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java
deleted file mode 100644
index 53f3b82..0000000
--- a/lucene/sandbox/src/test/org/apache/lucene/search/BaseRangeFieldQueryTestCase.java
+++ /dev/null
@@ -1,344 +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 java.util.HashSet;
-import java.util.Set;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.NumericDocValuesField;
-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.index.LeafReaderContext;
-import org.apache.lucene.index.MultiDocValues;
-import org.apache.lucene.index.MultiFields;
-import org.apache.lucene.index.NumericDocValues;
-import org.apache.lucene.index.SerialMergeScheduler;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.Bits;
-import org.apache.lucene.util.FixedBitSet;
-import org.apache.lucene.util.IOUtils;
-import org.apache.lucene.util.LuceneTestCase;
-
-/**
- * Abstract class to do basic tests for a RangeField query. Testing rigor inspired by {@code BaseGeoPointTestCase}
- */
-public abstract class BaseRangeFieldQueryTestCase extends LuceneTestCase {
-  protected abstract Field newRangeField(Range box);
-
-  protected abstract Query newIntersectsQuery(Range box);
-
-  protected abstract Query newContainsQuery(Range box);
-
-  protected abstract Query newWithinQuery(Range box);
-
-  protected abstract Query newCrossesQuery(Range box);
-
-  protected abstract Range nextRange(int dimensions);
-
-  protected int dimension() {
-    return random().nextInt(4) + 1;
-  }
-
-  public void testRandomTiny() throws Exception {
-    // Make sure single-leaf-node case is OK:
-    doTestRandom(10, false);
-  }
-
-  public void testRandomMedium() throws Exception {
-    doTestRandom(10000, false);
-  }
-
-  @Nightly
-  public void testRandomBig() throws Exception {
-    doTestRandom(200000, false);
-  }
-
-  public void testMultiValued() throws Exception {
-    doTestRandom(10000, true);
-  }
-
-  private void doTestRandom(int count, boolean multiValued) throws Exception {
-    int numDocs = atLeast(count);
-    int dimensions = dimension();
-
-    if (VERBOSE) {
-      System.out.println("TEST: numDocs=" + numDocs);
-    }
-
-    Range[][] ranges = new Range[numDocs][];
-
-    boolean haveRealDoc = true;
-
-    nextdoc: for (int id=0; id<numDocs; ++id) {
-      int x = random().nextInt(20);
-      if (ranges[id] == null) {
-        ranges[id] = new Range[] {nextRange(dimensions)};
-      }
-      if (x == 17) {
-        // some docs don't have a box:
-        ranges[id][0].isMissing = true;
-        if (VERBOSE) {
-          System.out.println("  id=" + id + " is missing");
-        }
-        continue;
-      }
-
-      if (multiValued == true && random().nextBoolean()) {
-        // randomly add multi valued documents (up to 2 fields)
-        int n = random().nextInt(2) + 1;
-        ranges[id] = new Range[n];
-        for (int i=0; i<n; ++i) {
-          ranges[id][i] = nextRange(dimensions);
-        }
-      }
-
-      if (id > 0 && x < 9 && haveRealDoc) {
-        int oldID;
-        int i=0;
-        // don't step on missing ranges:
-        while (true) {
-          oldID = random().nextInt(id);
-          if (ranges[oldID][0].isMissing == false) {
-            break;
-          } else if (++i > id) {
-            continue nextdoc;
-          }
-        }
-
-        if (x == dimensions*2) {
-          // Fully identical box (use first box in case current is multivalued but old is not)
-          for (int d=0; d<dimensions; ++d) {
-            ranges[id][0].setMin(d, ranges[oldID][0].getMin(d));
-            ranges[id][0].setMax(d, ranges[oldID][0].getMax(d));
-          }
-          if (VERBOSE) {
-            System.out.println("  id=" + id + " box=" + ranges[id] + " (same box as doc=" + oldID + ")");
-          }
-        } else {
-          for (int m = 0, even = dimensions % 2; m < dimensions * 2; ++m) {
-            if (x == m) {
-              int d = (int)Math.floor(m/2);
-              // current could be multivalue but old may not be, so use first box
-              if (even == 0) {
-                ranges[id][0].setMin(d, ranges[oldID][0].getMin(d));
-                if (VERBOSE) {
-                  System.out.println("  id=" + id + " box=" + ranges[id] + " (same min[" + d + "] as doc=" + oldID + ")");
-                }
-              } else {
-                ranges[id][0].setMax(d, ranges[oldID][0].getMax(d));
-                if (VERBOSE) {
-                  System.out.println("  id=" + id + " box=" + ranges[id] + " (same max[" + d + "] as doc=" + oldID + ")");
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-    verify(ranges);
-  }
-
-  private void verify(Range[][] ranges) throws Exception {
-    IndexWriterConfig iwc = newIndexWriterConfig();
-    // Else seeds may not reproduce:
-    iwc.setMergeScheduler(new SerialMergeScheduler());
-    // Else we can get O(N^2) merging
-    int mbd = iwc.getMaxBufferedDocs();
-    if (mbd != -1 && mbd < ranges.length/100) {
-      iwc.setMaxBufferedDocs(ranges.length/100);
-    }
-    Directory dir;
-    if (ranges.length > 50000) {
-      dir = newFSDirectory(createTempDir(getClass().getSimpleName()));
-    } else {
-      dir = newDirectory();
-    }
-
-    Set<Integer> deleted = new HashSet<>();
-    IndexWriter w = new IndexWriter(dir, iwc);
-    for (int id=0; id < ranges.length; ++id) {
-      Document doc = new Document();
-      doc.add(newStringField("id", ""+id, Field.Store.NO));
-      doc.add(new NumericDocValuesField("id", id));
-      if (ranges[id][0].isMissing == false) {
-        for (int n=0; n<ranges[id].length; ++n) {
-          doc.add(newRangeField(ranges[id][n]));
-        }
-      }
-      w.addDocument(doc);
-      if (id > 0 && random().nextInt(100) == 1) {
-        int idToDelete = random().nextInt(id);
-        w.deleteDocuments(new Term("id", ""+idToDelete));
-        deleted.add(idToDelete);
-        if (VERBOSE) {
-          System.out.println("  delete id=" + idToDelete);
-        }
-      }
-    }
-
-    if (random().nextBoolean()) {
-      w.forceMerge(1);
-    }
-    final IndexReader r = DirectoryReader.open(w);
-    w.close();
-    IndexSearcher s = newSearcher(r);
-
-    int dimensions = ranges[0][0].numDimensions();
-    int iters = atLeast(25);
-    Bits liveDocs = MultiFields.getLiveDocs(s.getIndexReader());
-    int maxDoc = s.getIndexReader().maxDoc();
-
-    for (int iter=0; iter<iters; ++iter) {
-      if (VERBOSE) {
-        System.out.println("\nTEST: iter=" + iter + " s=" + s);
-      }
-
-      // occasionally test open ended bounding ranges
-      Range queryRange = nextRange(dimensions);
-      int rv = random().nextInt(4);
-      Query query;
-      Range.QueryType queryType;
-      if (rv == 0) {
-        queryType = Range.QueryType.INTERSECTS;
-        query = newIntersectsQuery(queryRange);
-      } else if (rv == 1)  {
-        queryType = Range.QueryType.CONTAINS;
-        query = newContainsQuery(queryRange);
-      } else if (rv == 2) {
-        queryType = Range.QueryType.WITHIN;
-        query = newWithinQuery(queryRange);
-      } else {
-        queryType = Range.QueryType.CROSSES;
-        query = newCrossesQuery(queryRange);
-      }
-
-      if (VERBOSE) {
-        System.out.println("  query=" + query);
-      }
-
-      final FixedBitSet hits = new FixedBitSet(maxDoc);
-      s.search(query, new SimpleCollector() {
-        private int docBase;
-
-        @Override
-        public void collect(int doc) {
-          hits.set(docBase + doc);
-        }
-
-        @Override
-        protected void doSetNextReader(LeafReaderContext context) throws IOException {
-          docBase = context.docBase;
-        }
-
-        @Override
-        public boolean needsScores() { return false; }
-      });
-
-      NumericDocValues docIDToID = MultiDocValues.getNumericValues(r, "id");
-      for (int docID=0; docID<maxDoc; ++docID) {
-        assertEquals(docID, docIDToID.nextDoc());
-        int id = (int) docIDToID.longValue();
-        boolean expected;
-        if (liveDocs != null && liveDocs.get(docID) == false) {
-          // document is deleted
-          expected = false;
-        } else if (ranges[id][0].isMissing) {
-          expected = false;
-        } else {
-          expected = expectedResult(queryRange, ranges[id], queryType);
-        }
-
-        if (hits.get(docID) != expected) {
-          StringBuilder b = new StringBuilder();
-          b.append("FAIL (iter " + iter + "): ");
-          if (expected == true) {
-            b.append("id=" + id + (ranges[id].length > 1 ? " (MultiValue) " : " ") + "should match but did not\n");
-          } else {
-            b.append("id=" + id + " should not match but did\n");
-          }
-          b.append(" queryRange=" + queryRange + "\n");
-          b.append(" box" + ((ranges[id].length > 1) ? "es=" : "=" ) + ranges[id][0]);
-          for (int n=1; n<ranges[id].length; ++n) {
-            b.append(", ");
-            b.append(ranges[id][n]);
-          }
-          b.append("\n queryType=" + queryType + "\n");
-          b.append(" deleted?=" + (liveDocs != null && liveDocs.get(docID) == false));
-          fail("wrong hit (first of possibly more):\n\n" + b);
-        }
-      }
-    }
-    IOUtils.close(r, dir);
-  }
-
-  protected boolean expectedResult(Range queryRange, Range[] range, Range.QueryType queryType) {
-    for (int i=0; i<range.length; ++i) {
-      if (expectedBBoxQueryResult(queryRange, range[i], queryType) == true) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  protected boolean expectedBBoxQueryResult(Range queryRange, Range range, Range.QueryType queryType) {
-    if (queryRange.isEqual(range) && queryType != Range.QueryType.CROSSES) {
-      return true;
-    }
-    Range.QueryType relation = range.relate(queryRange);
-    if (queryType == Range.QueryType.INTERSECTS) {
-      return relation != null;
-    } else if (queryType == Range.QueryType.CROSSES) {
-      // by definition, RangeFields that CONTAIN the query are also considered to cross
-      return relation == queryType || relation == Range.QueryType.CONTAINS;
-    }
-    return relation == queryType;
-  }
-
-  abstract static class Range {
-    protected boolean isMissing = false;
-
-    enum QueryType { INTERSECTS, WITHIN, CONTAINS, CROSSES }
-
-    protected abstract int numDimensions();
-    protected abstract Object getMin(int dim);
-    protected abstract void setMin(int dim, Object val);
-    protected abstract Object getMax(int dim);
-    protected abstract void setMax(int dim, Object val);
-    protected abstract boolean isEqual(Range other);
-    protected abstract boolean isDisjoint(Range other);
-    protected abstract boolean isWithin(Range other);
-    protected abstract boolean contains(Range other);
-
-    protected QueryType relate(Range other) {
-      if (isDisjoint(other)) {
-        // if disjoint; return null:
-        return null;
-      } else if (isWithin(other)) {
-        return QueryType.WITHIN;
-      } else if (contains(other)) {
-        return QueryType.CONTAINS;
-      }
-      return QueryType.CROSSES;
-    }
-  }
-}


[06/23] lucene-solr:jira/solr-9835: remove stale comment

Posted by da...@apache.org.
remove stale comment


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

Branch: refs/heads/jira/solr-9835
Commit: a3f4896359bd0a113eacb0756ec2afe6c8d5d7b9
Parents: 0fb386a
Author: Mike McCandless <mi...@apache.org>
Authored: Sat Mar 11 06:41:49 2017 -0500
Committer: Mike McCandless <mi...@apache.org>
Committed: Sat Mar 11 06:41:49 2017 -0500

----------------------------------------------------------------------
 lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java | 1 -
 1 file changed, 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a3f48963/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
index 2ea0d0e..b1f507a 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
@@ -183,7 +183,6 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
 
   private BooleanQuery rewriteNoScoring() {
     BooleanQuery.Builder newQuery = new BooleanQuery.Builder();
-    // ignore disableCoord, which only matters for scores
     newQuery.setMinimumNumberShouldMatch(getMinimumNumberShouldMatch());
     for (BooleanClause clause : clauses) {
       if (clause.getOccur() == Occur.MUST) {


[21/23] lucene-solr:jira/solr-9835: SOLR-10236: Remove FieldType.getNumericType() from master

Posted by da...@apache.org.
SOLR-10236: Remove FieldType.getNumericType() from master


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

Branch: refs/heads/jira/solr-9835
Commit: abec54bd5722bc818fe46e111cf652cd7671db86
Parents: e3a0b42
Author: Tomas Fernandez Lobbe <tf...@apache.org>
Authored: Mon Mar 13 16:00:16 2017 -0700
Committer: Tomas Fernandez Lobbe <tf...@apache.org>
Committed: Mon Mar 13 16:00:16 2017 -0700

----------------------------------------------------------------------
 solr/CHANGES.txt                                   |  4 ++++
 .../org/apache/solr/schema/DatePointField.java     | 11 -----------
 .../org/apache/solr/schema/DoublePointField.java   | 12 ------------
 .../src/java/org/apache/solr/schema/EnumField.java |  9 ---------
 .../src/java/org/apache/solr/schema/FieldType.java | 11 -----------
 .../org/apache/solr/schema/FloatPointField.java    | 13 -------------
 .../java/org/apache/solr/schema/IntPointField.java | 11 -----------
 .../org/apache/solr/schema/LongPointField.java     | 11 -----------
 .../solr/schema/SpatialPointVectorFieldType.java   |  7 -------
 .../src/java/org/apache/solr/schema/TrieField.java | 17 -----------------
 10 files changed, 4 insertions(+), 102 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 63424dd..80ecea8 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -50,6 +50,8 @@ Upgrading from Solr 6.x
   factors should be indexed in a separate field and combined with the query
   score using a function query.
 
+* Deprecated method getNumericType() has been removed from FieldType. Use getNumberType() instead
+
 New Features
 ----------------------
 * SOLR-9857, SOLR-9858: Collect aggregated metrics from nodes and shard leaders in overseer. (ab)
@@ -80,6 +82,8 @@ Optimizations
   (yonik)
 
 Other Changes
+* SOLR-10236: Removed FieldType.getNumericType(). Use getNumberType() instead. (Tom�s Fern�ndez L�bbe)
+
 ----------------------
 
 ==================  6.5.0 ==================

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/solr/core/src/java/org/apache/solr/schema/DatePointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/DatePointField.java b/solr/core/src/java/org/apache/solr/schema/DatePointField.java
index b3517db..377f571 100644
--- a/solr/core/src/java/org/apache/solr/schema/DatePointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DatePointField.java
@@ -17,7 +17,6 @@
 
 package org.apache.solr.schema;
 
-import java.lang.invoke.MethodHandles;
 import java.time.Instant;
 import java.util.Collection;
 import java.util.Date;
@@ -25,7 +24,6 @@ import java.util.Date;
 import org.apache.lucene.document.LongPoint;
 import org.apache.lucene.document.StoredField;
 import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.legacy.LegacyNumericType;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.LongFieldSource;
 import org.apache.lucene.queries.function.valuesource.MultiValuedLongFieldSource;
@@ -39,13 +37,9 @@ import org.apache.lucene.util.mutable.MutableValueLong;
 import org.apache.solr.search.QParser;
 import org.apache.solr.uninverting.UninvertingReader;
 import org.apache.solr.util.DateMathParser;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class DatePointField extends PointField implements DateValueFieldType {
 
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
   public DatePointField() {
     type = NumberType.DATE;
   }
@@ -165,11 +159,6 @@ public class DatePointField extends PointField implements DateValueFieldType {
   }
 
   @Override
-  public LegacyNumericType getNumericType() {
-    return LegacyNumericType.LONG;
-  }
-
-  @Override
   public IndexableField createField(SchemaField field, Object value) {
     if (!isFieldUsed(field)) return null;
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
index e34ebec..6ae8349 100644
--- a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
@@ -17,14 +17,12 @@
 
 package org.apache.solr.schema;
 
-import java.lang.invoke.MethodHandles;
 import java.util.Collection;
 
 import org.apache.lucene.document.DoublePoint;
 import org.apache.lucene.document.StoredField;
 import org.apache.lucene.index.DocValuesType;
 import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.legacy.LegacyNumericType;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.DoubleFieldSource;
 import org.apache.lucene.queries.function.valuesource.MultiValuedDoubleFieldSource;
@@ -36,8 +34,6 @@ import org.apache.lucene.util.BytesRefBuilder;
 import org.apache.lucene.util.NumericUtils;
 import org.apache.solr.search.QParser;
 import org.apache.solr.uninverting.UninvertingReader.Type;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * {@code PointField} implementation for {@code Double} values.
@@ -46,8 +42,6 @@ import org.slf4j.LoggerFactory;
  */
 public class DoublePointField extends PointField implements DoubleValueFieldType {
 
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
   public DoublePointField() {
     type = NumberType.DOUBLE;
   }
@@ -172,12 +166,6 @@ public class DoublePointField extends PointField implements DoubleValueFieldType
   }
 
   @Override
-  public LegacyNumericType getNumericType() {
-    // TODO: refactor this to not use LegacyNumericType
-    return LegacyNumericType.DOUBLE;
-  }
-
-  @Override
   public IndexableField createField(SchemaField field, Object value) {
     if (!isFieldUsed(field)) return null;
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/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 3e83db4..2e73f74 100644
--- a/solr/core/src/java/org/apache/solr/schema/EnumField.java
+++ b/solr/core/src/java/org/apache/solr/schema/EnumField.java
@@ -233,15 +233,6 @@ public class EnumField extends PrimitiveFieldType {
    * {@inheritDoc}
    */
   @Override
-  @Deprecated
-  public LegacyNumericType getNumericType() {
-    return LegacyNumericType.INT;
-  }
-  
-  /**
-   * {@inheritDoc}
-   */
-  @Override
   public NumberType getNumberType() {
     return NumberType.INTEGER;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/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 c542a95..67b7be7 100644
--- a/solr/core/src/java/org/apache/solr/schema/FieldType.java
+++ b/solr/core/src/java/org/apache/solr/schema/FieldType.java
@@ -39,7 +39,6 @@ import org.apache.lucene.document.Field;
 import org.apache.lucene.document.SortedSetDocValuesField;
 import org.apache.lucene.index.IndexableField;
 import org.apache.lucene.index.Term;
-import org.apache.lucene.legacy.LegacyNumericType;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanQuery;
@@ -610,16 +609,6 @@ public abstract class FieldType extends FieldProperties {
     return similarityFactory;
   }
 
-
-  /** Return the numeric type of this field, or null if this field is not a
-   *  numeric field. 
-   *  @deprecated Please use {@link FieldType#getNumberType()} instead
-   */
-  @Deprecated
-  public LegacyNumericType getNumericType() {
-    return null;
-  }
-  
   /**
    * Return the numeric type of this field, or null if this field is not a
    * numeric field. 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
index 39453e7..0f42cfd 100644
--- a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
@@ -17,14 +17,12 @@
 
 package org.apache.solr.schema;
 
-import java.lang.invoke.MethodHandles;
 import java.util.Collection;
 
 import org.apache.lucene.document.FloatPoint;
 import org.apache.lucene.document.StoredField;
 import org.apache.lucene.index.DocValuesType;
 import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.legacy.LegacyNumericType;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.FloatFieldSource;
 import org.apache.lucene.queries.function.valuesource.MultiValuedFloatFieldSource;
@@ -36,8 +34,6 @@ import org.apache.lucene.util.BytesRefBuilder;
 import org.apache.lucene.util.NumericUtils;
 import org.apache.solr.search.QParser;
 import org.apache.solr.uninverting.UninvertingReader.Type;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * {@code PointField} implementation for {@code Float} values.
@@ -46,8 +42,6 @@ import org.slf4j.LoggerFactory;
  */
 public class FloatPointField extends PointField implements FloatValueFieldType {
 
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
   public FloatPointField() {
     type = NumberType.FLOAT;
   }
@@ -171,13 +165,6 @@ public class FloatPointField extends PointField implements FloatValueFieldType {
     return new MultiValuedFloatFieldSource(f.getName(), choice);
   }
 
-
-  @Override
-  public LegacyNumericType getNumericType() {
-    // TODO: refactor this to not use LegacyNumericType
-    return LegacyNumericType.FLOAT;
-  }
-
   @Override
   public IndexableField createField(SchemaField field, Object value) {
     if (!isFieldUsed(field)) return null;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/solr/core/src/java/org/apache/solr/schema/IntPointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/IntPointField.java b/solr/core/src/java/org/apache/solr/schema/IntPointField.java
index db26988..5eaf7e0 100644
--- a/solr/core/src/java/org/apache/solr/schema/IntPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/IntPointField.java
@@ -17,13 +17,11 @@
 
 package org.apache.solr.schema;
 
-import java.lang.invoke.MethodHandles;
 import java.util.Collection;
 
 import org.apache.lucene.document.IntPoint;
 import org.apache.lucene.document.StoredField;
 import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.legacy.LegacyNumericType;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.IntFieldSource;
 import org.apache.lucene.queries.function.valuesource.MultiValuedIntFieldSource;
@@ -34,8 +32,6 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
 import org.apache.solr.search.QParser;
 import org.apache.solr.uninverting.UninvertingReader.Type;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * {@code PointField} implementation for {@code Integer} values.
@@ -44,8 +40,6 @@ import org.slf4j.LoggerFactory;
  */
 public class IntPointField extends PointField implements IntValueFieldType {
 
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
   public IntPointField() {
     type = NumberType.INTEGER;
   }
@@ -164,11 +158,6 @@ public class IntPointField extends PointField implements IntValueFieldType {
   }
 
   @Override
-  public LegacyNumericType getNumericType() {
-    return LegacyNumericType.INT;
-  }
-
-  @Override
   public IndexableField createField(SchemaField field, Object value) {
     if (!isFieldUsed(field)) return null;
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/solr/core/src/java/org/apache/solr/schema/LongPointField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/LongPointField.java b/solr/core/src/java/org/apache/solr/schema/LongPointField.java
index f5d0948..e58fbcf 100644
--- a/solr/core/src/java/org/apache/solr/schema/LongPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/LongPointField.java
@@ -17,13 +17,11 @@
 
 package org.apache.solr.schema;
 
-import java.lang.invoke.MethodHandles;
 import java.util.Collection;
 
 import org.apache.lucene.document.LongPoint;
 import org.apache.lucene.document.StoredField;
 import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.legacy.LegacyNumericType;
 import org.apache.lucene.queries.function.ValueSource;
 import org.apache.lucene.queries.function.valuesource.LongFieldSource;
 import org.apache.lucene.queries.function.valuesource.MultiValuedLongFieldSource;
@@ -33,8 +31,6 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
 import org.apache.solr.search.QParser;
 import org.apache.solr.uninverting.UninvertingReader.Type;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * {@code PointField} implementation for {@code Long} values.
@@ -43,8 +39,6 @@ import org.slf4j.LoggerFactory;
  */
 public class LongPointField extends PointField implements LongValueFieldType {
 
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
   public LongPointField() {
     type = NumberType.LONG;
   }
@@ -169,11 +163,6 @@ public class LongPointField extends PointField implements LongValueFieldType {
   }
 
   @Override
-  public LegacyNumericType getNumericType() {
-    return LegacyNumericType.LONG;
-  }
-
-  @Override
   public IndexableField createField(SchemaField field, Object value) {
     if (!isFieldUsed(field)) return null;
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java b/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java
index b4b3d2b..5c7734e 100644
--- a/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java
+++ b/solr/core/src/java/org/apache/solr/schema/SpatialPointVectorFieldType.java
@@ -21,7 +21,6 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.lucene.legacy.LegacyFieldType;
-import org.apache.lucene.legacy.LegacyNumericType;
 import org.apache.lucene.spatial.vector.PointVectorStrategy;
 
 /**
@@ -80,12 +79,6 @@ public class SpatialPointVectorFieldType extends AbstractSpatialFieldType<PointV
   }
 
   @Override
-  @Deprecated
-  public LegacyNumericType getNumericType() {
-    return LegacyNumericType.DOUBLE;
-  }
-  
-  @Override
   public NumberType getNumberType() {
     return NumberType.DOUBLE;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/abec54bd/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 b70b2b0..46240c9 100644
--- a/solr/core/src/java/org/apache/solr/schema/TrieField.java
+++ b/solr/core/src/java/org/apache/solr/schema/TrieField.java
@@ -337,23 +337,6 @@ public class TrieField extends NumericFieldType {
   }
 
   @Override
-  public LegacyNumericType getNumericType() {
-    switch (type) {
-      case INTEGER:
-        return LegacyNumericType.INT;
-      case LONG:
-      case DATE:
-        return LegacyNumericType.LONG;
-      case FLOAT:
-        return LegacyNumericType.FLOAT;
-      case DOUBLE:
-        return LegacyNumericType.DOUBLE;
-      default:
-        throw new AssertionError();
-    }
-  }
-
-  @Override
   public Query getRangeQuery(QParser parser, SchemaField field, String min, String max, boolean minInclusive, boolean maxInclusive) {
     if (field.multiValued() && field.hasDocValues() && !field.indexed()) {
       // for the multi-valued dv-case, the default rangeimpl over toInternal is correct


[22/23] lucene-solr:jira/solr-9835: SOLR-8045: Fix smokeTestRelease.py from precommit

Posted by da...@apache.org.
SOLR-8045: Fix smokeTestRelease.py from precommit


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

Branch: refs/heads/jira/solr-9835
Commit: faeb1fe8c16f9e02aa5c3bba295bc24325b94a07
Parents: abec54b
Author: Cao Manh Dat <da...@apache.org>
Authored: Tue Mar 14 08:30:38 2017 +0700
Committer: Cao Manh Dat <da...@apache.org>
Committed: Tue Mar 14 08:30:38 2017 +0700

----------------------------------------------------------------------
 dev-tools/scripts/smokeTestRelease.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/faeb1fe8/dev-tools/scripts/smokeTestRelease.py
----------------------------------------------------------------------
diff --git a/dev-tools/scripts/smokeTestRelease.py b/dev-tools/scripts/smokeTestRelease.py
index b0e76e9..9de490a 100644
--- a/dev-tools/scripts/smokeTestRelease.py
+++ b/dev-tools/scripts/smokeTestRelease.py
@@ -856,7 +856,7 @@ def testSolrExample(unpackPath, javaPath, isSrc):
       print('FAILED: response is:\n%s' % s)
       raise RuntimeError('query on solr example instance failed')
     s = load('http://localhost:8983/v2/cores')
-    if s.find('"responseHeader":{"status":0,"QTime":1}') == -1:
+    if s.find('"responseHeader":{"status":0') == -1:
       print('FAILED: response is:\n%s' % s)
       raise RuntimeError('query api v2 on solr example instance failed')
   finally:


[14/23] lucene-solr:jira/solr-9835: fix IntRange.newIntersectsQuery to use newRelationQuery helper

Posted by da...@apache.org.
fix IntRange.newIntersectsQuery to use newRelationQuery helper


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

Branch: refs/heads/jira/solr-9835
Commit: 35e0c05dbb098fb14e04bf8e48efa1a7943f980a
Parents: d34d81f
Author: Nicholas Knize <nk...@gmail.com>
Authored: Mon Mar 13 02:36:57 2017 -0500
Committer: Nicholas Knize <nk...@gmail.com>
Committed: Mon Mar 13 02:36:57 2017 -0500

----------------------------------------------------------------------
 lucene/core/src/java/org/apache/lucene/document/IntRange.java | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/35e0c05d/lucene/core/src/java/org/apache/lucene/document/IntRange.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/IntRange.java b/lucene/core/src/java/org/apache/lucene/document/IntRange.java
index 2618f14..6d2b71c 100644
--- a/lucene/core/src/java/org/apache/lucene/document/IntRange.java
+++ b/lucene/core/src/java/org/apache/lucene/document/IntRange.java
@@ -188,12 +188,7 @@ public class IntRange extends Field {
    * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
    */
   public static Query newIntersectsQuery(String field, final int[] min, final int[] max) {
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.INTERSECTS) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return IntRange.toString(ranges, dimension);
-      }
-    };
+    return newRelationQuery(field, min, max, QueryType.INTERSECTS);
   }
 
   /**


[03/23] lucene-solr:jira/solr-9835: LUCENE-7700: Move throughput control and merge aborting out of IndexWriter's core.

Posted by da...@apache.org.
LUCENE-7700: Move throughput control and merge aborting out of IndexWriter's 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/9540bc37
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/9540bc37
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/9540bc37

Branch: refs/heads/jira/solr-9835
Commit: 9540bc37583dfd4e995b893154039fcf031dc3c3
Parents: d2bf30d
Author: Dawid Weiss <dw...@apache.org>
Authored: Fri Mar 10 10:23:29 2017 +0100
Committer: Dawid Weiss <dw...@apache.org>
Committed: Fri Mar 10 10:23:29 2017 +0100

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   6 +
 .../lucene/index/ConcurrentMergeScheduler.java  |  75 ++++++--
 .../org/apache/lucene/index/IndexWriter.java    |  94 ++++------
 .../org/apache/lucene/index/MergePolicy.java    | 184 +++++++++++++++++--
 .../apache/lucene/index/MergeRateLimiter.java   | 177 +++++++-----------
 .../org/apache/lucene/index/MergeScheduler.java |  12 ++
 .../apache/lucene/index/NoMergeScheduler.java   |   7 +
 .../lucene/index/TestMergeRateLimiter.java      |   4 +-
 8 files changed, 358 insertions(+), 201 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9540bc37/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 4040945..b6ee4b8 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -117,6 +117,12 @@ API Changes
   instead of once all shard responses are present. (Simon Willnauer,
   Mike McCandless)
 
+* LUCENE-7700: A cleanup of merge throughput control logic. Refactored all the
+  code previously scattered throughout the IndexWriter and 
+  ConcurrentMergeScheduler into a more accessible set of public methods (see 
+  MergePolicy.OneMergeProgress, MergeScheduler.wrapForMerge and 
+  OneMerge.mergeInit). (Dawid Weiss, Mike McCandless).
+
 * LUCENE-7734: FieldType's copy constructor was widened to accept any IndexableFieldType.
   (David Smiley)
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9540bc37/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java b/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java
index 0dd0a4d..6e930c4 100644
--- a/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java
+++ b/lucene/core/src/java/org/apache/lucene/index/ConcurrentMergeScheduler.java
@@ -25,6 +25,11 @@ import java.util.Locale;
 import org.apache.lucene.index.MergePolicy.OneMerge;
 import org.apache.lucene.store.AlreadyClosedException;
 import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FilterDirectory;
+import org.apache.lucene.store.IOContext;
+import org.apache.lucene.store.IndexOutput;
+import org.apache.lucene.store.RateLimitedIndexOutput;
+import org.apache.lucene.store.RateLimiter;
 import org.apache.lucene.util.CollectionUtil;
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.ThreadInterruptedException;
@@ -255,6 +260,36 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
     assert false: "merge thread " + currentThread + " was not found";
   }
 
+  @Override
+  public Directory wrapForMerge(OneMerge merge, Directory in) {
+    Thread mergeThread = Thread.currentThread();
+    if (!MergeThread.class.isInstance(mergeThread)) {
+      throw new AssertionError("wrapForMerge should be called from MergeThread. Current thread: "
+          + mergeThread);
+    }
+
+    // Return a wrapped Directory which has rate-limited output.
+    RateLimiter rateLimiter = ((MergeThread) mergeThread).rateLimiter;
+    return new FilterDirectory(in) {
+      @Override
+      public IndexOutput createOutput(String name, IOContext context) throws IOException {
+        ensureOpen();
+
+        // This Directory is only supposed to be used during merging,
+        // so all writes should have MERGE context, else there is a bug 
+        // somewhere that is failing to pass down the right IOContext:
+        assert context.context == IOContext.Context.MERGE: "got context=" + context.context;
+        
+        // Because rateLimiter is bound to a particular merge thread, this method should
+        // always be called from that context. Verify this.
+        assert mergeThread == Thread.currentThread() : "Not the same merge thread, current="
+          + Thread.currentThread() + ", expected=" + mergeThread;
+
+        return new RateLimitedIndexOutput(rateLimiter, in.createOutput(name, context));
+      }
+    };
+  }
+  
   /**
    * Called whenever the running merges have changed, to set merge IO limits.
    * This method sorts the merge threads by their merge size in
@@ -327,8 +362,9 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
         newMBPerSec = targetMBPerSec;
       }
 
-      double curMBPerSec = merge.rateLimiter.getMBPerSec();
-      
+      MergeRateLimiter rateLimiter = mergeThread.rateLimiter;
+      double curMBPerSec = rateLimiter.getMBPerSec();
+
       if (verbose()) {
         long mergeStartNS = merge.mergeStartNS;
         if (mergeStartNS == -1) {
@@ -339,11 +375,11 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
         message.append(String.format(Locale.ROOT, "merge thread %s estSize=%.1f MB (written=%.1f MB) runTime=%.1fs (stopped=%.1fs, paused=%.1fs) rate=%s\n",
                                      mergeThread.getName(),
                                      bytesToMB(merge.estimatedMergeBytes),
-                                     bytesToMB(merge.rateLimiter.totalBytesWritten),
+                                     bytesToMB(rateLimiter.getTotalBytesWritten()),
                                      nsToSec(now - mergeStartNS),
-                                     nsToSec(merge.rateLimiter.getTotalStoppedNS()),
-                                     nsToSec(merge.rateLimiter.getTotalPausedNS()),
-                                     rateToString(merge.rateLimiter.getMBPerSec())));
+                                     nsToSec(rateLimiter.getTotalStoppedNS()),
+                                     nsToSec(rateLimiter.getTotalPausedNS()),
+                                     rateToString(rateLimiter.getMBPerSec())));
 
         if (newMBPerSec != curMBPerSec) {
           if (newMBPerSec == 0.0) {
@@ -364,7 +400,7 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
         }
       }
 
-      merge.rateLimiter.setMBPerSec(newMBPerSec);
+      rateLimiter.setMBPerSec(newMBPerSec);
     }
     if (verbose()) {
       message(message.toString());
@@ -449,7 +485,7 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
     Thread currentThread = Thread.currentThread();
     int count = 0;
     for (MergeThread mergeThread : mergeThreads) {
-      if (currentThread != mergeThread && mergeThread.isAlive() && mergeThread.merge.rateLimiter.getAbort() == false) {
+      if (currentThread != mergeThread && mergeThread.isAlive() && mergeThread.merge.isAborted() == false) {
         count++;
       }
     }
@@ -497,8 +533,6 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
         return;
       }
 
-      updateIOThrottle(merge);
-
       boolean success = false;
       try {
         if (verbose()) {
@@ -507,14 +541,16 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
 
         // OK to spawn a new merge thread to handle this
         // merge:
-        final MergeThread merger = getMergeThread(writer, merge);
-        mergeThreads.add(merger);
+        final MergeThread newMergeThread = getMergeThread(writer, merge);
+        mergeThreads.add(newMergeThread);
+
+        updateIOThrottle(newMergeThread.merge, newMergeThread.rateLimiter);
 
         if (verbose()) {
-          message("    launch new thread [" + merger.getName() + "]");
+          message("    launch new thread [" + newMergeThread.getName() + "]");
         }
 
-        merger.start();
+        newMergeThread.start();
         updateMergeThreads();
 
         success = true;
@@ -598,16 +634,17 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
 
   /** Runs a merge thread to execute a single merge, then exits. */
   protected class MergeThread extends Thread implements Comparable<MergeThread> {
-
     final IndexWriter writer;
     final OneMerge merge;
+    final MergeRateLimiter rateLimiter;
 
     /** Sole constructor. */
     public MergeThread(IndexWriter writer, OneMerge merge) {
       this.writer = writer;
       this.merge = merge;
+      this.rateLimiter = new MergeRateLimiter(merge.getMergeProgress());
     }
-    
+
     @Override
     public int compareTo(MergeThread other) {
       // Larger merges sort first:
@@ -616,9 +653,7 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
 
     @Override
     public void run() {
-
       try {
-
         if (verbose()) {
           message("  merge thread: start");
         }
@@ -715,7 +750,7 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
   }
 
   /** Tunes IO throttle when a new merge starts. */
-  private synchronized void updateIOThrottle(OneMerge newMerge) throws IOException {
+  private synchronized void updateIOThrottle(OneMerge newMerge, MergeRateLimiter rateLimiter) throws IOException {
     if (doAutoIOThrottle == false) {
       return;
     }
@@ -794,7 +829,7 @@ public class ConcurrentMergeScheduler extends MergeScheduler {
     } else {
       rate = targetMBPerSec;
     }
-    newMerge.rateLimiter.setMBPerSec(rate);
+    rateLimiter.setMBPerSec(rate);
     targetMBPerSecChanged();
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9540bc37/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
index da030ca..aa28d99 100644
--- a/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/IndexWriter.java
@@ -36,6 +36,7 @@ import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.codecs.Codec;
@@ -51,22 +52,18 @@ import org.apache.lucene.search.Sort;
 import org.apache.lucene.store.AlreadyClosedException;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.FSDirectory;
-import org.apache.lucene.store.FilterDirectory;
 import org.apache.lucene.store.FlushInfo;
 import org.apache.lucene.store.IOContext;
-import org.apache.lucene.store.IndexOutput;
 import org.apache.lucene.store.Lock;
 import org.apache.lucene.store.LockObtainFailedException;
 import org.apache.lucene.store.LockValidatingDirectoryWrapper;
 import org.apache.lucene.store.MMapDirectory;
 import org.apache.lucene.store.MergeInfo;
-import org.apache.lucene.store.RateLimitedIndexOutput;
 import org.apache.lucene.store.TrackingDirectoryWrapper;
 import org.apache.lucene.util.Accountable;
 import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.CloseableThreadLocal;
 import org.apache.lucene.util.Constants;
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.InfoStream;
@@ -277,7 +274,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
 
   private final Directory directoryOrig;       // original user directory
   private final Directory directory;           // wrapped with additional checks
-  private final Directory mergeDirectory;      // wrapped with throttling: used for merging
   private final Analyzer analyzer;    // how to analyze text
 
   private final AtomicLong changeCount = new AtomicLong(); // increments every time a change is completed
@@ -353,8 +349,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
    *  card to make sure they can later charge you when you check out. */
   final AtomicLong pendingNumDocs = new AtomicLong();
 
-  final CloseableThreadLocal<MergeRateLimiter> rateLimiters = new CloseableThreadLocal<>();
-
   DirectoryReader getReader() throws IOException {
     return getReader(true, false);
   }
@@ -809,10 +803,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
       directoryOrig = d;
       directory = new LockValidatingDirectoryWrapper(d, writeLock);
 
-      // Directory we use for merging, so we can abort running merges, and so
-      // merge schedulers can optionally rate-limit per-merge IO:
-      mergeDirectory = addMergeRateLimiters(directory);
-
       analyzer = config.getAnalyzer();
       mergeScheduler = config.getMergeScheduler();
       mergeScheduler.setInfoStream(infoStream);
@@ -2212,8 +2202,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
     try {
       abortMerges();
 
-      rateLimiters.close();
-
       if (infoStream.isEnabled("IW")) {
         infoStream.message("IW", "rollback: done finish merges");
       }
@@ -2418,7 +2406,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
       if (infoStream.isEnabled("IW")) {
         infoStream.message("IW", "now abort pending merge " + segString(merge.segments));
       }
-      merge.rateLimiter.setAbort();
+      merge.setAborted();
       mergeFinish(merge);
     }
     pendingMerges.clear();
@@ -2427,7 +2415,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
       if (infoStream.isEnabled("IW")) {
         infoStream.message("IW", "now abort running merge " + segString(merge.segments));
       }
-      merge.rateLimiter.setAbort();
+      merge.setAborted();
     }
 
     // We wait here to make all merges stop.  It should not
@@ -2775,13 +2763,17 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
    * index.
    * 
    * <p>
-   * <b>NOTE:</b> this method merges all given {@link LeafReader}s in one
+   * <b>NOTE:</b> this merges all given {@link LeafReader}s in one
    * merge. If you intend to merge a large number of readers, it may be better
    * to call this method multiple times, each time with a small set of readers.
    * In principle, if you use a merge policy with a {@code mergeFactor} or
    * {@code maxMergeAtOnce} parameter, you should pass that many readers in one
    * call.
    * 
+   * <p>
+   * <b>NOTE:</b> this method does not call or make use of the {@link MergeScheduler},
+   * so any custom bandwidth throttling is at the moment ignored.
+   * 
    * @return The <a href="#sequence_number">sequence number</a>
    * for this operation
    *
@@ -2832,8 +2824,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
       SegmentMerger merger = new SegmentMerger(Arrays.asList(readers), info, infoStream, trackingDir,
                                                globalFieldNumberMap, 
                                                context);
-      
-      rateLimiters.set(new MergeRateLimiter(null));
 
       if (!merger.shouldMerge()) {
         return docWriter.deleteQueue.getNextSequenceNumber();
@@ -2864,7 +2854,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
       // Now create the compound file if needed
       if (useCompoundFile) {
         Collection<String> filesToDelete = infoPerCommit.files();
-        TrackingDirectoryWrapper trackingCFSDir = new TrackingDirectoryWrapper(mergeDirectory);
+        TrackingDirectoryWrapper trackingCFSDir = new TrackingDirectoryWrapper(directory);
         // TODO: unlike merge, on exception we arent sniping any trash cfs files here?
         // createCompoundFile tries to cleanup, but it might not always be able to...
         try {
@@ -3745,7 +3735,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
     // deleter.refresh() call that will remove any index
     // file that current segments does not reference), we
     // abort this merge
-    if (merge.rateLimiter.getAbort()) {
+    if (merge.isAborted()) {
       if (infoStream.isEnabled("IW")) {
         infoStream.message("IW", "commitMerge: skip: it was aborted");
       }
@@ -3905,8 +3895,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
 
     boolean success = false;
 
-    rateLimiters.set(merge.rateLimiter);
-
     final long t0 = System.currentTimeMillis();
 
     final MergePolicy mergePolicy = config.getMergePolicy();
@@ -3937,7 +3925,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
             if (infoStream.isEnabled("IW")) {
               infoStream.message("IW", "hit exception during merge");
             }
-          } else if (merge.rateLimiter.getAbort() == false && (merge.maxNumSegments != UNBOUNDED_MAX_MERGE_SEGMENTS || (!closed && !closing))) {
+          } else if (!merge.isAborted() && (merge.maxNumSegments != UNBOUNDED_MAX_MERGE_SEGMENTS || (!closed && !closing))) {
             // This merge (and, generally, any change to the
             // segments) may now enable new merges, so we call
             // merge policy & update pending merges.
@@ -3951,7 +3939,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
       tragicEvent(t, "merge");
     }
 
-    if (merge.info != null && merge.rateLimiter.getAbort() == false) {
+    if (merge.info != null && merge.isAborted() == false) {
       if (infoStream.isEnabled("IW")) {
         infoStream.message("IW", "merge time " + (System.currentTimeMillis()-t0) + " msec for " + merge.info.info.maxDoc() + " docs");
       }
@@ -3976,7 +3964,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
     assert merge.segments.size() > 0;
 
     if (stopMerges) {
-      merge.rateLimiter.setAbort();
+      merge.setAborted();
       throw new MergePolicy.MergeAbortedException("merge is aborted: " + segString(merge.segments));
     }
 
@@ -4087,7 +4075,9 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
       return;
     }
 
-    if (merge.rateLimiter.getAbort()) {
+    merge.mergeInit();
+
+    if (merge.isAborted()) {
       return;
     }
 
@@ -4239,9 +4229,9 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
    *  but without holding synchronized lock on IndexWriter
    *  instance */
   private int mergeMiddle(MergePolicy.OneMerge merge, MergePolicy mergePolicy) throws IOException {
+    merge.checkAborted();
 
-    merge.rateLimiter.checkAbort();
-
+    Directory mergeDirectory = config.getMergeScheduler().wrapForMerge(merge, directory);
     List<SegmentCommitInfo> sourceSegments = merge.segments;
     
     IOContext context = new IOContext(merge.getStoreMergeInfo());
@@ -4339,7 +4329,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
                                                      globalFieldNumberMap, 
                                                      context);
 
-      merge.rateLimiter.checkAbort();
+      merge.checkAborted();
 
       merge.mergeStartNS = System.nanoTime();
 
@@ -4354,11 +4344,20 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
 
       if (infoStream.isEnabled("IW")) {
         if (merger.shouldMerge()) {
+          String pauseInfo = merge.getMergeProgress().getPauseTimes().entrySet()
+            .stream()
+            .filter((e) -> e.getValue() > 0)
+            .map((e) -> String.format(Locale.ROOT, "%.1f sec %s", 
+                e.getValue() / 1000000000., 
+                e.getKey().name().toLowerCase(Locale.ROOT)))
+            .collect(Collectors.joining(", "));
+          if (!pauseInfo.isEmpty()) {
+            pauseInfo = " (" + pauseInfo + ")";
+          }
+
           long t1 = System.nanoTime();
           double sec = (t1-merge.mergeStartNS)/1000000000.;
           double segmentMB = (merge.info.sizeInBytes()/1024./1024.);
-          double stoppedSec = merge.rateLimiter.getTotalStoppedNS()/1000000000.;
-          double throttleSec = merge.rateLimiter.getTotalPausedNS()/1000000000.;
           infoStream.message("IW", "merge codec=" + codec + " maxDoc=" + merge.info.info.maxDoc() + "; merged segment has " +
                              (mergeState.mergeFieldInfos.hasVectors() ? "vectors" : "no vectors") + "; " +
                              (mergeState.mergeFieldInfos.hasNorms() ? "norms" : "no norms") + "; " + 
@@ -4367,10 +4366,9 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
                              (mergeState.mergeFieldInfos.hasProx() ? "freqs" : "no freqs") + "; " +
                              (mergeState.mergeFieldInfos.hasPointValues() ? "points" : "no points") + "; " +
                              String.format(Locale.ROOT,
-                                           "%.1f sec (%.1f sec stopped, %.1f sec paused) to merge segment [%.2f MB, %.2f MB/sec]",
+                                           "%.1f sec%s to merge segment [%.2f MB, %.2f MB/sec]",
                                            sec,
-                                           stoppedSec,
-                                           throttleSec,
+                                           pauseInfo,
                                            segmentMB,
                                            segmentMB / sec));
         } else {
@@ -4406,7 +4404,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
           success = true;
         } catch (Throwable t) {
           synchronized(this) {
-            if (merge.rateLimiter.getAbort()) {
+            if (merge.isAborted()) {
               // This can happen if rollback is called while we were building
               // our CFS -- fall through to logic below to remove the non-CFS
               // merged files:
@@ -4439,7 +4437,7 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
           // registered with IFD
           deleteNewFiles(filesToRemove);
 
-          if (merge.rateLimiter.getAbort()) {
+          if (merge.isAborted()) {
             if (infoStream.isEnabled("IW")) {
               infoStream.message("IW", "abort merge after building CFS");
             }
@@ -5063,30 +5061,6 @@ public class IndexWriter implements Closeable, TwoPhaseCommit, Accountable {
     throw new IllegalArgumentException("number of documents in the index cannot exceed " + actualMaxDocs + " (current document count is " + pendingNumDocs.get() + "; added numDocs is " + addedNumDocs + ")");
   }
 
-  /** Wraps the incoming {@link Directory} so that we assign a per-thread
-   *  {@link MergeRateLimiter} to all created {@link IndexOutput}s. */
-  private Directory addMergeRateLimiters(Directory in) {
-    return new FilterDirectory(in) {
-      @Override
-      public IndexOutput createOutput(String name, IOContext context) throws IOException {
-        ensureOpen();
-
-        // Paranoia defense: if this trips we have a bug somewhere...
-        IndexWriter.this.ensureOpen(false);
-
-        // This Directory is only supposed to be used during merging,
-        // so all writes should have MERGE context, else there is a bug 
-        // somewhere that is failing to pass down the right IOContext:
-        assert context.context == IOContext.Context.MERGE: "got context=" + context.context;
-
-        MergeRateLimiter rateLimiter = rateLimiters.get();
-        assert rateLimiter != null;
-
-        return new RateLimitedIndexOutput(rateLimiter, in.createOutput(name, context));
-      }
-    };
-  }
-
   /** Returns the highest <a href="#sequence_number">sequence number</a> across
    *  all completed operations, or 0 if no operations have finished yet.  Still
    *  in-flight operations (in other threads) are not counted until they finish.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9540bc37/lucene/core/src/java/org/apache/lucene/index/MergePolicy.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/MergePolicy.java b/lucene/core/src/java/org/apache/lucene/index/MergePolicy.java
index dbf37df..d9a0ab8 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MergePolicy.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MergePolicy.java
@@ -19,12 +19,19 @@ package org.apache.lucene.index;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.EnumMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.BooleanSupplier;
+import java.util.stream.Collectors;
 
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.store.MergeInfo;
-import org.apache.lucene.store.RateLimiter;
 
 /**
  * <p>Expert: a MergePolicy determines the sequence of
@@ -55,6 +62,125 @@ import org.apache.lucene.store.RateLimiter;
  * @lucene.experimental
  */
 public abstract class MergePolicy {
+  /**
+   * Progress and state for an executing merge. This class
+   * encapsulates the logic to pause and resume the merge thread
+   * or to abort the merge entirely.
+   * 
+   * @lucene.experimental */
+  public static class OneMergeProgress {
+    /** Reason for pausing the merge thread. */
+    public static enum PauseReason {
+      /** Stopped (because of throughput rate set to 0, typically). */
+      STOPPED,
+      /** Temporarily paused because of exceeded throughput rate. */
+      PAUSED,
+      /** Other reason. */
+      OTHER
+    };
+
+    private final ReentrantLock pauseLock = new ReentrantLock();
+    private final Condition pausing = pauseLock.newCondition();
+
+    /**
+     * Pause times (in nanoseconds) for each {@link PauseReason}.
+     */
+    private final EnumMap<PauseReason, AtomicLong> pauseTimesNS;
+    
+    private volatile boolean aborted;
+
+    /**
+     * This field is for sanity-check purposes only. Only the same thread that invoked
+     * {@link OneMerge#mergeInit()} is permitted to be calling 
+     * {@link #pauseNanos}. This is always verified at runtime. 
+     */
+    private Thread owner;
+
+    /** Creates a new merge progress info. */
+    public OneMergeProgress() {
+      // Place all the pause reasons in there immediately so that we can simply update values.
+      pauseTimesNS = new EnumMap<PauseReason,AtomicLong>(PauseReason.class);
+      for (PauseReason p : PauseReason.values()) {
+        pauseTimesNS.put(p, new AtomicLong());
+      }
+    }
+
+    /**
+     * Abort the merge this progress tracks at the next 
+     * possible moment.
+     */
+    public void abort() {
+      aborted = true;
+      wakeup(); // wakeup any paused merge thread.
+    }
+
+    /**
+     * Return the aborted state of this merge.
+     */
+    public boolean isAborted() {
+      return aborted;
+    }
+
+    /**
+     * Pauses the calling thread for at least <code>pauseNanos</code> nanoseconds
+     * unless the merge is aborted or the external condition returns <code>false</code>,
+     * in which case control returns immediately.
+     * 
+     * The external condition is required so that other threads can terminate the pausing immediately,
+     * before <code>pauseNanos</code> expires. We can't rely on just {@link Condition#awaitNanos(long)} alone
+     * because it can return due to spurious wakeups too.  
+     * 
+     * @param condition The pause condition that should return false if immediate return from this
+     *      method is needed. Other threads can wake up any sleeping thread by calling 
+     *      {@link #wakeup}, but it'd fall to sleep for the remainder of the requested time if this
+     *      condition 
+     */
+    public void pauseNanos(long pauseNanos, PauseReason reason, BooleanSupplier condition) throws InterruptedException {
+      if (Thread.currentThread() != owner) {
+        throw new RuntimeException("Only the merge owner thread can call pauseNanos(). This thread: "
+            + Thread.currentThread().getName() + ", owner thread: "
+            + owner);
+      }
+
+      long start = System.nanoTime();
+      AtomicLong timeUpdate = pauseTimesNS.get(reason);
+      pauseLock.lock();
+      try {
+        while (pauseNanos > 0 && !aborted && condition.getAsBoolean()) {
+          pauseNanos = pausing.awaitNanos(pauseNanos);
+        }
+      } finally {
+        pauseLock.unlock();
+        timeUpdate.addAndGet(System.nanoTime() - start);
+      }
+    }
+
+    /**
+     * Request a wakeup for any threads stalled in {@link #pauseNanos}.
+     */
+    public void wakeup() {
+      pauseLock.lock();
+      try {
+        pausing.signalAll();
+      } finally {
+        pauseLock.unlock();
+      }
+    }
+
+    /** Returns pause reasons and associated times in nanoseconds. */
+    public Map<PauseReason,Long> getPauseTimes() {
+      Set<Entry<PauseReason,AtomicLong>> entries = pauseTimesNS.entrySet();
+      return entries.stream()
+          .collect(Collectors.toMap(
+              (e) -> e.getKey(),
+              (e) -> e.getValue().get()));
+    }
+
+    final void setMergeThread(Thread owner) {
+      assert this.owner == null;
+      this.owner = owner;
+    }
+  }
 
   /** OneMerge provides the information necessary to perform
    *  an individual primitive merge operation, resulting in
@@ -64,7 +190,6 @@ public abstract class MergePolicy {
    *
    * @lucene.experimental */
   public static class OneMerge {
-
     SegmentCommitInfo info;         // used by IndexWriter
     boolean registerDone;           // used by IndexWriter
     long mergeGen;                  // used by IndexWriter
@@ -82,8 +207,10 @@ public abstract class MergePolicy {
     /** Segments to be merged. */
     public final List<SegmentCommitInfo> segments;
 
-    /** A private {@link RateLimiter} for this merge, used to rate limit writes and abort. */
-    public final MergeRateLimiter rateLimiter;
+    /**
+     * Control used to pause/stop/resume the merge thread. 
+     */
+    private final OneMergeProgress mergeProgress;
 
     volatile long mergeStartNS = -1;
 
@@ -106,9 +233,17 @@ public abstract class MergePolicy {
       }
       totalMaxDoc = count;
 
-      rateLimiter = new MergeRateLimiter(this);
+      mergeProgress = new OneMergeProgress();
     }
 
+    /** 
+     * Called by {@link IndexWriter} after the merge started and from the
+     * thread that will be executing the merge.
+     */
+    public void mergeInit() throws IOException {
+      mergeProgress.setMergeThread(Thread.currentThread());
+    }
+    
     /** Called by {@link IndexWriter} after the merge is done and all readers have been closed. */
     public void mergeFinished() throws IOException {
     }
@@ -163,7 +298,7 @@ public abstract class MergePolicy {
       if (maxNumSegments != -1) {
         b.append(" [maxNumSegments=" + maxNumSegments + "]");
       }
-      if (rateLimiter.getAbort()) {
+      if (isAborted()) {
         b.append(" [ABORTED]");
       }
       return b.toString();
@@ -194,7 +329,32 @@ public abstract class MergePolicy {
     /** Return {@link MergeInfo} describing this merge. */
     public MergeInfo getStoreMergeInfo() {
       return new MergeInfo(totalMaxDoc, estimatedMergeBytes, isExternal, maxNumSegments);
-    }    
+    }
+
+    /** Returns true if this merge was or should be aborted. */
+    public boolean isAborted() {
+      return mergeProgress.isAborted();
+    }
+
+    /** Marks this merge as aborted. The merge thread should terminate at the soonest possible moment. */
+    public void setAborted() {
+      this.mergeProgress.abort();
+    }
+
+    /** Checks if merge has been aborted and throws a merge exception if so. */
+    public void checkAborted() throws MergeAbortedException {
+      if (isAborted()) {
+        throw new MergePolicy.MergeAbortedException("merge is aborted: " + segString());
+      }
+    }
+
+    /**
+     * Returns a {@link OneMergeProgress} instance for this merge, which provides
+     * statistics of the merge threads (run time vs. sleep time) if merging is throttled.
+     */
+    public OneMergeProgress getMergeProgress() {
+      return mergeProgress;
+    }
   }
 
   /**
@@ -222,8 +382,7 @@ public abstract class MergePolicy {
       merges.add(merge);
     }
 
-    /** Returns a description of the merges in this
-    *  specification. */
+    /** Returns a description of the merges in this specification. */
     public String segString(Directory dir) {
       StringBuilder b = new StringBuilder();
       b.append("MergeSpec:\n");
@@ -235,8 +394,7 @@ public abstract class MergePolicy {
     }
   }
 
-  /** Exception thrown if there are any problems while
-   *  executing a merge. */
+  /** Exception thrown if there are any problems while executing a merge. */
   public static class MergeException extends RuntimeException {
     private Directory dir;
 
@@ -259,9 +417,9 @@ public abstract class MergePolicy {
     }
   }
 
-  /** Thrown when a merge was explicity aborted because
+  /** Thrown when a merge was explicitly aborted because
    *  {@link IndexWriter#abortMerges} was called.  Normally
-   *  this exception is privately caught and suppresed by
+   *  this exception is privately caught and suppressed by
    *  {@link IndexWriter}. */
   public static class MergeAbortedException extends IOException {
     /** Create a {@link MergeAbortedException}. */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9540bc37/lucene/core/src/java/org/apache/lucene/index/MergeRateLimiter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/MergeRateLimiter.java b/lucene/core/src/java/org/apache/lucene/index/MergeRateLimiter.java
index d04c2d2..e5361d5 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MergeRateLimiter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MergeRateLimiter.java
@@ -20,118 +20,107 @@ package org.apache.lucene.index;
 import org.apache.lucene.store.RateLimiter;
 import org.apache.lucene.util.ThreadInterruptedException;
 
-import static org.apache.lucene.store.RateLimiter.SimpleRateLimiter;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.lucene.index.MergePolicy.OneMergeProgress;
+import org.apache.lucene.index.MergePolicy.OneMergeProgress.PauseReason;
 
 /** This is the {@link RateLimiter} that {@link IndexWriter} assigns to each running merge, to 
  *  give {@link MergeScheduler}s ionice like control.
  *
- *  This is similar to {@link SimpleRateLimiter}, except it's merge-private,
- *  it will wake up if its rate changes while it's paused, it tracks how
- *  much time it spent stopped and paused, and it supports aborting.
- *
  *  @lucene.internal */
 
 public class MergeRateLimiter extends RateLimiter {
 
   private final static int MIN_PAUSE_CHECK_MSEC = 25;
-  volatile long totalBytesWritten;
+  
+  private final static long MIN_PAUSE_NS = TimeUnit.MILLISECONDS.toNanos(2);
+  private final static long MAX_PAUSE_NS = TimeUnit.MILLISECONDS.toNanos(250);
+
+  private volatile double mbPerSec;
+  private volatile long minPauseCheckBytes;
 
-  double mbPerSec;
   private long lastNS;
-  private long minPauseCheckBytes;
-  private boolean abort;
-  long totalPausedNS;
-  long totalStoppedNS;
-  final MergePolicy.OneMerge merge;
 
-  /** Returned by {@link #maybePause}. */
-  private static enum PauseResult {NO, STOPPED, PAUSED};
+  private AtomicLong totalBytesWritten = new AtomicLong();
 
-  /** Sole constructor. */
-  public MergeRateLimiter(MergePolicy.OneMerge merge) {
-    this.merge = merge;
+  private final OneMergeProgress mergeProgress;
 
+  /** Sole constructor. */
+  public MergeRateLimiter(OneMergeProgress mergeProgress) {
     // Initially no IO limit; use setter here so minPauseCheckBytes is set:
+    this.mergeProgress = mergeProgress;
     setMBPerSec(Double.POSITIVE_INFINITY);
   }
 
   @Override
-  public synchronized void setMBPerSec(double mbPerSec) {
-    // 0.0 is allowed: it means the merge is paused
-    if (mbPerSec < 0.0) {
-      throw new IllegalArgumentException("mbPerSec must be positive; got: " + mbPerSec);
+  public void setMBPerSec(double mbPerSec) {
+    // Synchronized to make updates to mbPerSec and minPauseCheckBytes atomic. 
+    synchronized (this) {
+      // 0.0 is allowed: it means the merge is paused
+      if (mbPerSec < 0.0) {
+        throw new IllegalArgumentException("mbPerSec must be positive; got: " + mbPerSec);
+      }
+      this.mbPerSec = mbPerSec;
+  
+      // NOTE: Double.POSITIVE_INFINITY casts to Long.MAX_VALUE
+      this.minPauseCheckBytes = Math.min(1024*1024, (long) ((MIN_PAUSE_CHECK_MSEC / 1000.0) * mbPerSec * 1024 * 1024));
+      assert minPauseCheckBytes >= 0;
     }
-    this.mbPerSec = mbPerSec;
-    // NOTE: Double.POSITIVE_INFINITY casts to Long.MAX_VALUE
-    minPauseCheckBytes = Math.min(1024*1024, (long) ((MIN_PAUSE_CHECK_MSEC / 1000.0) * mbPerSec * 1024 * 1024));
-    assert minPauseCheckBytes >= 0;
-    notify();
+
+    mergeProgress.wakeup();
   }
 
   @Override
-  public synchronized double getMBPerSec() {
+  public double getMBPerSec() {
     return mbPerSec;
   }
 
   /** Returns total bytes written by this merge. */
   public long getTotalBytesWritten() {
-    return totalBytesWritten;
+    return totalBytesWritten.get();
   }
 
   @Override
   public long pause(long bytes) throws MergePolicy.MergeAbortedException {
+    totalBytesWritten.addAndGet(bytes);
 
-    totalBytesWritten += bytes;
-
-    long startNS = System.nanoTime();
-    long curNS = startNS;
-
-    // While loop because 1) Thread.wait doesn't always sleep long
-    // enough, and 2) we wake up and check again when our rate limit
+    // While loop because we may wake up and check again when our rate limit
     // is changed while we were pausing:
-    long pausedNS = 0;
-    while (true) {
-      PauseResult result = maybePause(bytes, curNS);
-      if (result == PauseResult.NO) {
-        // Set to curNS, not targetNS, to enforce the instant rate, not
-        // the "averaaged over all history" rate:
-        lastNS = curNS;
-        break;
-      }
-      curNS = System.nanoTime();
-      long ns = curNS - startNS;
-      startNS = curNS;
-
-      // Separately track when merge was stopped vs rate limited:
-      if (result == PauseResult.STOPPED) {
-        totalStoppedNS += ns;
-      } else {
-        assert result == PauseResult.PAUSED;
-        totalPausedNS += ns;
-      }
-      pausedNS += ns;
+    long paused = 0;
+    long delta;
+    while ((delta = maybePause(bytes, System.nanoTime())) >= 0) {
+      // Keep waiting.
+      paused += delta;
     }
 
-    return pausedNS;
+    return paused;
   }
 
   /** Total NS merge was stopped. */
-  public synchronized long getTotalStoppedNS() {
-    return totalStoppedNS;
+  public long getTotalStoppedNS() {
+    return mergeProgress.getPauseTimes().get(PauseReason.STOPPED);
   } 
 
   /** Total NS merge was paused to rate limit IO. */
-  public synchronized long getTotalPausedNS() {
-    return totalPausedNS;
+  public long getTotalPausedNS() {
+    return mergeProgress.getPauseTimes().get(PauseReason.PAUSED);
   } 
 
-  /** Returns NO if no pause happened, STOPPED if pause because rate was 0.0 (merge is stopped), PAUSED if paused with a normal rate limit. */
-  private synchronized PauseResult maybePause(long bytes, long curNS) throws MergePolicy.MergeAbortedException {
-
+  /** 
+   * Returns the number of nanoseconds spent in a paused state or <code>-1</code>
+   * if no pause was applied. If the thread needs pausing, this method delegates 
+   * to the linked {@link OneMergeProgress}. 
+   */
+  private long maybePause(long bytes, long curNS) throws MergePolicy.MergeAbortedException {
     // Now is a good time to abort the merge:
-    checkAbort();
+    if (mergeProgress.isAborted()) {
+      throw new MergePolicy.MergeAbortedException("Merge aborted.");
+    }
 
-    double secondsToPause = (bytes/1024./1024.) / mbPerSec;
+    double rate = mbPerSec; // read from volatile rate once.
+    double secondsToPause = (bytes/1024./1024.) / rate;
 
     // Time we should sleep until; this is purely instantaneous
     // rate (just adds seconds onto the last time we had paused to);
@@ -140,54 +129,30 @@ public class MergeRateLimiter extends RateLimiter {
 
     long curPauseNS = targetNS - curNS;
 
-    // NOTE: except maybe on real-time JVMs, minimum realistic
-    // wait/sleep time is 1 msec; if you pass just 1 nsec the impl
-    // rounds up to 1 msec, so we don't bother unless it's > 2 msec:
-
-    if (curPauseNS <= 2000000) {
-      return PauseResult.NO;
+    // We don't bother with thread pausing if the pause is smaller than 2 msec.
+    if (curPauseNS <= MIN_PAUSE_NS) {
+      // Set to curNS, not targetNS, to enforce the instant rate, not
+      // the "averaged over all history" rate:
+      lastNS = curNS;
+      return -1;
     }
 
-    // Defensive: sleep for at most 250 msec; the loop above will call us again if we should keep sleeping:
-    if (curPauseNS > 250L*1000000) {
-      curPauseNS = 250L*1000000;
+    // Defensive: don't sleep for too long; the loop above will call us again if
+    // we should keep sleeping and the rate may be adjusted in between.
+    if (curPauseNS > MAX_PAUSE_NS) {
+      curPauseNS = MAX_PAUSE_NS;
     }
 
-    int sleepMS = (int) (curPauseNS / 1000000);
-    int sleepNS = (int) (curPauseNS % 1000000);
-
-    double rate = mbPerSec;
-
+    long start = System.nanoTime();
     try {
-      // CMS can wake us up here if it changes our target rate:
-      wait(sleepMS, sleepNS);
+      mergeProgress.pauseNanos(
+          curPauseNS, 
+          rate == 0.0 ? PauseReason.STOPPED : PauseReason.PAUSED,
+          () -> rate == mbPerSec);
     } catch (InterruptedException ie) {
       throw new ThreadInterruptedException(ie);
     }
-
-    if (rate == 0.0) {
-      return PauseResult.STOPPED;
-    } else {
-      return PauseResult.PAUSED;
-    }
-  }
-
-  /** Throws {@link MergePolicy.MergeAbortedException} if this merge was aborted. */
-  public synchronized void checkAbort() throws MergePolicy.MergeAbortedException {
-    if (abort) {
-      throw new MergePolicy.MergeAbortedException("merge is aborted: " + merge.segString());
-    }
-  }
-
-  /** Mark this merge aborted. */
-  public synchronized void setAbort() {
-    abort = true;
-    notify();
-  }
-
-  /** Returns true if this merge was aborted. */
-  public synchronized boolean getAbort() {
-    return abort;
+    return System.nanoTime() - start;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9540bc37/lucene/core/src/java/org/apache/lucene/index/MergeScheduler.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/MergeScheduler.java b/lucene/core/src/java/org/apache/lucene/index/MergeScheduler.java
index 65af45b..66d0870 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MergeScheduler.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MergeScheduler.java
@@ -20,6 +20,9 @@ package org.apache.lucene.index;
 import java.io.Closeable;
 import java.io.IOException;
 
+import org.apache.lucene.index.MergePolicy.OneMerge;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RateLimitedIndexOutput;
 import org.apache.lucene.util.InfoStream;
 
 /** <p>Expert: {@link IndexWriter} uses an instance
@@ -42,6 +45,15 @@ public abstract class MergeScheduler implements Closeable {
    * */
   public abstract void merge(IndexWriter writer, MergeTrigger trigger, boolean newMergesFound) throws IOException;
 
+  /** 
+   * Wraps the incoming {@link Directory} so that we can merge-throttle it
+   * using {@link RateLimitedIndexOutput}. 
+   */
+  public Directory wrapForMerge(OneMerge merge, Directory in) {
+    // A no-op by default.
+    return in;
+  }
+
   /** Close this MergeScheduler. */
   @Override
   public abstract void close() throws IOException;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9540bc37/lucene/core/src/java/org/apache/lucene/index/NoMergeScheduler.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/NoMergeScheduler.java b/lucene/core/src/java/org/apache/lucene/index/NoMergeScheduler.java
index 1630653..e4c0136 100644
--- a/lucene/core/src/java/org/apache/lucene/index/NoMergeScheduler.java
+++ b/lucene/core/src/java/org/apache/lucene/index/NoMergeScheduler.java
@@ -16,6 +16,8 @@
  */
 package org.apache.lucene.index;
 
+import org.apache.lucene.index.MergePolicy.OneMerge;
+import org.apache.lucene.store.Directory;
 
 /**
  * A {@link MergeScheduler} which never executes any merges. It is also a
@@ -41,6 +43,11 @@ public final class NoMergeScheduler extends MergeScheduler {
 
   @Override
   public void merge(IndexWriter writer, MergeTrigger trigger, boolean newMergesFound) {}
+  
+  @Override
+  public Directory wrapForMerge(OneMerge merge, Directory in) {
+    return in;
+  }
 
   @Override
   public MergeScheduler clone() {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9540bc37/lucene/core/src/test/org/apache/lucene/index/TestMergeRateLimiter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestMergeRateLimiter.java b/lucene/core/src/test/org/apache/lucene/index/TestMergeRateLimiter.java
index ef922bb..723cfbc 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestMergeRateLimiter.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestMergeRateLimiter.java
@@ -27,8 +27,8 @@ public class TestMergeRateLimiter extends LuceneTestCase {
     RandomIndexWriter w = new RandomIndexWriter(random(), dir);
     w.addDocument(new Document());
     w.close();
-    MergePolicy.OneMerge merge = new MergePolicy.OneMerge(SegmentInfos.readLatestCommit(dir).asList());
-    MergeRateLimiter rateLimiter = new MergeRateLimiter(merge);
+
+    MergeRateLimiter rateLimiter = new MergeRateLimiter(new MergePolicy.OneMergeProgress());
     assertEquals(Double.POSITIVE_INFINITY, rateLimiter.getMBPerSec(), 0.0);
     assertTrue(rateLimiter.getMinPauseCheckBytes() > 0);
     dir.close();


[12/23] lucene-solr:jira/solr-9835: LUCENE-7740: Refactor Range Fields to remove Field suffix (e.g., DoubleRange), move InetAddressRange and InetAddressPoint from sandbox to misc module, and refactor all other range fields from sandbox to core.

Posted by da...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java
new file mode 100644
index 0000000..14771c9
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/TestIntRangeFieldQueries.java
@@ -0,0 +1,251 @@
+/*
+ * 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.util.Arrays;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.IntRange;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+
+/**
+ * Random testing for IntRange Queries.
+ */
+public class TestIntRangeFieldQueries extends BaseRangeFieldQueryTestCase {
+  private static final String FIELD_NAME = "intRangeField";
+
+  private int nextIntInternal() {
+    if (rarely()) {
+      return random().nextBoolean() ? Integer.MAX_VALUE : Integer.MIN_VALUE;
+    }
+    int max = Integer.MAX_VALUE / 2;
+    return (max + max) * random().nextInt() - max;
+  }
+
+  @Override
+  protected Range nextRange(int dimensions) throws Exception {
+    int[] min = new int[dimensions];
+    int[] max = new int[dimensions];
+
+    int minV, maxV;
+    for (int d=0; d<dimensions; ++d) {
+      minV = nextIntInternal();
+      maxV = nextIntInternal();
+      min[d] = Math.min(minV, maxV);
+      max[d] = Math.max(minV, maxV);
+    }
+    return new IntTestRange(min, max);
+  }
+
+  @Override
+  protected org.apache.lucene.document.IntRange newRangeField(Range r) {
+    return new IntRange(FIELD_NAME, ((IntTestRange)r).min, ((IntTestRange)r).max);
+  }
+
+  @Override
+  protected Query newIntersectsQuery(Range r) {
+    return IntRange.newIntersectsQuery(FIELD_NAME, ((IntTestRange)r).min, ((IntTestRange)r).max);
+  }
+
+  @Override
+  protected Query newContainsQuery(Range r) {
+    return IntRange.newContainsQuery(FIELD_NAME, ((IntTestRange)r).min, ((IntTestRange)r).max);
+  }
+
+  @Override
+  protected Query newWithinQuery(Range r) {
+    return IntRange.newWithinQuery(FIELD_NAME, ((IntTestRange)r).min, ((IntTestRange)r).max);
+  }
+
+  @Override
+  protected Query newCrossesQuery(Range r) {
+    return IntRange.newCrossesQuery(FIELD_NAME, ((IntTestRange)r).min, ((IntTestRange)r).max);
+  }
+
+  /** Basic test */
+  public void testBasics() throws Exception {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+
+    // intersects (within)
+    Document document = new Document();
+    document.add(new IntRange(FIELD_NAME, new int[] {-10, -10}, new int[] {9, 10}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new IntRange(FIELD_NAME, new int[] {10, -10}, new int[] {20, 10}));
+    writer.addDocument(document);
+
+    // intersects (contains / crosses)
+    document = new Document();
+    document.add(new IntRange(FIELD_NAME, new int[] {-20, -20}, new int[] {30, 30}));
+    writer.addDocument(document);
+
+    // intersects (within)
+    document = new Document();
+    document.add(new IntRange(FIELD_NAME, new int[] {-11, -11}, new int[] {1, 11}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new IntRange(FIELD_NAME, new int[] {12, 1}, new int[] {15, 29}));
+    writer.addDocument(document);
+
+    // disjoint
+    document = new Document();
+    document.add(new IntRange(FIELD_NAME, new int[] {-122, 1}, new int[] {-115, 29}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new IntRange(FIELD_NAME, new int[] {Integer.MIN_VALUE, 1}, new int[] {-11, 29}));
+    writer.addDocument(document);
+
+    // equal (within, contains, intersects)
+    document = new Document();
+    document.add(new IntRange(FIELD_NAME, new int[] {-11, -15}, new int[] {15, 20}));
+    writer.addDocument(document);
+
+    // search
+    IndexReader reader = writer.getReader();
+    IndexSearcher searcher = newSearcher(reader);
+    assertEquals(7, searcher.count(IntRange.newIntersectsQuery(FIELD_NAME,
+        new int[] {-11, -15}, new int[] {15, 20})));
+    assertEquals(3, searcher.count(IntRange.newWithinQuery(FIELD_NAME,
+        new int[] {-11, -15}, new int[] {15, 20})));
+    assertEquals(2, searcher.count(IntRange.newContainsQuery(FIELD_NAME,
+        new int[] {-11, -15}, new int[] {15, 20})));
+    assertEquals(4, searcher.count(IntRange.newCrossesQuery(FIELD_NAME,
+        new int[] {-11, -15}, new int[] {15, 20})));
+
+    reader.close();
+    writer.close();
+    dir.close();
+  }
+
+  /** IntRange test class implementation - use to validate IntRange */
+  private class IntTestRange extends Range {
+    int[] min;
+    int[] max;
+
+    IntTestRange(int[] min, int[] max) {
+      assert min != null && max != null && min.length > 0 && max.length > 0
+          : "test box: min/max cannot be null or empty";
+      assert min.length == max.length : "test box: min/max length do not agree";
+      this.min = min;
+      this.max = max;
+    }
+
+    @Override
+    protected int numDimensions() {
+      return min.length;
+    }
+
+    @Override
+    protected Integer getMin(int dim) {
+      return min[dim];
+    }
+
+    @Override
+    protected void setMin(int dim, Object val) {
+      int v = (Integer)val;
+      if (min[dim] < v) {
+        max[dim] = v;
+      } else {
+        min[dim] = v;
+      }
+    }
+
+    @Override
+    protected Integer getMax(int dim) {
+      return max[dim];
+    }
+
+    @Override
+    protected void setMax(int dim, Object val) {
+      int v = (Integer)val;
+      if (max[dim] > v) {
+        min[dim] = v;
+      } else {
+        max[dim] = v;
+      }
+    }
+
+    @Override
+    protected boolean isEqual(Range other) {
+      IntTestRange o = (IntTestRange)other;
+      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
+    }
+
+    @Override
+    protected boolean isDisjoint(Range o) {
+      IntTestRange other = (IntTestRange)o;
+      for (int d=0; d<this.min.length; ++d) {
+        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
+          // disjoint:
+          return true;
+        }
+      }
+      return false;
+    }
+
+    @Override
+    protected boolean isWithin(Range o) {
+      IntTestRange other = (IntTestRange)o;
+      for (int d=0; d<this.min.length; ++d) {
+        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
+          // not within:
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    protected boolean contains(Range o) {
+      IntTestRange other = (IntTestRange) o;
+      for (int d=0; d<this.min.length; ++d) {
+        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
+          // not contains:
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder b = new StringBuilder();
+      b.append("Box(");
+      b.append(min[0]);
+      b.append(" TO ");
+      b.append(max[0]);
+      for (int d=1; d<min.length; ++d) {
+        b.append(", ");
+        b.append(min[d]);
+        b.append(" TO ");
+        b.append(max[d]);
+      }
+      b.append(")");
+
+      return b.toString();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/core/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java
new file mode 100644
index 0000000..60d7ea3
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/TestLongRangeFieldQueries.java
@@ -0,0 +1,251 @@
+/*
+ * 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.util.Arrays;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.LongRange;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.store.Directory;
+
+/**
+ * Random testing for LongRange Queries.
+ */
+public class TestLongRangeFieldQueries extends BaseRangeFieldQueryTestCase {
+  private static final String FIELD_NAME = "longRangeField";
+
+  private long nextLongInternal() {
+    if (rarely()) {
+      return random().nextBoolean() ? Long.MAX_VALUE : Long.MIN_VALUE;
+    }
+    long max = Long.MAX_VALUE / 2;
+    return (max + max) * random().nextLong() - max;
+  }
+
+  @Override
+  protected Range nextRange(int dimensions) throws Exception {
+    long[] min = new long[dimensions];
+    long[] max = new long[dimensions];
+
+    long minV, maxV;
+    for (int d=0; d<dimensions; ++d) {
+      minV = nextLongInternal();
+      maxV = nextLongInternal();
+      min[d] = Math.min(minV, maxV);
+      max[d] = Math.max(minV, maxV);
+    }
+    return new LongTestRange(min, max);
+  }
+
+  @Override
+  protected LongRange newRangeField(Range r) {
+    return new LongRange(FIELD_NAME, ((LongTestRange)r).min, ((LongTestRange)r).max);
+  }
+
+  @Override
+  protected Query newIntersectsQuery(Range r) {
+    return LongRange.newIntersectsQuery(FIELD_NAME, ((LongTestRange)r).min, ((LongTestRange)r).max);
+  }
+
+  @Override
+  protected Query newContainsQuery(Range r) {
+    return LongRange.newContainsQuery(FIELD_NAME, ((LongTestRange)r).min, ((LongTestRange)r).max);
+  }
+
+  @Override
+  protected Query newWithinQuery(Range r) {
+    return LongRange.newWithinQuery(FIELD_NAME, ((LongTestRange)r).min, ((LongTestRange)r).max);
+  }
+
+  @Override
+  protected Query newCrossesQuery(Range r) {
+    return LongRange.newCrossesQuery(FIELD_NAME, ((LongTestRange)r).min, ((LongTestRange)r).max);
+  }
+
+  /** Basic test */
+  public void testBasics() throws Exception {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+
+    // intersects (within)
+    Document document = new Document();
+    document.add(new LongRange(FIELD_NAME, new long[] {-10, -10}, new long[] {9, 10}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new LongRange(FIELD_NAME, new long[] {10, -10}, new long[] {20, 10}));
+    writer.addDocument(document);
+
+    // intersects (contains, crosses)
+    document = new Document();
+    document.add(new LongRange(FIELD_NAME, new long[] {-20, -20}, new long[] {30, 30}));
+    writer.addDocument(document);
+
+    // intersects (within)
+    document = new Document();
+    document.add(new LongRange(FIELD_NAME, new long[] {-11, -11}, new long[] {1, 11}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new LongRange(FIELD_NAME, new long[] {12, 1}, new long[] {15, 29}));
+    writer.addDocument(document);
+
+    // disjoint
+    document = new Document();
+    document.add(new LongRange(FIELD_NAME, new long[] {-122, 1}, new long[] {-115, 29}));
+    writer.addDocument(document);
+
+    // intersects (crosses)
+    document = new Document();
+    document.add(new LongRange(FIELD_NAME, new long[] {Long.MIN_VALUE, 1}, new long[] {-11, 29}));
+    writer.addDocument(document);
+
+    // equal (within, contains, intersects)
+    document = new Document();
+    document.add(new LongRange(FIELD_NAME, new long[] {-11, -15}, new long[] {15, 20}));
+    writer.addDocument(document);
+
+    // search
+    IndexReader reader = writer.getReader();
+    IndexSearcher searcher = newSearcher(reader);
+    assertEquals(7, searcher.count(LongRange.newIntersectsQuery(FIELD_NAME,
+        new long[] {-11, -15}, new long[] {15, 20})));
+    assertEquals(3, searcher.count(LongRange.newWithinQuery(FIELD_NAME,
+        new long[] {-11, -15}, new long[] {15, 20})));
+    assertEquals(2, searcher.count(LongRange.newContainsQuery(FIELD_NAME,
+        new long[] {-11, -15}, new long[] {15, 20})));
+    assertEquals(4, searcher.count(LongRange.newCrossesQuery(FIELD_NAME,
+        new long[] {-11, -15}, new long[] {15, 20})));
+
+    reader.close();
+    writer.close();
+    dir.close();
+  }
+
+  /** LongRange test class implementation - use to validate LongRange */
+  private class LongTestRange extends Range {
+    long[] min;
+    long[] max;
+
+    LongTestRange(long[] min, long[] max) {
+      assert min != null && max != null && min.length > 0 && max.length > 0
+          : "test box: min/max cannot be null or empty";
+      assert min.length == max.length : "test box: min/max length do not agree";
+      this.min = min;
+      this.max = max;
+    }
+
+    @Override
+    protected int numDimensions() {
+      return min.length;
+    }
+
+    @Override
+    protected Long getMin(int dim) {
+      return min[dim];
+    }
+
+    @Override
+    protected void setMin(int dim, Object val) {
+      long v = (Long)val;
+      if (min[dim] < v) {
+        max[dim] = v;
+      } else {
+        min[dim] = v;
+      }
+    }
+
+    @Override
+    protected Long getMax(int dim) {
+      return max[dim];
+    }
+
+    @Override
+    protected void setMax(int dim, Object val) {
+      long v = (Long)val;
+      if (max[dim] > v) {
+        min[dim] = v;
+      } else {
+        max[dim] = v;
+      }
+    }
+
+    @Override
+    protected boolean isEqual(Range other) {
+      LongTestRange o = (LongTestRange)other;
+      return Arrays.equals(min, o.min) && Arrays.equals(max, o.max);
+    }
+
+    @Override
+    protected boolean isDisjoint(Range o) {
+      LongTestRange other = (LongTestRange)o;
+      for (int d=0; d<this.min.length; ++d) {
+        if (this.min[d] > other.max[d] || this.max[d] < other.min[d]) {
+          // disjoint:
+          return true;
+        }
+      }
+      return false;
+    }
+
+    @Override
+    protected boolean isWithin(Range o) {
+      LongTestRange other = (LongTestRange)o;
+      for (int d=0; d<this.min.length; ++d) {
+        if ((this.min[d] >= other.min[d] && this.max[d] <= other.max[d]) == false) {
+          // not within:
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    protected boolean contains(Range o) {
+      LongTestRange other = (LongTestRange) o;
+      for (int d=0; d<this.min.length; ++d) {
+        if ((this.min[d] <= other.min[d] && this.max[d] >= other.max[d]) == false) {
+          // not contains:
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder b = new StringBuilder();
+      b.append("Box(");
+      b.append(min[0]);
+      b.append(" TO ");
+      b.append(max[0]);
+      for (int d=1; d<min.length; ++d) {
+        b.append(", ");
+        b.append(min[d]);
+        b.append(" TO ");
+        b.append(max[d]);
+      }
+      b.append(")");
+
+      return b.toString();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/misc/src/java/org/apache/lucene/document/InetAddressPoint.java
----------------------------------------------------------------------
diff --git a/lucene/misc/src/java/org/apache/lucene/document/InetAddressPoint.java b/lucene/misc/src/java/org/apache/lucene/document/InetAddressPoint.java
new file mode 100644
index 0000000..5cda742
--- /dev/null
+++ b/lucene/misc/src/java/org/apache/lucene/document/InetAddressPoint.java
@@ -0,0 +1,313 @@
+/*
+ * 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.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.lucene.index.PointValues;
+import org.apache.lucene.search.PointInSetQuery;
+import org.apache.lucene.search.PointRangeQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.lucene.util.StringHelper;
+
+/** 
+ * An indexed 128-bit {@code InetAddress} field.
+ * <p>
+ * Finding all documents within a range at search time is
+ * efficient.  Multiple values for the same field in one document
+ * is allowed. 
+ * <p>
+ * This field defines static factory methods for creating common queries:
+ * <ul>
+ *   <li>{@link #newExactQuery(String, InetAddress)} for matching an exact network address.
+ *   <li>{@link #newPrefixQuery(String, InetAddress, int)} for matching a network based on CIDR prefix.
+ *   <li>{@link #newRangeQuery(String, InetAddress, InetAddress)} for matching arbitrary network address ranges.
+ *   <li>{@link #newSetQuery(String, InetAddress...)} for matching a set of network addresses.
+ * </ul>
+ * <p>
+ * This field supports both IPv4 and IPv6 addresses: IPv4 addresses are converted
+ * to <a href="https://tools.ietf.org/html/rfc4291#section-2.5.5">IPv4-Mapped IPv6 Addresses</a>:
+ * indexing {@code 1.2.3.4} is the same as indexing {@code ::FFFF:1.2.3.4}.
+ * @see PointValues
+ */
+public class InetAddressPoint extends Field {
+
+  // implementation note: we convert all addresses to IPv6: we expect prefix compression of values,
+  // so its not wasteful, but allows one field to handle both IPv4 and IPv6.
+  /** The number of bytes per dimension: 128 bits */
+  public static final int BYTES = 16;
+  
+  // rfc4291 prefix
+  static final byte[] IPV4_PREFIX = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1 }; 
+
+  private static final FieldType TYPE;
+  static {
+    TYPE = new FieldType();
+    TYPE.setDimensions(1, BYTES);
+    TYPE.freeze();
+  }
+
+  /** The minimum value that an ip address can hold. */
+  public static final InetAddress MIN_VALUE;
+  /** The maximum value that an ip address can hold. */
+  public static final InetAddress MAX_VALUE;
+  static {
+    MIN_VALUE = decode(new byte[BYTES]);
+    byte[] maxValueBytes = new byte[BYTES];
+    Arrays.fill(maxValueBytes, (byte) 0xFF);
+    MAX_VALUE = decode(maxValueBytes);
+  }
+
+  /**
+   * Return the {@link InetAddress} that compares immediately greater than
+   * {@code address}.
+   * @throws ArithmeticException if the provided address is the
+   *              {@link #MAX_VALUE maximum ip address}
+   */
+  public static InetAddress nextUp(InetAddress address) {
+    if (address.equals(MAX_VALUE)) {
+      throw new ArithmeticException("Overflow: there is no greater InetAddress than "
+          + address.getHostAddress());
+    }
+    byte[] delta = new byte[BYTES];
+    delta[BYTES-1] = 1;
+    byte[] nextUpBytes = new byte[InetAddressPoint.BYTES];
+    NumericUtils.add(InetAddressPoint.BYTES, 0, encode(address), delta, nextUpBytes);
+    return decode(nextUpBytes);
+  }
+
+  /**
+   * Return the {@link InetAddress} that compares immediately less than
+   * {@code address}.
+   * @throws ArithmeticException if the provided address is the
+   *              {@link #MIN_VALUE minimum ip address}
+   */
+  public static InetAddress nextDown(InetAddress address) {
+    if (address.equals(MIN_VALUE)) {
+      throw new ArithmeticException("Underflow: there is no smaller InetAddress than "
+          + address.getHostAddress());
+    }
+    byte[] delta = new byte[BYTES];
+    delta[BYTES-1] = 1;
+    byte[] nextDownBytes = new byte[InetAddressPoint.BYTES];
+    NumericUtils.subtract(InetAddressPoint.BYTES, 0, encode(address), delta, nextDownBytes);
+    return decode(nextDownBytes);
+  }
+
+  /** Change the values of this field */
+  public void setInetAddressValue(InetAddress value) {
+    if (value == null) {
+      throw new IllegalArgumentException("point must not be null");
+    }
+    fieldsData = new BytesRef(encode(value));
+  }
+
+  @Override
+  public void setBytesValue(BytesRef bytes) {
+    throw new IllegalArgumentException("cannot change value type from InetAddress to BytesRef");
+  }
+
+  /** Creates a new InetAddressPoint, indexing the
+   *  provided address.
+   *
+   *  @param name field name
+   *  @param point InetAddress value
+   *  @throws IllegalArgumentException if the field name or value is null.
+   */
+  public InetAddressPoint(String name, InetAddress point) {
+    super(name, TYPE);
+    setInetAddressValue(point);
+  }
+  
+  @Override
+  public String toString() {
+    StringBuilder result = new StringBuilder();
+    result.append(getClass().getSimpleName());
+    result.append(" <");
+    result.append(name);
+    result.append(':');
+
+    // IPv6 addresses are bracketed, to not cause confusion with historic field:value representation
+    BytesRef bytes = (BytesRef) fieldsData;
+    InetAddress address = decode(BytesRef.deepCopyOf(bytes).bytes);
+    if (address.getAddress().length == 16) {
+      result.append('[');
+      result.append(address.getHostAddress());
+      result.append(']');
+    } else {
+      result.append(address.getHostAddress());
+    }
+
+    result.append('>');
+    return result.toString();
+  }
+  
+  // public helper methods (e.g. for queries)
+
+  /** Encode InetAddress value into binary encoding */
+  public static byte[] encode(InetAddress value) {
+    byte[] address = value.getAddress();
+    if (address.length == 4) {
+      byte[] mapped = new byte[16];
+      System.arraycopy(IPV4_PREFIX, 0, mapped, 0, IPV4_PREFIX.length);
+      System.arraycopy(address, 0, mapped, IPV4_PREFIX.length, address.length);
+      address = mapped;
+    } else if (address.length != 16) {
+      // more of an assertion, how did you create such an InetAddress :)
+      throw new UnsupportedOperationException("Only IPv4 and IPv6 addresses are supported");
+    }
+    return address;
+  }
+  
+  /** Decodes InetAddress value from binary encoding */
+  public static InetAddress decode(byte value[]) {
+    try {
+      return InetAddress.getByAddress(value);
+    } catch (UnknownHostException e) {
+      // this only happens if value.length != 4 or 16, strange exception class
+      throw new IllegalArgumentException("encoded bytes are of incorrect length", e);
+    }
+  }
+
+  // static methods for generating queries
+
+  /** 
+   * Create a query for matching a network address.
+   *
+   * @param field field name. must not be {@code null}.
+   * @param value exact value
+   * @throws IllegalArgumentException if {@code field} is null.
+   * @return a query matching documents with this exact value
+   */
+  public static Query newExactQuery(String field, InetAddress value) {
+    return newRangeQuery(field, value, value);
+  }
+  
+  /** 
+   * Create a prefix query for matching a CIDR network range.
+   *
+   * @param field field name. must not be {@code null}.
+   * @param value any host address
+   * @param prefixLength the network prefix length for this address. This is also known as the subnet mask in the context of IPv4 addresses.
+   * @throws IllegalArgumentException if {@code field} is null, or prefixLength is invalid.
+   * @return a query matching documents with addresses contained within this network
+   */
+  public static Query newPrefixQuery(String field, InetAddress value, int prefixLength) {
+    if (value == null) {
+      throw new IllegalArgumentException("InetAddress must not be null");
+    }
+    if (prefixLength < 0 || prefixLength > 8 * value.getAddress().length) {
+      throw new IllegalArgumentException("illegal prefixLength '" + prefixLength + "'. Must be 0-32 for IPv4 ranges, 0-128 for IPv6 ranges");
+    }
+    // create the lower value by zeroing out the host portion, upper value by filling it with all ones.
+    byte lower[] = value.getAddress();
+    byte upper[] = value.getAddress();
+    for (int i = prefixLength; i < 8 * lower.length; i++) {
+      int m = 1 << (7 - (i & 7));
+      lower[i >> 3] &= ~m;
+      upper[i >> 3] |= m;
+    }
+    try {
+      return newRangeQuery(field, InetAddress.getByAddress(lower), InetAddress.getByAddress(upper));
+    } catch (UnknownHostException e) {
+      throw new AssertionError(e); // values are coming from InetAddress
+    }
+  }
+
+  /** 
+   * Create a range query for network addresses.
+   * <p>
+   * You can have half-open ranges (which are in fact &lt;/&le; or &gt;/&ge; queries)
+   * by setting {@code lowerValue = InetAddressPoint.MIN_VALUE} or
+   * {@code upperValue = InetAddressPoint.MAX_VALUE}.
+   * <p> Ranges are inclusive. For exclusive ranges, pass {@code InetAddressPoint#nextUp(lowerValue)}
+   * or {@code InetAddressPoint#nexDown(upperValue)}.
+   *
+   * @param field field name. must not be {@code null}.
+   * @param lowerValue lower portion of the range (inclusive). must not be null.
+   * @param upperValue upper portion of the range (inclusive). must not be null.
+   * @throws IllegalArgumentException if {@code field} is null, {@code lowerValue} is null, 
+   *                                  or {@code upperValue} is null
+   * @return a query matching documents within this range.
+   */
+  public static Query newRangeQuery(String field, InetAddress lowerValue, InetAddress upperValue) {
+    PointRangeQuery.checkArgs(field, lowerValue, upperValue);
+    return new PointRangeQuery(field, encode(lowerValue), encode(upperValue), 1) {
+      @Override
+      protected String toString(int dimension, byte[] value) {
+        return decode(value).getHostAddress(); // for ranges, the range itself is already bracketed
+      }
+    };
+  }
+
+  /**
+   * Create a query matching any of the specified 1D values.  This is the points equivalent of {@code TermsQuery}.
+   * 
+   * @param field field name. must not be {@code null}.
+   * @param values all values to match
+   */
+  public static Query newSetQuery(String field, InetAddress... values) {
+
+    // We must compare the encoded form (InetAddress doesn't implement Comparable, and even if it
+    // did, we do our own thing with ipv4 addresses):
+
+    // NOTE: we could instead convert-per-comparison and save this extra array, at cost of slower sort:
+    byte[][] sortedValues = new byte[values.length][];
+    for(int i=0;i<values.length;i++) {
+      sortedValues[i] = encode(values[i]);
+    }
+
+    Arrays.sort(sortedValues,
+                new Comparator<byte[]>() {
+                  @Override
+                  public int compare(byte[] a, byte[] b) {
+                    return StringHelper.compare(BYTES, a, 0, b, 0);
+                  }
+                });
+
+    final BytesRef encoded = new BytesRef(new byte[BYTES]);
+
+    return new PointInSetQuery(field, 1, BYTES,
+                               new PointInSetQuery.Stream() {
+
+                                 int upto;
+
+                                 @Override
+                                 public BytesRef next() {
+                                   if (upto == sortedValues.length) {
+                                     return null;
+                                   } else {
+                                     encoded.bytes = sortedValues[upto];
+                                     assert encoded.bytes.length == encoded.length;
+                                     upto++;
+                                     return encoded;
+                                   }
+                                 }
+                               }) {
+      @Override
+      protected String toString(byte[] value) {
+        assert value.length == BYTES;
+        return decode(value).getHostAddress();
+      }
+    };
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/misc/src/java/org/apache/lucene/document/InetAddressRange.java
----------------------------------------------------------------------
diff --git a/lucene/misc/src/java/org/apache/lucene/document/InetAddressRange.java b/lucene/misc/src/java/org/apache/lucene/document/InetAddressRange.java
new file mode 100644
index 0000000..5fa1fb9
--- /dev/null
+++ b/lucene/misc/src/java/org/apache/lucene/document/InetAddressRange.java
@@ -0,0 +1,168 @@
+/*
+ * 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.net.InetAddress;
+
+import org.apache.lucene.document.RangeFieldQuery.QueryType;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.StringHelper;
+
+/**
+ * An indexed InetAddress Range Field
+ * <p>
+ * This field indexes an {@code InetAddress} range defined as a min/max pairs. It is single
+ * dimension only (indexed as two 16 byte paired values).
+ * <p>
+ * Multiple values are supported.
+ *
+ * <p>
+ * This field defines the following static factory methods for common search operations over Ip Ranges
+ * <ul>
+ *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ip ranges that intersect the defined search range.
+ *   <li>{@link #newWithinQuery newWithinQuery()} matches ip ranges that are within the defined search range.
+ *   <li>{@link #newContainsQuery newContainsQuery()} matches ip ranges that contain the defined search range.
+ *   <li>{@link #newCrossesQuery newCrossesQuery()} matches ip ranges that cross the defined search range
+ * </ul>
+ */
+public class InetAddressRange extends Field {
+  /** The number of bytes per dimension : sync w/ {@code InetAddressPoint} */
+  public static final int BYTES = InetAddressPoint.BYTES;
+
+  private static final FieldType TYPE;
+  static {
+    TYPE = new FieldType();
+    TYPE.setDimensions(2, BYTES);
+    TYPE.freeze();
+  }
+
+  /**
+   * Create a new InetAddressRange from min/max value
+   * @param name field name. must not be null.
+   * @param min range min value; defined as an {@code InetAddress}
+   * @param max range max value; defined as an {@code InetAddress}
+   */
+  public InetAddressRange(String name, final InetAddress min, final InetAddress max) {
+    super(name, TYPE);
+    setRangeValues(min, max);
+  }
+
+  /**
+   * Change (or set) the min/max values of the field.
+   * @param min range min value; defined as an {@code InetAddress}
+   * @param max range max value; defined as an {@code InetAddress}
+   */
+  public void setRangeValues(InetAddress min, InetAddress max) {
+    if (StringHelper.compare(BYTES, min.getAddress(), 0, max.getAddress(), 0) > 0) {
+      throw new IllegalArgumentException("min value cannot be greater than max value for range field (name=" + name + ")");
+    }
+    final byte[] bytes;
+    if (fieldsData == null) {
+      bytes = new byte[BYTES*2];
+      fieldsData = new BytesRef(bytes);
+    } else {
+      bytes = ((BytesRef)fieldsData).bytes;
+    }
+    encode(min, max, bytes);
+  }
+
+  /** encode the min/max range into the provided byte array */
+  private static void encode(final InetAddress min, final InetAddress max, final byte[] bytes) {
+    System.arraycopy(InetAddressPoint.encode(min), 0, bytes, 0, BYTES);
+    System.arraycopy(InetAddressPoint.encode(max), 0, bytes, BYTES, BYTES);
+  }
+
+  /** encode the min/max range and return the byte array */
+  private static byte[] encode(InetAddress min, InetAddress max) {
+    byte[] b = new byte[BYTES*2];
+    encode(min, max, b);
+    return b;
+  }
+
+  /**
+   * Create a query for matching indexed ip ranges that {@code INTERSECT} the defined range.
+   * @param field field name. must not be null.
+   * @param min range min value; provided as an {@code InetAddress}
+   * @param max range max value; provided as an {@code InetAddress}
+   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newIntersectsQuery(String field, final InetAddress min, final InetAddress max) {
+    return newRelationQuery(field, min, max, QueryType.INTERSECTS);
+  }
+
+  /**
+   * Create a query for matching indexed ip ranges that {@code CONTAINS} the defined range.
+   * @param field field name. must not be null.
+   * @param min range min value; provided as an {@code InetAddress}
+   * @param max range max value; provided as an {@code InetAddress}
+   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newContainsQuery(String field, final InetAddress min, final InetAddress max) {
+    return newRelationQuery(field, min, max, QueryType.CONTAINS);
+  }
+
+  /**
+   * Create a query for matching indexed ip ranges that are {@code WITHIN} the defined range.
+   * @param field field name. must not be null.
+   * @param min range min value; provided as an {@code InetAddress}
+   * @param max range max value; provided as an {@code InetAddress}
+   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newWithinQuery(String field, final InetAddress min, final InetAddress max) {
+    return newRelationQuery(field, min, max, QueryType.WITHIN);
+  }
+
+  /**
+   * Create a query for matching indexed ip ranges that {@code CROSS} the defined range.
+   * @param field field name. must not be null.
+   * @param min range min value; provided as an {@code InetAddress}
+   * @param max range max value; provided as an {@code InetAddress}
+   * @return query for matching intersecting ranges (overlap, within, crosses, or contains)
+   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
+   */
+  public static Query newCrossesQuery(String field, final InetAddress min, final InetAddress max) {
+    return newRelationQuery(field, min, max, QueryType.CROSSES);
+  }
+
+  /** helper method for creating the desired relational query */
+  private static Query newRelationQuery(String field, final InetAddress min, final InetAddress max, QueryType relation) {
+    return new RangeFieldQuery(field, encode(min, max), 1, relation) {
+      @Override
+      protected String toString(byte[] ranges, int dimension) {
+        return InetAddressRange.toString(ranges, dimension);
+      }
+    };
+  }
+
+  /**
+   * Returns the String representation for the range at the given dimension
+   * @param ranges the encoded ranges, never null
+   * @param dimension the dimension of interest (not used for this field)
+   * @return The string representation for the range at the provided dimension
+   */
+  private static String toString(byte[] ranges, int dimension) {
+    byte[] min = new byte[BYTES];
+    System.arraycopy(ranges, 0, min, 0, BYTES);
+    byte[] max = new byte[BYTES];
+    System.arraycopy(ranges, BYTES, max, 0, BYTES);
+    return "[" + InetAddressPoint.decode(min) + " : " + InetAddressPoint.decode(max) + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/misc/src/test/org/apache/lucene/document/TestInetAddressPoint.java
----------------------------------------------------------------------
diff --git a/lucene/misc/src/test/org/apache/lucene/document/TestInetAddressPoint.java b/lucene/misc/src/test/org/apache/lucene/document/TestInetAddressPoint.java
new file mode 100644
index 0000000..0e0901b
--- /dev/null
+++ b/lucene/misc/src/test/org/apache/lucene/document/TestInetAddressPoint.java
@@ -0,0 +1,176 @@
+/*
+ * 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.net.InetAddress;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.LuceneTestCase;
+
+/** Simple tests for {@link InetAddressPoint} */
+public class TestInetAddressPoint extends LuceneTestCase {
+
+  /** Add a single address and search for it */
+  public void testBasics() throws Exception {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+
+    // add a doc with an address
+    Document document = new Document();
+    InetAddress address = InetAddress.getByName("1.2.3.4");
+    document.add(new InetAddressPoint("field", address));
+    writer.addDocument(document);
+    
+    // search and verify we found our doc
+    IndexReader reader = writer.getReader();
+    IndexSearcher searcher = newSearcher(reader);
+    assertEquals(1, searcher.count(InetAddressPoint.newExactQuery("field", address)));
+    assertEquals(1, searcher.count(InetAddressPoint.newPrefixQuery("field", address, 24)));
+    assertEquals(1, searcher.count(InetAddressPoint.newRangeQuery("field", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"))));
+    assertEquals(1, searcher.count(InetAddressPoint.newSetQuery("field", InetAddress.getByName("1.2.3.4"))));
+    assertEquals(1, searcher.count(InetAddressPoint.newSetQuery("field", InetAddress.getByName("1.2.3.4"), InetAddress.getByName("1.2.3.5"))));
+    assertEquals(0, searcher.count(InetAddressPoint.newSetQuery("field", InetAddress.getByName("1.2.3.3"))));
+    assertEquals(0, searcher.count(InetAddressPoint.newSetQuery("field")));
+
+    reader.close();
+    writer.close();
+    dir.close();
+  }
+  
+  /** Add a single address and search for it */
+  public void testBasicsV6() throws Exception {
+    Directory dir = newDirectory();
+    RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+
+    // add a doc with an address
+    Document document = new Document();
+    InetAddress address = InetAddress.getByName("fec0::f66d");
+    document.add(new InetAddressPoint("field", address));
+    writer.addDocument(document);
+    
+    // search and verify we found our doc
+    IndexReader reader = writer.getReader();
+    IndexSearcher searcher = newSearcher(reader);
+    assertEquals(1, searcher.count(InetAddressPoint.newExactQuery("field", address)));
+    assertEquals(1, searcher.count(InetAddressPoint.newPrefixQuery("field", address, 64)));
+    assertEquals(1, searcher.count(InetAddressPoint.newRangeQuery("field", InetAddress.getByName("fec0::f66c"), InetAddress.getByName("fec0::f66e"))));
+
+    reader.close();
+    writer.close();
+    dir.close();
+  }
+    
+  public void testToString() throws Exception {
+    assertEquals("InetAddressPoint <field:1.2.3.4>", new InetAddressPoint("field", InetAddress.getByName("1.2.3.4")).toString());
+    assertEquals("InetAddressPoint <field:1.2.3.4>", new InetAddressPoint("field", InetAddress.getByName("::FFFF:1.2.3.4")).toString());
+    assertEquals("InetAddressPoint <field:[fdc8:57ed:f042:ad1:f66d:4ff:fe90:ce0c]>", new InetAddressPoint("field", InetAddress.getByName("fdc8:57ed:f042:0ad1:f66d:4ff:fe90:ce0c")).toString());
+    
+    assertEquals("field:[1.2.3.4 TO 1.2.3.4]", InetAddressPoint.newExactQuery("field", InetAddress.getByName("1.2.3.4")).toString());
+    assertEquals("field:[0:0:0:0:0:0:0:1 TO 0:0:0:0:0:0:0:1]", InetAddressPoint.newExactQuery("field", InetAddress.getByName("::1")).toString());
+    
+    assertEquals("field:[1.2.3.0 TO 1.2.3.255]", InetAddressPoint.newPrefixQuery("field", InetAddress.getByName("1.2.3.4"), 24).toString());
+    assertEquals("field:[fdc8:57ed:f042:ad1:0:0:0:0 TO fdc8:57ed:f042:ad1:ffff:ffff:ffff:ffff]", InetAddressPoint.newPrefixQuery("field", InetAddress.getByName("fdc8:57ed:f042:0ad1:f66d:4ff:fe90:ce0c"), 64).toString());
+    assertEquals("field:{fdc8:57ed:f042:ad1:f66d:4ff:fe90:ce0c}", InetAddressPoint.newSetQuery("field", InetAddress.getByName("fdc8:57ed:f042:0ad1:f66d:4ff:fe90:ce0c")).toString());
+  }
+
+  public void testQueryEquals() throws Exception {
+    Query q1, q2;
+    q1 = InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
+    q2 = InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
+    assertEquals(q1, q2);
+    assertEquals(q1.hashCode(), q2.hashCode());
+    assertFalse(q1.equals(InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.7"))));
+    assertFalse(q1.equals(InetAddressPoint.newRangeQuery("b", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"))));
+
+    q1 = InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.3"), 16);
+    q2 = InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.3"), 16);
+    assertEquals(q1, q2);
+    assertEquals(q1.hashCode(), q2.hashCode());
+    assertFalse(q1.equals(InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.1.3.5"), 16)));
+    assertFalse(q1.equals(InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.5"), 24)));
+
+    q1 = InetAddressPoint.newExactQuery("a", InetAddress.getByName("1.2.3.3"));
+    q2 = InetAddressPoint.newExactQuery("a", InetAddress.getByName("1.2.3.3"));
+    assertEquals(q1, q2);
+    assertEquals(q1.hashCode(), q2.hashCode());
+    assertFalse(q1.equals(InetAddressPoint.newExactQuery("a", InetAddress.getByName("1.2.3.5"))));
+
+    q1 = InetAddressPoint.newSetQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
+    q2 = InetAddressPoint.newSetQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
+    assertEquals(q1, q2);
+    assertEquals(q1.hashCode(), q2.hashCode());
+    assertFalse(q1.equals(InetAddressPoint.newSetQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.7"))));
+  }
+
+  public void testPrefixQuery() throws Exception {
+    assertEquals(
+        InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.0"), InetAddress.getByName("1.2.3.255")),
+        InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.127"), 24));
+    assertEquals(
+        InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.128"), InetAddress.getByName("1.2.3.255")),
+        InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.213"), 25));
+    assertEquals(
+        InetAddressPoint.newRangeQuery("a", InetAddress.getByName("2001::a000:0"), InetAddress.getByName("2001::afff:ffff")),
+        InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("2001::a6bd:fc80"), 100));
+  }
+
+  public void testNextUp() throws Exception {
+    assertEquals(InetAddress.getByName("::1"),
+        InetAddressPoint.nextUp(InetAddress.getByName("::")));
+
+    assertEquals(InetAddress.getByName("::1:0"),
+        InetAddressPoint.nextUp(InetAddress.getByName("::ffff")));
+
+    assertEquals(InetAddress.getByName("1.2.4.0"),
+        InetAddressPoint.nextUp(InetAddress.getByName("1.2.3.255")));
+
+    assertEquals(InetAddress.getByName("0.0.0.0"),
+        InetAddressPoint.nextUp(InetAddress.getByName("::fffe:ffff:ffff")));
+
+    assertEquals(InetAddress.getByName("::1:0:0:0"),
+        InetAddressPoint.nextUp(InetAddress.getByName("255.255.255.255")));
+
+    ArithmeticException e = expectThrows(ArithmeticException.class,
+        () -> InetAddressPoint.nextUp(InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+    assertEquals("Overflow: there is no greater InetAddress than ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", e.getMessage());
+  }
+
+  public void testNextDown() throws Exception {
+    assertEquals(InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"),
+        InetAddressPoint.nextDown(InetAddress.getByName("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
+
+    assertEquals(InetAddress.getByName("::ffff"),
+        InetAddressPoint.nextDown(InetAddress.getByName("::1:0")));
+
+    assertEquals(InetAddress.getByName("1.2.3.255"),
+        InetAddressPoint.nextDown(InetAddress.getByName("1.2.4.0")));
+
+    assertEquals(InetAddress.getByName("::fffe:ffff:ffff"),
+        InetAddressPoint.nextDown(InetAddress.getByName("0.0.0.0")));
+
+    assertEquals(InetAddress.getByName("255.255.255.255"),
+        InetAddressPoint.nextDown(InetAddress.getByName("::1:0:0:0")));
+
+    ArithmeticException e = expectThrows(ArithmeticException.class,
+        () -> InetAddressPoint.nextDown(InetAddress.getByName("::")));
+    assertEquals("Underflow: there is no smaller InetAddress than 0:0:0:0:0:0:0:0", e.getMessage());
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/misc/src/test/org/apache/lucene/search/TestInetAddressRangeQueries.java
----------------------------------------------------------------------
diff --git a/lucene/misc/src/test/org/apache/lucene/search/TestInetAddressRangeQueries.java b/lucene/misc/src/test/org/apache/lucene/search/TestInetAddressRangeQueries.java
new file mode 100644
index 0000000..e22cf9b
--- /dev/null
+++ b/lucene/misc/src/test/org/apache/lucene/search/TestInetAddressRangeQueries.java
@@ -0,0 +1,215 @@
+/*
+ * 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.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.apache.lucene.document.InetAddressRange;
+import org.apache.lucene.util.StringHelper;
+
+/**
+ * Random testing for {@link InetAddressRange}
+ */
+public class TestInetAddressRangeQueries extends BaseRangeFieldQueryTestCase {
+  private static final String FIELD_NAME = "ipRangeField";
+
+  private IPVersion ipVersion;
+
+  private enum IPVersion {IPv4, IPv6}
+
+  @Override
+  protected Range nextRange(int dimensions) throws Exception {
+    InetAddress min = nextInetaddress();
+    byte[] bMin = min.getAddress();
+    InetAddress max = nextInetaddress();
+    byte[] bMax = max.getAddress();
+    if (StringHelper.compare(bMin.length, bMin, 0, bMax, 0) > 0) {
+      return new IpRange(max, min);
+    }
+    return new IpRange(min, max);
+  }
+
+  /** return random IPv4 or IPv6 address */
+  private InetAddress nextInetaddress() throws UnknownHostException {
+    byte[] b;
+    switch (ipVersion) {
+      case IPv4:
+        b = new byte[4];
+        break;
+      case IPv6:
+        b = new byte[16];
+        break;
+      default:
+        throw new IllegalArgumentException("incorrect IP version: " + ipVersion);
+    }
+    random().nextBytes(b);
+    return InetAddress.getByAddress(b);
+  }
+
+  /** randomly select version across tests */
+  private IPVersion ipVersion() {
+    return random().nextBoolean() ? IPVersion.IPv4 : IPVersion.IPv6;
+  }
+
+  @Override
+  public void testRandomTiny() throws Exception {
+    ipVersion = ipVersion();
+    super.testRandomTiny();
+  }
+
+  @Override
+  public void testMultiValued() throws Exception {
+    ipVersion = ipVersion();
+    super.testRandomMedium();
+  }
+
+  @Override
+  public void testRandomMedium() throws Exception {
+    ipVersion = ipVersion();
+    super.testMultiValued();
+  }
+
+  @Nightly
+  @Override
+  public void testRandomBig() throws Exception {
+    ipVersion = ipVersion();
+    super.testRandomBig();
+  }
+
+  /** return random range */
+  @Override
+  protected InetAddressRange newRangeField(Range r) {
+    return new InetAddressRange(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** return random intersects query */
+  @Override
+  protected Query newIntersectsQuery(Range r) {
+    return InetAddressRange.newIntersectsQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** return random contains query */
+  @Override
+  protected Query newContainsQuery(Range r) {
+    return InetAddressRange.newContainsQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** return random within query */
+  @Override
+  protected Query newWithinQuery(Range r) {
+    return InetAddressRange.newWithinQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** return random crosses query */
+  @Override
+  protected Query newCrossesQuery(Range r) {
+    return InetAddressRange.newCrossesQuery(FIELD_NAME, ((IpRange)r).min, ((IpRange)r).max);
+  }
+
+  /** encapsulated IpRange for test validation */
+  private class IpRange extends Range {
+    InetAddress min;
+    InetAddress max;
+
+    IpRange(InetAddress min, InetAddress max) {
+      this.min = min;
+      this.max = max;
+    }
+
+    @Override
+    protected int numDimensions() {
+      return 1;
+    }
+
+    @Override
+    protected InetAddress getMin(int dim) {
+      return min;
+    }
+
+    @Override
+    protected void setMin(int dim, Object val) {
+      byte[] v = ((InetAddress)val).getAddress();
+
+      if (StringHelper.compare(v.length, min.getAddress(), 0, v, 0) < 0) {
+        max = (InetAddress)val;
+      } else {
+        min = (InetAddress) val;
+      }
+    }
+
+    @Override
+    protected InetAddress getMax(int dim) {
+      return max;
+    }
+
+    @Override
+    protected void setMax(int dim, Object val) {
+      byte[] v = ((InetAddress)val).getAddress();
+
+      if (StringHelper.compare(v.length, max.getAddress(), 0, v, 0) > 0) {
+        min = (InetAddress)val;
+      } else {
+        max = (InetAddress) val;
+      }
+    }
+
+    @Override
+    protected boolean isEqual(Range o) {
+      IpRange other = (IpRange)o;
+      return this.min.equals(other.min) && this.max.equals(other.max);
+    }
+
+    @Override
+    protected boolean isDisjoint(Range o) {
+      IpRange other = (IpRange)o;
+      byte[] bMin = min.getAddress();
+      byte[] bMax = max.getAddress();
+      return StringHelper.compare(bMin.length, bMin, 0, other.max.getAddress(), 0) > 0 ||
+          StringHelper.compare(bMax.length, bMax, 0, other.min.getAddress(), 0) < 0;
+    }
+
+    @Override
+    protected boolean isWithin(Range o) {
+      IpRange other = (IpRange)o;
+      byte[] bMin = min.getAddress();
+      byte[] bMax = max.getAddress();
+      return StringHelper.compare(bMin.length, bMin, 0, other.min.getAddress(), 0) >= 0 &&
+          StringHelper.compare(bMax.length, bMax, 0, other.max.getAddress(), 0) <= 0;
+    }
+
+    @Override
+    protected boolean contains(Range o) {
+      IpRange other = (IpRange)o;
+      byte[] bMin = min.getAddress();
+      byte[] bMax = max.getAddress();
+      return StringHelper.compare(bMin.length, bMin, 0, other.min.getAddress(), 0) <= 0 &&
+          StringHelper.compare(bMax.length, bMax, 0, other.max.getAddress(), 0) >= 0;
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder b = new StringBuilder();
+      b.append("Box(");
+      b.append(min.getHostAddress());
+      b.append(" TO ");
+      b.append(max.getHostAddress());
+      b.append(")");
+      return b.toString();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/java/org/apache/lucene/document/DoubleRangeField.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/DoubleRangeField.java b/lucene/sandbox/src/java/org/apache/lucene/document/DoubleRangeField.java
deleted file mode 100644
index c5ae0e7..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/DoubleRangeField.java
+++ /dev/null
@@ -1,282 +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.document;
-
-import org.apache.lucene.document.RangeFieldQuery.QueryType;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.NumericUtils;
-
-/**
- * An indexed Double Range field.
- * <p>
- * This field indexes dimensional ranges defined as min/max pairs. It supports
- * up to a maximum of 4 dimensions (indexed as 8 numeric values). With 1 dimension representing a single double range,
- * 2 dimensions representing a bounding box, 3 dimensions a bounding cube, and 4 dimensions a tesseract.
- * <p>
- * Multiple values for the same field in one document is supported, and open ended ranges can be defined using
- * {@code Double.NEGATIVE_INFINITY} and {@code Double.POSITIVE_INFINITY}.
- *
- * <p>
- * This field defines the following static factory methods for common search operations over double ranges:
- * <ul>
- *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ranges that intersect the defined search range.
- *   <li>{@link #newWithinQuery newWithinQuery()} matches ranges that are within the defined search range.
- *   <li>{@link #newContainsQuery newContainsQuery()} matches ranges that contain the defined search range.
- * </ul>
- */
-public class DoubleRangeField extends Field {
-  /** stores double values so number of bytes is 8 */
-  public static final int BYTES = Double.BYTES;
-
-  /**
-   * Create a new DoubleRangeField type, from min/max parallel arrays
-   *
-   * @param name field name. must not be null.
-   * @param min range min values; each entry is the min value for the dimension
-   * @param max range max values; each entry is the max value for the dimension
-   */
-  public DoubleRangeField(String name, final double[] min, final double[] max) {
-    super(name, getType(min.length));
-    setRangeValues(min, max);
-  }
-
-  /** set the field type */
-  private static FieldType getType(int dimensions) {
-    if (dimensions > 4) {
-      throw new IllegalArgumentException("DoubleRangeField does not support greater than 4 dimensions");
-    }
-
-    FieldType ft = new FieldType();
-    // dimensions is set as 2*dimension size (min/max per dimension)
-    ft.setDimensions(dimensions*2, BYTES);
-    ft.freeze();
-    return ft;
-  }
-
-  /**
-   * Changes the values of the field.
-   * @param min array of min values. (accepts {@code Double.NEGATIVE_INFINITY})
-   * @param max array of max values. (accepts {@code Double.POSITIVE_INFINITY})
-   * @throws IllegalArgumentException if {@code min} or {@code max} is invalid
-   */
-  public void setRangeValues(double[] min, double[] max) {
-    checkArgs(min, max);
-    if (min.length*2 != type.pointDimensionCount() || max.length*2 != type.pointDimensionCount()) {
-      throw new IllegalArgumentException("field (name=" + name + ") uses " + type.pointDimensionCount()/2
-          + " dimensions; cannot change to (incoming) " + min.length + " dimensions");
-    }
-
-    final byte[] bytes;
-    if (fieldsData == null) {
-      bytes = new byte[BYTES*2*min.length];
-      fieldsData = new BytesRef(bytes);
-    } else {
-      bytes = ((BytesRef)fieldsData).bytes;
-    }
-    verifyAndEncode(min, max, bytes);
-  }
-
-  /** validate the arguments */
-  private static void checkArgs(final double[] min, final double[] max) {
-    if (min == null || max == null || min.length == 0 || max.length == 0) {
-      throw new IllegalArgumentException("min/max range values cannot be null or empty");
-    }
-    if (min.length != max.length) {
-      throw new IllegalArgumentException("min/max ranges must agree");
-    }
-    if (min.length > 4) {
-      throw new IllegalArgumentException("DoubleRangeField does not support greater than 4 dimensions");
-    }
-  }
-
-  /**
-   * Encodes the min, max ranges into a byte array
-   */
-  private static byte[] encode(double[] min, double[] max) {
-    checkArgs(min, max);
-    byte[] b = new byte[BYTES*2*min.length];
-    verifyAndEncode(min, max, b);
-    return b;
-  }
-
-  /**
-   * encode the ranges into a sortable byte array ({@code Double.NaN} not allowed)
-   * <p>
-   * example for 4 dimensions (8 bytes per dimension value):
-   * minD1 ... minD4 | maxD1 ... maxD4
-   */
-  static void verifyAndEncode(double[] min, double[] max, byte[] bytes) {
-    for (int d=0,i=0,j=min.length*BYTES; d<min.length; ++d, i+=BYTES, j+=BYTES) {
-      if (Double.isNaN(min[d])) {
-        throw new IllegalArgumentException("invalid min value (" + Double.NaN + ")" + " in DoubleRangeField");
-      }
-      if (Double.isNaN(max[d])) {
-        throw new IllegalArgumentException("invalid max value (" + Double.NaN + ")" + " in DoubleRangeField");
-      }
-      if (min[d] > max[d]) {
-        throw new IllegalArgumentException("min value (" + min[d] + ") is greater than max value (" + max[d] + ")");
-      }
-      encode(min[d], bytes, i);
-      encode(max[d], bytes, j);
-    }
-  }
-
-  /** encode the given value into the byte array at the defined offset */
-  private static void encode(double val, byte[] bytes, int offset) {
-    NumericUtils.longToSortableBytes(NumericUtils.doubleToSortableLong(val), bytes, offset);
-  }
-
-  /**
-   * Get the min value for the given dimension
-   * @param dimension the dimension, always positive
-   * @return the decoded min value
-   */
-  public double getMin(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
-    return decodeMin(((BytesRef)fieldsData).bytes, dimension);
-  }
-
-  /**
-   * Get the max value for the given dimension
-   * @param dimension the dimension, always positive
-   * @return the decoded max value
-   */
-  public double getMax(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
-    return decodeMax(((BytesRef)fieldsData).bytes, dimension);
-  }
-
-  /** decodes the min value (for the defined dimension) from the encoded input byte array */
-  static double decodeMin(byte[] b, int dimension) {
-    int offset = dimension*BYTES;
-    return NumericUtils.sortableLongToDouble(NumericUtils.sortableBytesToLong(b, offset));
-  }
-
-  /** decodes the max value (for the defined dimension) from the encoded input byte array */
-  static double decodeMax(byte[] b, int dimension) {
-    int offset = b.length/2 + dimension*BYTES;
-    return NumericUtils.sortableLongToDouble(NumericUtils.sortableBytesToLong(b, offset));
-  }
-
-  /**
-   * Create a query for matching indexed ranges that intersect the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Double.NEGATIVE_INFINITY})
-   * @param max array of max values. (accepts {@code Double.POSITIVE_INFINITY})
-   * @return query for matching intersecting ranges (overlap, within, or contains)
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newIntersectsQuery(String field, final double[] min, final double[] max) {
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.INTERSECTS) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return DoubleRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that contain the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Double.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Double.MAX_VALUE})
-   * @return query for matching ranges that contain the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newContainsQuery(String field, final double[] min, final double[] max) {
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.CONTAINS) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return DoubleRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that are within the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Double.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Double.MAX_VALUE})
-   * @return query for matching ranges within the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newWithinQuery(String field, final double[] min, final double[] max) {
-    checkArgs(min, max);
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.WITHIN) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return DoubleRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that cross the defined range.
-   * A CROSSES is defined as any set of ranges that are not disjoint and not wholly contained by
-   * the query. Effectively, its the complement of union(WITHIN, DISJOINT).
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Double.MIN_VALUE})
-   * @param max array of max values. (accepts {@code Double.MAX_VALUE})
-   * @return query for matching ranges within the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newCrossesQuery(String field, final double[] min, final double[] max) {
-    checkArgs(min, max);
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.CROSSES) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return DoubleRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder();
-    sb.append(getClass().getSimpleName());
-    sb.append(" <");
-    sb.append(name);
-    sb.append(':');
-    byte[] b = ((BytesRef)fieldsData).bytes;
-    toString(b, 0);
-    for (int d=1; d<type.pointDimensionCount(); ++d) {
-      sb.append(' ');
-      toString(b, d);
-    }
-    sb.append('>');
-
-    return sb.toString();
-  }
-
-  /**
-   * Returns the String representation for the range at the given dimension
-   * @param ranges the encoded ranges, never null
-   * @param dimension the dimension of interest
-   * @return The string representation for the range at the provided dimension
-   */
-  private static String toString(byte[] ranges, int dimension) {
-    return "[" + Double.toString(decodeMin(ranges, dimension)) + " : "
-        + Double.toString(decodeMax(ranges, dimension)) + "]";
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d34d81f9/lucene/sandbox/src/java/org/apache/lucene/document/FloatRangeField.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/FloatRangeField.java b/lucene/sandbox/src/java/org/apache/lucene/document/FloatRangeField.java
deleted file mode 100644
index 60e0acf..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/FloatRangeField.java
+++ /dev/null
@@ -1,282 +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.document;
-
-import org.apache.lucene.document.RangeFieldQuery.QueryType;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.NumericUtils;
-
-/**
- * An indexed Float Range field.
- * <p>
- * This field indexes dimensional ranges defined as min/max pairs. It supports
- * up to a maximum of 4 dimensions (indexed as 8 numeric values). With 1 dimension representing a single float range,
- * 2 dimensions representing a bounding box, 3 dimensions a bounding cube, and 4 dimensions a tesseract.
- * <p>
- * Multiple values for the same field in one document is supported, and open ended ranges can be defined using
- * {@code Float.NEGATIVE_INFINITY} and {@code Float.POSITIVE_INFINITY}.
- *
- * <p>
- * This field defines the following static factory methods for common search operations over float ranges:
- * <ul>
- *   <li>{@link #newIntersectsQuery newIntersectsQuery()} matches ranges that intersect the defined search range.
- *   <li>{@link #newWithinQuery newWithinQuery()} matches ranges that are within the defined search range.
- *   <li>{@link #newContainsQuery newContainsQuery()} matches ranges that contain the defined search range.
- * </ul>
- */
-public class FloatRangeField extends Field {
-  /** stores float values so number of bytes is 4 */
-  public static final int BYTES = Float.BYTES;
-
-  /**
-   * Create a new FloatRangeField type, from min/max parallel arrays
-   *
-   * @param name field name. must not be null.
-   * @param min range min values; each entry is the min value for the dimension
-   * @param max range max values; each entry is the max value for the dimension
-   */
-  public FloatRangeField(String name, final float[] min, final float[] max) {
-    super(name, getType(min.length));
-    setRangeValues(min, max);
-  }
-
-  /** set the field type */
-  private static FieldType getType(int dimensions) {
-    if (dimensions > 4) {
-      throw new IllegalArgumentException("FloatRangeField does not support greater than 4 dimensions");
-    }
-
-    FieldType ft = new FieldType();
-    // dimensions is set as 2*dimension size (min/max per dimension)
-    ft.setDimensions(dimensions*2, BYTES);
-    ft.freeze();
-    return ft;
-  }
-
-  /**
-   * Changes the values of the field.
-   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
-   * @param max array of max values. (accepts {@code Float.POSITIVE_INFINITY})
-   * @throws IllegalArgumentException if {@code min} or {@code max} is invalid
-   */
-  public void setRangeValues(float[] min, float[] max) {
-    checkArgs(min, max);
-    if (min.length*2 != type.pointDimensionCount() || max.length*2 != type.pointDimensionCount()) {
-      throw new IllegalArgumentException("field (name=" + name + ") uses " + type.pointDimensionCount()/2
-          + " dimensions; cannot change to (incoming) " + min.length + " dimensions");
-    }
-
-    final byte[] bytes;
-    if (fieldsData == null) {
-      bytes = new byte[BYTES*2*min.length];
-      fieldsData = new BytesRef(bytes);
-    } else {
-      bytes = ((BytesRef)fieldsData).bytes;
-    }
-    verifyAndEncode(min, max, bytes);
-  }
-
-  /** validate the arguments */
-  private static void checkArgs(final float[] min, final float[] max) {
-    if (min == null || max == null || min.length == 0 || max.length == 0) {
-      throw new IllegalArgumentException("min/max range values cannot be null or empty");
-    }
-    if (min.length != max.length) {
-      throw new IllegalArgumentException("min/max ranges must agree");
-    }
-    if (min.length > 4) {
-      throw new IllegalArgumentException("FloatRangeField does not support greater than 4 dimensions");
-    }
-  }
-
-  /**
-   * Encodes the min, max ranges into a byte array
-   */
-  private static byte[] encode(float[] min, float[] max) {
-    checkArgs(min, max);
-    byte[] b = new byte[BYTES*2*min.length];
-    verifyAndEncode(min, max, b);
-    return b;
-  }
-
-  /**
-   * encode the ranges into a sortable byte array ({@code Float.NaN} not allowed)
-   * <p>
-   * example for 4 dimensions (8 bytes per dimension value):
-   * minD1 ... minD4 | maxD1 ... maxD4
-   */
-  static void verifyAndEncode(float[] min, float[] max, byte[] bytes) {
-    for (int d=0,i=0,j=min.length*BYTES; d<min.length; ++d, i+=BYTES, j+=BYTES) {
-      if (Double.isNaN(min[d])) {
-        throw new IllegalArgumentException("invalid min value (" + Float.NaN + ")" + " in FloatRangeField");
-      }
-      if (Double.isNaN(max[d])) {
-        throw new IllegalArgumentException("invalid max value (" + Float.NaN + ")" + " in FloatRangeField");
-      }
-      if (min[d] > max[d]) {
-        throw new IllegalArgumentException("min value (" + min[d] + ") is greater than max value (" + max[d] + ")");
-      }
-      encode(min[d], bytes, i);
-      encode(max[d], bytes, j);
-    }
-  }
-
-  /** encode the given value into the byte array at the defined offset */
-  private static void encode(float val, byte[] bytes, int offset) {
-    NumericUtils.intToSortableBytes(NumericUtils.floatToSortableInt(val), bytes, offset);
-  }
-
-  /**
-   * Get the min value for the given dimension
-   * @param dimension the dimension, always positive
-   * @return the decoded min value
-   */
-  public float getMin(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
-    return decodeMin(((BytesRef)fieldsData).bytes, dimension);
-  }
-
-  /**
-   * Get the max value for the given dimension
-   * @param dimension the dimension, always positive
-   * @return the decoded max value
-   */
-  public float getMax(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
-    return decodeMax(((BytesRef)fieldsData).bytes, dimension);
-  }
-
-  /** decodes the min value (for the defined dimension) from the encoded input byte array */
-  static float decodeMin(byte[] b, int dimension) {
-    int offset = dimension*BYTES;
-    return NumericUtils.sortableIntToFloat(NumericUtils.sortableBytesToInt(b, offset));
-  }
-
-  /** decodes the max value (for the defined dimension) from the encoded input byte array */
-  static float decodeMax(byte[] b, int dimension) {
-    int offset = b.length/2 + dimension*BYTES;
-    return NumericUtils.sortableIntToFloat(NumericUtils.sortableBytesToInt(b, offset));
-  }
-
-  /**
-   * Create a query for matching indexed ranges that intersect the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
-   * @param max array of max values. (accepts {@code Float.MAX_VALUE})
-   * @return query for matching intersecting ranges (overlap, within, or contains)
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newIntersectsQuery(String field, final float[] min, final float[] max) {
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.INTERSECTS) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return FloatRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed float ranges that contain the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
-   * @param max array of max values. (accepts {@code Float.POSITIVE_INFINITY})
-   * @return query for matching ranges that contain the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newContainsQuery(String field, final float[] min, final float[] max) {
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.CONTAINS) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return FloatRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that are within the defined range.
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
-   * @param max array of max values. (accepts {@code Float.POSITIVE_INFINITY})
-   * @return query for matching ranges within the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newWithinQuery(String field, final float[] min, final float[] max) {
-    checkArgs(min, max);
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.WITHIN) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return FloatRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  /**
-   * Create a query for matching indexed ranges that cross the defined range.
-   * A CROSSES is defined as any set of ranges that are not disjoint and not wholly contained by
-   * the query. Effectively, its the complement of union(WITHIN, DISJOINT).
-   * @param field field name. must not be null.
-   * @param min array of min values. (accepts {@code Float.NEGATIVE_INFINITY})
-   * @param max array of max values. (accepts {@code Float.POSITIVE_INFINITY})
-   * @return query for matching ranges within the defined range
-   * @throws IllegalArgumentException if {@code field} is null, {@code min} or {@code max} is invalid
-   */
-  public static Query newCrossesQuery(String field, final float[] min, final float[] max) {
-    checkArgs(min, max);
-    return new RangeFieldQuery(field, encode(min, max), min.length, QueryType.CROSSES) {
-      @Override
-      protected String toString(byte[] ranges, int dimension) {
-        return FloatRangeField.toString(ranges, dimension);
-      }
-    };
-  }
-
-  @Override
-  public String toString() {
-    StringBuilder sb = new StringBuilder();
-    sb.append(getClass().getSimpleName());
-    sb.append(" <");
-    sb.append(name);
-    sb.append(':');
-    byte[] b = ((BytesRef)fieldsData).bytes;
-    toString(b, 0);
-    for (int d=1; d<type.pointDimensionCount(); ++d) {
-      sb.append(' ');
-      toString(b, d);
-    }
-    sb.append('>');
-
-    return sb.toString();
-  }
-
-  /**
-   * Returns the String representation for the range at the given dimension
-   * @param ranges the encoded ranges, never null
-   * @param dimension the dimension of interest
-   * @return The string representation for the range at the provided dimension
-   */
-  private static String toString(byte[] ranges, int dimension) {
-    return "[" + Float.toString(decodeMin(ranges, dimension)) + " : "
-        + Float.toString(decodeMax(ranges, dimension)) + "]";
-  }
-}


[15/23] lucene-solr:jira/solr-9835: SOLR-10079: Force in-place standalone test to always use NoMergePolicy, also assert that it was used

Posted by da...@apache.org.
SOLR-10079: Force in-place standalone test to always use NoMergePolicy, also assert that it was used


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

Branch: refs/heads/jira/solr-9835
Commit: b64382bb07ebae4c6f8711b5e0fb4341d2f09f4e
Parents: 35e0c05
Author: Ishan Chattopadhyaya <is...@apache.org>
Authored: Mon Mar 13 15:22:49 2017 +0530
Committer: Ishan Chattopadhyaya <is...@apache.org>
Committed: Mon Mar 13 15:22:49 2017 +0530

----------------------------------------------------------------------
 .../update/TestInPlaceUpdatesStandalone.java    | 27 ++++++++++++++++++++
 1 file changed, 27 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b64382bb/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesStandalone.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesStandalone.java b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesStandalone.java
index 9a5031f..877467e 100644
--- a/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesStandalone.java
+++ b/solr/core/src/test/org/apache/solr/update/TestInPlaceUpdatesStandalone.java
@@ -32,6 +32,8 @@ import java.util.Random;
 import java.util.Set;
 
 import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.NoMergePolicy;
 import org.apache.lucene.util.TestUtil;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
@@ -41,6 +43,7 @@ import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.SolrInputField;
+import org.apache.solr.index.NoMergePolicyFactory;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.update.processor.DistributedUpdateProcessor;
 import org.apache.solr.schema.IndexSchema;
@@ -49,6 +52,7 @@ import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.update.processor.AtomicUpdateDocumentMerger;
 import org.apache.solr.util.RefCounted;
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -67,6 +71,14 @@ public class TestInPlaceUpdatesStandalone extends SolrTestCaseJ4 {
     System.setProperty("solr.tests.floatClassName", random().nextBoolean()? "TrieFloatField": "FloatPointField");
     System.setProperty("solr.tests.doubleClassName", random().nextBoolean()? "TrieDoubleField": "DoublePointField");
 
+    // we need consistent segments that aren't re-ordered on merge because we're
+    // asserting inplace updates happen by checking the internal [docid]
+    systemSetPropertySolrTestsMergePolicyFactory(NoMergePolicyFactory.class.getName());
+
+    // HACK: Don't use a RandomMergePolicy, but only use the mergePolicyFactory that we've just set
+    System.setProperty(SYSTEM_PROPERTY_SOLR_TESTS_USEMERGEPOLICYFACTORY, "true");
+    System.setProperty(SYSTEM_PROPERTY_SOLR_TESTS_USEMERGEPOLICY, "false");
+
     initCore("solrconfig-tlog.xml", "schema-inplace-updates.xml");
 
     // sanity check that autocommits are disabled
@@ -75,6 +87,16 @@ public class TestInPlaceUpdatesStandalone extends SolrTestCaseJ4 {
     assertEquals(-1, h.getCore().getSolrConfig().getUpdateHandlerInfo().autoCommmitMaxDocs);
     assertEquals(-1, h.getCore().getSolrConfig().getUpdateHandlerInfo().autoSoftCommmitMaxDocs);
 
+    // assert that NoMergePolicy was chosen
+    RefCounted<IndexWriter> iw = h.getCore().getSolrCoreState().getIndexWriter(h.getCore());
+    try {
+      IndexWriter writer = iw.get();
+      assertTrue("Actual merge policy is: " + writer.getConfig().getMergePolicy(),
+          writer.getConfig().getMergePolicy() instanceof NoMergePolicy); 
+    } finally {
+      iw.decref();
+    }
+
     // validate that the schema was not changed to an unexpected state
     IndexSchema schema = h.getCore().getLatestSchema();
     for (String fieldName : Arrays.asList("_version_",
@@ -98,6 +120,11 @@ public class TestInPlaceUpdatesStandalone extends SolrTestCaseJ4 {
     client = new EmbeddedSolrServer(h.getCoreContainer(), h.coreName);
   }
 
+  @AfterClass
+  public static void afterClass() {
+    client = null;
+  }
+
   @After
   public void after() {
     System.clearProperty("solr.tests.intClassName");