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

[17/50] [abbrv] lucene-solr:apiv2: SOLR-9187: Support dates and booleans in /export handler, support boolean DocValues fields

SOLR-9187: Support dates and booleans in /export handler, support boolean DocValues fields


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

Branch: refs/heads/apiv2
Commit: 844ca4a348e282b5f857aa7ce4de6f9781766ef9
Parents: 79d1b8c
Author: Erick Erickson <er...@apache.org>
Authored: Sat Jun 11 17:38:19 2016 -0700
Committer: Erick Erickson <er...@apache.org>
Committed: Sat Jun 11 17:38:19 2016 -0700

----------------------------------------------------------------------
 solr/CHANGES.txt                                |  13 ++
 .../org/apache/solr/request/SimpleFacets.java   |   4 +-
 .../solr/response/SortingResponseWriter.java    | 126 ++++++++++++++++--
 .../java/org/apache/solr/schema/BoolField.java  |  43 ++++++-
 .../apache/solr/search/SolrIndexSearcher.java   |  12 +-
 .../solr/collection1/conf/schema-docValues.xml  |   5 +-
 .../conf/schema-docValuesMissing.xml            |  13 ++
 .../collection1/conf/schema-docValuesMulti.xml  |   1 +
 .../solr/collection1/conf/schema15.xml          |   1 +
 .../solr/schema/DocValuesMissingTest.java       | 129 +++++++++++++++++++
 .../apache/solr/schema/DocValuesMultiTest.java  | 103 ++++++++++++++-
 .../org/apache/solr/schema/DocValuesTest.java   |  87 +++++++++++--
 .../org/apache/solr/client/solrj/io/Tuple.java  |  53 +++++++-
 .../solr/configsets/streaming/conf/schema.xml   |  13 +-
 .../client/solrj/io/stream/StreamingTest.java   | 128 ++++++++++++++++++
 15 files changed, 692 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index b9b88d3..659a1d7 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -47,6 +47,19 @@ Optimizations
 
 ==================  6.2.0 ==================
 
+
+Upgrading from Solr any prior release
+----------------------
+
+Detailed Change List
+----------------------
+
+New Features
+----------------------
+
+* SOLR-9187: Support dates and booleans in /export handler, support boolean DocValues fields
+
+
 Bug Fixes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
index c804b74..017deb4 100644
--- a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
+++ b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
@@ -554,8 +554,8 @@ public class SimpleFacets {
 
      /*The user did not specify any preference*/
      if (method == null) {
-      /* Always use filters for booleans... we know the number of values is very small. */
-       if (type instanceof BoolField) {
+       /* Always use filters for booleans if not DocValues only... we know the number of values is very small. */
+       if (type instanceof BoolField && (field.indexed() == true || field.hasDocValues() == false)) {
          method = FacetMethod.ENUM;
        } else if (type.getNumericType() != null && !field.multiValued()) {
         /* the per-segment approach is optimal for numeric field types since there

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/java/org/apache/solr/response/SortingResponseWriter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/response/SortingResponseWriter.java b/solr/core/src/java/org/apache/solr/response/SortingResponseWriter.java
index 8daf90f..b3752a8 100644
--- a/solr/core/src/java/org/apache/solr/response/SortingResponseWriter.java
+++ b/solr/core/src/java/org/apache/solr/response/SortingResponseWriter.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.Writer;
 import java.lang.invoke.MethodHandles;
+import java.util.Date;
 import java.util.List;
 import java.util.ArrayList;
 
@@ -44,10 +45,12 @@ import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestInfo;
+import org.apache.solr.schema.BoolField;
 import org.apache.solr.schema.FieldType;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.schema.SchemaField;
 import org.apache.solr.schema.StrField;
+import org.apache.solr.schema.TrieDateField;
 import org.apache.solr.schema.TrieDoubleField;
 import org.apache.solr.schema.TrieFloatField;
 import org.apache.solr.schema.TrieIntField;
@@ -99,14 +102,24 @@ public class SortingResponseWriter implements QueryResponseWriter {
       exception = new IOException(new SyntaxError("Scoring is not currently supported with xsort."));
     }
 
-    FixedBitSet[] sets = (FixedBitSet[])req.getContext().get("export");
-    Integer th = (Integer)req.getContext().get("totalHits");
-
-    if(sets == null) {
-      exception = new IOException(new SyntaxError("xport RankQuery is required for xsort: rq={!xport}"));
+    // There is a bailout in SolrIndexSearcher.getDocListNC when there are _no_ docs in the index at all.
+    // if (lastDocRequested <= 0) {
+    // That causes the totalHits and export entries in the context to _not_ get set.
+    // The only time that really matters is when we search against an _empty_ set. That's too obscure
+    // a condition to handle as part of this patch, if someone wants to pursue it it can be reproduced with:
+    // ant test  -Dtestcase=StreamingTest -Dtests.method=testAllValidExportTypes -Dtests.seed=10F13879D0D1D6AD -Dtests.slow=true -Dtests.locale=es-PA -Dtests.timezone=America/Bahia_Banderas -Dtests.asserts=true -Dtests.file.encoding=ISO-8859-1
+    // You'll have to uncomment the if below to hit the null pointer exception.
+    // This is such an unusual case (i.e. an empty index) that catching this concdition here is probably OK.
+    // This came to light in the very artifical case of indexing a single doc to Cloud.
+    int totalHits = 0;
+    FixedBitSet[] sets = null;
+    if (req.getContext().get("totalHits") != null) {
+      totalHits = ((Integer)req.getContext().get("totalHits")).intValue();
+      sets = (FixedBitSet[]) req.getContext().get("export");
+      if (sets == null) {
+        exception = new IOException(new SyntaxError("xport RankQuery is required for xsort: rq={!xport}"));
+      }
     }
-
-    int totalHits = th.intValue();
     SolrParams params = req.getParams();
     String fl = params.get("fl");
 
@@ -132,7 +145,7 @@ public class SortingResponseWriter implements QueryResponseWriter {
 
     try {
       fieldWriters = getFieldWriters(fields, req.getSearcher());
-    }catch(Exception e) {
+    } catch (Exception e) {
       exception = e;
     }
 
@@ -309,8 +322,21 @@ public class SortingResponseWriter implements QueryResponseWriter {
         } else {
           writers[i] = new StringFieldWriter(field, fieldType);
         }
-      } else {
-        throw new IOException("Export fields must either be one of the following types: int,float,long,double,string");
+      } else if (fieldType instanceof TrieDateField) {
+        if (multiValued) {
+          writers[i] = new MultiFieldWriter(field, fieldType, false);
+        } else {
+          writers[i] = new DateFieldWriter(field);
+        }
+      } else if(fieldType instanceof BoolField) {
+        if(multiValued) {
+          writers[i] = new MultiFieldWriter(field, fieldType, true);
+        } else {
+          writers[i] = new BoolFieldWriter(field, fieldType);
+        }
+      }
+      else {
+        throw new IOException("Export fields must either be one of the following types: int,float,long,double,string,date,boolean");
       }
     }
     return writers;
@@ -362,8 +388,25 @@ public class SortingResponseWriter implements QueryResponseWriter {
         } else {
           sortValues[i] = new StringValue(vals, field, new IntAsc());
         }
+      } else if (ft instanceof TrieDateField) {
+        if (reverse) {
+          sortValues[i] = new LongValue(field, new LongDesc());
+        } else {
+          sortValues[i] = new LongValue(field, new LongAsc());
+        }
+      } else if (ft instanceof BoolField) {
+        // This is a bit of a hack, but since the boolean field stores ByteRefs, just like Strings
+        // _and_ since "F" happens to sort before "T" (thus false sorts "less" than true)
+        // we can just use the existing StringValue here.
+        LeafReader reader = searcher.getLeafReader();
+        SortedDocValues vals =  reader.getSortedDocValues(field);
+        if(reverse) {
+          sortValues[i] = new StringValue(vals, field, new IntDesc());
+        } else {
+          sortValues[i] = new StringValue(vals, field, new IntAsc());
+        }
       } else {
-        throw new IOException("Sort fields must be one of the following types: int,float,long,double,string");
+        throw new IOException("Sort fields must be one of the following types: int,float,long,double,string,date,boolean");
       }
     }
 
@@ -1296,6 +1339,65 @@ public class SortingResponseWriter implements QueryResponseWriter {
     }
   }
 
+  class DateFieldWriter extends FieldWriter {
+    private String field;
+
+    public DateFieldWriter(String field) {
+      this.field = field;
+    }
+
+    public boolean write(int docId, LeafReader reader, Writer out, int fieldIndex) throws IOException {
+      NumericDocValues vals = DocValues.getNumeric(reader, this.field);
+      long val = vals.get(docId);
+
+      if (fieldIndex > 0) {
+        out.write(',');
+      }
+      out.write('"');
+      out.write(this.field);
+      out.write('"');
+      out.write(':');
+      out.write('"');
+      writeStr(new Date(val).toInstant().toString(), out);
+      out.write('"');
+      return true;
+    }
+  }
+
+  class BoolFieldWriter extends FieldWriter {
+    private String field;
+    private FieldType fieldType;
+    private CharsRefBuilder cref = new CharsRefBuilder();
+
+    public BoolFieldWriter(String field, FieldType fieldType) {
+      this.field = field;
+      this.fieldType = fieldType;
+    }
+
+    public boolean write(int docId, LeafReader reader, Writer out, int fieldIndex) throws IOException {
+      SortedDocValues vals = DocValues.getSorted(reader, this.field);
+      int ord = vals.getOrd(docId);
+      if(ord == -1) {
+        return false;
+      }
+
+      BytesRef ref = vals.lookupOrd(ord);
+      fieldType.indexedToReadable(ref, cref);
+
+      if (fieldIndex > 0) {
+        out.write(',');
+      }
+      out.write('"');
+      out.write(this.field);
+      out.write('"');
+      out.write(':');
+      //out.write('"');
+      writeStr(cref.toString(), out);
+      //out.write('"');
+      return true;
+    }
+  }
+
   class FloatFieldWriter extends FieldWriter {
     private String field;
 
@@ -1614,4 +1716,4 @@ public class SortingResponseWriter implements QueryResponseWriter {
       return (Object[]) heap;
     }
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/java/org/apache/solr/schema/BoolField.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/BoolField.java b/solr/core/src/java/org/apache/solr/schema/BoolField.java
index 01161e7..1ecdb59 100644
--- a/solr/core/src/java/org/apache/solr/schema/BoolField.java
+++ b/solr/core/src/java/org/apache/solr/schema/BoolField.java
@@ -17,11 +17,16 @@
 package org.apache.solr.schema;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.Tokenizer;
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
+import org.apache.lucene.document.SortedDocValuesField;
+import org.apache.lucene.document.SortedSetDocValuesField;
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.IndexableField;
 import org.apache.lucene.index.LeafReaderContext;
@@ -123,7 +128,11 @@ public class BoolField extends PrimitiveFieldType {
 
   @Override
   public String toExternal(IndexableField f) {
-    return indexedToReadable(f.stringValue());
+    if (f.binaryValue() == null) {
+      return null;
+    }
+
+    return indexedToReadable(f.binaryValue().utf8ToString());
   }
 
   @Override
@@ -144,7 +153,7 @@ public class BoolField extends PrimitiveFieldType {
 
   private static final CharsRef TRUE = new CharsRef("true");
   private static final CharsRef FALSE = new CharsRef("false");
-  
+
   @Override
   public CharsRef indexedToReadable(BytesRef input, CharsRefBuilder charsRef) {
     if (input.length > 0 && input.bytes[input.offset] == 'T') {
@@ -169,6 +178,36 @@ public class BoolField extends PrimitiveFieldType {
   public Object unmarshalSortValue(Object value) {
     return unmarshalStringSortValue(value);
   }
+
+  @Override
+  public List<IndexableField> createFields(SchemaField field, Object value, float boost) {
+    IndexableField fval = createField(field, value, boost);
+
+    if (field.hasDocValues()) {
+      IndexableField docval;
+      final BytesRef bytes = new BytesRef(toInternal(value.toString()));
+      if (field.multiValued()) {
+        docval = new SortedSetDocValuesField(field.getName(), bytes);
+      } else {
+        docval = new SortedDocValuesField(field.getName(), bytes);
+      }
+
+      // Only create a list of we have 2 values...
+      if (fval != null) {
+        List<IndexableField> fields = new ArrayList<>(2);
+        fields.add(fval);
+        fields.add(docval);
+        return fields;
+      }
+
+      fval = docval;
+    }
+    return Collections.singletonList(fval);
+  }
+
+  @Override
+  public void checkSchemaField(final SchemaField field) {
+  }
 }
 
 // TODO - this can be much more efficient - use FixedBitSet or Bits

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
index 33d616e..213f758 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
@@ -112,6 +112,7 @@ import org.apache.solr.request.LocalSolrQueryRequest;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestInfo;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.BoolField;
 import org.apache.solr.schema.EnumField;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.schema.SchemaField;
@@ -841,8 +842,15 @@ public class SolrIndexSearcher extends IndexSearcher implements Closeable, SolrI
             break;
           case SORTED:
             SortedDocValues sdv = leafReader.getSortedDocValues(fieldName);
-            if (sdv.getOrd(docid) >= 0) {
-              doc.addField(fieldName, sdv.get(docid).utf8ToString());
+            int ord = sdv.getOrd(docid);
+            if (ord >= 0) {
+              // Special handling for Boolean fields since they're stored as 'T' and 'F'.
+              if (schemaField.getType() instanceof BoolField) {
+                final BytesRef bRef = sdv.lookupOrd(ord);
+                doc.addField(fieldName, schemaField.getType().toObject(schemaField, bRef));
+              } else {
+                doc.addField(fieldName, sdv.get(docid).utf8ToString());
+              }
             }
             break;
           case SORTED_NUMERIC:

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/test-files/solr/collection1/conf/schema-docValues.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema-docValues.xml b/solr/core/src/test-files/solr/collection1/conf/schema-docValues.xml
index 59fe99a..c7b7de8 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema-docValues.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema-docValues.xml
@@ -61,7 +61,8 @@
   <field name="longdv" type="long" indexed="false" stored="false" docValues="true" default="4"/>
   <field name="datedv" type="date" indexed="false" stored="false" docValues="true" default="1995-12-31T23:59:59.999Z"/>
 
-  <field name="stringdv" type="string" indexed="false" stored="false" docValues="true" default="solr"/>
+  <field name="stringdv" type="string" indexed="false" stored="false" docValues="true" default="solr" />
+  <field name="booldv" type="boolean" indexed="false" stored="false" docValues="true" default="true" />
 
   <field name="floatdvs" type="float" indexed="false" stored="false" docValues="true" default="1"/>
   <field name="intdvs" type="int" indexed="false" stored="false" docValues="true" default="2"/>
@@ -69,7 +70,7 @@
   <field name="longdvs" type="long" indexed="false" stored="false" docValues="true" default="4"/>
   <field name="datedvs" type="date" indexed="false" stored="false" docValues="true" default="1995-12-31T23:59:59.999Z"/>
   <field name="stringdvs" type="string" indexed="false" stored="false" docValues="true" default="solr"/>
-
+  <field name="booldvs" type="boolean" indexed="false" stored="false" docValues="true" default="true"/>
 
   <uniqueKey>id</uniqueKey>
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMissing.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMissing.xml b/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMissing.xml
index 2204749..ac319e2 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMissing.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMissing.xml
@@ -25,6 +25,7 @@
   <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
   <fieldType name="date" class="solr.TrieDateField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
   <fieldType name="string" class="solr.StrField"/>
+  <fieldType name="boolean" class="solr.BoolField"/>
 
   <field name="id" type="string" required="true"/>
 
@@ -61,6 +62,13 @@
          sortMissingLast="true"/>
 
 
+  <field name="booldv" type="boolean" indexed="false" stored="false" docValues="true"/>
+  <field name="booldv_missingfirst" type="boolean" indexed="false" stored="false" docValues="true"
+         sortMissingFirst="true"/>
+  <field name="booldv_missinglast" type="boolean" indexed="false" stored="false" docValues="true"
+         sortMissingLast="true"/>
+
+
   <dynamicField name="*_floatdv" type="float" indexed="false" stored="false" docValues="true"/>
   <dynamicField name="*_floatdv_missingfirst" type="float" indexed="false" stored="false" docValues="true"
                 sortMissingFirst="true"/>
@@ -97,6 +105,11 @@
   <dynamicField name="*_stringdv_missinglast" type="string" indexed="false" stored="false" docValues="true"
                 sortMissingLast="true"/>
 
+  <dynamicField name="*_booldv" type="boolean" indexed="false" stored="false" docValues="true"/>
+  <dynamicField name="*_booldv_missingfirst" type="boolean" indexed="false" stored="false" docValues="true"
+                sortMissingFirst="true"/>
+  <dynamicField name="*_booldv_missinglast" type="boolean" indexed="false" stored="false" docValues="true"
+                sortMissingLast="true"/>
 
   <uniqueKey>id</uniqueKey>
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMulti.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMulti.xml b/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMulti.xml
index 93a8588..81c7836 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMulti.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema-docValuesMulti.xml
@@ -42,6 +42,7 @@
   <field name="datedv" type="date" indexed="false" stored="false" docValues="true" multiValued="true"/>
 
   <field name="stringdv" type="string" indexed="false" stored="false" docValues="true" multiValued="true"/>
+  <field name="booldv" type="boolean" indexed="false" stored="false" docValues="true" multiValued="true"/>
 
   <uniqueKey>id</uniqueKey>
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/test-files/solr/collection1/conf/schema15.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema15.xml b/solr/core/src/test-files/solr/collection1/conf/schema15.xml
index 82faa6a..d545149 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema15.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema15.xml
@@ -452,6 +452,7 @@
   <field name="bind" type="boolean" indexed="true" stored="false"/>
   <field name="bsto" type="boolean" indexed="false" stored="true"/>
   <field name="bindsto" type="boolean" indexed="true" stored="true"/>
+  <field name="bindstom" type="boolean" indexed="true" stored="true" multiValued="true"/>
   <field name="isto" type="int" indexed="false" stored="true"/>
   <field name="iind" type="int" indexed="true" stored="false"/>
   <field name="ssto" type="string" indexed="false" stored="true"/>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/test/org/apache/solr/schema/DocValuesMissingTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/schema/DocValuesMissingTest.java b/solr/core/src/test/org/apache/solr/schema/DocValuesMissingTest.java
index 847130b..04d38fd 100644
--- a/solr/core/src/test/org/apache/solr/schema/DocValuesMissingTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/DocValuesMissingTest.java
@@ -18,6 +18,7 @@ package org.apache.solr.schema;
 
 import org.apache.solr.SolrTestCaseJ4;
 import org.junit.BeforeClass;
+import org.junit.Test;
 
 /**
  * Tests things like sorting on docvalues with missing values
@@ -123,239 +124,290 @@ public class DocValuesMissingTest extends SolrTestCaseJ4 {
   }
 
   /** float with default lucene sort (treats as 0) */
+  @Test
   public void testFloatSort() throws Exception {
     checkSortMissingDefault("floatdv", "-1.3", "4.2");
   }
   /** dynamic float with default lucene sort (treats as 0) */
+  @Test
   public void testDynFloatSort() throws Exception {
     checkSortMissingDefault("dyn_floatdv", "-1.3", "4.2");
   }
 
   /** float with sort missing always first */
+  @Test
   public void testFloatSortMissingFirst() throws Exception {
     checkSortMissingFirst("floatdv_missingfirst", "-1.3", "4.2");
   }
   /** dynamic float with sort missing always first */
+  @Test
   public void testDynFloatSortMissingFirst() throws Exception {
     checkSortMissingFirst("dyn_floatdv_missingfirst", "-1.3", "4.2");
   }
 
   /** float with sort missing always last */
+  @Test
   public void testFloatSortMissingLast() throws Exception {
     checkSortMissingLast("floatdv_missinglast", "-1.3", "4.2");
   }
   /** dynamic float with sort missing always last */
+  @Test
   public void testDynFloatSortMissingLast() throws Exception {
     checkSortMissingLast("dyn_floatdv_missinglast", "-1.3", "4.2");
   }
   
   /** float function query based on missing */
+  @Test
   public void testFloatMissingFunction() throws Exception {
     checkSortMissingFunction("floatdv", "-1.3", "4.2");
   }
   /** dyanmic float function query based on missing */
+  @Test
   public void testDynFloatMissingFunction() throws Exception {
     checkSortMissingFunction("dyn_floatdv", "-1.3", "4.2");
   }
   
   /** float missing facet count */
+  @Test
   public void testFloatMissingFacet() throws Exception {
     checkSortMissingFacet("floatdv", "-1.3", "4.2");
   }
   /** dynamic float missing facet count */
+  @Test
   public void testDynFloatMissingFacet() throws Exception {
     checkSortMissingFacet("dyn_floatdv", "-1.3", "4.2");
   }
 
   /** int with default lucene sort (treats as 0) */
+  @Test
   public void testIntSort() throws Exception {
     checkSortMissingDefault("intdv", "-1", "4");
   }
   /** dynamic int with default lucene sort (treats as 0) */
+  @Test
   public void testDynIntSort() throws Exception {
     checkSortMissingDefault("dyn_intdv", "-1", "4");
   }
   
   /** int with sort missing always first */
+  @Test
   public void testIntSortMissingFirst() throws Exception {
     checkSortMissingFirst("intdv_missingfirst", "-1", "4");
   }
   /** dynamic int with sort missing always first */
+  @Test
   public void testDynIntSortMissingFirst() throws Exception {
     checkSortMissingFirst("dyn_intdv_missingfirst", "-1", "4");
   }
   
   /** int with sort missing always last */
+  @Test
   public void testIntSortMissingLast() throws Exception {
     checkSortMissingLast("intdv_missinglast", "-1", "4");
   }
   /** dynamic int with sort missing always last */
+  @Test
   public void testDynIntSortMissingLast() throws Exception {
     checkSortMissingLast("dyn_intdv_missinglast", "-1", "4");
   }
   
   /** int function query based on missing */
+  @Test
   public void testIntMissingFunction() throws Exception {
     checkSortMissingFunction("intdv", "-1", "4");
   }
   /** dynamic int function query based on missing */
+  @Test
   public void testDynIntMissingFunction() throws Exception {
     checkSortMissingFunction("dyn_intdv", "-1", "4");
   }
   
   /** int missing facet count */
+  @Test
   public void testIntMissingFacet() throws Exception {
     checkSortMissingFacet("intdv", "-1", "4");
   }
   /** dynamic int missing facet count */
+  @Test
   public void testDynIntMissingFacet() throws Exception {
     checkSortMissingFacet("dyn_intdv", "-1", "4");
   }
   
   /** double with default lucene sort (treats as 0) */
+  @Test
   public void testDoubleSort() throws Exception {
     checkSortMissingDefault("doubledv", "-1.3", "4.2");
   }
   /** dynamic double with default lucene sort (treats as 0) */
+  @Test
   public void testDynDoubleSort() throws Exception {
     checkSortMissingDefault("dyn_doubledv", "-1.3", "4.2");
   }
   
   /** double with sort missing always first */
+  @Test
   public void testDoubleSortMissingFirst() throws Exception {
     checkSortMissingFirst("doubledv_missingfirst", "-1.3", "4.2");
   }
   /** dynamic double with sort missing always first */
+  @Test
   public void testDynDoubleSortMissingFirst() throws Exception {
     checkSortMissingFirst("dyn_doubledv_missingfirst", "-1.3", "4.2");
   }
 
   /** double with sort missing always last */
+  @Test
   public void testDoubleSortMissingLast() throws Exception {
     checkSortMissingLast("doubledv_missinglast", "-1.3", "4.2");
   }
   /** dynamic double with sort missing always last */
+  @Test
   public void testDynDoubleSortMissingLast() throws Exception {
     checkSortMissingLast("dyn_doubledv_missinglast", "-1.3", "4.2");
   }
   
   /** double function query based on missing */
+  @Test
   public void testDoubleMissingFunction() throws Exception {
     checkSortMissingFunction("doubledv", "-1.3", "4.2");
   }
   /** dyanmic double function query based on missing */
+  @Test
   public void testDynDoubleMissingFunction() throws Exception {
     checkSortMissingFunction("dyn_doubledv", "-1.3", "4.2");
   }
   
   /** double missing facet count */
+  @Test
   public void testDoubleMissingFacet() throws Exception {
     checkSortMissingFacet("doubledv", "-1.3", "4.2");
   }
   /** dynamic double missing facet count */
+  @Test
   public void testDynDoubleMissingFacet() throws Exception {
     checkSortMissingFacet("dyn_doubledv", "-1.3", "4.2");
   }
   
   /** long with default lucene sort (treats as 0) */
+  @Test
   public void testLongSort() throws Exception {
     checkSortMissingDefault("longdv", "-1", "4");
   }
   /** dynamic long with default lucene sort (treats as 0) */
+  @Test
   public void testDynLongSort() throws Exception {
     checkSortMissingDefault("dyn_longdv", "-1", "4");
   }
 
   /** long with sort missing always first */
+  @Test
   public void testLongSortMissingFirst() throws Exception {
     checkSortMissingFirst("longdv_missingfirst", "-1", "4");
   }
   /** dynamic long with sort missing always first */
+  @Test
   public void testDynLongSortMissingFirst() throws Exception {
     checkSortMissingFirst("dyn_longdv_missingfirst", "-1", "4");
   }
 
   /** long with sort missing always last */
+  @Test
   public void testLongSortMissingLast() throws Exception {
     checkSortMissingLast("longdv_missinglast", "-1", "4");
   }
   /** dynamic long with sort missing always last */
+  @Test
   public void testDynLongSortMissingLast() throws Exception {
     checkSortMissingLast("dyn_longdv_missinglast", "-1", "4");
   }
   
   /** long function query based on missing */
+  @Test
   public void testLongMissingFunction() throws Exception {
     checkSortMissingFunction("longdv", "-1", "4");
   }
   /** dynamic long function query based on missing */
+  @Test
   public void testDynLongMissingFunction() throws Exception {
     checkSortMissingFunction("dyn_longdv", "-1", "4");
   }
   
   /** long missing facet count */
+  @Test
   public void testLongMissingFacet() throws Exception {
     checkSortMissingFacet("longdv", "-1", "4");
   }
   /** dynamic long missing facet count */
+  @Test
   public void testDynLongMissingFacet() throws Exception {
     checkSortMissingFacet("dyn_longdv", "-1", "4");
   }
   
   /** date with default lucene sort (treats as 1970) */
+  @Test
   public void testDateSort() throws Exception {
     checkSortMissingDefault("datedv", "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   /** dynamic date with default lucene sort (treats as 1970) */
+  @Test
   public void testDynDateSort() throws Exception {
     checkSortMissingDefault("dyn_datedv", "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   
   /** date with sort missing always first */
+  @Test
   public void testDateSortMissingFirst() throws Exception {
     checkSortMissingFirst("datedv_missingfirst", 
                           "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   /** dynamic date with sort missing always first */
+  @Test
   public void testDynDateSortMissingFirst() throws Exception {
     checkSortMissingFirst("dyn_datedv_missingfirst", 
                           "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   
   /** date with sort missing always last */
+  @Test
   public void testDateSortMissingLast() throws Exception {
     checkSortMissingLast("datedv_missinglast", 
                           "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   /** dynamic date with sort missing always last */
+  @Test
   public void testDynDateSortMissingLast() throws Exception {
     checkSortMissingLast("dyn_datedv_missinglast", 
                          "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   
   /** date function query based on missing */
+  @Test
   public void testDateMissingFunction() throws Exception {
     checkSortMissingFunction("datedv", 
                              "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   /** dynamic date function query based on missing */
+  @Test
   public void testDynDateMissingFunction() throws Exception {
     checkSortMissingFunction("dyn_datedv", 
                              "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   
   /** date missing facet count */
+  @Test
   public void testDateMissingFacet() throws Exception {
     checkSortMissingFacet("datedv", 
                           "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   /** dynamic date missing facet count */
+  @Test
   public void testDynDateMissingFacet() throws Exception {
     checkSortMissingFacet("dyn_datedv", 
                           "1900-12-31T23:59:59.999Z", "2005-12-31T23:59:59.999Z");
   }
   
   /** string (and dynamic string) with default lucene sort (treats as "") */
+  @Test
   public void testStringSort() throws Exception {
 
     // note: cant use checkSortMissingDefault because 
@@ -377,33 +429,40 @@ public class DocValuesMissingTest extends SolrTestCaseJ4 {
   }
   
   /** string with sort missing always first */
+  @Test
   public void testStringSortMissingFirst() throws Exception {
     checkSortMissingFirst("stringdv_missingfirst", "a", "z");
   }
   /** dynamic string with sort missing always first */
+  @Test
   public void testDynStringSortMissingFirst() throws Exception {
     checkSortMissingFirst("dyn_stringdv_missingfirst", "a", "z");
   }
   
   /** string with sort missing always last */
+  @Test
   public void testStringSortMissingLast() throws Exception {
     checkSortMissingLast("stringdv_missinglast", "a", "z");
   }
   /** dynamic string with sort missing always last */
+  @Test
   public void testDynStringSortMissingLast() throws Exception {
     checkSortMissingLast("dyn_stringdv_missinglast", "a", "z");
   }
 
   /** string function query based on missing */
+  @Test
   public void testStringMissingFunction() throws Exception {
     checkSortMissingFunction("stringdv", "a", "z");
   }
   /** dynamic string function query based on missing */
+  @Test
   public void testDynStringMissingFunction() throws Exception {
     checkSortMissingFunction("dyn_stringdv", "a", "z");
   }
   
   /** string missing facet count */
+  @Test
   public void testStringMissingFacet() throws Exception {
     assertU(adoc("id", "0")); // missing
     assertU(adoc("id", "1")); // missing
@@ -415,4 +474,74 @@ public class DocValuesMissingTest extends SolrTestCaseJ4 {
         "//lst[@name='facet_fields']/lst[@name='stringdv']/int[@name='z'][.=1]",
         "//lst[@name='facet_fields']/lst[@name='stringdv']/int[.=2]");
   }
+
+  /** bool (and dynamic bool) with default lucene sort (treats as "") */
+  @Test
+  public void testBoolSort() throws Exception {
+    // note: cant use checkSortMissingDefault because 
+    // nothing sorts lower then the default of "" and
+    // bool fields are, at root, string fields.
+    for (String field : new String[] {"booldv","dyn_booldv"}) {
+      assertU(adoc("id", "0")); // missing
+      assertU(adoc("id", "1", field, "false"));
+      assertU(adoc("id", "2", field, "true"));
+      assertU(commit());
+      assertQ(req("q", "*:*", "sort", field+" asc"),
+          "//result/doc[1]/str[@name='id'][.=0]",
+          "//result/doc[2]/str[@name='id'][.=1]",
+          "//result/doc[3]/str[@name='id'][.=2]");
+      assertQ(req("q", "*:*", "sort", field+" desc"),
+          "//result/doc[1]/str[@name='id'][.=2]",
+          "//result/doc[2]/str[@name='id'][.=1]",
+          "//result/doc[3]/str[@name='id'][.=0]");
+    }
+  }
+
+  /** bool with sort missing always first */
+  @Test
+  public void testBoolSortMissingFirst() throws Exception {
+    checkSortMissingFirst("booldv_missingfirst", "false", "ture");
+  }
+  /** dynamic bool with sort missing always first */
+  @Test
+  public void testDynBoolSortMissingFirst() throws Exception {
+    checkSortMissingFirst("dyn_booldv_missingfirst", "false", "true");
+  }
+
+  /** bool with sort missing always last */
+  @Test
+  public void testBoolSortMissingLast() throws Exception {
+    checkSortMissingLast("booldv_missinglast", "false", "true");
+  }
+  /** dynamic bool with sort missing always last */
+  @Test
+  public void testDynBoolSortMissingLast() throws Exception {
+    checkSortMissingLast("dyn_booldv_missinglast", "false", "true");
+  }
+
+  /** bool function query based on missing */
+  @Test
+  public void testBoolMissingFunction() throws Exception {
+    checkSortMissingFunction("booldv", "false", "true");
+  }
+  /** dynamic bool function query based on missing */
+  @Test
+  public void testDynBoolMissingFunction() throws Exception {
+    checkSortMissingFunction("dyn_booldv", "false", "true");
+  }
+
+  /** bool missing facet count */
+  @Test
+  public void testBoolMissingFacet() throws Exception {
+    assertU(adoc("id", "0")); // missing
+    assertU(adoc("id", "1")); // missing
+    assertU(adoc("id", "2", "booldv", "false"));
+    assertU(adoc("id", "3", "booldv", "true"));
+    assertU(commit());
+    assertQ(req("q", "*:*", "facet", "true", "facet.field", "booldv", "facet.mincount", "1", "facet.missing", "true"),
+        "//lst[@name='facet_fields']/lst[@name='booldv']/int[@name='false'][.=1]",
+        "//lst[@name='facet_fields']/lst[@name='booldv']/int[@name='true'][.=1]",
+        "//lst[@name='facet_fields']/lst[@name='booldv']/int[.=2]");
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/test/org/apache/solr/schema/DocValuesMultiTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/schema/DocValuesMultiTest.java b/solr/core/src/test/org/apache/solr/schema/DocValuesMultiTest.java
index 7b4a5db..90c8b73 100644
--- a/solr/core/src/test/org/apache/solr/schema/DocValuesMultiTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/DocValuesMultiTest.java
@@ -25,6 +25,7 @@ import org.apache.solr.core.SolrCore;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.util.RefCounted;
 import org.junit.BeforeClass;
+import org.junit.Test;
 
 import java.io.IOException;
 
@@ -36,7 +37,7 @@ public class DocValuesMultiTest extends SolrTestCaseJ4 {
     
     // sanity check our schema meets our expectations
     final IndexSchema schema = h.getCore().getLatestSchema();
-    for (String f : new String[] {"floatdv", "intdv", "doubledv", "longdv", "datedv", "stringdv"}) {
+    for (String f : new String[] {"floatdv", "intdv", "doubledv", "longdv", "datedv", "stringdv", "booldv"}) {
       final SchemaField sf = schema.getField(f);
       assertTrue(f + " is not multiValued, test is useless, who changed the schema?",
                  sf.multiValued());
@@ -52,8 +53,11 @@ public class DocValuesMultiTest extends SolrTestCaseJ4 {
     assertU(delQ("*:*"));
   }
 
+  @Test
   public void testDocValues() throws IOException {
-    assertU(adoc("id", "1", "floatdv", "4.5", "intdv", "-1", "intdv", "3", "stringdv", "value1", "stringdv", "value2"));
+    assertU(adoc("id", "1", "floatdv", "4.5", "intdv", "-1", "intdv", "3",
+        "stringdv", "value1", "stringdv", "value2",
+        "booldv", "false", "booldv", "true"));
     assertU(commit());
     try (SolrCore core = h.getCoreInc()) {
       final RefCounted<SolrIndexSearcher> searcherRef = core.openNewSearcher(true, true);
@@ -63,6 +67,7 @@ public class DocValuesMultiTest extends SolrTestCaseJ4 {
         assertEquals(1, reader.numDocs());
         final FieldInfos infos = reader.getFieldInfos();
         assertEquals(DocValuesType.SORTED_SET, infos.fieldInfo("stringdv").getDocValuesType());
+        assertEquals(DocValuesType.SORTED_SET, infos.fieldInfo("booldv").getDocValuesType());
         assertEquals(DocValuesType.SORTED_SET, infos.fieldInfo("floatdv").getDocValuesType());
         assertEquals(DocValuesType.SORTED_SET, infos.fieldInfo("intdv").getDocValuesType());
 
@@ -71,6 +76,14 @@ public class DocValuesMultiTest extends SolrTestCaseJ4 {
         assertEquals(0, dv.nextOrd());
         assertEquals(1, dv.nextOrd());
         assertEquals(SortedSetDocValues.NO_MORE_ORDS, dv.nextOrd());
+
+        dv = reader.getSortedSetDocValues("booldv");
+        dv.setDocument(0);
+        assertEquals(0, dv.nextOrd());
+        assertEquals(1, dv.nextOrd());
+        assertEquals(SortedSetDocValues.NO_MORE_ORDS, dv.nextOrd());
+
+
       } finally {
         searcherRef.decref();
       }
@@ -80,6 +93,7 @@ public class DocValuesMultiTest extends SolrTestCaseJ4 {
   /** Tests the ability to do basic queries (without scoring, just match-only) on
    *  string docvalues fields that are not inverted (indexed "forward" only)
    */
+  @Test
   public void testStringDocValuesMatch() throws Exception {
     assertU(adoc("id", "1", "stringdv", "b"));
     assertU(adoc("id", "2", "stringdv", "a"));
@@ -123,10 +137,49 @@ public class DocValuesMultiTest extends SolrTestCaseJ4 {
         "//result/doc[1]/str[@name='id'][.=4]"
     );
   }
-  
+
+  /** Tests the ability to do basic queries (without scoring, just match-only) on
+   *  boolean docvalues fields that are not inverted (indexed "forward" only)
+   */
+  @Test
+  public void testBoolDocValuesMatch() throws Exception {
+    assertU(adoc("id", "1", "booldv", "true"));
+    assertU(adoc("id", "2", "booldv", "false"));
+    assertU(adoc("id", "3", "booldv", "true"));
+    assertU(adoc("id", "4", "booldv", "false"));
+    assertU(adoc("id", "5", "booldv", "true", "booldv", "false"));
+    assertU(commit());
+
+    // string: termquery
+    assertQ(req("q", "booldv:true", "sort", "id asc"),
+        "//*[@numFound='3']",
+        "//result/doc[1]/str[@name='id'][.=1]",
+        "//result/doc[2]/str[@name='id'][.=3]",
+        "//result/doc[3]/str[@name='id'][.=5]"
+    );
+
+    // boolean: range query, 
+    assertQ(req("q", "booldv:[false TO false]", "sort", "id asc"),
+        "//*[@numFound='3']",
+        "//result/doc[1]/str[@name='id'][.=2]",
+        "//result/doc[2]/str[@name='id'][.=4]",
+        "//result/doc[3]/str[@name='id'][.=5]");
+
+
+    assertQ(req("q", "*:*", "sort", "id asc", "rows", "10", "fl", "booldv"),
+        "//result/doc[1]/arr[@name='booldv']/bool[1][.='true']",
+        "//result/doc[2]/arr[@name='booldv']/bool[1][.='false']",
+        "//result/doc[3]/arr[@name='booldv']/bool[1][.='true']",
+        "//result/doc[4]/arr[@name='booldv']/bool[1][.='false']",
+        "//result/doc[5]/arr[@name='booldv']/bool[1][.='false']",
+        "//result/doc[5]/arr[@name='booldv']/bool[2][.='true']"
+    );
+
+  }
   /** Tests the ability to do basic queries (without scoring, just match-only) on
    *  float docvalues fields that are not inverted (indexed "forward" only)
    */
+  @Test
   public void testFloatDocValuesMatch() throws Exception {
     assertU(adoc("id", "1", "floatdv", "2"));
     assertU(adoc("id", "2", "floatdv", "-5"));
@@ -166,6 +219,7 @@ public class DocValuesMultiTest extends SolrTestCaseJ4 {
   /** Tests the ability to do basic queries (without scoring, just match-only) on
    *  double docvalues fields that are not inverted (indexed "forward" only)
    */
+  @Test
   public void testDoubleDocValuesMatch() throws Exception {
     assertU(adoc("id", "1", "doubledv", "2"));
     assertU(adoc("id", "2", "doubledv", "-5"));
@@ -201,17 +255,34 @@ public class DocValuesMultiTest extends SolrTestCaseJ4 {
             "//result/doc[1]/str[@name='id'][.=2]"
             );
   }
-  
+  @Test
   public void testDocValuesFacetingSimple() {
     // this is the random test verbatim from DocValuesTest, so it populates with the default values defined in its schema.
     for (int i = 0; i < 50; ++i) {
-      assertU(adoc("id", "" + i, "floatdv", "1", "intdv", "2", "doubledv", "3", "longdv", "4", "datedv", "1995-12-31T23:59:59.999Z"));
+      assertU(adoc("id", "" + i, "floatdv", "1", "intdv", "2", "doubledv", "3", "longdv", "4", 
+          "datedv", "1995-12-31T23:59:59.999Z",
+          "stringdv", "abc", "booldv", "true"));
     }
     for (int i = 0; i < 50; ++i) {
       if (rarely()) {
         assertU(commit()); // to have several segments
       }
-      assertU(adoc("id", "1000" + i, "floatdv", "" + i, "intdv", "" + i, "doubledv", "" + i, "longdv", "" + i, "datedv", (1900+i) + "-12-31T23:59:59.999Z", "stringdv", "abc" + i));
+      switch (i % 3) {
+        case 0:
+          assertU(adoc("id", "1000" + i, "floatdv", "" + i, "intdv", "" + i, "doubledv", "" + i, "longdv", "" + i, 
+              "datedv", (1900+i) + "-12-31T23:59:59.999Z", "stringdv", "abc" + i, "booldv", "true", "booldv", "true"));
+          break;
+        case 1:
+          assertU(adoc("id", "1000" + i, "floatdv", "" + i, "intdv", "" + i, "doubledv", "" + i, "longdv", "" + i,
+              "datedv", (1900+i) + "-12-31T23:59:59.999Z", "stringdv", "abc" + i, "booldv", "false", "booldv", "false"));
+          break;
+        case 2:
+          assertU(adoc("id", "1000" + i, "floatdv", "" + i, "intdv", "" + i, "doubledv", "" + i, "longdv", "" + i,
+              "datedv", (1900+i) + "-12-31T23:59:59.999Z", "stringdv", "abc" + i, "booldv", "true", "booldv", "false"));
+          break;
+      }
+
+
     }
     assertU(commit());
     assertQ(req("q", "*:*", "facet", "true", "rows", "0", "facet.field", "longdv", "facet.sort", "count", "facet.limit", "1"),
@@ -248,5 +319,25 @@ public class DocValuesMultiTest extends SolrTestCaseJ4 {
         "//lst[@name='datedv']/int[@name='1900-12-31T23:59:59.999Z'][.='1']");
     assertQ(req("q", "*:*", "facet", "true", "rows", "0", "facet.field", "datedv", "facet.sort", "index", "facet.offset", "33", "facet.limit", "1", "facet.mincount", "1"),
         "//lst[@name='datedv']/int[@name='1933-12-31T23:59:59.999Z'][.='1']");
+
+
+    assertQ(req("q", "*:*", "facet", "true", "rows", "0", "facet.field", "stringdv", "facet.sort", "count", "facet.limit", "1"),
+        "//lst[@name='stringdv']/int[@name='abc'][.='50']");
+    assertQ(req("q", "*:*", "facet", "true", "rows", "0", "facet.field", "stringdv", "facet.sort", "count", "facet.offset", "1", "facet.limit", "-1", "facet.mincount", "1"),
+        "//lst[@name='stringdv']/int[@name='abc1'][.='1']",
+        "//lst[@name='stringdv']/int[@name='abc13'][.='1']",
+        "//lst[@name='stringdv']/int[@name='abc19'][.='1']",
+        "//lst[@name='stringdv']/int[@name='abc49'][.='1']"
+        );
+    
+    // Even though offseting by 33, the sort order is abc1 abc11....abc2 so it throws the position in the return list off.
+    assertQ(req("q", "*:*", "facet", "true", "rows", "0", "facet.field", "stringdv", "facet.sort", "index", "facet.offset", "33", "facet.limit", "1", "facet.mincount", "1"),
+        "//lst[@name='stringdv']/int[@name='abc38'][.='1']");
+
+
+    assertQ(req("q", "*:*", "facet", "true", "rows", "0", "facet.field", "booldv", "facet.sort", "count"),
+        "//lst[@name='booldv']/int[@name='true'][.='83']",
+        "//lst[@name='booldv']/int[@name='false'][.='33']");
+
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/core/src/test/org/apache/solr/schema/DocValuesTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/schema/DocValuesTest.java b/solr/core/src/test/org/apache/solr/schema/DocValuesTest.java
index b4248da..d59d326 100644
--- a/solr/core/src/test/org/apache/solr/schema/DocValuesTest.java
+++ b/solr/core/src/test/org/apache/solr/schema/DocValuesTest.java
@@ -27,6 +27,7 @@ import org.apache.solr.core.SolrCore;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.util.RefCounted;
 import org.junit.BeforeClass;
+import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -49,7 +50,7 @@ public class DocValuesTest extends SolrTestCaseJ4 {
 
     // sanity check our schema meets our expectations
     final IndexSchema schema = h.getCore().getLatestSchema();
-    for (String f : new String[] {"floatdv", "intdv", "doubledv", "longdv", "datedv", "stringdv"}) {
+    for (String f : new String[] {"floatdv", "intdv", "doubledv", "longdv", "datedv", "stringdv", "booldv"}) {
       final SchemaField sf = schema.getField(f);
       assertFalse(f + " is multiValued, test is useless, who changed the schema?",
                   sf.multiValued());
@@ -65,6 +66,7 @@ public class DocValuesTest extends SolrTestCaseJ4 {
     assertU(delQ("*:*"));
   }
 
+  @Test
   public void testDocValues() throws IOException {
     assertU(adoc("id", "1"));
     assertU(commit());
@@ -80,17 +82,21 @@ public class DocValuesTest extends SolrTestCaseJ4 {
         assertEquals(DocValuesType.NUMERIC, infos.fieldInfo("doubledv").getDocValuesType());
         assertEquals(DocValuesType.NUMERIC, infos.fieldInfo("longdv").getDocValuesType());
         assertEquals(DocValuesType.SORTED, infos.fieldInfo("stringdv").getDocValuesType());
+        assertEquals(DocValuesType.SORTED, infos.fieldInfo("booldv").getDocValuesType());
 
         assertEquals((long) Float.floatToIntBits(1), reader.getNumericDocValues("floatdv").get(0));
         assertEquals(2L, reader.getNumericDocValues("intdv").get(0));
         assertEquals(Double.doubleToLongBits(3), reader.getNumericDocValues("doubledv").get(0));
         assertEquals(4L, reader.getNumericDocValues("longdv").get(0));
+        assertEquals("solr", reader.getSortedDocValues("stringdv").get(0).utf8ToString());
+        assertEquals("T", reader.getSortedDocValues("booldv").get(0).utf8ToString());
 
         final IndexSchema schema = core.getLatestSchema();
         final SchemaField floatDv = schema.getField("floatdv");
         final SchemaField intDv = schema.getField("intdv");
         final SchemaField doubleDv = schema.getField("doubledv");
         final SchemaField longDv = schema.getField("longdv");
+        final SchemaField boolDv = schema.getField("booldv");
 
         FunctionValues values = floatDv.getType().getValueSource(floatDv, null).getValues(null, searcher.getLeafReader().leaves().get(0));
         assertEquals(1f, values.floatVal(0), 0f);
@@ -104,6 +110,10 @@ public class DocValuesTest extends SolrTestCaseJ4 {
         values = longDv.getType().getValueSource(longDv, null).getValues(null, searcher.getLeafReader().leaves().get(0));
         assertEquals(4L, values.longVal(0));
         assertEquals(4L, values.objectVal(0));
+        
+        values = boolDv.getType().getValueSource(boolDv, null).getValues(null, searcher.getLeafReader().leaves().get(0));
+        assertEquals("true", values.strVal(0));
+        assertEquals(true, values.objectVal(0));
 
         // check reversibility of created fields
         tstToObj(schema.getField("floatdv"), -1.5f);
@@ -118,6 +128,8 @@ public class DocValuesTest extends SolrTestCaseJ4 {
         tstToObj(schema.getField("datedvs"), new Date(1000));
         tstToObj(schema.getField("stringdv"), "foo");
         tstToObj(schema.getField("stringdvs"), "foo");
+        tstToObj(schema.getField("booldv"), true);
+        tstToObj(schema.getField("booldvs"), true);
 
       } finally {
         searcherRef.decref();
@@ -132,10 +144,11 @@ public class DocValuesTest extends SolrTestCaseJ4 {
     }
   }
 
+  @Test
   public void testDocValuesSorting() {
-    assertU(adoc("id", "1", "floatdv", "2", "intdv", "3", "doubledv", "4", "longdv", "5", "datedv", "1995-12-31T23:59:59.999Z", "stringdv", "b"));
-    assertU(adoc("id", "2", "floatdv", "5", "intdv", "4", "doubledv", "3", "longdv", "2", "datedv", "1997-12-31T23:59:59.999Z", "stringdv", "a"));
-    assertU(adoc("id", "3", "floatdv", "3", "intdv", "1", "doubledv", "2", "longdv", "1", "datedv", "1996-12-31T23:59:59.999Z", "stringdv", "c"));
+    assertU(adoc("id", "1", "floatdv", "2", "intdv", "3", "doubledv", "4", "longdv", "5", "datedv", "1995-12-31T23:59:59.999Z", "stringdv", "b", "booldv", "true"));
+    assertU(adoc("id", "2", "floatdv", "5", "intdv", "4", "doubledv", "3", "longdv", "2", "datedv", "1997-12-31T23:59:59.999Z", "stringdv", "a", "booldv", "false"));
+    assertU(adoc("id", "3", "floatdv", "3", "intdv", "1", "doubledv", "2", "longdv", "1", "datedv", "1996-12-31T23:59:59.999Z", "stringdv", "c", "booldv", "true"));
     assertU(adoc("id", "4"));
     assertU(commit());
     assertQ(req("q", "*:*", "sort", "floatdv desc", "rows", "1", "fl", "id"),
@@ -146,8 +159,10 @@ public class DocValuesTest extends SolrTestCaseJ4 {
         "//int[@name='id'][.='1']");
     assertQ(req("q", "*:*", "sort", "longdv desc", "rows", "1", "fl", "id"),
         "//int[@name='id'][.='1']");
-    assertQ(req("q", "*:*", "sort", "datedv desc", "rows", "1", "fl", "id"),
-        "//int[@name='id'][.='2']");
+    assertQ(req("q", "*:*", "sort", "datedv desc", "rows", "1", "fl", "id,datedv"),
+        "//int[@name='id'][.='2']",
+        "//result/doc[1]/date[@name='datedv'][.='1997-12-31T23:59:59.999Z']"
+        );
     assertQ(req("q", "*:*", "sort", "stringdv desc", "rows", "1", "fl", "id"),
         "//int[@name='id'][.='4']");
     assertQ(req("q", "*:*", "sort", "floatdv asc", "rows", "1", "fl", "id"),
@@ -162,8 +177,17 @@ public class DocValuesTest extends SolrTestCaseJ4 {
         "//int[@name='id'][.='1']");
     assertQ(req("q", "*:*", "sort", "stringdv asc", "rows", "1", "fl", "id"),
         "//int[@name='id'][.='2']");
+    assertQ(req("q", "*:*", "sort", "booldv asc", "rows", "10", "fl", "booldv,stringdv"),
+        "//result/doc[1]/bool[@name='booldv'][.='false']",
+        "//result/doc[2]/bool[@name='booldv'][.='true']",
+        "//result/doc[3]/bool[@name='booldv'][.='true']",
+        "//result/doc[4]/bool[@name='booldv'][.='true']"
+        );
+        
+
   }
   
+  @Test
   public void testDocValuesSorting2() {
     assertU(adoc("id", "1", "doubledv", "12"));
     assertU(adoc("id", "2", "doubledv", "50.567"));
@@ -184,6 +208,7 @@ public class DocValuesTest extends SolrTestCaseJ4 {
         );
   }
 
+  @Test
   public void testDocValuesFaceting() {
     for (int i = 0; i < 50; ++i) {
       assertU(adoc("id", "" + i));
@@ -192,7 +217,20 @@ public class DocValuesTest extends SolrTestCaseJ4 {
       if (rarely()) {
         assertU(commit()); // to have several segments
       }
-      assertU(adoc("id", "1000" + i, "floatdv", "" + i, "intdv", "" + i, "doubledv", "" + i, "longdv", "" + i, "datedv", (1900+i) + "-12-31T23:59:59.999Z", "stringdv", "abc" + i));
+      switch (i % 3) {
+        case 0:
+          assertU(adoc("id", "1000" + i, "floatdv", "" + i, "intdv", "" + i, "doubledv", "" + i, "longdv", "" + i,
+              "datedv", (1900 + i) + "-12-31T23:59:59.999Z", "stringdv", "abc" + i, "booldv", "false"));
+          break;
+        case 1:
+          assertU(adoc("id", "1000" + i, "floatdv", "" + i, "intdv", "" + i, "doubledv", "" + i, "longdv", "" + i,
+              "datedv", (1900 + i) + "-12-31T23:59:59.999Z", "stringdv", "abc" + i, "booldv", "true"));
+          break;
+        case 2:
+          assertU(adoc("id", "1000" + i, "floatdv", "" + i, "intdv", "" + i, "doubledv", "" + i, "longdv", "" + i,
+              "datedv", (1900 + i) + "-12-31T23:59:59.999Z", "stringdv", "abc" + i));
+          break;
+      }
     }
     assertU(commit());
     assertQ(req("q", "*:*", "facet", "true", "rows", "0", "facet.field", "longdv", "facet.sort", "count", "facet.limit", "1"),
@@ -229,8 +267,20 @@ public class DocValuesTest extends SolrTestCaseJ4 {
         "//lst[@name='datedv']/int[@name='1900-12-31T23:59:59.999Z'][.='1']");
     assertQ(req("q", "*:*", "facet", "true", "rows", "0", "facet.field", "datedv", "facet.sort", "index", "facet.offset", "33", "facet.limit", "1", "facet.mincount", "1"),
         "//lst[@name='datedv']/int[@name='1933-12-31T23:59:59.999Z'][.='1']");
+
+    assertQ(req("q", "booldv:true"),
+        "//*[@numFound='83']");
+
+    assertQ(req("q", "booldv:false"),
+        "//*[@numFound='17']");
+
+    assertQ(req("q", "*:*", "facet", "true", "rows", "0", "facet.field", "booldv", "facet.sort", "index", "facet.mincount", "1"),
+        "//lst[@name='booldv']/int[@name='false'][.='17']",
+        "//lst[@name='booldv']/int[@name='true'][.='83']");
+
   }
 
+  @Test
   public void testDocValuesStats() {
     for (int i = 0; i < 50; ++i) {
       assertU(adoc("id", "1000" + i, "floatdv", "" + i%2, "intdv", "" + i%3, "doubledv", "" + i%4, "longdv", "" + i%5, "datedv", (1900+i%6) + "-12-31T23:59:59.999Z", "stringdv", "abc" + i%7));
@@ -293,10 +343,11 @@ public class DocValuesTest extends SolrTestCaseJ4 {
   /** Tests the ability to do basic queries (without scoring, just match-only) on
    *  docvalues fields that are not inverted (indexed "forward" only)
    */
+  @Test
   public void testDocValuesMatch() throws Exception {
-    assertU(adoc("id", "1", "floatdv", "2", "intdv", "3", "doubledv", "3.1", "longdv", "5", "datedv", "1995-12-31T23:59:59.999Z", "stringdv", "b"));
-    assertU(adoc("id", "2", "floatdv", "-5", "intdv", "4", "doubledv", "-4.3", "longdv", "2", "datedv", "1997-12-31T23:59:59.999Z", "stringdv", "a"));
-    assertU(adoc("id", "3", "floatdv", "3", "intdv", "1", "doubledv", "2.1", "longdv", "1", "datedv", "1996-12-31T23:59:59.999Z", "stringdv", "c"));
+    assertU(adoc("id", "1", "floatdv", "2", "intdv", "3", "doubledv", "3.1", "longdv", "5", "datedv", "1995-12-31T23:59:59.999Z", "stringdv", "b", "booldv", "false"));
+    assertU(adoc("id", "2", "floatdv", "-5", "intdv", "4", "doubledv", "-4.3", "longdv", "2", "datedv", "1997-12-31T23:59:59.999Z", "stringdv", "a", "booldv", "true"));
+    assertU(adoc("id", "3", "floatdv", "3", "intdv", "1", "doubledv", "2.1", "longdv", "1", "datedv", "1996-12-31T23:59:59.999Z", "stringdv", "c", "booldv", "false"));
     assertU(adoc("id", "4", "floatdv", "3", "intdv", "-1", "doubledv", "1.5", "longdv", "1", "datedv", "1996-12-31T23:59:59.999Z", "stringdv", "car"));
     assertU(commit());
 
@@ -439,8 +490,23 @@ public class DocValuesTest extends SolrTestCaseJ4 {
             "//result/doc[1]/int[@name='id'][.=2]",
             "//result/doc[2]/int[@name='id'][.=4]"
             );
+    // boolean basic queries:
+
+    assertQ(req("q", "booldv:false", "sort", "id asc"),
+        "//*[@numFound='2']",
+        "//result/doc[1]/int[@name='id'][.=1]",
+        "//result/doc[2]/int[@name='id'][.=3]"
+    );
+
+    assertQ(req("q", "booldv:true", "sort", "id asc"),
+        "//*[@numFound='2']",
+        "//result/doc[1]/int[@name='id'][.=2]",
+        "//result/doc[2]/int[@name='id'][.=4]"
+    );
+
   }
 
+  @Test
   public void testFloatAndDoubleRangeQueryRandom() throws Exception {
 
     String fieldName[] = new String[] {"floatdv", "doubledv"};
@@ -556,6 +622,7 @@ public class DocValuesTest extends SolrTestCaseJ4 {
     }
   }
 
+  @Test
   public void testFloatAndDoubleRangeQuery() throws Exception {
     String fieldName[] = new String[] {"floatdv", "doubledv"};
     String largestNegative[] = new String[] {String.valueOf(0f-Float.MIN_NORMAL), String.valueOf(0f-Double.MIN_NORMAL)};

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/solrj/src/java/org/apache/solr/client/solrj/io/Tuple.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/Tuple.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/Tuple.java
index dee19ab..2f64651 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/Tuple.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/Tuple.java
@@ -16,10 +16,12 @@
  */
 package org.apache.solr.client.solrj.io;
 
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.List;
-import java.util.Iterator;
 
 
 /**
@@ -87,6 +89,53 @@ public class Tuple implements Cloneable {
     }
   }
 
+  // Convenience method since Booleans can be pased around as Strings.
+  public Boolean getBool(Object key) {
+    Object o = this.fields.get(key);
+
+    if (o == null) {
+      return null;
+    }
+
+    if (o instanceof Boolean) {
+      return (Boolean) o;
+    } else {
+      //Attempt to parse the Boolean
+      return Boolean.parseBoolean(o.toString());
+    }
+  }
+
+  public List<Boolean> getBools(Object key) {
+    return (List<Boolean>) this.fields.get(key);
+  }
+
+  // Convenience methods since the dates are actually shipped around as Strings.
+  public Date getDate(Object key) {
+    Object o = this.fields.get(key);
+
+    if (o == null) {
+      return null;
+    }
+
+    if (o instanceof Date) {
+      return (Date) o;
+    } else {
+      //Attempt to parse the Date from a String
+      return new Date(Instant.parse(o.toString()).toEpochMilli());
+    }
+  }
+
+  public List<Date> getDates(Object key) {
+    List<String> vals = (List<String>) this.fields.get(key);
+    if (vals == null) return null;
+    
+    List<Date> ret = new ArrayList<>();
+    for (String dateStr : (List<String>) this.fields.get(key)) {
+      ret.add(new Date(Instant.parse(dateStr).toEpochMilli()));
+    }
+    return ret;
+  }
+
   public Double getDouble(Object key) {
     Object o = this.fields.get(key);
 
@@ -144,4 +193,4 @@ public class Tuple implements Cloneable {
   public void merge(Tuple other){
     fields.putAll(other.getMap());
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/solrj/src/test-files/solrj/solr/configsets/streaming/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test-files/solrj/solr/configsets/streaming/conf/schema.xml b/solr/solrj/src/test-files/solrj/solr/configsets/streaming/conf/schema.xml
index c10f6cf..34ecdcb 100644
--- a/solr/solrj/src/test-files/solrj/solr/configsets/streaming/conf/schema.xml
+++ b/solr/solrj/src/test-files/solrj/solr/configsets/streaming/conf/schema.xml
@@ -395,8 +395,19 @@
     <field name="f_multi" type="float" indexed="true" stored="true" docValues="true" multiValued="true"/>
     <field name="l_multi" type="long" indexed="true" stored="true" docValues="true" multiValued="true"/>
     <field name="d_multi" type="double" indexed="true" stored="true" docValues="true" multiValued="true"/>
+    <field name="dt_multi" type="date" indexed="true" stored="true" docValues="true" multiValued="true"/>
+    <field name="b_multi" type="boolean" indexed="true" stored="true" docValues="true" multiValued="true"/>
 
-    <field name="uuid" type="uuid" stored="true" />
+    <field name="s_sing" type="string" indexed="true" stored="true" docValues="true" multiValued="false"/>
+    <field name="i_sing" type="int" indexed="true" stored="true" docValues="true" multiValued="false"/>
+    <field name="f_sing" type="float" indexed="true" stored="true" docValues="true" multiValued="false"/>
+    <field name="l_sing" type="long" indexed="true" stored="true" docValues="true" multiValued="false"/>
+    <field name="d_sing" type="double" indexed="true" stored="true" docValues="true" multiValued="false"/>
+    <field name="dt_sing" type="tdate" indexed="true" stored="true" docValues="true" multiValued="false"/>
+    <field name="b_sing" type="boolean" indexed="true" stored="true" docValues="true" multiValued="false"/>
+
+
+  <field name="uuid" type="uuid" stored="true" />
     <field name="name" type="nametext" indexed="true" stored="true"/>
     <field name="text" type="text" indexed="true" stored="false"/>
     <field name="subject" type="text" indexed="true" stored="true"/>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/844ca4a3/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamingTest.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamingTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamingTest.java
index 1cea311..9685b74 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamingTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/StreamingTest.java
@@ -17,7 +17,9 @@
 package org.apache.solr.client.solrj.io.stream;
 
 import java.io.IOException;
+import java.time.Instant;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -1740,6 +1742,132 @@ public class StreamingTest extends SolrCloudTestCase {
 
   }
 
+  @Test
+  public void testDateBoolSorting() throws Exception {
+
+    new UpdateRequest()
+        .add(id, "0", "b_sing", "false", "dt_sing", "1981-03-04T01:02:03.78Z")
+        .add(id, "3", "b_sing", "true", "dt_sing", "1980-03-04T01:02:03.78Z")
+        .add(id, "2", "b_sing", "false", "dt_sing", "1981-04-04T01:02:03.78Z")
+        .add(id, "1", "b_sing", "true", "dt_sing", "1980-04-04T01:02:03.78Z")
+        .add(id, "4", "b_sing", "true", "dt_sing", "1980-04-04T01:02:03.78Z")
+        .commit(cluster.getSolrClient(), COLLECTION);
+
+
+    trySortWithQt("/export");
+    trySortWithQt("/select");
+  }
+  private void trySortWithQt(String which) throws Exception {
+    //Basic CloudSolrStream Test bools desc
+
+    SolrParams sParams = mapParams("q", "*:*", "qt", which, "fl", "id,b_sing", "sort", "b_sing asc,id asc");
+    CloudSolrStream stream = new CloudSolrStream(zkHost, COLLECTION, sParams);
+    try  {
+      List<Tuple> tuples = getTuples(stream);
+
+      assert (tuples.size() == 5);
+      assertOrder(tuples, 0, 2, 1, 3, 4);
+
+      //Basic CloudSolrStream Test bools desc
+      sParams = mapParams("q", "*:*", "qt", which, "fl", "id,b_sing", "sort", "b_sing desc,id desc");
+      stream = new CloudSolrStream(zkHost, COLLECTION, sParams);
+      tuples = getTuples(stream);
+
+      assert (tuples.size() == 5);
+      assertOrder(tuples, 4, 3, 1, 2, 0);
+
+      //Basic CloudSolrStream Test dates desc
+      sParams = mapParams("q", "*:*", "qt", which, "fl", "id,dt_sing", "sort", "dt_sing desc,id asc");
+      stream = new CloudSolrStream(zkHost, COLLECTION, sParams);
+      tuples = getTuples(stream);
+
+      assert (tuples.size() == 5);
+      assertOrder(tuples, 2, 0, 1, 4, 3);
+
+      //Basic CloudSolrStream Test ates desc
+      sParams = mapParams("q", "*:*", "qt", which, "fl", "id,dt_sing", "sort", "dt_sing asc,id desc");
+      stream = new CloudSolrStream(zkHost, COLLECTION, sParams);
+      tuples = getTuples(stream);
+
+      assert (tuples.size() == 5);
+      assertOrder(tuples, 3, 4, 1, 0, 2);
+    } finally {
+      if (stream != null) {
+        stream.close();
+      }
+    }
+
+  }
+
+
+  @Test
+  public void testAllValidExportTypes() throws Exception {
+
+    //Test whether all the expected types are actually returned, including booleans and dates.
+    // The contract is that the /select and /export handlers return the same format, so we can test this once each
+    // way
+    new UpdateRequest()
+        .add(id, "0", "i_sing", "11", "i_multi", "12", "i_multi", "13",
+            "l_sing", "14", "l_multi", "15", "l_multi", "16",
+            "f_sing", "1.70", "f_multi", "1.80", "f_multi", "1.90",
+            "d_sing", "1.20", "d_multi", "1.21", "d_multi", "1.22",
+            "s_sing", "single", "s_multi", "sm1", "s_multi", "sm2",
+            "dt_sing", "1980-01-02T11:11:33.89Z", "dt_multi", "1981-03-04T01:02:03.78Z", "dt_multi", "1981-05-24T04:05:06.99Z",
+            "b_sing", "true", "b_multi", "false", "b_multi", "true"
+        )
+        .commit(cluster.getSolrClient(), COLLECTION);
+
+    tryWithQt("/export");
+    tryWithQt("/select");
+  }
+  
+  // We should be getting the exact same thing back with both the export and select handlers, so test
+  private void tryWithQt(String which) throws IOException {
+    SolrParams sParams = StreamingTest.mapParams("q", "*:*", "qt", which, "fl", 
+        "id,i_sing,i_multi,l_sing,l_multi,f_sing,f_multi,d_sing,d_multi,dt_sing,dt_multi,s_sing,s_multi,b_sing,b_multi", 
+        "sort", "i_sing asc");
+    try (CloudSolrStream stream = new CloudSolrStream(zkHost, COLLECTION, sParams)) {
+
+      Tuple tuple = getTuple(stream); // All I really care about is that all the fields are returned. There's
+
+      assertTrue("Integers should be returned", tuple.getLong("i_sing") == 11L);
+      assertTrue("MV should be returned for i_multi", tuple.getLongs("i_multi").get(0) == 12);
+      assertTrue("MV should be returned for i_multi", tuple.getLongs("i_multi").get(1) == 13);
+
+      assertTrue("longs should be returned", tuple.getLong("l_sing") == 14L);
+      assertTrue("MV should be returned for l_multi", tuple.getLongs("l_multi").get(0) == 15);
+      assertTrue("MV should be returned for l_multi", tuple.getLongs("l_multi").get(1) == 16);
+
+      assertTrue("floats should be returned", tuple.getDouble("f_sing") == 1.7);
+      assertTrue("MV should be returned for f_multi", tuple.getDoubles("f_multi").get(0) == 1.8);
+      assertTrue("MV should be returned for f_multi", tuple.getDoubles("f_multi").get(1) == 1.9);
+
+      assertTrue("doubles should be returned", tuple.getDouble("d_sing") == 1.2);
+      assertTrue("MV should be returned for d_multi", tuple.getDoubles("d_multi").get(0) == 1.21);
+      assertTrue("MV should be returned for d_multi", tuple.getDoubles("d_multi").get(1) == 1.22);
+
+      assertTrue("Strings should be returned", tuple.getString("s_sing").equals("single"));
+      assertTrue("MV should be returned for s_multi", tuple.getStrings("s_multi").get(0).equals("sm1"));
+      assertTrue("MV should be returned for s_multi", tuple.getStrings("s_multi").get(1).equals("sm2"));
+
+      assertTrue("Dates should be returned as Strings", tuple.getString("dt_sing").equals("1980-01-02T11:11:33.890Z"));
+      assertTrue("MV dates should be returned as Strings for dt_multi", tuple.getStrings("dt_multi").get(0).equals("1981-03-04T01:02:03.780Z"));
+      assertTrue("MV dates should be returned as Strings for dt_multi", tuple.getStrings("dt_multi").get(1).equals("1981-05-24T04:05:06.990Z"));
+
+      // Also test native type conversion
+      Date dt = new Date(Instant.parse("1980-01-02T11:11:33.890Z").toEpochMilli());
+      assertTrue("Dates should be returned as Dates", tuple.getDate("dt_sing").equals(dt));
+      dt = new Date(Instant.parse("1981-03-04T01:02:03.780Z").toEpochMilli());
+      assertTrue("MV dates should be returned as Dates for dt_multi", tuple.getDates("dt_multi").get(0).equals(dt));
+      dt = new Date(Instant.parse("1981-05-24T04:05:06.990Z").toEpochMilli());
+      assertTrue("MV dates should be returned as Dates  for dt_multi", tuple.getDates("dt_multi").get(1).equals(dt));
+      
+      assertTrue("Booleans should be returned", tuple.getBool("b_sing"));
+      assertFalse("MV boolean should be returned for b_multi", tuple.getBools("b_multi").get(0));
+      assertTrue("MV boolean should be returned for b_multi", tuple.getBools("b_multi").get(1));
+    }
+
+  }
   protected List<Tuple> getTuples(TupleStream tupleStream) throws IOException {
     tupleStream.open();
     List<Tuple> tuples = new ArrayList();