diff --git a/CHANGES.txt b/CHANGES.txt
index 9d788400d5d..a740ac377dc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -266,6 +266,9 @@ Bug fixes
on EOF, removed numeric overflow possibilities and added support
for a hack to unmap the buffers on closing IndexInput.
(Uwe Schindler)
+
+16. LUCENE-1681: Fix infinite loop caused by a call to DocValues methods
+ getMinValue, getMaxValue, getAverageValue. (Simon Willnauer via Mark Miller)
New features
diff --git a/src/java/org/apache/lucene/search/function/DocValues.java b/src/java/org/apache/lucene/search/function/DocValues.java
index 61d700441e8..c637e7e07fc 100755
--- a/src/java/org/apache/lucene/search/function/DocValues.java
+++ b/src/java/org/apache/lucene/search/function/DocValues.java
@@ -114,17 +114,15 @@ public abstract class DocValues {
}
// --- some simple statistics on values
- private float minVal;
- private float maxVal;
- private float avgVal;
+ private float minVal = Float.NaN;
+ private float maxVal = Float.NaN;
+ private float avgVal = Float.NaN;
private boolean computed=false;
// compute optional values
- private void compute () {
+ private void compute() {
if (computed) {
return;
}
- minVal = Float.MAX_VALUE;
- maxVal = 0;
float sum = 0;
int n = 0;
while (true) {
@@ -135,34 +133,56 @@ public abstract class DocValues {
break;
}
sum += val;
- minVal = Math.min(minVal,val);
- maxVal = Math.max(maxVal,val);
+ minVal = Float.isNaN(minVal) ? val : Math.min(minVal, val);
+ maxVal = Float.isNaN(maxVal) ? val : Math.max(maxVal, val);
+ ++n;
}
- avgVal = sum / n;
+
+ avgVal = n == 0 ? Float.NaN : sum / n;
computed = true;
}
+
/**
- * Optional op.
- * Returns the minimum of all values.
+ * Returns the minimum of all values or Float.NaN
if this
+ * DocValues instance does not contain any value.
+ *
+ * This operation is optional + *
+ * + * @return the minimum of all values orFloat.NaN
if this
+ * DocValues instance does not contain any value.
*/
- public float getMinValue () {
+ public float getMinValue() {
compute();
return minVal;
}
-
+
/**
- * Optional op.
- * Returns the maximum of all values.
+ * Returns the maximum of all values or Float.NaN
if this
+ * DocValues instance does not contain any value.
+ * + * This operation is optional + *
+ * + * @return the maximum of all values orFloat.NaN
if this
+ * DocValues instance does not contain any value.
*/
- public float getMaxValue () {
+ public float getMaxValue() {
compute();
return maxVal;
}
-
+
/**
- * Returns the average of all values.
+ * Returns the average of all values or Float.NaN
if this
+ * DocValues instance does not contain any value. *
+ * + * This operation is optional + *
+ * + * @return the average of all values orFloat.NaN
if this
+ * DocValues instance does not contain any value
*/
- public float getAverageValue () {
+ public float getAverageValue() {
compute();
return avgVal;
}
diff --git a/src/test/org/apache/lucene/search/function/TestDocValues.java b/src/test/org/apache/lucene/search/function/TestDocValues.java
new file mode 100644
index 00000000000..d1625a9f952
--- /dev/null
+++ b/src/test/org/apache/lucene/search/function/TestDocValues.java
@@ -0,0 +1,126 @@
+package org.apache.lucene.search.function;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.util.LuceneTestCase;
+
+/**
+ * DocValues TestCase
+ */
+public class TestDocValues extends LuceneTestCase {
+
+ /* @override constructor */
+ public TestDocValues(String name) {
+ super(name);
+ }
+
+ /* @override */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ /* @override */
+ protected void setUp() throws Exception {
+ // prepare a small index with just a few documents.
+ super.setUp();
+ }
+
+ public void testGetMinValue() {
+ float[] innerArray = new float[] { 1.0f, 2.0f, -1.0f, 100.0f };
+ DocValuesTestImpl docValues = new DocValuesTestImpl(innerArray);
+ assertEquals("-1.0f is the min value in the source array", -1.0f, docValues
+ .getMinValue());
+
+ // test with without values - NaN
+ innerArray = new float[] {};
+ docValues = new DocValuesTestImpl(innerArray);
+ assertTrue("max is NaN - no values in inner array", Float.isNaN(docValues
+ .getMinValue()));
+ }
+
+ public void testGetMaxValue() {
+ float[] innerArray = new float[] { 1.0f, 2.0f, -1.0f, 10.0f };
+ DocValuesTestImpl docValues = new DocValuesTestImpl(innerArray);
+ assertEquals("10.0f is the max value in the source array", 10.0f, docValues
+ .getMaxValue());
+
+ innerArray = new float[] { -3.0f, -1.0f, -100.0f };
+ docValues = new DocValuesTestImpl(innerArray);
+ assertEquals("-1.0f is the max value in the source array", -1.0f, docValues
+ .getMaxValue());
+
+ innerArray = new float[] { -3.0f, -1.0f, 100.0f, Float.MAX_VALUE,
+ Float.MAX_VALUE - 1 };
+ docValues = new DocValuesTestImpl(innerArray);
+ assertEquals(Float.MAX_VALUE + " is the max value in the source array",
+ Float.MAX_VALUE, docValues.getMaxValue());
+
+ // test with without values - NaN
+ innerArray = new float[] {};
+ docValues = new DocValuesTestImpl(innerArray);
+ assertTrue("max is NaN - no values in inner array", Float.isNaN(docValues
+ .getMaxValue()));
+ }
+
+ public void testGetAverageValue() {
+ float[] innerArray = new float[] { 1.0f, 1.0f, 1.0f, 1.0f };
+ DocValuesTestImpl docValues = new DocValuesTestImpl(innerArray);
+ assertEquals("the average is 1.0f", 1.0f, docValues.getAverageValue());
+
+ innerArray = new float[] { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f };
+ docValues = new DocValuesTestImpl(innerArray);
+ assertEquals("the average is 3.5f", 3.5f, docValues.getAverageValue());
+
+ // test with negative values
+ innerArray = new float[] { -1.0f, 2.0f };
+ docValues = new DocValuesTestImpl(innerArray);
+ assertEquals("the average is 0.5f", 0.5f, docValues.getAverageValue());
+
+ // test with without values - NaN
+ innerArray = new float[] {};
+ docValues = new DocValuesTestImpl(innerArray);
+ assertTrue("the average is NaN - no values in inner array", Float
+ .isNaN(docValues.getAverageValue()));
+ }
+
+ static class DocValuesTestImpl extends DocValues {
+ float[] innerArray;
+
+ DocValuesTestImpl(float[] innerArray) {
+ this.innerArray = innerArray;
+ }
+
+ /**
+ * @see org.apache.lucene.search.function.DocValues#floatVal(int)
+ */
+ /* @Override */
+ public float floatVal(int doc) {
+ return innerArray[doc];
+ }
+
+ /**
+ * @see org.apache.lucene.search.function.DocValues#toString(int)
+ */
+ /* @Override */
+ public String toString(int doc) {
+ return Integer.toString(doc);
+ }
+
+ }
+
+}
\ No newline at end of file