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

[31/50] lucene-solr:jira/solr-9858: LUCENE-7673: Add MultiValued[Int/Long/Float/Double]FieldSource for SortedNumericDocValues

LUCENE-7673: Add MultiValued[Int/Long/Float/Double]FieldSource for SortedNumericDocValues


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

Branch: refs/heads/jira/solr-9858
Commit: 7f13105fbe0023771b581c0423df7eaa6a76335e
Parents: f47bbb3
Author: Tomas Fernandez Lobbe <tf...@apache.org>
Authored: Mon Feb 6 10:44:09 2017 -0800
Committer: Tomas Fernandez Lobbe <tf...@apache.org>
Committed: Mon Feb 6 10:44:09 2017 -0800

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   4 +
 .../function/valuesource/DoubleFieldSource.java |   6 +-
 .../function/valuesource/FloatFieldSource.java  |   6 +-
 .../function/valuesource/IntFieldSource.java    |   8 +-
 .../function/valuesource/LongFieldSource.java   |   6 +-
 .../MultiValuedDoubleFieldSource.java           |  78 +++++++++++++
 .../MultiValuedFloatFieldSource.java            |  78 +++++++++++++
 .../valuesource/MultiValuedIntFieldSource.java  |  78 +++++++++++++
 .../valuesource/MultiValuedLongFieldSource.java |  78 +++++++++++++
 .../queries/function/FunctionTestSetup.java     |  58 ++++++++++
 .../function/TestDocValuesFieldSources.java     |  14 ++-
 .../queries/function/TestFieldScoreQuery.java   |  26 +++++
 .../function/TestFunctionRangeQuery.java        |  37 +++++-
 .../queries/function/TestValueSources.java      | 113 +++++++++++++------
 14 files changed, 544 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 02829f9..0585506 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -103,6 +103,10 @@ New Features
   on doc values. These new methods should be wrapped in an IndexOrDocValuesQuery
   for best performance. (Adrien Grand)
 
+* LUCENE-7673: Added MultiValued[Int/Long/Float/Double]FieldSource that given a
+  SortedNumericSelector.Type can give a ValueSource view of a 
+  SortedNumericDocValues field. (Tom�s Fern�ndez L�bbe)
+
 Bug Fixes
 
 * LUCENE-7630: Fix (Edge)NGramTokenFilter to no longer drop payloads

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/DoubleFieldSource.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/DoubleFieldSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/DoubleFieldSource.java
index d525dab..3f5f454 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/DoubleFieldSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/DoubleFieldSource.java
@@ -52,7 +52,7 @@ public class DoubleFieldSource extends FieldCacheSource {
   @Override
   public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
 
-    final NumericDocValues values = DocValues.getNumeric(readerContext.reader(), field);
+    final NumericDocValues values = getNumericDocValues(context, readerContext);
 
     return new DoubleDocValues(this) {
       int lastDocID;
@@ -104,6 +104,10 @@ public class DoubleFieldSource extends FieldCacheSource {
     };
   }
 
+  protected NumericDocValues getNumericDocValues(Map context, LeafReaderContext readerContext) throws IOException {
+    return DocValues.getNumeric(readerContext.reader(), field);
+  }
+
   @Override
   public boolean equals(Object o) {
     if (o.getClass() != DoubleFieldSource.class) return false;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/FloatFieldSource.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/FloatFieldSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/FloatFieldSource.java
index c4ab4b9..87285f4 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/FloatFieldSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/FloatFieldSource.java
@@ -52,7 +52,7 @@ public class FloatFieldSource extends FieldCacheSource {
   @Override
   public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
 
-    final NumericDocValues arr = DocValues.getNumeric(readerContext.reader(), field);
+    final NumericDocValues arr = getNumericDocValues(context, readerContext);
     
     return new FloatDocValues(this) {
       int lastDocID;
@@ -105,6 +105,10 @@ public class FloatFieldSource extends FieldCacheSource {
     };
   }
 
+  protected NumericDocValues getNumericDocValues(Map context, LeafReaderContext readerContext) throws IOException {
+    return DocValues.getNumeric(readerContext.reader(), field);
+  }
+
   @Override
   public boolean equals(Object o) {
     if (o.getClass() !=  FloatFieldSource.class) return false;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/IntFieldSource.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/IntFieldSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/IntFieldSource.java
index 2887e03..8d2ab7f 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/IntFieldSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/IntFieldSource.java
@@ -52,11 +52,9 @@ public class IntFieldSource extends FieldCacheSource {
   @Override
   public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
     
-    final NumericDocValues arr = DocValues.getNumeric(readerContext.reader(), field);
+    final NumericDocValues arr = getNumericDocValues(context, readerContext);
 
     return new IntDocValues(this) {
-      final MutableValueInt val = new MutableValueInt();
-
       int lastDocID;
 
       private int getValueForDoc(int doc) throws IOException {
@@ -111,6 +109,10 @@ public class IntFieldSource extends FieldCacheSource {
     };
   }
 
+  protected NumericDocValues getNumericDocValues(Map context, LeafReaderContext readerContext) throws IOException {
+    return DocValues.getNumeric(readerContext.reader(), field);
+  }
+
   @Override
   public boolean equals(Object o) {
     if (o.getClass() !=  IntFieldSource.class) return false;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/LongFieldSource.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/LongFieldSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/LongFieldSource.java
index 2a43f60..8474362 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/LongFieldSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/LongFieldSource.java
@@ -64,7 +64,7 @@ public class LongFieldSource extends FieldCacheSource {
   @Override
   public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
     
-    final NumericDocValues arr = DocValues.getNumeric(readerContext.reader(), field);
+    final NumericDocValues arr = getNumericDocValues(context, readerContext);
 
     return new LongDocValues(this) {
       int lastDocID;
@@ -142,6 +142,10 @@ public class LongFieldSource extends FieldCacheSource {
     };
   }
 
+  protected NumericDocValues getNumericDocValues(Map context, LeafReaderContext readerContext) throws IOException {
+    return DocValues.getNumeric(readerContext.reader(), field);
+  }
+
   protected MutableValueLong newMutableValueLong() {
     return new MutableValueLong();  
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedDoubleFieldSource.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedDoubleFieldSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedDoubleFieldSource.java
new file mode 100644
index 0000000..b0728cf
--- /dev/null
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedDoubleFieldSource.java
@@ -0,0 +1,78 @@
+/*
+ * 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.queries.function.valuesource;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.SortedNumericSelector;
+import org.apache.lucene.search.SortedNumericSelector.Type;
+import org.apache.lucene.search.SortedNumericSortField;
+
+/**
+ * Obtains double field values from {@link org.apache.lucene.index.LeafReader#getSortedNumericDocValues} and using a 
+ * {@link org.apache.lucene.search.SortedNumericSelector} it gives a single-valued ValueSource view of a field.
+ */
+public class MultiValuedDoubleFieldSource extends DoubleFieldSource {
+
+  protected final SortedNumericSelector.Type selector;
+
+  public MultiValuedDoubleFieldSource(String field, Type selector) {
+    super(field);
+    this.selector = selector;
+    Objects.requireNonNull(field, "Field is required to create a MultiValuedDoubleFieldSource");
+    Objects.requireNonNull(selector, "SortedNumericSelector is required to create a MultiValuedDoubleFieldSource");
+  }
+  
+  @Override
+  public SortField getSortField(boolean reverse) {
+    return new SortedNumericSortField(field, SortField.Type.DOUBLE, reverse, selector);
+  }
+  
+  @Override
+  public String description() {
+    return "double(" + field + ',' + selector.name() + ')';
+  }
+  
+  @Override
+  protected NumericDocValues getNumericDocValues(Map context, LeafReaderContext readerContext) throws IOException {
+    SortedNumericDocValues sortedDv = DocValues.getSortedNumeric(readerContext.reader(), field);
+    return SortedNumericSelector.wrap(sortedDv, selector, SortField.Type.DOUBLE);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o.getClass() !=  MultiValuedDoubleFieldSource.class) return false;
+    MultiValuedDoubleFieldSource other = (MultiValuedDoubleFieldSource)o;
+    if (this.selector != other.selector) return false;
+    return this.field.equals(other.field);
+  }
+
+  @Override
+  public int hashCode() {
+    int h = super.hashCode();
+    h += selector.hashCode();
+    return h;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedFloatFieldSource.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedFloatFieldSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedFloatFieldSource.java
new file mode 100644
index 0000000..af8eacf
--- /dev/null
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedFloatFieldSource.java
@@ -0,0 +1,78 @@
+/*
+ * 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.queries.function.valuesource;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.SortedNumericSelector;
+import org.apache.lucene.search.SortedNumericSelector.Type;
+import org.apache.lucene.search.SortedNumericSortField;
+
+/**
+ * Obtains float field values from {@link org.apache.lucene.index.LeafReader#getSortedNumericDocValues} and using a 
+ * {@link org.apache.lucene.search.SortedNumericSelector} it gives a single-valued ValueSource view of a field.
+ */
+public class MultiValuedFloatFieldSource extends FloatFieldSource {
+
+  protected final SortedNumericSelector.Type selector;
+
+  public MultiValuedFloatFieldSource(String field, Type selector) {
+    super(field);
+    this.selector = selector;
+    Objects.requireNonNull(field, "Field is required to create a MultiValuedFloatFieldSource");
+    Objects.requireNonNull(selector, "SortedNumericSelector is required to create a MultiValuedFloatFieldSource");
+  }
+  
+  @Override
+  public SortField getSortField(boolean reverse) {
+    return new SortedNumericSortField(field, SortField.Type.FLOAT, reverse, selector);
+  }
+  
+  @Override
+  public String description() {
+    return "float(" + field + ',' + selector.name() + ')';
+  }
+  
+  @Override
+  protected NumericDocValues getNumericDocValues(Map context, LeafReaderContext readerContext) throws IOException {
+    SortedNumericDocValues sortedDv = DocValues.getSortedNumeric(readerContext.reader(), field);
+    return SortedNumericSelector.wrap(sortedDv, selector, SortField.Type.FLOAT);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o.getClass() !=  MultiValuedFloatFieldSource.class) return false;
+    MultiValuedFloatFieldSource other = (MultiValuedFloatFieldSource)o;
+    if (this.selector != other.selector) return false;
+    return this.field.equals(other.field);
+  }
+
+  @Override
+  public int hashCode() {
+    int h = super.hashCode();
+    h += selector.hashCode();
+    return h;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedIntFieldSource.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedIntFieldSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedIntFieldSource.java
new file mode 100644
index 0000000..3110f8d
--- /dev/null
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedIntFieldSource.java
@@ -0,0 +1,78 @@
+/*
+ * 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.queries.function.valuesource;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.SortedNumericSelector;
+import org.apache.lucene.search.SortedNumericSelector.Type;
+import org.apache.lucene.search.SortedNumericSortField;
+
+/**
+ * Obtains int field values from {@link org.apache.lucene.index.LeafReader#getSortedNumericDocValues} and using a 
+ * {@link org.apache.lucene.search.SortedNumericSelector} it gives a single-valued ValueSource view of a field.
+ */
+public class MultiValuedIntFieldSource extends IntFieldSource {
+
+  protected final SortedNumericSelector.Type selector;
+
+  public MultiValuedIntFieldSource(String field, Type selector) {
+    super(field);
+    this.selector = selector;
+    Objects.requireNonNull(field, "Field is required to create a MultiValuedIntFieldSource");
+    Objects.requireNonNull(selector, "SortedNumericSelector is required to create a MultiValuedIntFieldSource");
+  }
+  
+  @Override
+  public SortField getSortField(boolean reverse) {
+    return new SortedNumericSortField(field, SortField.Type.INT, reverse, selector);
+  }
+  
+  @Override
+  public String description() {
+    return "int(" + field + ',' + selector.name() + ')';
+  }
+  
+  @Override
+  protected NumericDocValues getNumericDocValues(Map context, LeafReaderContext readerContext) throws IOException {
+    SortedNumericDocValues sortedDv = DocValues.getSortedNumeric(readerContext.reader(), field);
+    return SortedNumericSelector.wrap(sortedDv, selector, SortField.Type.INT);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o.getClass() !=  MultiValuedIntFieldSource.class) return false;
+    MultiValuedIntFieldSource other = (MultiValuedIntFieldSource)o;
+    if (this.selector != other.selector) return false;
+    return this.field.equals(other.field);
+  }
+
+  @Override
+  public int hashCode() {
+    int h = super.hashCode();
+    h += selector.hashCode();
+    return h;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedLongFieldSource.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedLongFieldSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedLongFieldSource.java
new file mode 100644
index 0000000..5e31e13
--- /dev/null
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/MultiValuedLongFieldSource.java
@@ -0,0 +1,78 @@
+/*
+ * 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.queries.function.valuesource;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.SortedNumericSelector;
+import org.apache.lucene.search.SortedNumericSelector.Type;
+import org.apache.lucene.search.SortedNumericSortField;
+
+/**
+ * Obtains long field values from {@link org.apache.lucene.index.LeafReader#getSortedNumericDocValues} and using a 
+ * {@link org.apache.lucene.search.SortedNumericSelector} it gives a single-valued ValueSource view of a field.
+ */
+public class MultiValuedLongFieldSource extends LongFieldSource {
+
+  protected final SortedNumericSelector.Type selector;
+
+  public MultiValuedLongFieldSource(String field, Type selector) {
+    super(field);
+    this.selector = selector;
+    Objects.requireNonNull(field, "Field is required to create a MultiValuedLongFieldSource");
+    Objects.requireNonNull(selector, "SortedNumericSelector is required to create a MultiValuedLongFieldSource");
+  }
+  
+  @Override
+  public SortField getSortField(boolean reverse) {
+    return new SortedNumericSortField(field, SortField.Type.LONG, reverse, selector);
+  }
+  
+  @Override
+  public String description() {
+    return "long(" + field + ',' + selector.name() + ')';
+  }
+  
+  @Override
+  protected NumericDocValues getNumericDocValues(Map context, LeafReaderContext readerContext) throws IOException {
+    SortedNumericDocValues sortedDv = DocValues.getSortedNumeric(readerContext.reader(), field);
+    return SortedNumericSelector.wrap(sortedDv, selector, SortField.Type.LONG);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o.getClass() !=  MultiValuedLongFieldSource.class) return false;
+    MultiValuedLongFieldSource other = (MultiValuedLongFieldSource)o;
+    if (this.selector != other.selector) return false;
+    return this.field.equals(other.field);
+  }
+
+  @Override
+  public int hashCode() {
+    int h = super.hashCode();
+    h += selector.hashCode();
+    return h;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/test/org/apache/lucene/queries/function/FunctionTestSetup.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/test/org/apache/lucene/queries/function/FunctionTestSetup.java b/lucene/queries/src/test/org/apache/lucene/queries/function/FunctionTestSetup.java
index 2764a8f..4b4c2a9 100644
--- a/lucene/queries/src/test/org/apache/lucene/queries/function/FunctionTestSetup.java
+++ b/lucene/queries/src/test/org/apache/lucene/queries/function/FunctionTestSetup.java
@@ -23,15 +23,20 @@ import org.apache.lucene.document.Field;
 import org.apache.lucene.document.FieldType;
 import org.apache.lucene.document.NumericDocValuesField;
 import org.apache.lucene.document.SortedDocValuesField;
+import org.apache.lucene.document.SortedNumericDocValuesField;
 import org.apache.lucene.document.StoredField;
 import org.apache.lucene.document.TextField;
 import org.apache.lucene.index.IndexWriterConfig;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.queries.function.valuesource.FloatFieldSource;
 import org.apache.lucene.queries.function.valuesource.IntFieldSource;
+import org.apache.lucene.queries.function.valuesource.MultiValuedFloatFieldSource;
+import org.apache.lucene.queries.function.valuesource.MultiValuedIntFieldSource;
+import org.apache.lucene.search.SortedNumericSelector;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.NumericUtils;
 import org.apache.lucene.util.TestUtil;
 import org.junit.AfterClass;
 import org.junit.Ignore;
@@ -53,10 +58,35 @@ public abstract class FunctionTestSetup extends LuceneTestCase {
   protected static final String ID_FIELD = "id";
   protected static final String TEXT_FIELD = "text";
   protected static final String INT_FIELD = "iii";
+  /**
+   * This field is multiValued and should give the exact same results as
+   * {@link #INT_FIELD} when used with MIN selector
+   */
+  protected static final String INT_FIELD_MV_MIN = "iii_min";
+  /**
+   * This field is multiValued and should give the exact same results as
+   * {@link #INT_FIELD} when used with MAX selector
+   */
+  protected static final String INT_FIELD_MV_MAX = "iii_max";
+
   protected static final String FLOAT_FIELD = "fff";
+  /**
+   * This field is multiValued and should give the exact same results as
+   * {@link #FLOAT_FIELD} when used with MIN selector
+   */
+  protected static final String FLOAT_FIELD_MV_MIN = "fff_min";
+  /**
+   * This field is multiValued and should give the exact same results as
+   * {@link #FLOAT_FIELD} when used with MAX selector
+   */
+  protected static final String FLOAT_FIELD_MV_MAX = "fff_max";
 
   protected ValueSource INT_VALUESOURCE = new IntFieldSource(INT_FIELD);
+  protected ValueSource INT_MV_MIN_VALUESOURCE = new MultiValuedIntFieldSource(INT_FIELD_MV_MIN, SortedNumericSelector.Type.MIN);
+  protected ValueSource INT_MV_MAX_VALUESOURCE = new MultiValuedIntFieldSource(INT_FIELD_MV_MAX, SortedNumericSelector.Type.MAX);
   protected ValueSource FLOAT_VALUESOURCE = new FloatFieldSource(FLOAT_FIELD);
+  protected ValueSource FLOAT_MV_MIN_VALUESOURCE = new MultiValuedFloatFieldSource(FLOAT_FIELD_MV_MIN, SortedNumericSelector.Type.MIN);
+  protected ValueSource FLOAT_MV_MAX_VALUESOURCE = new MultiValuedFloatFieldSource(FLOAT_FIELD_MV_MAX, SortedNumericSelector.Type.MAX);
 
   private static final String DOC_TEXT_LINES[] = {
           "Well, this is just some plain text we use for creating the ",
@@ -148,6 +178,34 @@ public abstract class FunctionTestSetup extends LuceneTestCase {
     f = new StoredField(FLOAT_FIELD, scoreAndID); // for function scoring
     d.add(f);
     d.add(new NumericDocValuesField(FLOAT_FIELD, Float.floatToRawIntBits(scoreAndID)));
+    
+    f = new StoredField(INT_FIELD_MV_MIN, scoreAndID);
+    d.add(f);
+    f = new StoredField(INT_FIELD_MV_MIN, scoreAndID + 1);
+    d.add(f);
+    d.add(new SortedNumericDocValuesField(INT_FIELD_MV_MIN, scoreAndID));
+    d.add(new SortedNumericDocValuesField(INT_FIELD_MV_MIN, scoreAndID + 1));
+    
+    f = new StoredField(INT_FIELD_MV_MAX, scoreAndID);
+    d.add(f);
+    f = new StoredField(INT_FIELD_MV_MAX, scoreAndID - 1);
+    d.add(f);
+    d.add(new SortedNumericDocValuesField(INT_FIELD_MV_MAX, scoreAndID));
+    d.add(new SortedNumericDocValuesField(INT_FIELD_MV_MAX, scoreAndID - 1));
+    
+    f = new StoredField(FLOAT_FIELD_MV_MIN, scoreAndID);
+    d.add(f);
+    f = new StoredField(FLOAT_FIELD_MV_MIN, scoreAndID + 1);
+    d.add(f);
+    d.add(new SortedNumericDocValuesField(FLOAT_FIELD_MV_MIN, NumericUtils.floatToSortableInt(scoreAndID)));
+    d.add(new SortedNumericDocValuesField(FLOAT_FIELD_MV_MIN, NumericUtils.floatToSortableInt(scoreAndID + 1)));
+    
+    f = new StoredField(FLOAT_FIELD_MV_MAX, scoreAndID);
+    d.add(f);
+    f = new StoredField(FLOAT_FIELD_MV_MAX, scoreAndID - 1);
+    d.add(f);
+    d.add(new SortedNumericDocValuesField(FLOAT_FIELD_MV_MAX, NumericUtils.floatToSortableInt(scoreAndID)));
+    d.add(new SortedNumericDocValuesField(FLOAT_FIELD_MV_MAX, NumericUtils.floatToSortableInt(scoreAndID - 1)));
 
     iw.addDocument(d);
     log("added: " + d);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/test/org/apache/lucene/queries/function/TestDocValuesFieldSources.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/test/org/apache/lucene/queries/function/TestDocValuesFieldSources.java b/lucene/queries/src/test/org/apache/lucene/queries/function/TestDocValuesFieldSources.java
index 7af2d49..479ac3c 100644
--- a/lucene/queries/src/test/org/apache/lucene/queries/function/TestDocValuesFieldSources.java
+++ b/lucene/queries/src/test/org/apache/lucene/queries/function/TestDocValuesFieldSources.java
@@ -24,6 +24,7 @@ import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.NumericDocValuesField;
 import org.apache.lucene.document.SortedDocValuesField;
+import org.apache.lucene.document.SortedNumericDocValuesField;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.DocValuesType;
@@ -31,6 +32,8 @@ import org.apache.lucene.index.IndexWriterConfig;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.queries.function.valuesource.BytesRefFieldSource;
 import org.apache.lucene.queries.function.valuesource.LongFieldSource;
+import org.apache.lucene.queries.function.valuesource.MultiValuedLongFieldSource;
+import org.apache.lucene.search.SortedNumericSelector.Type;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
@@ -60,6 +63,9 @@ public class TestDocValuesFieldSources extends LuceneTestCase {
       case NUMERIC:
         f = new NumericDocValuesField("dv", 0);
         break;
+      case SORTED_NUMERIC:
+        f = new SortedNumericDocValuesField("dv", 0);
+        break;
       default:
         throw new AssertionError();
     }
@@ -81,6 +87,7 @@ public class TestDocValuesFieldSources extends LuceneTestCase {
           f.setBytesValue(new BytesRef((String) vals[i]));
           break;
         case NUMERIC:
+        case SORTED_NUMERIC:
           final int bitsPerValue = RandomNumbers.randomIntBetween(random(), 1, 31); // keep it an int
           vals[i] = (long) random().nextInt((int) PackedInts.maxValue(bitsPerValue));
           f.setLongValue((Long) vals[i]);
@@ -105,6 +112,10 @@ public class TestDocValuesFieldSources extends LuceneTestCase {
         case NUMERIC:
           vs = new LongFieldSource("dv");
           break;
+        case SORTED_NUMERIC:
+          // Since we are not indexing multiple values, MIN and MAX should work the same way
+          vs = random().nextBoolean()? new MultiValuedLongFieldSource("dv", Type.MIN): new MultiValuedLongFieldSource("dv", Type.MAX);
+          break;
         default:
           throw new AssertionError();
       }
@@ -136,6 +147,7 @@ public class TestDocValuesFieldSources extends LuceneTestCase {
             assertEquals(new BytesRef((String) expected), bytes.get());
             break;
           case NUMERIC:
+          case SORTED_NUMERIC:
             assertEquals(((Number) expected).longValue(), values.longVal(i));
             break;
         }
@@ -147,7 +159,7 @@ public class TestDocValuesFieldSources extends LuceneTestCase {
 
   public void test() throws IOException {
     for (DocValuesType type : DocValuesType.values()) {
-      if (type != DocValuesType.SORTED_SET && type != DocValuesType.SORTED_NUMERIC && type != DocValuesType.NONE) {
+      if (type != DocValuesType.SORTED_SET && type != DocValuesType.NONE) {
         test(type);
       }
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java b/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java
index 843f452..977d801 100644
--- a/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java
+++ b/lucene/queries/src/test/org/apache/lucene/queries/function/TestFieldScoreQuery.java
@@ -49,6 +49,12 @@ public class TestFieldScoreQuery extends FunctionTestSetup {
   public void testRankInt () throws Exception {
     doTestRank(INT_VALUESOURCE);
   }
+  
+  @Test
+  public void testRankIntMultiValued() throws Exception {
+    doTestRank(INT_MV_MAX_VALUESOURCE);
+    doTestRank(INT_MV_MIN_VALUESOURCE);
+  }
 
   /** Test that FieldScoreQuery of Type.FLOAT returns docs in expected order. */
   @Test
@@ -56,6 +62,13 @@ public class TestFieldScoreQuery extends FunctionTestSetup {
     // same values, but in flot format
     doTestRank(FLOAT_VALUESOURCE);
   }
+  
+  @Test
+  public void testRankFloatMultiValued() throws Exception {
+    // same values, but in flot format
+    doTestRank(FLOAT_MV_MAX_VALUESOURCE);
+    doTestRank(FLOAT_MV_MIN_VALUESOURCE);
+  }
 
   // Test that FieldScoreQuery returns docs in expected order.
   private void doTestRank (ValueSource valueSource) throws Exception {
@@ -82,6 +95,12 @@ public class TestFieldScoreQuery extends FunctionTestSetup {
   public void testExactScoreInt () throws  Exception {
     doTestExactScore(INT_VALUESOURCE);
   }
+  
+  @Test
+  public void testExactScoreIntMultiValued() throws  Exception {
+    doTestExactScore(INT_MV_MAX_VALUESOURCE);
+    doTestExactScore(INT_MV_MIN_VALUESOURCE);
+  }
 
   /** Test that FieldScoreQuery of Type.FLOAT returns the expected scores. */
   @Test
@@ -89,6 +108,13 @@ public class TestFieldScoreQuery extends FunctionTestSetup {
     // same values, but in flot format
     doTestExactScore(FLOAT_VALUESOURCE);
   }
+  
+  @Test
+  public void testExactScoreFloatMultiValued() throws  Exception {
+    // same values, but in flot format
+    doTestExactScore(FLOAT_MV_MAX_VALUESOURCE);
+    doTestExactScore(FLOAT_MV_MIN_VALUESOURCE);
+  }
 
   // Test that FieldScoreQuery returns docs with expected score.
   private void doTestExactScore (ValueSource valueSource) throws Exception {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java b/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java
index 5f3a412..22b3ed5 100644
--- a/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java
+++ b/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionRangeQuery.java
@@ -57,11 +57,23 @@ public class TestFunctionRangeQuery extends FunctionTestSetup {
   public void testRangeInt() throws IOException {
     doTestRange(INT_VALUESOURCE);
   }
+  
+  @Test
+  public void testRangeIntMultiValued() throws IOException {
+    doTestRange(INT_MV_MAX_VALUESOURCE);
+    doTestRange(INT_MV_MIN_VALUESOURCE);
+  }
 
   @Test
   public void testRangeFloat() throws IOException {
     doTestRange(FLOAT_VALUESOURCE);
   }
+  
+  @Test
+  public void testRangeFloatMultiValued() throws IOException {
+    doTestRange(FLOAT_MV_MAX_VALUESOURCE);
+    doTestRange(FLOAT_MV_MIN_VALUESOURCE);
+  }
 
   private void doTestRange(ValueSource valueSource) throws IOException {
     Query rangeQuery = new FunctionRangeQuery(valueSource, 2, 4, true, false);
@@ -75,8 +87,17 @@ public class TestFunctionRangeQuery extends FunctionTestSetup {
 
   @Test
   public void testDeleted() throws IOException {
-    // We delete doc with #3. Note we don't commit it to disk; we search using a near eal-time reader.
-    final ValueSource valueSource = INT_VALUESOURCE;
+    doTestDeleted(INT_VALUESOURCE);
+  }
+  
+  @Test
+  public void testDeletedMultiValued() throws IOException {
+    doTestDeleted(INT_MV_MAX_VALUESOURCE);
+    doTestDeleted(INT_MV_MIN_VALUESOURCE);
+  }
+  
+  private void doTestDeleted(ValueSource valueSource) throws IOException {
+    // We delete doc with #3. Note we don't commit it to disk; we search using a near real-time reader.
     IndexWriter writer = new IndexWriter(dir, new IndexWriterConfig(null));
     try {
       writer.deleteDocuments(new FunctionRangeQuery(valueSource, 3, 3, true, true));//delete the one with #3
@@ -103,6 +124,18 @@ public class TestFunctionRangeQuery extends FunctionTestSetup {
             "  2.0 = int(" + INT_FIELD + ")=2\n",
         explain.toString());
   }
+  
+  @Test
+  public void testExplainMultiValued() throws IOException {
+    Query rangeQuery = new FunctionRangeQuery(INT_MV_MIN_VALUESOURCE, 2, 2, true, true);
+    ScoreDoc[] scoreDocs = indexSearcher.search(rangeQuery, N_DOCS).scoreDocs;
+    Explanation explain = indexSearcher.explain(rangeQuery, scoreDocs[0].doc);
+    // Just validate it looks reasonable
+    assertEquals(
+            "2.0 = frange(int(" + INT_FIELD_MV_MIN + ",MIN)):[2 TO 2]\n" +
+            "  2.0 = int(" + INT_FIELD_MV_MIN + ",MIN)=2\n",
+        explain.toString());
+  }
 
   private void expectScores(ScoreDoc[] scoreDocs, int... docScores) {
     assertEquals(docScores.length, scoreDocs.length);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f13105f/lucene/queries/src/test/org/apache/lucene/queries/function/TestValueSources.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/test/org/apache/lucene/queries/function/TestValueSources.java b/lucene/queries/src/test/org/apache/lucene/queries/function/TestValueSources.java
index 57caac6..e008293 100644
--- a/lucene/queries/src/test/org/apache/lucene/queries/function/TestValueSources.java
+++ b/lucene/queries/src/test/org/apache/lucene/queries/function/TestValueSources.java
@@ -16,10 +16,10 @@
  */
 package org.apache.lucene.queries.function;
 
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-import java.io.IOException;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.MockAnalyzer;
@@ -27,13 +27,14 @@ import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.NumericDocValuesField;
 import org.apache.lucene.document.SortedDocValuesField;
+import org.apache.lucene.document.SortedNumericDocValuesField;
 import org.apache.lucene.document.StringField;
 import org.apache.lucene.document.TextField;
-import org.apache.lucene.index.Term;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.Term;
 import org.apache.lucene.queries.function.docvalues.FloatDocValues;
 import org.apache.lucene.queries.function.valuesource.BytesRefFieldSource;
 import org.apache.lucene.queries.function.valuesource.ConstValueSource;
@@ -54,6 +55,10 @@ import org.apache.lucene.queries.function.valuesource.MaxFloatFunction;
 import org.apache.lucene.queries.function.valuesource.MinFloatFunction;
 import org.apache.lucene.queries.function.valuesource.MultiFloatFunction;
 import org.apache.lucene.queries.function.valuesource.MultiFunction;
+import org.apache.lucene.queries.function.valuesource.MultiValuedDoubleFieldSource;
+import org.apache.lucene.queries.function.valuesource.MultiValuedFloatFieldSource;
+import org.apache.lucene.queries.function.valuesource.MultiValuedIntFieldSource;
+import org.apache.lucene.queries.function.valuesource.MultiValuedLongFieldSource;
 import org.apache.lucene.queries.function.valuesource.NormValueSource;
 import org.apache.lucene.queries.function.valuesource.NumDocsValueSource;
 import org.apache.lucene.queries.function.valuesource.PowFloatFunction;
@@ -73,13 +78,15 @@ import org.apache.lucene.search.Query;
 import org.apache.lucene.search.ScoreDoc;
 import org.apache.lucene.search.Sort;
 import org.apache.lucene.search.SortField;
-import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.SortedNumericSelector.Type;
 import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.search.similarities.ClassicSimilarity;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.NumericUtils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 
@@ -99,9 +106,9 @@ public class TestValueSources extends LuceneTestCase {
   static final ValueSource BOGUS_LONG_VS = new LongFieldSource("bogus_field");
 
   static final List<String[]> documents = Arrays.asList(new String[][] {
-      /*             id,  double, float, int,  long,   string, text */ 
-      new String[] { "0", "3.63", "5.2", "35", "4343", "test", "this is a test test test" },
-      new String[] { "1", "5.65", "9.3", "54", "1954", "bar",  "second test" },
+      /*             id,  double, float, int,  long,   string, text,                       double MV (x3),             int MV (x3)*/ 
+      new String[] { "0", "3.63", "5.2", "35", "4343", "test", "this is a test test test", "2.13", "3.69",  "-0.11",   "1", "7", "5"},
+      new String[] { "1", "5.65", "9.3", "54", "1954", "bar",  "second test",              "12.79", "123.456", "0.01", "12", "900", "-1" },
   });
   
   @BeforeClass
@@ -111,36 +118,29 @@ public class TestValueSources extends LuceneTestCase {
     IndexWriterConfig iwConfig = newIndexWriterConfig(analyzer);
     iwConfig.setMergePolicy(newLogMergePolicy());
     RandomIndexWriter iw = new RandomIndexWriter(random(), dir, iwConfig);
-    Document document = new Document();
-    Field idField = new StringField("id", "", Field.Store.NO);
-    document.add(idField);
-    Field idDVField = new SortedDocValuesField("id", new BytesRef());
-    document.add(idDVField);
-    Field doubleDVField = new NumericDocValuesField("double", 0);
-    document.add(doubleDVField);
-    Field floatDVField = new NumericDocValuesField("float", 0);
-    document.add(floatDVField);
-    Field intDVField = new NumericDocValuesField("int", 0);
-    document.add(intDVField);
-    Field longDVField = new NumericDocValuesField("long", 0);
-    document.add(longDVField);
-    Field stringField = new StringField("string", "", Field.Store.NO);
-    document.add(stringField);
-    Field stringDVField = new SortedDocValuesField("string", new BytesRef());
-    document.add(stringDVField);
-    Field textField = new TextField("text", "", Field.Store.NO);
-    document.add(textField);
-    
     for (String [] doc : documents) {
-      idField.setStringValue(doc[0]);
-      idDVField.setBytesValue(new BytesRef(doc[0]));
-      doubleDVField.setLongValue(Double.doubleToRawLongBits(Double.valueOf(doc[1])));
-      floatDVField.setLongValue(Float.floatToRawIntBits(Float.valueOf(doc[2])));
-      intDVField.setLongValue(Integer.valueOf(doc[3]));
-      longDVField.setLongValue(Long.valueOf(doc[4]));
-      stringField.setStringValue(doc[5]);
-      stringDVField.setBytesValue(new BytesRef(doc[5]));
-      textField.setStringValue(doc[6]);
+      Document document = new Document();
+      document.add(new StringField("id", doc[0], Field.Store.NO));
+      document.add(new SortedDocValuesField("id", new BytesRef(doc[0])));
+      document.add(new NumericDocValuesField("double", Double.doubleToRawLongBits(Double.parseDouble(doc[1]))));
+      document.add(new NumericDocValuesField("float", Float.floatToRawIntBits(Float.valueOf(doc[2]))));
+      document.add(new NumericDocValuesField("int", Integer.valueOf(doc[3])));
+      document.add(new NumericDocValuesField("long", Long.valueOf(doc[4])));
+      document.add(new StringField("string", doc[5], Field.Store.NO));
+      document.add(new SortedDocValuesField("string", new BytesRef(doc[5])));
+      document.add(new TextField("text", doc[6], Field.Store.NO));
+      document.add(new SortedNumericDocValuesField("floatMv", NumericUtils.floatToSortableInt(Float.parseFloat(doc[7]))));
+      document.add(new SortedNumericDocValuesField("floatMv", NumericUtils.floatToSortableInt(Float.parseFloat(doc[8]))));
+      document.add(new SortedNumericDocValuesField("floatMv", NumericUtils.floatToSortableInt(Float.parseFloat(doc[9]))));
+      document.add(new SortedNumericDocValuesField("doubleMv", NumericUtils.doubleToSortableLong(Double.parseDouble(doc[7]))));
+      document.add(new SortedNumericDocValuesField("doubleMv", NumericUtils.doubleToSortableLong(Double.parseDouble(doc[8]))));
+      document.add(new SortedNumericDocValuesField("doubleMv", NumericUtils.doubleToSortableLong(Double.parseDouble(doc[9]))));
+      document.add(new SortedNumericDocValuesField("intMv", Long.parseLong(doc[10])));
+      document.add(new SortedNumericDocValuesField("intMv", Long.parseLong(doc[11])));
+      document.add(new SortedNumericDocValuesField("intMv", Long.parseLong(doc[12])));
+      document.add(new SortedNumericDocValuesField("longMv", Long.parseLong(doc[10])));
+      document.add(new SortedNumericDocValuesField("longMv", Long.parseLong(doc[11])));
+      document.add(new SortedNumericDocValuesField("longMv", Long.parseLong(doc[12])));
       iw.addDocument(document);
     }
     
@@ -197,6 +197,16 @@ public class TestValueSources extends LuceneTestCase {
     assertNoneExist(BOGUS_DOUBLE_VS);
   }
   
+  public void testDoubleMultiValued() throws Exception {
+    ValueSource vs = new MultiValuedDoubleFieldSource("doubleMv", Type.MAX);
+    assertHits(new FunctionQuery(vs), new float[] { 3.69f, 123.456f });
+    assertAllExist(vs);
+    
+    vs = new MultiValuedDoubleFieldSource("doubleMv", Type.MIN);
+    assertHits(new FunctionQuery(vs), new float[] { -0.11f, 0.01f });
+    assertAllExist(vs);
+  }
+  
   public void testFloat() throws Exception {
     ValueSource vs = new FloatFieldSource("float");
     assertHits(new FunctionQuery(vs), new float[] { 5.2f, 9.3f });
@@ -204,6 +214,16 @@ public class TestValueSources extends LuceneTestCase {
     assertNoneExist(BOGUS_FLOAT_VS);
   }
   
+  public void testFloatMultiValued() throws Exception {
+    ValueSource vs = new MultiValuedFloatFieldSource("floatMv", Type.MAX);
+    assertHits(new FunctionQuery(vs), new float[] { 3.69f, 123.456f });
+    assertAllExist(vs);
+    
+    vs = new MultiValuedFloatFieldSource("floatMv", Type.MIN);
+    assertHits(new FunctionQuery(vs), new float[] { -0.11f, 0.01f });
+    assertAllExist(vs);
+  }
+  
   public void testIDF() throws Exception {
     Similarity saved = searcher.getSimilarity(true);
     try {
@@ -249,7 +269,16 @@ public class TestValueSources extends LuceneTestCase {
     assertHits(new FunctionQuery(vs), new float[] { 35f, 54f });
     assertAllExist(vs);
     assertNoneExist(BOGUS_INT_VS);
-                                 
+  }
+  
+  public void testIntMultiValued() throws Exception {
+    ValueSource vs = new MultiValuedIntFieldSource("intMv", Type.MAX);
+    assertHits(new FunctionQuery(vs), new float[] { 7f, 900f });
+    assertAllExist(vs);
+    
+    vs = new MultiValuedIntFieldSource("intMv", Type.MIN);
+    assertHits(new FunctionQuery(vs), new float[] { 1f, -1f });
+    assertAllExist(vs);
   }
   
   public void testJoinDocFreq() throws Exception {
@@ -274,6 +303,16 @@ public class TestValueSources extends LuceneTestCase {
     assertNoneExist(BOGUS_LONG_VS);
   }
   
+  public void testLongMultiValued() throws Exception {
+    ValueSource vs = new MultiValuedLongFieldSource("longMv", Type.MAX);
+    assertHits(new FunctionQuery(vs), new float[] { 7f, 900f });
+    assertAllExist(vs);
+    
+    vs = new MultiValuedLongFieldSource("longMv", Type.MIN);
+    assertHits(new FunctionQuery(vs), new float[] { 1f, -1f });
+    assertAllExist(vs);
+  }
+  
   public void testMaxDoc() throws Exception {
     ValueSource vs = new MaxDocValueSource();
     assertHits(new FunctionQuery(vs), new float[] { 2f, 2f });