You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mv...@apache.org on 2012/04/06 14:46:55 UTC

svn commit: r1310305 - in /lucene/dev/trunk/solr: ./ core/src/java/org/apache/solr/handler/component/ core/src/java/org/apache/solr/search/grouping/ core/src/java/org/apache/solr/search/grouping/distributed/command/ core/src/java/org/apache/solr/search...

Author: mvg
Date: Fri Apr  6 12:46:54 2012
New Revision: 1310305

URL: http://svn.apache.org/viewvc?rev=1310305&view=rev
Log:
SOLR-3316: Distributed grouping failed when rows parameter was set to 0 and sometimes returned a wrong
  hit count as matches.

Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/CommandHandler.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/TopGroupsFieldCommand.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SearchGroupShardResponseProcessor.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/EndResultTransformer.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/GroupedEndResultTransformer.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/MainEndResultTransformer.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/SimpleEndResultTransformer.java
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/TestDistributedGrouping.java

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Fri Apr  6 12:46:54 2012
@@ -369,6 +369,9 @@ Bug Fixes
 * SOLR-3214: If you use multiple fl entries rather than a comma separated list, all but the first
   entry can be ignored if you are using distributed search. (Tomas Fernandez Lobbe via Mark Miller)
 
+* SOLR-3316: Distributed grouping failed when rows parameter was set to 0 and sometimes returned a wrong
+  hit count as matches. (Cody Young, Martijn van Groningen)
+
 Other Changes
 ----------------------
 

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java Fri Apr  6 12:46:54 2012
@@ -280,6 +280,7 @@ public class QueryComponent extends Sear
           CommandHandler.Builder topsGroupsActionBuilder = new CommandHandler.Builder()
               .setQueryCommand(cmd)
               .setNeedDocSet(false) // Order matters here
+              .setIncludeHitCount(true)
               .setSearcher(searcher);
 
           for (String field : groupingSpec.getFields()) {
@@ -295,6 +296,7 @@ public class QueryComponent extends Sear
           commandHandler.execute();
           SearchGroupsResultTransformer serializer = new SearchGroupsResultTransformer(searcher);
           rsp.add("firstPhase", commandHandler.processResult(result, serializer));
+          rsp.add("totalHitCount", commandHandler.getTotalHitCount());
           rb.setResult(result);
           return;
         } else if (params.getBool(GroupParams.GROUP_DISTRIBUTED_SECOND, false)) {
@@ -306,7 +308,7 @@ public class QueryComponent extends Sear
           for (String field : groupingSpec.getFields()) {
             String[] topGroupsParam = params.getParams(GroupParams.GROUP_DISTRIBUTED_TOPGROUPS_PREFIX + field);
             if (topGroupsParam == null) {
-              continue;
+              topGroupsParam = new String[0];
             }
 
             List<SearchGroup<BytesRef>> topGroups = new ArrayList<SearchGroup<BytesRef>>(topGroupsParam.length);
@@ -685,7 +687,7 @@ public class QueryComponent extends Sear
     Map<String, Object> combinedMap = new LinkedHashMap<String, Object>();
     combinedMap.putAll(rb.mergedTopGroups);
     combinedMap.putAll(rb.mergedQueryCommandResults);
-    endResultTransformer.transform(combinedMap, rb.rsp, rb.getGroupingSpec(), solrDocumentSource);
+    endResultTransformer.transform(combinedMap, rb, solrDocumentSource);
   }
 
   private void regularFinishStage(ResponseBuilder rb) {

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java Fri Apr  6 12:46:54 2012
@@ -172,6 +172,7 @@ public class ResponseBuilder
   public final Map<String, TopGroups<BytesRef>> mergedTopGroups = new HashMap<String, TopGroups<BytesRef>>();
   public final Map<String, QueryCommandResult> mergedQueryCommandResults = new HashMap<String, QueryCommandResult>();
   public final Map<Object, SolrDocument> retrievedDocuments = new HashMap<Object, SolrDocument>();
+  public int totalHitCount; // Hit count used when distributed grouping is performed.
   // Used for timeAllowed parameter. First phase elapsed time is subtracted from the time allowed for the second phase.
   public int firstPhaseElapsedTime;
 

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/CommandHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/CommandHandler.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/CommandHandler.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/CommandHandler.java Fri Apr  6 12:46:54 2012
@@ -17,11 +17,7 @@ package org.apache.solr.search.grouping;
  * limitations under the License.
  */
 
-import org.apache.lucene.search.Collector;
-import org.apache.lucene.search.Filter;
-import org.apache.lucene.search.MultiCollector;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.TimeLimitingCollector;
+import org.apache.lucene.search.*;
 import org.apache.lucene.search.grouping.AbstractAllGroupHeadsCollector;
 import org.apache.lucene.search.grouping.term.TermAllGroupHeadsCollector;
 import org.apache.lucene.util.OpenBitSet;
@@ -50,6 +46,7 @@ public class CommandHandler {
     private SolrIndexSearcher searcher;
     private boolean needDocSet = false;
     private boolean truncateGroups = false;
+    private boolean includeHitCount = false;
 
     public Builder setQueryCommand(SolrIndexSearcher.QueryCommand queryCommand) {
       this.queryCommand = queryCommand;
@@ -84,12 +81,17 @@ public class CommandHandler {
       return this;
     }
 
+    public Builder setIncludeHitCount(boolean includeHitCount) {
+      this.includeHitCount = includeHitCount;
+      return this;
+    }
+
     public CommandHandler build() {
       if (queryCommand == null || searcher == null) {
         throw new IllegalStateException("All fields must be set");
       }
 
-      return new CommandHandler(queryCommand, commands, searcher, needDocSet, truncateGroups);
+      return new CommandHandler(queryCommand, commands, searcher, needDocSet, truncateGroups, includeHitCount);
     }
 
   }
@@ -101,19 +103,24 @@ public class CommandHandler {
   private final SolrIndexSearcher searcher;
   private final boolean needDocset;
   private final boolean truncateGroups;
+  private final boolean includeHitCount;
   private boolean partialResults = false;
+  private int totalHitCount;
 
   private DocSet docSet;
 
   private CommandHandler(SolrIndexSearcher.QueryCommand queryCommand,
                          List<Command> commands,
                          SolrIndexSearcher searcher,
-                         boolean needDocset, boolean truncateGroups) {
+                         boolean needDocset,
+                         boolean truncateGroups,
+                         boolean includeHitCount) {
     this.queryCommand = queryCommand;
     this.commands = commands;
     this.searcher = searcher;
     this.needDocset = needDocset;
     this.truncateGroups = truncateGroups;
+    this.includeHitCount = includeHitCount;
   }
 
   @SuppressWarnings("unchecked")
@@ -130,12 +137,14 @@ public class CommandHandler {
     Filter luceneFilter = pf.filter;
     Query query = QueryUtils.makeQueryable(queryCommand.getQuery());
 
-    if (truncateGroups && nrOfCommands > 0) {
+    if (truncateGroups) {
       docSet = computeGroupedDocSet(query, luceneFilter, collectors);
     } else if (needDocset) {
       docSet = computeDocSet(query, luceneFilter, collectors);
-    } else {
+    } else if (!collectors.isEmpty()) {
       searchWithTimeLimiter(query, luceneFilter, MultiCollector.wrap(collectors.toArray(new Collector[nrOfCommands])));
+    } else {
+      searchWithTimeLimiter(query, luceneFilter, null);
     }
   }
 
@@ -185,12 +194,25 @@ public class CommandHandler {
     if (queryCommand.getTimeAllowed() > 0 ) {
       collector = new TimeLimitingCollector(collector, TimeLimitingCollector.getGlobalCounter(), queryCommand.getTimeAllowed());
     }
+
+    TotalHitCountCollector hitCountCollector = new TotalHitCountCollector();
+    if (includeHitCount) {
+      collector = MultiCollector.wrap(collector, hitCountCollector);
+    }
+
     try {
       searcher.search(query, luceneFilter, collector);
     } catch (TimeLimitingCollector.TimeExceededException x) {
       partialResults = true;
       logger.warn( "Query: " + query + "; " + x.getMessage() );
     }
+
+    if (includeHitCount) {
+      totalHitCount = hitCountCollector.getTotalHits();
+    }
   }
 
+  public int getTotalHitCount() {
+    return totalHitCount;
+  }
 }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java Fri Apr  6 12:46:54 2012
@@ -28,6 +28,7 @@ import org.apache.solr.search.grouping.C
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -70,7 +71,7 @@ public class SearchGroupsFieldCommand im
   private final Sort groupSort;
   private final int topNGroups;
 
-  private TermFirstPassGroupingCollector collector;
+  private TermFirstPassGroupingCollector firstPassGroupingCollector;
 
   private SearchGroupsFieldCommand(SchemaField field, Sort groupSort, int topNGroups) {
     this.field = field;
@@ -79,12 +80,20 @@ public class SearchGroupsFieldCommand im
   }
 
   public List<Collector> create() throws IOException {
-    collector = new TermFirstPassGroupingCollector(field.getName(), groupSort, topNGroups);
-    return Arrays.asList((Collector) collector);
+    if (topNGroups > 0) {
+      firstPassGroupingCollector = new TermFirstPassGroupingCollector(field.getName(), groupSort, topNGroups);
+      return Arrays.asList((Collector) firstPassGroupingCollector);
+    } else {
+      return Collections.emptyList();
+    }
   }
 
   public Collection<SearchGroup<BytesRef>> result() {
-    return collector.getTopGroups(0, true);
+    if (topNGroups > 0) {
+      return firstPassGroupingCollector.getTopGroups(0, true);
+    } else {
+      return Collections.emptyList();
+    }
   }
 
   public Sort getSortWithinGroup() {

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/TopGroupsFieldCommand.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/TopGroupsFieldCommand.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/TopGroupsFieldCommand.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/TopGroupsFieldCommand.java Fri Apr  6 12:46:54 2012
@@ -19,6 +19,7 @@ package org.apache.solr.search.grouping.
 
 import org.apache.lucene.search.Collector;
 import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.grouping.GroupDocs;
 import org.apache.lucene.search.grouping.SearchGroup;
 import org.apache.lucene.search.grouping.term.TermAllGroupsCollector;
 import org.apache.lucene.search.grouping.term.TermSecondPassGroupingCollector;
@@ -30,6 +31,7 @@ import org.apache.solr.search.grouping.C
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -130,6 +132,10 @@ public class TopGroupsFieldCommand imple
   }
 
   public List<Collector> create() throws IOException {
+    if (firstPhaseGroups.isEmpty()) {
+      return Collections.emptyList();
+    }
+
     List<Collector> collectors = new ArrayList<Collector>();
     secondPassCollector = new TermSecondPassGroupingCollector(
           field.getName(), firstPhaseGroups, groupSort, sortWithinGroup, maxDocPerGroup, needScores, needMaxScore, true
@@ -143,7 +149,12 @@ public class TopGroupsFieldCommand imple
     return collectors;
   }
 
+  @SuppressWarnings("unchecked")
   public TopGroups<BytesRef> result() {
+    if (firstPhaseGroups.isEmpty()) {
+      return new TopGroups<BytesRef>(groupSort.getSort(), sortWithinGroup.getSort(), 0, 0, new GroupDocs[0]);
+    }
+
     TopGroups<BytesRef> result = secondPassCollector.getTopGroups(0);
     if (allGroupsCollector != null) {
       result = new TopGroups<BytesRef>(result, allGroupsCollector.getGroupCount());

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SearchGroupShardResponseProcessor.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SearchGroupShardResponseProcessor.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SearchGroupShardResponseProcessor.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SearchGroupShardResponseProcessor.java Fri Apr  6 12:46:54 2012
@@ -58,6 +58,7 @@ public class SearchGroupShardResponsePro
     SearchGroupsResultTransformer serializer = new SearchGroupsResultTransformer(rb.req.getSearcher());
     try {
       int maxElapsedTime = 0;
+      int hitCountDuringFirstPhase = 0;
       for (ShardResponse srsp : shardRequest.responses) {
         maxElapsedTime = (int) Math.max(maxElapsedTime, srsp.getSolrResponse().getElapsedTime());
         @SuppressWarnings("unchecked")
@@ -80,7 +81,9 @@ public class SearchGroupShardResponsePro
             shards.add(srsp.getShard());
           }
         }
+        hitCountDuringFirstPhase += (Integer) srsp.getSolrResponse().getResponse().get("totalHitCount");
       }
+      rb.totalHitCount = hitCountDuringFirstPhase;
       rb.firstPhaseElapsedTime = maxElapsedTime;
       for (String groupField : commandSearchGroups.keySet()) {
         List<Collection<SearchGroup<BytesRef>>> topGroups = commandSearchGroups.get(groupField);

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/EndResultTransformer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/EndResultTransformer.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/EndResultTransformer.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/EndResultTransformer.java Fri Apr  6 12:46:54 2012
@@ -19,8 +19,7 @@ package org.apache.solr.search.grouping.
 
 import org.apache.lucene.search.ScoreDoc;
 import org.apache.solr.common.SolrDocument;
-import org.apache.solr.response.SolrQueryResponse;
-import org.apache.solr.search.grouping.GroupingSpecification;
+import org.apache.solr.handler.component.ResponseBuilder;
 
 import java.util.Map;
 
@@ -35,11 +34,10 @@ public interface EndResultTransformer {
    * Transforms the specified result into its final form and puts it into the specified response.
    *
    * @param result The map containing the grouping result (for grouping by field and query)
-   * @param response The response that will be rendered to the client
-   * @param groupingSpecification The grouping specification
+   * @param rb The response builder containing the response used to render the result and the grouping specification
    * @param solrDocumentSource The source of {@link SolrDocument} instances
    */
-  void transform(Map<String, ?> result, SolrQueryResponse response, GroupingSpecification groupingSpecification, SolrDocumentSource solrDocumentSource);
+  void transform(Map<String, ?> result, ResponseBuilder rb, SolrDocumentSource solrDocumentSource);
 
   /**
    * Abstracts the source for {@link SolrDocument} instances.

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/GroupedEndResultTransformer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/GroupedEndResultTransformer.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/GroupedEndResultTransformer.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/GroupedEndResultTransformer.java Fri Apr  6 12:46:54 2012
@@ -24,11 +24,10 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
-import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.handler.component.ResponseBuilder;
 import org.apache.solr.schema.FieldType;
 import org.apache.solr.schema.SchemaField;
 import org.apache.solr.search.SolrIndexSearcher;
-import org.apache.solr.search.grouping.GroupingSpecification;
 import org.apache.solr.search.grouping.distributed.command.QueryCommandResult;
 
 import java.util.ArrayList;
@@ -36,7 +35,7 @@ import java.util.List;
 import java.util.Map;
 
 /**
- *
+ * Implementation of {@link EndResultTransformer} that keeps each grouped result separate in the final response.
  */
 public class GroupedEndResultTransformer implements EndResultTransformer {
 
@@ -46,7 +45,10 @@ public class GroupedEndResultTransformer
     this.searcher = searcher;
   }
 
-  public void transform(Map<String, ?> result, SolrQueryResponse response, GroupingSpecification groupingSpecification, SolrDocumentSource solrDocumentSource) {
+  /**
+   * {@inheritDoc}
+   */
+  public void transform(Map<String, ?> result, ResponseBuilder rb, SolrDocumentSource solrDocumentSource) {
     NamedList<Object> commands = new NamedList<Object>();
     for (Map.Entry<String, ?> entry : result.entrySet()) {
       Object value = entry.getValue();
@@ -54,7 +56,7 @@ public class GroupedEndResultTransformer
         @SuppressWarnings("unchecked")
         TopGroups<BytesRef> topGroups = (TopGroups<BytesRef>) value;
         NamedList<Object> command = new SimpleOrderedMap<Object>();
-        command.add("matches", topGroups.totalHitCount);
+        command.add("matches", rb.totalHitCount);
         if (topGroups.totalGroupCount != null) {
           command.add("ngroups", topGroups.totalGroupCount);
         }
@@ -76,7 +78,7 @@ public class GroupedEndResultTransformer
           if (!Float.isNaN(group.maxScore)) {
             docList.setMaxScore(group.maxScore);
           }
-          docList.setStart(groupingSpecification.getGroupOffset());
+          docList.setStart(rb.getGroupingSpec().getGroupOffset());
           for (ScoreDoc scoreDoc : group.scoreDocs) {
             docList.add(solrDocumentSource.retrieve(scoreDoc));
           }
@@ -94,7 +96,7 @@ public class GroupedEndResultTransformer
         if (!Float.isNaN(queryCommandResult.getTopDocs().getMaxScore())) {
           docList.setMaxScore(queryCommandResult.getTopDocs().getMaxScore());
         }
-        docList.setStart(groupingSpecification.getGroupOffset());
+        docList.setStart(rb.getGroupingSpec().getGroupOffset());
         for (ScoreDoc scoreDoc :queryCommandResult.getTopDocs().scoreDocs){
           docList.add(solrDocumentSource.retrieve(scoreDoc));
         }
@@ -102,7 +104,7 @@ public class GroupedEndResultTransformer
         commands.add(entry.getKey(), command);
       }
     }
-    response.add("grouped", commands);
+    rb.rsp.add("grouped", commands);
   }
 
 }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/MainEndResultTransformer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/MainEndResultTransformer.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/MainEndResultTransformer.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/MainEndResultTransformer.java Fri Apr  6 12:46:54 2012
@@ -22,24 +22,27 @@ import org.apache.lucene.search.grouping
 import org.apache.lucene.search.grouping.TopGroups;
 import org.apache.lucene.util.BytesRef;
 import org.apache.solr.common.SolrDocumentList;
-import org.apache.solr.response.SolrQueryResponse;
-import org.apache.solr.search.grouping.GroupingSpecification;
+import org.apache.solr.handler.component.ResponseBuilder;
 
 import java.util.Map;
 
 /**
- *
+ * Implementation of {@link EndResultTransformer} that transforms the grouped result into the main result list in the
+ * response.
  */
 public class MainEndResultTransformer implements EndResultTransformer {
 
-  public void transform(Map<String, ?> result, SolrQueryResponse response, GroupingSpecification groupingSpecification, SolrDocumentSource solrDocumentSource) {
-    Object value = result.get(groupingSpecification.getFields()[0]);
+  /**
+   * {@inheritDoc}
+   */
+  public void transform(Map<String, ?> result, ResponseBuilder rb, SolrDocumentSource solrDocumentSource) {
+    Object value = result.get(rb.getGroupingSpec().getFields()[0]);
     if (TopGroups.class.isInstance(value)) {
       @SuppressWarnings("unchecked")
       TopGroups<BytesRef> topGroups = (TopGroups<BytesRef>) value;
       SolrDocumentList docList = new SolrDocumentList();
-      docList.setStart(groupingSpecification.getOffset());
-      docList.setNumFound(topGroups.totalHitCount);
+      docList.setStart(rb.getGroupingSpec().getOffset());
+      docList.setNumFound(rb.totalHitCount);
 
       Float maxScore = Float.NEGATIVE_INFINITY;
       for (GroupDocs<BytesRef> group : topGroups.groups) {
@@ -53,7 +56,7 @@ public class MainEndResultTransformer im
       if (maxScore != Float.NEGATIVE_INFINITY) {
         docList.setMaxScore(maxScore);
       }
-      response.add("response", docList);
+      rb.rsp.add("response", docList);
     }
   }
 }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/SimpleEndResultTransformer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/SimpleEndResultTransformer.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/SimpleEndResultTransformer.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/SimpleEndResultTransformer.java Fri Apr  6 12:46:54 2012
@@ -24,18 +24,19 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
-import org.apache.solr.response.SolrQueryResponse;
-import org.apache.solr.search.grouping.GroupingSpecification;
+import org.apache.solr.handler.component.ResponseBuilder;
 
 import java.util.Map;
 
 /**
- *
+ * Implementation of {@link EndResultTransformer} that transforms the grouped result into a single flat list.
  */
 public class SimpleEndResultTransformer implements EndResultTransformer {
 
-  @Override
-  public void transform(Map<String, ?> result, SolrQueryResponse response, GroupingSpecification groupingSpecification, SolrDocumentSource solrDocumentSource) {
+  /**
+   * {@inheritDoc}
+   */
+  public void transform(Map<String, ?> result, ResponseBuilder rb, SolrDocumentSource solrDocumentSource) {
     NamedList<Object> commands = new SimpleOrderedMap<Object>();
     for (Map.Entry<String, ?> entry : result.entrySet()) {
       Object value = entry.getValue();
@@ -43,12 +44,12 @@ public class SimpleEndResultTransformer 
         @SuppressWarnings("unchecked")
         TopGroups<BytesRef> topGroups = (TopGroups<BytesRef>) value;
         NamedList<Object> command = new SimpleOrderedMap<Object>();
-        command.add("matches", topGroups.totalHitCount);
+        command.add("matches", rb.totalHitCount);
         if (topGroups.totalGroupCount != null) {
           command.add("ngroups", topGroups.totalGroupCount);
         }
         SolrDocumentList docList = new SolrDocumentList();
-        docList.setStart(groupingSpecification.getOffset());
+        docList.setStart(rb.getGroupingSpec().getOffset());
         docList.setNumFound(topGroups.totalHitCount);
 
         Float maxScore = Float.NEGATIVE_INFINITY;
@@ -68,6 +69,6 @@ public class SimpleEndResultTransformer 
       }
     }
 
-    response.add("grouped", commands);
+    rb.rsp.add("grouped", commands);
   }
 }

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/TestDistributedGrouping.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/TestDistributedGrouping.java?rev=1310305&r1=1310304&r2=1310305&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/TestDistributedGrouping.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/TestDistributedGrouping.java Fri Apr  6 12:46:54 2012
@@ -17,9 +17,12 @@ package org.apache.solr;
  * limitations under the License.
  */
 
+import org.apache.solr.client.solrj.SolrServer;
 import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.NamedList;
 
 /**
  * TODO? perhaps use:
@@ -142,7 +145,7 @@ public class TestDistributedGrouping ext
     query("q", "*:*", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", "id asc, _docid_ asc");
     query("q", "*:*", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", "{!func}add(" + i1 + ",5) asc, id asc");
     query("q", "*:*", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "facet", "true", "facet.field", t1);
-    query("q", "*:*", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "stats", "true", "stats.field", i1);
+    query("q", "*:*", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "stats", "true", "stats.field", tlong);
     query("q", "kings", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "spellcheck", "true", "spellcheck.build", "true", "qt", "spellCheckCompRH");
     query("q", "*:*", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "facet", "true", "hl","true","hl.fl",t1);
     query("q", "*:*", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "group.sort", "id desc");
@@ -166,6 +169,27 @@ public class TestDistributedGrouping ext
     query("q", "*:*", "fq", s1 + ":a", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "group.truncate", "true");
     query("q", "*:*", "fq", s1 + ":a", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "group.truncate", "true", "facet", "true", "facet.field", t1);
 
+    query("q", "*:*", "fq", s1 + ":a", "rows", 0, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "facet", "true", "facet.field", t1);
+    query("q", "*:*", "fq", s1 + ":a", "rows", 0, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " asc, id asc", "group.truncate", "true", "facet", "true", "facet.field", t1);
+
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    Object[] q =  {"q", "*:*", "fq", s1 + ":a", "rows", 1, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10};
+
+    for (int i = 0; i < q.length; i += 2) {
+      params.add(q[i].toString(), q[i + 1].toString());
+    }
+
+    setDistributedParams(params);
+
+    int which = r.nextInt(clients.size());
+    SolrServer client = clients.get(which);
+    QueryResponse rsp = client.query(params);
+    NamedList nl = (NamedList<?>) rsp.getResponse().get("grouped");
+    nl = (NamedList<?>) nl.getVal(0);
+    int matches = (Integer) nl.getVal(0);
+    assertEquals(100 * shardsArr.length, matches);
+
+
     // We cannot validate distributed grouping with scoring as first sort. since there is no global idf. We can check if no errors occur
     simpleQuery("q", "*:*", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", i1 + " desc", "group.sort", "score desc"); // SOLR-2955
     simpleQuery("q", "*:*", "rows", 100, "fl", "id," + i1, "group", "true", "group.field", i1, "group.limit", 10, "sort", "score desc, _docid_ asc, id asc");