You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2014/06/07 01:42:59 UTC

svn commit: r1601037 - in /lucene/dev/branches/branch_4x: ./ dev-tools/ lucene/ lucene/analysis/ lucene/analysis/common/src/java/org/apache/lucene/analysis/miscellaneous/ lucene/analysis/common/src/java/org/apache/lucene/analysis/standard/std40/ lucene...

Author: hossman
Date: Fri Jun  6 23:42:57 2014
New Revision: 1601037

URL: http://svn.apache.org/r1601037
Log:
SOLR-5285: Added a new [child ...] DocTransformer for optionally including Block-Join decendent documents inline in the results of a search (merge r1601028)

Added:
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/ResponseWriterUtil.java
      - copied, changed from r1601028, lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/ResponseWriterUtil.java
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java
      - copied, changed from r1601028, lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java
    lucene/dev/branches/branch_4x/solr/core/src/test/org/apache/solr/response/TestChildDocTransformer.java
      - copied unchanged from r1601028, lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestChildDocTransformer.java
    lucene/dev/branches/branch_4x/solr/solrj/src/test-files/solrj/javabin_backcompat_child_docs.bin
      - copied unchanged from r1601028, lucene/dev/trunk/solr/solrj/src/test-files/solrj/javabin_backcompat_child_docs.bin
Modified:
    lucene/dev/branches/branch_4x/   (props changed)
    lucene/dev/branches/branch_4x/dev-tools/   (props changed)
    lucene/dev/branches/branch_4x/lucene/   (props changed)
    lucene/dev/branches/branch_4x/lucene/BUILD.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/CHANGES.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/JRE_VERSION_MIGRATION.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/LICENSE.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/MIGRATE.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/NOTICE.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/README.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/SYSTEM_REQUIREMENTS.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/miscellaneous/Lucene47WordDelimiterFilter.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/standard/std40/ASCIITLD.jflex-macro   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/standard/std40/SUPPLEMENTARY.jflex-macro   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/standard/std40/StandardTokenizerImpl40.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/standard/std40/StandardTokenizerImpl40.jflex   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/standard/std40/UAX29URLEmailTokenizerImpl40.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/standard/std40/UAX29URLEmailTokenizerImpl40.jflex   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/common/src/java/org/apache/lucene/analysis/standard/std40/package.html   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/common/src/test/org/apache/lucene/analysis/miscellaneous/TestLucene47WordDelimiterFilter.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/analysis/icu/src/java/org/apache/lucene/collation/ICUCollationKeyFilterFactory.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/backwards/   (props changed)
    lucene/dev/branches/branch_4x/lucene/benchmark/   (props changed)
    lucene/dev/branches/branch_4x/lucene/build.xml   (props changed)
    lucene/dev/branches/branch_4x/lucene/classification/   (props changed)
    lucene/dev/branches/branch_4x/lucene/classification/build.xml   (props changed)
    lucene/dev/branches/branch_4x/lucene/classification/ivy.xml   (props changed)
    lucene/dev/branches/branch_4x/lucene/classification/src/   (props changed)
    lucene/dev/branches/branch_4x/lucene/codecs/   (props changed)
    lucene/dev/branches/branch_4x/lucene/common-build.xml   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/index/TestBackwardsCompatibility.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/index/TestIndexWriterExceptions2.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/index/index.40.cfs.zip   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/index/index.40.nocfs.zip   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/index/index.40.optimized.cfs.zip   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/index/index.40.optimized.nocfs.zip   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/search/TestSort.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/search/TestSortDocValues.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/search/TestSortRandom.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/search/TestTopFieldCollector.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/core/src/test/org/apache/lucene/search/TestTotalHitCountCollector.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/demo/   (props changed)
    lucene/dev/branches/branch_4x/lucene/expressions/   (props changed)
    lucene/dev/branches/branch_4x/lucene/facet/   (props changed)
    lucene/dev/branches/branch_4x/lucene/grouping/   (props changed)
    lucene/dev/branches/branch_4x/lucene/highlighter/   (props changed)
    lucene/dev/branches/branch_4x/lucene/ivy-ignore-conflicts.properties   (props changed)
    lucene/dev/branches/branch_4x/lucene/ivy-settings.xml   (props changed)
    lucene/dev/branches/branch_4x/lucene/ivy-versions.properties   (props changed)
    lucene/dev/branches/branch_4x/lucene/join/   (props changed)
    lucene/dev/branches/branch_4x/lucene/licenses/   (props changed)
    lucene/dev/branches/branch_4x/lucene/memory/   (props changed)
    lucene/dev/branches/branch_4x/lucene/misc/   (props changed)
    lucene/dev/branches/branch_4x/lucene/module-build.xml   (props changed)
    lucene/dev/branches/branch_4x/lucene/queries/   (props changed)
    lucene/dev/branches/branch_4x/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionQuerySort.java   (props changed)
    lucene/dev/branches/branch_4x/lucene/queryparser/   (props changed)
    lucene/dev/branches/branch_4x/lucene/replicator/   (props changed)
    lucene/dev/branches/branch_4x/lucene/sandbox/   (props changed)
    lucene/dev/branches/branch_4x/lucene/site/   (props changed)
    lucene/dev/branches/branch_4x/lucene/spatial/   (props changed)
    lucene/dev/branches/branch_4x/lucene/spatial/src/test-files/data/simple-bbox.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/spatial/src/test-files/simple-Queries-BBox.txt   (props changed)
    lucene/dev/branches/branch_4x/lucene/suggest/   (props changed)
    lucene/dev/branches/branch_4x/lucene/test-framework/   (props changed)
    lucene/dev/branches/branch_4x/lucene/test-framework/src/java/org/apache/lucene/codecs/cranky/   (props changed)
    lucene/dev/branches/branch_4x/lucene/tools/   (props changed)
    lucene/dev/branches/branch_4x/solr/   (props changed)
    lucene/dev/branches/branch_4x/solr/CHANGES.txt   (contents, props changed)
    lucene/dev/branches/branch_4x/solr/LICENSE.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/NOTICE.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/README.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/SYSTEM_REQUIREMENTS.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/build.xml   (props changed)
    lucene/dev/branches/branch_4x/solr/cloud-dev/   (props changed)
    lucene/dev/branches/branch_4x/solr/common-build.xml   (props changed)
    lucene/dev/branches/branch_4x/solr/contrib/   (props changed)
    lucene/dev/branches/branch_4x/solr/core/   (props changed)
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/TextResponseWriter.java
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/XMLWriter.java
    lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/TransformerFactory.java
    lucene/dev/branches/branch_4x/solr/core/src/test/org/apache/solr/core/TestConfig.java   (props changed)
    lucene/dev/branches/branch_4x/solr/example/   (props changed)
    lucene/dev/branches/branch_4x/solr/licenses/   (props changed)
    lucene/dev/branches/branch_4x/solr/licenses/httpclient-LICENSE-ASL.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/licenses/httpclient-NOTICE.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/licenses/httpcore-LICENSE-ASL.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/licenses/httpcore-NOTICE.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/licenses/httpmime-LICENSE-ASL.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/licenses/httpmime-NOTICE.txt   (props changed)
    lucene/dev/branches/branch_4x/solr/scripts/   (props changed)
    lucene/dev/branches/branch_4x/solr/site/   (props changed)
    lucene/dev/branches/branch_4x/solr/solrj/   (props changed)
    lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/XMLResponseParser.java
    lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/SolrDocument.java
    lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java
    lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java
    lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/common/util/TestJavaBinCodec.java
    lucene/dev/branches/branch_4x/solr/test-framework/   (props changed)
    lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
    lucene/dev/branches/branch_4x/solr/webapp/   (props changed)

Modified: lucene/dev/branches/branch_4x/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/CHANGES.txt?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/CHANGES.txt (original)
+++ lucene/dev/branches/branch_4x/solr/CHANGES.txt Fri Jun  6 23:42:57 2014
@@ -62,6 +62,13 @@ New Features
 * SOLR-6088: Add query re-ranking with the ReRankingQParserPlugin
   (Joel Bernstein)
 
+* SOLR-5285: Added a new [child ...] DocTransformer for optionally including 
+  Block-Join decendent documents inline in the results of a search.  This works 
+  independent of whether the search itself is a block-join related query and is 
+  supported by he xml, json, and javabin response formats.
+  (Varun Thacker via hossman)
+
+
 Bug Fixes
 ----------------------
 

Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/JSONResponseWriter.java Fri Jun  6 23:42:57 2014
@@ -35,6 +35,7 @@ import org.apache.solr.common.util.Simpl
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.schema.SchemaField;
 import org.apache.solr.search.ReturnFields;
+import org.apache.solr.search.SolrReturnFields;
 
 /**
  *
@@ -355,6 +356,21 @@ class JSONWriter extends TextResponseWri
         writeVal(fname, val);
       }
     }
+
+    if(doc.hasChildDocuments()) {
+      if(first == false) {
+        writeMapSeparator();
+        indent();
+      }
+      writeKey("_childDocuments_", true);
+      writeArrayOpener(doc.getChildDocumentCount());
+      List<SolrDocument> childDocs = doc.getChildDocuments();
+      ReturnFields rf = new SolrReturnFields();
+      for(int i=0; i<childDocs.size(); i++) {
+        writeSolrDocument(null, childDocs.get(i), rf, i);
+      }
+      writeArrayCloser();
+    }
     
     decLevel();
     writeMapCloser();

Copied: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/ResponseWriterUtil.java (from r1601028, lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/ResponseWriterUtil.java)
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/ResponseWriterUtil.java?p2=lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/ResponseWriterUtil.java&p1=lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/ResponseWriterUtil.java&r1=1601028&r2=1601037&rev=1601037&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/ResponseWriterUtil.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/ResponseWriterUtil.java Fri Jun  6 23:42:57 2014
@@ -17,8 +17,8 @@ package org.apache.solr.response;
  * limitations under the License.
  */
 
-import org.apache.lucene.index.StorableField;
-import org.apache.lucene.index.StoredDocument;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.document.Document;
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.schema.SchemaField;
@@ -29,12 +29,12 @@ import java.util.List;
 public class ResponseWriterUtil {
 
   /**
-   * Utility method for converting a {@link StoredDocument} from the index into a 
+   * Utility method for converting a {@link Document} from the index into a 
    * {@link SolrDocument} suitable for inclusion in a {@link SolrQueryResponse}
    */
-  public static final SolrDocument toSolrDocument( StoredDocument doc, final IndexSchema schema ) {
+  public static final SolrDocument toSolrDocument( Document doc, final IndexSchema schema ) {
     SolrDocument out = new SolrDocument();
-    for( StorableField f : doc.getFields()) {
+    for( IndexableField f : doc.getFields()) {
       // Make sure multivalued fields are represented as lists
       Object existing = out.get(f.name());
       if (existing == null) {

Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/TextResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/TextResponseWriter.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/TextResponseWriter.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/TextResponseWriter.java Fri Jun  6 23:42:57 2014
@@ -78,8 +78,6 @@ public abstract class TextResponseWriter
     returnFields = rsp.getReturnFields();
   }
 
-
-
   /** done with this ResponseWriter... make sure any buffers are flushed to writer */
   public void close() throws IOException {
     writer.flushBuffer();
@@ -226,26 +224,7 @@ public abstract class TextResponseWriter
 
   public final SolrDocument toSolrDocument( Document doc )
   {
-    SolrDocument out = new SolrDocument();
-    for( IndexableField f : doc) {
-      // Make sure multivalued fields are represented as lists
-      Object existing = out.get(f.name());
-      if (existing == null) {
-        SchemaField sf = schema.getFieldOrNull(f.name());
-        if (sf != null && sf.multiValued()) {
-          List<Object> vals = new ArrayList<>();
-          vals.add( f );
-          out.setField( f.name(), vals );
-        } 
-        else{
-          out.setField( f.name(), f );
-        }
-      }
-      else {
-        out.addField( f.name(), f );
-      }
-    }
-    return out;
+    return ResponseWriterUtil.toSolrDocument(doc, schema);
   }
   
   public final void writeDocuments(String name, ResultContext res, ReturnFields fields ) throws IOException {

Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/XMLWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/XMLWriter.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/XMLWriter.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/XMLWriter.java Fri Jun  6 23:42:57 2014
@@ -31,6 +31,7 @@ import org.apache.solr.common.util.Named
 import org.apache.solr.common.util.XML;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.search.ReturnFields;
+import org.apache.solr.search.SolrReturnFields;
 
 
 /**
@@ -198,6 +199,12 @@ public class XMLWriter extends TextRespo
       }
       writeVal(fname, val);
     }
+
+    if(doc.hasChildDocuments()) {
+      for(SolrDocument childDoc : doc.getChildDocuments()) {
+        writeSolrDocument(null, childDoc, new SolrReturnFields(), idx);
+      }
+    }
     
     decLevel();
     writer.write("</doc>");

Copied: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java (from r1601028, lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java)
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java?p2=lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java&p1=lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java&r1=1601028&r2=1601037&rev=1601037&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/ChildDocTransformerFactory.java Fri Jun  6 23:42:57 2014
@@ -23,8 +23,8 @@ import java.util.List;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.document.LazyDocument;
 import org.apache.lucene.document.StoredField;
-import org.apache.lucene.index.StorableField;
-import org.apache.lucene.index.StoredDocument;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.document.Document;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.Filter;
 import org.apache.lucene.search.Query;
@@ -153,7 +153,7 @@ class ChildDocTransformer extends Transf
         DocIterator i = children.iterator();
         while(i.hasNext()) {
           Integer childDocNum = i.next();
-          StoredDocument childDoc = context.searcher.doc(childDocNum);
+          Document childDoc = context.searcher.doc(childDocNum);
           SolrDocument solrChildDoc = ResponseWriterUtil.toSolrDocument(childDoc, schema);
 
           // TODO: future enhancement...

Modified: lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/TransformerFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/TransformerFactory.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/TransformerFactory.java (original)
+++ lucene/dev/branches/branch_4x/solr/core/src/java/org/apache/solr/response/transform/TransformerFactory.java Fri Jun  6 23:42:57 2014
@@ -47,5 +47,6 @@ public abstract class TransformerFactory
     defaultFactories.put( "value", new ValueAugmenterFactory() );
     defaultFactories.put( "docid", new DocIdAugmenterFactory() );
     defaultFactories.put( "shard", new ShardAugmenterFactory() );
+    defaultFactories.put( "child", new ChildDocTransformerFactory() );
   }
 }

Modified: lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/XMLResponseParser.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/XMLResponseParser.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/XMLResponseParser.java (original)
+++ lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/client/solrj/impl/XMLResponseParser.java Fri Jun  6 23:42:57 2014
@@ -410,6 +410,15 @@ public class XMLResponseParser extends R
             break;
           }
         }
+
+        //Nested documents
+        while( type == KnownType.DOC) {
+          doc.addChildDocument(readDocument(parser));
+          int event = parser.next();
+          if (event == XMLStreamConstants.END_ELEMENT) { //Doc ends
+            return doc;
+          }
+        }
         
         if( name == null ) {
           throw new XMLStreamException( "requires 'name' attribute: "+parser.getLocalName(), parser.getLocation() );

Modified: lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/SolrDocument.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/SolrDocument.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/SolrDocument.java (original)
+++ lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/SolrDocument.java Fri Jun  6 23:42:57 2014
@@ -23,6 +23,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -44,6 +45,8 @@ public class SolrDocument implements Map
 {
   private final Map<String,Object> _fields;
   
+  private List<SolrDocument> _childDocuments;
+  
   public SolrDocument()
   {
     _fields = new LinkedHashMap<>();
@@ -68,6 +71,10 @@ public class SolrDocument implements Map
   public void clear()
   {
     _fields.clear();
+
+    if(_childDocuments != null) {
+      _childDocuments.clear();
+    }
   }
   
   /**
@@ -359,4 +366,31 @@ public class SolrDocument implements Map
   public Collection<Object> values() {
     return _fields.values();
   }
+  
+  public void addChildDocument(SolrDocument child) {
+    if (_childDocuments == null) {
+      _childDocuments = new ArrayList<>();
+    }
+     _childDocuments.add(child);
+   }
+   
+   public void addChildDocuments(Collection<SolrDocument> childs) {
+     for (SolrDocument child : childs) {
+       addChildDocument(child);
+     }
+   }
+
+   /** Returns the list of child documents, or null if none. */
+   public List<SolrDocument> getChildDocuments() {
+     return _childDocuments;
+   }
+   
+   public boolean hasChildDocuments() {
+     boolean isEmpty = (_childDocuments == null || _childDocuments.isEmpty());
+     return !isEmpty;
+   }
+
+  public int getChildDocumentCount() {
+    return _childDocuments.size();
+  }
 }

Modified: lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java (original)
+++ lucene/dev/branches/branch_4x/solr/solrj/src/java/org/apache/solr/common/util/JavaBinCodec.java Fri Jun  6 23:42:57 2014
@@ -327,23 +327,38 @@ public class JavaBinCodec {
   }
 
   public void writeSolrDocument(SolrDocument doc) throws IOException {
+    List<SolrDocument> children = doc.getChildDocuments();
+    int sz = doc.size() + (children==null ? 0 : children.size());
     writeTag(SOLRDOC);
-    writeTag(ORDERED_MAP, doc.size());
+    writeTag(ORDERED_MAP, sz);
     for (Map.Entry<String, Object> entry : doc) {
       String name = entry.getKey();
       writeExternString(name);
       Object val = entry.getValue();
       writeVal(val);
     }
+    if (children != null) {
+      for (SolrDocument child : children) {
+        writeSolrDocument(child);
+      }
+    }
   }
 
   public SolrDocument readSolrDocument(DataInputInputStream dis) throws IOException {
-    NamedList nl = (NamedList) readVal(dis);
+    tagByte = dis.readByte();
+    int size = readSize(dis);
     SolrDocument doc = new SolrDocument();
-    for (int i = 0; i < nl.size(); i++) {
-      String name = nl.getName(i);
-      Object val = nl.getVal(i);
-      doc.setField(name, val);
+    for (int i = 0; i < size; i++) {
+      String fieldName;
+      Object obj = readVal(dis); // could be a field name, or a child document
+      if (obj instanceof SolrDocument) {
+        doc.addChildDocument((SolrDocument)obj);
+        continue;
+      } else {
+        fieldName = (String)obj;
+      }
+      Object fieldVal = readVal(dis);
+      doc.setField(fieldName, fieldVal);
     }
     return doc;
   }
@@ -409,7 +424,7 @@ public class JavaBinCodec {
       writeVal(inputField.getValue());
     }
     if (children != null) {
-      for (SolrInputDocument child : sdoc.getChildDocuments()) {
+      for (SolrInputDocument child : children) {
         writeSolrInputDocument(child);
       }
     }

Modified: lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java (original)
+++ lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java Fri Jun  6 23:42:57 2014
@@ -18,19 +18,8 @@
 package org.apache.solr.client.solrj;
 
 
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Random;
-
 import com.google.common.collect.Maps;
-
 import junit.framework.Assert;
-
 import org.apache.lucene.util.TestUtil;
 import org.apache.solr.SolrTestCaseJ4.SuppressSSL;
 import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
@@ -38,30 +27,43 @@ import org.apache.solr.client.solrj.impl
 import org.apache.solr.client.solrj.impl.ConcurrentUpdateSolrServer;
 import org.apache.solr.client.solrj.impl.HttpSolrServer;
 import org.apache.solr.client.solrj.impl.XMLResponseParser;
+import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
+import org.apache.solr.client.solrj.request.AbstractUpdateRequest.ACTION;
+import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
 import org.apache.solr.client.solrj.request.LukeRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
-import org.apache.solr.client.solrj.response.FieldStatsInfo;
 import org.apache.solr.client.solrj.request.UpdateRequest;
-import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
-import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
-import org.apache.solr.client.solrj.request.AbstractUpdateRequest.ACTION;
+import org.apache.solr.client.solrj.response.FacetField;
+import org.apache.solr.client.solrj.response.FieldStatsInfo;
 import org.apache.solr.client.solrj.response.LukeResponse;
 import org.apache.solr.client.solrj.response.PivotField;
 import org.apache.solr.client.solrj.response.QueryResponse;
-import org.apache.solr.client.solrj.response.FacetField;
 import org.apache.solr.client.solrj.response.UpdateResponse;
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
-import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.params.AnalysisParams;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.FacetParams;
+import org.apache.solr.common.util.NamedList;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
 /**
  * This should include tests against the example solr config
  * 
@@ -1259,5 +1261,189 @@ abstract public class SolrExampleTests e
         "true",
         ((NamedList) resp.getResponseHeader().get("params")).get("debug"));
   }
-  
+
+
+  @Test
+  public void testChildDoctransformer() throws IOException, SolrServerException {
+    SolrServer server = getSolrServer();
+    server.deleteByQuery("*:*");
+    server.commit();
+
+    int numRootDocs = TestUtil.nextInt(random(), 10, 100);
+    int maxDepth = TestUtil.nextInt(random(), 2, 5);
+
+    Map<String,SolrInputDocument> allDocs = new HashMap<>();
+
+    for (int i =0; i < numRootDocs; i++) {
+      server.add(genNestedDocuments(allDocs, 0, maxDepth));
+    }
+
+    server.commit();
+
+    // sanity check
+    SolrQuery q = new SolrQuery("*:*");
+    QueryResponse resp = server.query(q);
+    assertEquals("Doc count does not match", 
+                 allDocs.size(), resp.getResults().getNumFound());
+
+
+    // base check - we know there is an exact number of these root docs
+    q = new SolrQuery("level_i:0");
+    q.setFields("*", "[child parentFilter=\"level_i:0\"]");
+    resp = server.query(q);
+    assertEquals("topLevel count does not match", numRootDocs,
+                 resp.getResults().getNumFound());
+    for (SolrDocument outDoc : resp.getResults()) {
+      String docId = (String)outDoc.getFieldValue("id");
+      SolrInputDocument origDoc = allDocs.get(docId);
+      assertNotNull("docId not found: " + docId, origDoc);
+      assertEquals("kids mismatch", 
+                   origDoc.hasChildDocuments(), outDoc.hasChildDocuments());
+      if (outDoc.hasChildDocuments()) {
+        for (SolrDocument kid : outDoc.getChildDocuments()) {
+          String kidId = (String)kid.getFieldValue("id");
+          SolrInputDocument origChild = findDecendent(origDoc, kidId);
+          assertNotNull(docId + " doesn't have decendent " + kidId,
+                        origChild);
+        }
+      }
+    }
+
+    // simple check: direct verification of direct children on random docs
+    {
+      int parentLevel = TestUtil.nextInt(random(), 0, maxDepth);
+      int kidLevel = parentLevel+1;
+      String parentFilter = "level_i:" + parentLevel;
+      String childFilter = "level_i:" + kidLevel;
+      int maxKidCount = TestUtil.nextInt(random(), 1, 37);
+      
+      q = new SolrQuery("*:*");
+      q.setFilterQueries(parentFilter);
+      q.setFields("id,[child parentFilter=\"" + parentFilter +
+                  "\" childFilter=\"" + childFilter + 
+                  "\" limit=\"" + maxKidCount + "\"]");
+      resp = server.query(q);
+      for (SolrDocument outDoc : resp.getResults()) {
+        String docId = (String)outDoc.getFieldValue("id");
+        SolrInputDocument origDoc = allDocs.get(docId);
+        assertNotNull("docId not found: " + docId, origDoc);
+        assertEquals("kids mismatch", 
+                     origDoc.hasChildDocuments(), outDoc.hasChildDocuments());
+        if (outDoc.hasChildDocuments()) {
+          // since we know we are looking at our direct children
+          // we can verify the count
+          int numOrigKids = origDoc.getChildDocuments().size();
+          int numOutKids = outDoc.getChildDocuments().size();
+          assertEquals("Num kids mismatch: " + numOrigKids + "/" + maxKidCount,
+                       (maxKidCount < numOrigKids ? maxKidCount : numOrigKids),
+                       numOutKids);
+          
+          for (SolrDocument kid : outDoc.getChildDocuments()) {
+            String kidId = (String)kid.getFieldValue("id");
+            assertEquals("kid is the wrong level",
+                         kidLevel, (int)kid.getFieldValue("level_i"));
+            SolrInputDocument origChild = findDecendent(origDoc, kidId);
+            assertNotNull(docId + " doesn't have decendent " + kidId,
+                          origChild);
+          }
+        }
+      }
+    }
+
+    // fully randomized
+    // verifications are driven only by the results
+    {
+      int parentLevel = TestUtil.nextInt(random(), 0, maxDepth-1);
+      int kidLevelMin = TestUtil.nextInt(random(), parentLevel, maxDepth);
+      int kidLevelMax = TestUtil.nextInt(random(), kidLevelMin, maxDepth);
+
+      String parentFilter = "level_i:" + parentLevel;
+      String childFilter = "level_i:[" + kidLevelMin + " TO " + kidLevelMax + "]";
+      int maxKidCount = TestUtil.nextInt(random(), 1, 7);
+      
+      q = new SolrQuery("*:*");
+      if (random().nextBoolean()) {
+        String name = names[TestUtil.nextInt(random(), 0, names.length-1)];
+        q = new SolrQuery("name:" + name);
+      }
+      q.setFilterQueries(parentFilter);
+      q.setFields("id,[child parentFilter=\"" + parentFilter +
+                  "\" childFilter=\"" + childFilter + 
+                  "\" limit=\"" + maxKidCount + "\"]");
+      resp = server.query(q);
+      for (SolrDocument outDoc : resp.getResults()) {
+        String docId = (String)outDoc.getFieldValue("id");
+        SolrInputDocument origDoc = allDocs.get(docId);
+        assertNotNull("docId not found: " + docId, origDoc);
+        // we can't always assert origHasKids==outHasKids, original kids
+        // might not go deep enough for childFilter...
+        if (outDoc.hasChildDocuments()) {
+          // ...however if there are out kids, there *have* to be orig kids
+          assertTrue("orig doc had no kids at all", origDoc.hasChildDocuments());
+          for (SolrDocument kid : outDoc.getChildDocuments()) {
+            String kidId = (String)kid.getFieldValue("id");
+            int kidLevel = (int)kid.getFieldValue("level_i");
+            assertTrue("kid level to high: " + kidLevelMax + "<" + kidLevel,
+                       kidLevel <= kidLevelMax);
+            assertTrue("kid level to low: " + kidLevelMin + ">" + kidLevel,
+                       kidLevelMin <= kidLevel);
+            SolrInputDocument origChild = findDecendent(origDoc, kidId);
+            assertNotNull(docId + " doesn't have decendent " + kidId,
+                          origChild);
+          }
+        }
+      }
+    }
+  }
+
+  /** 
+   * Depth first search of a SolrInputDocument looking for a decendent by id, 
+   * returns null if it's not a decendent 
+   */
+  private SolrInputDocument findDecendent(SolrInputDocument parent, String childId) {
+    if (childId.equals(parent.getFieldValue("id"))) {
+      return parent;
+    }
+    if (! parent.hasChildDocuments() ) {
+      return null;
+    }
+    for (SolrInputDocument kid : parent.getChildDocuments()) {
+      SolrInputDocument result = findDecendent(kid, childId);
+      if (null != result) {
+        return result;
+      }
+    }
+    return null;
+  }
+
+  /** used by genNestedDocuments */
+  private int idCounter = 0;
+  /** used by genNestedDocuments */
+  private static final String[] names 
+    = new String[] { "java","pyhon","scala","ruby","clojure" };
+
+  /**
+   * recursive method for generating a document, which may also have child documents;
+   * adds all documents constructed (including decendents) to allDocs via their id 
+   */
+  private SolrInputDocument genNestedDocuments(Map<String,SolrInputDocument> allDocs, 
+                                               int thisLevel,
+                                               int maxDepth) {
+    String id = "" + (idCounter++);
+    SolrInputDocument sdoc = new SolrInputDocument();
+    allDocs.put(id, sdoc);
+
+    sdoc.addField("id", id);
+    sdoc.addField("level_i", thisLevel);
+    sdoc.addField("name", names[TestUtil.nextInt(random(), 0, names.length-1)]);
+    
+    if (0 < maxDepth) {
+      // NOTE: range include negative to increase odds of no kids
+      int numKids = TestUtil.nextInt(random(), -2, 7);
+      for(int i=0; i<numKids; i++) {
+        sdoc.addChildDocument(genNestedDocuments(allDocs, thisLevel+1, maxDepth-1));
+      }
+    }
+    return sdoc;
+  }
 }

Modified: lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/common/util/TestJavaBinCodec.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/common/util/TestJavaBinCodec.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/common/util/TestJavaBinCodec.java (original)
+++ lucene/dev/branches/branch_4x/solr/solrj/src/test/org/apache/solr/common/util/TestJavaBinCodec.java Fri Jun  6 23:42:57 2014
@@ -44,10 +44,13 @@ import org.junit.Test;
 
 public class TestJavaBinCodec extends SolrTestCaseJ4 {
 
- private static final String SOLRJ_JAVABIN_BACKCOMPAT_BIN = "/solrj/javabin_backcompat.bin";
-private final String BIN_FILE_LOCATION = "./solr/solrj/src/test-files/solrj/javabin_backcompat.bin";
+  private static final String SOLRJ_JAVABIN_BACKCOMPAT_BIN = "/solrj/javabin_backcompat.bin";
+  private final String BIN_FILE_LOCATION = "./solr/solrj/src/test-files/solrj/javabin_backcompat.bin";
 
- public void testStrings() throws Exception {
+  private static final String SOLRJ_JAVABIN_BACKCOMPAT_BIN_CHILD_DOCS = "/solrj/javabin_backcompat_child_docs.bin";
+  private final String BIN_FILE_LOCATION_CHILD_DOCS = "./solr/solrj/src/test-files/solrj/javabin_backcompat_child_docs.bin";
+
+  public void testStrings() throws Exception {
     JavaBinCodec javabin = new JavaBinCodec();
     for (int i = 0; i < 10000 * RANDOM_MULTIPLIER; i++) {
       String s = TestUtil.randomUnicodeString(random());
@@ -59,6 +62,29 @@ private final String BIN_FILE_LOCATION =
     }
   }
 
+  private SolrDocument generateSolrDocumentWithChildDocs() {
+    SolrDocument parentDocument = new SolrDocument();
+    parentDocument.addField("id", "1");
+    parentDocument.addField("subject", "parentDocument");
+
+    SolrDocument childDocument = new SolrDocument();
+    childDocument.addField("id", "2");
+    childDocument.addField("cat", "foo");
+
+    SolrDocument secondKid = new SolrDocument();
+    secondKid.addField("id", "22");
+    secondKid.addField("cat", "bar");
+
+    SolrDocument grandChildDocument = new SolrDocument();
+    grandChildDocument.addField("id", "3");
+
+    childDocument.addChildDocument(grandChildDocument);
+    parentDocument.addChildDocument(childDocument);
+    parentDocument.addChildDocument(secondKid);
+
+    return parentDocument;
+  }
+
   private List<Object> generateAllDataTypes() {
     List<Object> types = new ArrayList<>();
 
@@ -137,7 +163,7 @@ private final String BIN_FILE_LOCATION =
   }
 
   @Test
-  public void testBackCompat() {
+  public void testBackCompat() throws IOException {
     JavaBinCodec javabin = new JavaBinCodec(){
       @Override
       public List<Object> readIterator(DataInputInputStream fis) throws IOException {
@@ -170,13 +196,31 @@ private final String BIN_FILE_LOCATION =
 
       }
     } catch (IOException e) {
-      fail(e.getMessage());
+      throw e;
     }
 
   }
 
   @Test
-  public void testForwardCompat() {
+  public void testBackCompatForSolrDocumentWithChildDocs() throws IOException {
+    JavaBinCodec javabin = new JavaBinCodec(){
+      @Override
+      public List<Object> readIterator(DataInputInputStream fis) throws IOException {
+        return super.readIterator(fis);
+      }
+    };
+    try {
+      InputStream is = getClass().getResourceAsStream(SOLRJ_JAVABIN_BACKCOMPAT_BIN_CHILD_DOCS);
+      SolrDocument sdoc = (SolrDocument) javabin.unmarshal(is);
+      SolrDocument matchSolrDoc = generateSolrDocumentWithChildDocs();
+      assertTrue(assertSolrDocumentEquals(sdoc, matchSolrDoc));
+    } catch (IOException e) {
+      throw e;
+    }
+  }
+
+  @Test
+  public void testForwardCompat() throws IOException {
     JavaBinCodec javabin = new JavaBinCodec();
     ByteArrayOutputStream os = new ByteArrayOutputStream();
 
@@ -189,17 +233,76 @@ private final String BIN_FILE_LOCATION =
       byte[] currentFormatBytes = IOUtils.toByteArray(is);
 
       for (int i = 1; i < currentFormatBytes.length; i++) {//ignore the first byte. It is version information
-        assertEquals(currentFormatBytes[i], newFormatBytes[i]);
+        assertEquals(newFormatBytes[i], currentFormatBytes[i]);
       }
 
     } catch (IOException e) {
-      e.printStackTrace();
-      fail(e.getMessage());
+      throw e;
     }
 
   }
 
-  public void genBinaryFile() throws IOException {
+  @Test
+  public void testForwardCompatForSolrDocumentWithChildDocs() throws IOException {
+    JavaBinCodec javabin = new JavaBinCodec();
+    ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+    SolrDocument sdoc = generateSolrDocumentWithChildDocs();
+    try {
+      javabin.marshal(sdoc, os);
+      byte[] newFormatBytes = os.toByteArray();
+
+      InputStream is = getClass().getResourceAsStream(SOLRJ_JAVABIN_BACKCOMPAT_BIN_CHILD_DOCS);
+      byte[] currentFormatBytes = IOUtils.toByteArray(is);
+
+      for (int i = 1; i < currentFormatBytes.length; i++) {//ignore the first byte. It is version information
+        assertEquals(newFormatBytes[i], currentFormatBytes[i]);
+      }
+
+    } catch (IOException e) {
+      throw e;
+    }
+
+  }
+
+  @Test
+  public void testResponseChildDocuments() throws IOException {
+
+
+    JavaBinCodec javabin = new JavaBinCodec();
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    javabin.marshal(generateSolrDocumentWithChildDocs(), baos);
+
+    SolrDocument result = (SolrDocument) javabin.unmarshal(new ByteArrayInputStream(baos.toByteArray()));
+    assertEquals(2, result.size());
+    assertEquals("1", result.getFieldValue("id"));
+    assertEquals("parentDocument", result.getFieldValue("subject"));
+    assertTrue(result.hasChildDocuments());
+
+    List<SolrDocument> childDocuments = result.getChildDocuments();
+    assertNotNull(childDocuments);
+    assertEquals(2, childDocuments.size());
+    assertEquals(2, childDocuments.get(0).size());
+    assertEquals("2", childDocuments.get(0).getFieldValue("id"));
+    assertEquals("foo", childDocuments.get(0).getFieldValue("cat"));
+
+    assertEquals(2, childDocuments.get(1).size());
+    assertEquals("22", childDocuments.get(1).getFieldValue("id"));
+    assertEquals("bar", childDocuments.get(1).getFieldValue("cat"));
+    assertFalse(childDocuments.get(1).hasChildDocuments());
+    assertNull(childDocuments.get(1).getChildDocuments());
+
+    assertTrue(childDocuments.get(0).hasChildDocuments());
+    List<SolrDocument> grandChildDocuments = childDocuments.get(0).getChildDocuments();
+    assertNotNull(grandChildDocuments);
+    assertEquals(1, grandChildDocuments.size());
+    assertEquals(1, grandChildDocuments.get(0).size());
+    assertEquals("3", grandChildDocuments.get(0).getFieldValue("id"));
+    assertFalse(grandChildDocuments.get(0).hasChildDocuments());
+    assertNull(grandChildDocuments.get(0).getChildDocuments());
+  }
+
+  public void genBinaryFiles() throws IOException {
     JavaBinCodec javabin = new JavaBinCodec();
     ByteArrayOutputStream os = new ByteArrayOutputStream();
     
@@ -211,11 +314,22 @@ private final String BIN_FILE_LOCATION =
     BufferedOutputStream bos = new BufferedOutputStream(fs);
     bos.write(out);
     bos.close();
+
+    //Binary file with child documents
+    javabin = new JavaBinCodec();
+    SolrDocument sdoc = generateSolrDocumentWithChildDocs();
+    os = new ByteArrayOutputStream();
+    javabin.marshal(sdoc, os);
+    fs = new FileOutputStream(new File(BIN_FILE_LOCATION_CHILD_DOCS));
+    bos = new BufferedOutputStream(fs);
+    bos.write(os.toByteArray());
+    bos.close();
+
   }
 
   public static void main(String[] args) throws IOException {
     TestJavaBinCodec test = new TestJavaBinCodec();
-    test.genBinaryFile();
+    test.genBinaryFiles();
   }
 
 }

Modified: lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java?rev=1601037&r1=1601036&r2=1601037&view=diff
==============================================================================
--- lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java (original)
+++ lucene/dev/branches/branch_4x/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java Fri Jun  6 23:42:57 2014
@@ -1065,9 +1065,20 @@ public abstract class SolrTestCaseJ4 ext
   private static Pattern escapedSingleQuotePattern = Pattern.compile("\\\\\'");
 
 
-  /** Creates JSON from a SolrInputDocument.  Doesn't currently handle boosts. */
+  /** Creates JSON from a SolrInputDocument.  Doesn't currently handle boosts.
+   *  @see #json(SolrInputDocument,CharArr)
+   */
   public static String json(SolrInputDocument doc) {
-     CharArr out = new CharArr();
+    CharArr out = new CharArr();
+    json(doc, out);
+    return out.toString();
+  }
+
+  /**
+   * Appends to the <code>out</code> array with JSON from the <code>doc</code>.
+   * Doesn't currently handle boosts, but does recursively handle child documents
+   */
+  public static void json(SolrInputDocument doc, CharArr out) {
     try {
       out.append('{');
       boolean firstField = true;
@@ -1090,11 +1101,22 @@ public abstract class SolrTestCaseJ4 ext
           out.append(JSONUtil.toJSON(sfield.getValue()));
         }
       }
+
+      boolean firstChildDoc = true;
+      if(doc.hasChildDocuments()) {
+        out.append(",\"_childDocuments_\": [");
+        List<SolrInputDocument> childDocuments = doc.getChildDocuments();
+        for(SolrInputDocument childDocument : childDocuments) {
+          if (firstChildDoc) firstChildDoc=false;
+          else out.append(',');
+          json(childDocument, out);
+        }
+        out.append(']');
+      }
       out.append('}');
     } catch (IOException e) {
       // should never happen
     }
-    return out.toString();
   }
 
   /** Creates a JSON add command from a SolrInputDocument list.  Doesn't currently handle boosts. */
@@ -1867,7 +1889,23 @@ public abstract class SolrTestCaseJ4 ext
       }
     }
 
-    return true;
+    if(solrDocument1.getChildDocuments() == null && solrDocument2.getChildDocuments() == null) {
+      return true;
+    }
+    if(solrDocument1.getChildDocuments() == null || solrDocument2.getChildDocuments() == null) {
+      return false;
+    } else if(solrDocument1.getChildDocuments().size() != solrDocument2.getChildDocuments().size()) {
+      return false;
+    } else {
+      Iterator<SolrDocument> childDocsIter1 = solrDocument1.getChildDocuments().iterator();
+      Iterator<SolrDocument> childDocsIter2 = solrDocument2.getChildDocuments().iterator();
+      while(childDocsIter1.hasNext()) {
+        if(!assertSolrDocumentEquals(childDocsIter1.next(), childDocsIter2.next())) {
+          return false;
+        }
+      }
+      return true;
+    }
   }
 
   public boolean assertSolrDocumentList(Object expected, Object actual) {