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 2011/09/17 14:48:28 UTC
svn commit: r1171970 [2/3] - in /lucene/dev/trunk:
lucene/src/java/org/apache/lucene/search/
modules/grouping/src/java/org/apache/lucene/search/grouping/ solr/
solr/core/src/java/org/apache/solr/handler/component/
solr/core/src/java/org/apache/solr/sea...
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/QueryCommandResult.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/QueryCommandResult.java?rev=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/QueryCommandResult.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/QueryCommandResult.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,42 @@
+package org.apache.solr.search.grouping.distributed.command;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.TopDocs;
+
+/**
+ * Encapsulates {@link TopDocs} and the number of matches.
+ */
+public class QueryCommandResult {
+
+ private final TopDocs topDocs;
+ private final int matches;
+
+ public QueryCommandResult(TopDocs topDocs, int matches) {
+ this.topDocs = topDocs;
+ this.matches = matches;
+ }
+
+ public TopDocs getTopDocs() {
+ return topDocs;
+ }
+
+ public int getMatches() {
+ return matches;
+ }
+}
Added: 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=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,101 @@
+package org.apache.solr.search.grouping.distributed.command;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.grouping.SearchGroup;
+import org.apache.lucene.search.grouping.TermFirstPassGroupingCollector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.grouping.Command;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ *
+ */
+public class SearchGroupsFieldCommand implements Command<Collection<SearchGroup<BytesRef>>> {
+
+ public static class Builder {
+
+ private SchemaField field;
+ private Sort groupSort;
+ private Integer topNGroups;
+
+ public Builder setField(SchemaField field) {
+ this.field = field;
+ return this;
+ }
+
+ public Builder setGroupSort(Sort groupSort) {
+ this.groupSort = groupSort;
+ return this;
+ }
+
+ public Builder setTopNGroups(int topNGroups) {
+ this.topNGroups = topNGroups;
+ return this;
+ }
+
+ public SearchGroupsFieldCommand build() {
+ if (field == null || groupSort == null || topNGroups == null) {
+ throw new IllegalStateException("All fields must be set");
+ }
+
+ return new SearchGroupsFieldCommand(field, groupSort, topNGroups);
+ }
+
+ }
+
+ private final SchemaField field;
+ private final Sort groupSort;
+ private final int topNGroups;
+
+ private TermFirstPassGroupingCollector collector;
+
+ private SearchGroupsFieldCommand(SchemaField field, Sort groupSort, int topNGroups) {
+ this.field = field;
+ this.groupSort = groupSort;
+ this.topNGroups = topNGroups;
+ }
+
+ public List<Collector> create() throws IOException {
+ collector = new TermFirstPassGroupingCollector(field.getName(), groupSort, topNGroups);
+ return Arrays.asList((Collector) collector);
+ }
+
+ public Collection<SearchGroup<BytesRef>> result() {
+ return collector.getTopGroups(0, true);
+ }
+
+ public Sort getSortWithinGroup() {
+ return null;
+ }
+
+ public Sort getGroupSort() {
+ return groupSort;
+ }
+
+ public String getKey() {
+ return field.getName();
+ }
+}
Added: 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=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/TopGroupsFieldCommand.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/TopGroupsFieldCommand.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,165 @@
+package org.apache.solr.search.grouping.distributed.command;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.grouping.SearchGroup;
+import org.apache.lucene.search.grouping.TermAllGroupsCollector;
+import org.apache.lucene.search.grouping.TermSecondPassGroupingCollector;
+import org.apache.lucene.search.grouping.TopGroups;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.grouping.Command;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ *
+ */
+public class TopGroupsFieldCommand implements Command<TopGroups<BytesRef>> {
+
+ public static class Builder {
+
+ private SchemaField field;
+ private Sort groupSort;
+ private Sort sortWithinGroup;
+ private Collection<SearchGroup<BytesRef>> firstPhaseGroups;
+ private Integer maxDocPerGroup;
+ private boolean needScores = false;
+ private boolean needMaxScore = false;
+ private boolean needGroupCount = false;
+
+ public Builder setField(SchemaField field) {
+ this.field = field;
+ return this;
+ }
+
+ public Builder setGroupSort(Sort groupSort) {
+ this.groupSort = groupSort;
+ return this;
+ }
+
+ public Builder setSortWithinGroup(Sort sortWithinGroup) {
+ this.sortWithinGroup = sortWithinGroup;
+ return this;
+ }
+
+ public Builder setFirstPhaseGroups(Collection<SearchGroup<BytesRef>> firstPhaseGroups) {
+ this.firstPhaseGroups = firstPhaseGroups;
+ return this;
+ }
+
+ public Builder setMaxDocPerGroup(int maxDocPerGroup) {
+ this.maxDocPerGroup = maxDocPerGroup;
+ return this;
+ }
+
+ public Builder setNeedScores(Boolean needScores) {
+ this.needScores = needScores;
+ return this;
+ }
+
+ public Builder setNeedMaxScore(Boolean needMaxScore) {
+ this.needMaxScore = needMaxScore;
+ return this;
+ }
+
+ public Builder setNeedGroupCount(Boolean needGroupCount) {
+ this.needGroupCount = needGroupCount;
+ return this;
+ }
+
+ public TopGroupsFieldCommand build() {
+ if (field == null || groupSort == null || sortWithinGroup == null || firstPhaseGroups == null ||
+ maxDocPerGroup == null) {
+ throw new IllegalStateException("All required fields must be set");
+ }
+
+ return new TopGroupsFieldCommand(field, groupSort, sortWithinGroup, firstPhaseGroups, maxDocPerGroup, needScores, needMaxScore, needGroupCount);
+ }
+
+ }
+
+ private final SchemaField field;
+ private final Sort groupSort;
+ private final Sort sortWithinGroup;
+ private final Collection<SearchGroup<BytesRef>> firstPhaseGroups;
+ private final int maxDocPerGroup;
+ private final boolean needScores;
+ private final boolean needMaxScore;
+ private final boolean needGroupCount;
+
+ private TermSecondPassGroupingCollector secondPassCollector;
+ private TermAllGroupsCollector allGroupsCollector;
+
+ private TopGroupsFieldCommand(SchemaField field,
+ Sort groupSort,
+ Sort sortWithinGroup,
+ Collection<SearchGroup<BytesRef>> firstPhaseGroups,
+ int maxDocPerGroup,
+ boolean needScores,
+ boolean needMaxScore,
+ boolean needGroupCount) {
+ this.field = field;
+ this.groupSort = groupSort;
+ this.sortWithinGroup = sortWithinGroup;
+ this.firstPhaseGroups = firstPhaseGroups;
+ this.maxDocPerGroup = maxDocPerGroup;
+ this.needScores = needScores;
+ this.needMaxScore = needMaxScore;
+ this.needGroupCount = needGroupCount;
+ }
+
+ public List<Collector> create() throws IOException {
+ List<Collector> collectors = new ArrayList<Collector>();
+ secondPassCollector = new TermSecondPassGroupingCollector(
+ field.getName(), firstPhaseGroups, groupSort, sortWithinGroup, maxDocPerGroup, needScores, needMaxScore, true
+ );
+ collectors.add(secondPassCollector);
+ if (!needGroupCount) {
+ return collectors;
+ }
+ allGroupsCollector = new TermAllGroupsCollector(field.getName());
+ collectors.add(allGroupsCollector);
+ return collectors;
+ }
+
+ public TopGroups<BytesRef> result() {
+ TopGroups<BytesRef> result = secondPassCollector.getTopGroups(0);
+ if (allGroupsCollector != null) {
+ result = new TopGroups<BytesRef>(result, allGroupsCollector.getGroupCount());
+ }
+ return result;
+ }
+
+ public String getKey() {
+ return field.getName();
+ }
+
+ public Sort getGroupSort() {
+ return groupSort;
+ }
+
+ public Sort getSortWithinGroup() {
+ return sortWithinGroup;
+ }
+}
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/SearchGroupsRequestFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/SearchGroupsRequestFactory.java?rev=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/SearchGroupsRequestFactory.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/SearchGroupsRequestFactory.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,84 @@
+package org.apache.solr.search.grouping.distributed.requestfactory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.ShardParams;
+import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.ShardRequest;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.grouping.GroupingSpecification;
+import org.apache.solr.search.grouping.distributed.ShardRequestFactory;
+
+/**
+ * Concrete implementation of {@link ShardRequestFactory} that creates {@link ShardRequest} instances for getting the
+ * search groups from all shards.
+ */
+public class SearchGroupsRequestFactory implements ShardRequestFactory {
+
+ /**
+ * {@inheritDoc}
+ */
+ public ShardRequest[] constructRequest(ResponseBuilder rb) {
+ ShardRequest sreq = new ShardRequest();
+ GroupingSpecification groupingSpecification = rb.getGroupingSpec();
+ if (groupingSpecification.getFields().length == 0) {
+ return new ShardRequest[0];
+ }
+
+ sreq.purpose = ShardRequest.PURPOSE_GET_TOP_GROUPS;
+
+ sreq.params = new ModifiableSolrParams(rb.req.getParams());
+ // TODO: base on current params or original params?
+
+ // don't pass through any shards param
+ sreq.params.remove(ShardParams.SHARDS);
+
+ // set the start (offset) to 0 for each shard request so we can properly merge
+ // results from the start.
+ if(rb.shards_start > -1) {
+ // if the client set shards.start set this explicitly
+ sreq.params.set(CommonParams.START,rb.shards_start);
+ } else {
+ sreq.params.set(CommonParams.START, "0");
+ }
+ // TODO: should we even use the SortSpec? That's obtained from the QParser, and
+ // perhaps we shouldn't attempt to parse the query at this level?
+ // Alternate Idea: instead of specifying all these things at the upper level,
+ // we could just specify that this is a shard request.
+ if(rb.shards_rows > -1) {
+ // if the client set shards.rows set this explicity
+ sreq.params.set(CommonParams.ROWS,rb.shards_rows);
+ } else {
+ sreq.params.set(CommonParams.ROWS, rb.getSortSpec().getOffset() + rb.getSortSpec().getCount());
+ }
+
+ // in this first phase, request only the unique key field
+ // and any fields needed for merging.
+ sreq.params.set("group.distibuted.first","true");
+
+ if ( (rb.getFieldFlags() & SolrIndexSearcher.GET_SCORES)!=0 || rb.getSortSpec().includesScore()) {
+ sreq.params.set(CommonParams.FL, rb.req.getSchema().getUniqueKeyField().getName() + ",score");
+ } else {
+ sreq.params.set(CommonParams.FL, rb.req.getSchema().getUniqueKeyField().getName());
+ }
+ return new ShardRequest[] {sreq};
+ }
+
+}
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/StoredFieldsShardRequestFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/StoredFieldsShardRequestFactory.java?rev=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/StoredFieldsShardRequestFactory.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/StoredFieldsShardRequestFactory.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,100 @@
+package org.apache.solr.search.grouping.distributed.requestfactory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.grouping.GroupDocs;
+import org.apache.lucene.search.grouping.TopGroups;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.GroupParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.ShardParams;
+import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.ShardDoc;
+import org.apache.solr.handler.component.ShardRequest;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.grouping.distributed.ShardRequestFactory;
+import org.apache.solr.search.grouping.distributed.command.QueryCommandResult;
+
+import java.util.*;
+
+/**
+ *
+ */
+public class StoredFieldsShardRequestFactory implements ShardRequestFactory {
+
+ @Override
+ public ShardRequest[] constructRequest(ResponseBuilder rb) {
+ HashMap<String, Set<ShardDoc>> shardMap = new HashMap<String,Set<ShardDoc>>();
+ for (TopGroups<BytesRef> topGroups : rb.mergedTopGroups.values()) {
+ for (GroupDocs<BytesRef> group : topGroups.groups) {
+ mapShardToDocs(shardMap, group.scoreDocs);
+ }
+ }
+
+ for (QueryCommandResult queryCommandResult : rb.mergedQueryCommandResults.values()) {
+ mapShardToDocs(shardMap, queryCommandResult.getTopDocs().scoreDocs);
+ }
+
+ ShardRequest[] shardRequests = new ShardRequest[shardMap.size()];
+ SchemaField uniqueField = rb.req.getSchema().getUniqueKeyField();
+ int i = 0;
+ for (Collection<ShardDoc> shardDocs : shardMap.values()) {
+ ShardRequest sreq = new ShardRequest();
+ sreq.purpose = ShardRequest.PURPOSE_GET_FIELDS;
+ sreq.shards = new String[] {shardDocs.iterator().next().shard};
+ sreq.params = new ModifiableSolrParams();
+ sreq.params.add( rb.req.getParams());
+ sreq.params.remove(GroupParams.GROUP);
+ sreq.params.remove(CommonParams.SORT);
+ sreq.params.remove(ResponseBuilder.FIELD_SORT_VALUES);
+ String fl = sreq.params.get(CommonParams.FL);
+ if (fl != null) {
+ fl = fl.trim();
+ // currently, "score" is synonymous with "*,score" so
+ // don't add "id" if the fl is empty or "score" or it would change the meaning.
+ if (fl.length()!=0 && !"score".equals(fl) && !"*".equals(fl)) {
+ sreq.params.set(CommonParams.FL, fl+','+uniqueField.getName());
+ }
+ }
+
+ List<String> ids = new ArrayList<String>(shardDocs.size());
+ for (ShardDoc shardDoc : shardDocs) {
+ ids.add(shardDoc.id.toString());
+ }
+ sreq.params.add(ShardParams.IDS, StrUtils.join(ids, ','));
+ shardRequests[i++] = sreq;
+ }
+
+ return shardRequests;
+ }
+
+ private void mapShardToDocs(HashMap<String, Set<ShardDoc>> shardMap, ScoreDoc[] scoreDocs) {
+ for (ScoreDoc scoreDoc : scoreDocs) {
+ ShardDoc solrDoc = (ShardDoc) scoreDoc;
+ Set<ShardDoc> shardDocs = shardMap.get(solrDoc.shard);
+ if (shardDocs == null) {
+ shardMap.put(solrDoc.shard, shardDocs = new HashSet<ShardDoc>());
+ }
+ shardDocs.add(solrDoc);
+ }
+ }
+
+}
\ No newline at end of file
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/TopGroupsShardRequestFactory.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/TopGroupsShardRequestFactory.java?rev=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/TopGroupsShardRequestFactory.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/requestfactory/TopGroupsShardRequestFactory.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,182 @@
+package org.apache.solr.search.grouping.distributed.requestfactory;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.analysis.reverse.ReverseStringFilter;
+import org.apache.lucene.search.grouping.SearchGroup;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.GroupParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.ShardParams;
+import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.ShardRequest;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.search.Grouping;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.grouping.distributed.ShardRequestFactory;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Concrete implementation of {@link ShardRequestFactory} that creates {@link ShardRequest} instances for getting the
+ * top groups from all shards.
+ */
+public class TopGroupsShardRequestFactory implements ShardRequestFactory {
+
+ /**
+ * Represents a string value for
+ */
+ public static final String GROUP_NULL_VALUE = "" + ReverseStringFilter.START_OF_HEADING_MARKER;
+
+ /**
+ * {@inheritDoc}
+ */
+ public ShardRequest[] constructRequest(ResponseBuilder rb) {
+ // If we have a group.query we need to query all shards... Or we move this to the group first phase queries
+ boolean containsGroupByQuery = rb.getGroupingSpec().getQueries().length > 0;
+ // TODO: If groups.truncate=true we only have to query the specific shards even faceting and statistics are enabled
+ if ((rb.getQueryCommand().getFlags() & SolrIndexSearcher.GET_DOCSET) != 0 || containsGroupByQuery) {
+ // In case we need more results such as faceting and statistics we have to query all shards
+ return createRequestForAllShards(rb);
+ } else {
+ // In case we only need top groups we only have to query the shards that contain these groups.
+ return createRequestForSpecificShards(rb);
+ }
+ }
+
+ private ShardRequest[] createRequestForSpecificShards(ResponseBuilder rb) {
+ // Determine all unique shards to query for TopGroups
+ Set<String> shards = new HashSet<String>();
+ for (String command : rb.searchGroupToShard.keySet()) {
+ Map<SearchGroup<BytesRef>, String> groupsToShard = rb.searchGroupToShard.get(command);
+ shards.addAll(groupsToShard.values());
+ }
+
+ ShardRequest[] sreqs = new ShardRequest[shards.size()];
+ int i = 0;
+ for (String shard : shards) {
+ ShardRequest sreq = new ShardRequest();
+ sreq.purpose = ShardRequest.PURPOSE_GET_TOP_IDS;
+ sreq.actualShards = new String[] {shard};
+ sreq.params = new ModifiableSolrParams(rb.req.getParams());
+
+ // If group.format=simple group.offset doesn't make sense
+ Grouping.Format responseFormat = rb.getGroupingSpec().getResponseFormat();
+ if (responseFormat == Grouping.Format.simple || rb.getGroupingSpec().isMain()) {
+ sreq.params.remove(GroupParams.GROUP_OFFSET);
+ }
+
+ sreq.params.remove(ShardParams.SHARDS);
+
+ // set the start (offset) to 0 for each shard request so we can properly merge
+ // results from the start.
+ if(rb.shards_start > -1) {
+ // if the client set shards.start set this explicitly
+ sreq.params.set(CommonParams.START,rb.shards_start);
+ } else {
+ sreq.params.set(CommonParams.START, "0");
+ }
+ if(rb.shards_rows > -1) {
+ // if the client set shards.rows set this explicity
+ sreq.params.set(CommonParams.ROWS,rb.shards_rows);
+ } else {
+ sreq.params.set(CommonParams.ROWS, rb.getSortSpec().getOffset() + rb.getSortSpec().getCount());
+ }
+
+ sreq.params.set("group.distibuted.second","true");
+ for (Map.Entry<String, Collection<SearchGroup<BytesRef>>> entry : rb.mergedSearchGroups.entrySet()) {
+ for (SearchGroup<BytesRef> searchGroup : entry.getValue()) {
+ String groupValue;
+ if (searchGroup.groupValue != null) {
+ String rawGroupValue = searchGroup.groupValue.utf8ToString();
+ FieldType fieldType = rb.req.getSearcher().getSchema().getField(entry.getKey()).getType();
+ groupValue = fieldType.indexedToReadable(rawGroupValue);
+ } else {
+ groupValue = GROUP_NULL_VALUE;
+ }
+ sreq.params.add("group.topgroups." + entry.getKey(), groupValue);
+ }
+ }
+
+ if ((rb.getFieldFlags() & SolrIndexSearcher.GET_SCORES) != 0 || rb.getSortSpec().includesScore()) {
+ sreq.params.set(CommonParams.FL, rb.req.getSchema().getUniqueKeyField().getName() + ",score");
+ } else {
+ sreq.params.set(CommonParams.FL, rb.req.getSchema().getUniqueKeyField().getName());
+ }
+ sreqs[i++] = sreq;
+ }
+
+ return sreqs;
+ }
+
+ private ShardRequest[] createRequestForAllShards(ResponseBuilder rb) {
+ ShardRequest sreq = new ShardRequest();
+ sreq.purpose = ShardRequest.PURPOSE_GET_TOP_IDS;
+
+ sreq.params = new ModifiableSolrParams(rb.req.getParams());
+ // If group.format=simple group.offset doesn't make sense
+ Grouping.Format responseFormat = rb.getGroupingSpec().getResponseFormat();
+ if (responseFormat == Grouping.Format.simple || rb.getGroupingSpec().isMain()) {
+ sreq.params.remove(GroupParams.GROUP_OFFSET);
+ }
+ sreq.params.remove(ShardParams.SHARDS);
+
+ // set the start (offset) to 0 for each shard request so we can properly merge
+ // results from the start.
+ if(rb.shards_start > -1) {
+ // if the client set shards.start set this explicitly
+ sreq.params.set(CommonParams.START,rb.shards_start);
+ } else {
+ sreq.params.set(CommonParams.START, "0");
+ }
+ if(rb.shards_rows > -1) {
+ // if the client set shards.rows set this explicity
+ sreq.params.set(CommonParams.ROWS,rb.shards_rows);
+ } else {
+ sreq.params.set(CommonParams.ROWS, rb.getSortSpec().getOffset() + rb.getSortSpec().getCount());
+ }
+
+ sreq.params.set("group.distibuted.second","true");
+ for (Map.Entry<String, Collection<SearchGroup<BytesRef>>> entry : rb.mergedSearchGroups.entrySet()) {
+ for (SearchGroup<BytesRef> searchGroup : entry.getValue()) {
+ String groupValue;
+ if (searchGroup.groupValue != null) {
+ String rawGroupValue = searchGroup.groupValue.utf8ToString();
+ FieldType fieldType = rb.req.getSearcher().getSchema().getField(entry.getKey()).getType();
+ groupValue = fieldType.indexedToReadable(rawGroupValue);
+ } else {
+ groupValue = GROUP_NULL_VALUE;
+ }
+ sreq.params.add("group.topgroups." + entry.getKey(), groupValue);
+ }
+ }
+
+ if ( (rb.getFieldFlags() & SolrIndexSearcher.GET_SCORES)!=0 || rb.getSortSpec().includesScore()) {
+ sreq.params.set(CommonParams.FL, rb.req.getSchema().getUniqueKeyField().getName() + ",score");
+ } else {
+ sreq.params.set(CommonParams.FL, rb.req.getSchema().getUniqueKeyField().getName());
+ }
+
+ return new ShardRequest[] {sreq};
+ }
+
+}
Added: 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=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SearchGroupShardResponseProcessor.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SearchGroupShardResponseProcessor.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,93 @@
+package org.apache.solr.search.grouping.distributed.responseprocessor;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.grouping.SearchGroup;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.ShardRequest;
+import org.apache.solr.handler.component.ShardResponse;
+import org.apache.solr.search.SortSpec;
+import org.apache.solr.search.grouping.distributed.ShardResponseProcessor;
+import org.apache.solr.search.grouping.distributed.shardresultserializer.SearchGroupsResultTransformer;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Concrete implementation for merging {@link SearchGroup} instances from shard responses.
+ */
+public class SearchGroupShardResponseProcessor implements ShardResponseProcessor {
+
+ /**
+ * {@inheritDoc}
+ */
+ public void process(ResponseBuilder rb, ShardRequest shardRequest) {
+ SortSpec ss = rb.getSortSpec();
+ Sort groupSort = rb.getGroupingSpec().getGroupSort();
+ String[] fields = rb.getGroupingSpec().getFields();
+
+ Map<String, List<Collection<SearchGroup<BytesRef>>>> commandSearchGroups = new HashMap<String, List<Collection<SearchGroup<BytesRef>>>>();
+ Map<String, Map<SearchGroup<BytesRef>, String>> tempSearchGroupToShard = new HashMap<String, Map<SearchGroup<BytesRef>, String>>();
+ for (String field : fields) {
+ commandSearchGroups.put(field, new ArrayList<Collection<SearchGroup<BytesRef>>>(shardRequest.responses.size()));
+ tempSearchGroupToShard.put(field, new HashMap<SearchGroup<BytesRef>, String>());
+ if (!rb.searchGroupToShard.containsKey(field)) {
+ rb.searchGroupToShard.put(field, new HashMap<SearchGroup<BytesRef>, String>());
+ }
+ }
+
+ SearchGroupsResultTransformer serializer = new SearchGroupsResultTransformer(rb.req.getSearcher());
+ try {
+ for (ShardResponse srsp : shardRequest.responses) {
+ @SuppressWarnings("unchecked")
+ NamedList<NamedList> firstPhaseResult = (NamedList<NamedList>) srsp.getSolrResponse().getResponse().get("firstPhase");
+ Map<String, Collection<SearchGroup<BytesRef>>> result = serializer.transformToNative(firstPhaseResult, groupSort, null, srsp.getShard());
+ for (String field : commandSearchGroups.keySet()) {
+ Collection<SearchGroup<BytesRef>> searchGroups = result.get(field);
+ if (searchGroups == null) {
+ continue;
+ }
+
+ commandSearchGroups.get(field).add(searchGroups);
+ for (SearchGroup<BytesRef> searchGroup : searchGroups) {
+ tempSearchGroupToShard.get(field).put(searchGroup, srsp.getShard());
+ }
+ }
+ }
+ for (String groupField : commandSearchGroups.keySet()) {
+ List<Collection<SearchGroup<BytesRef>>> topGroups = commandSearchGroups.get(groupField);
+ Collection<SearchGroup<BytesRef>> mergedTopGroups = SearchGroup.merge(topGroups, ss.getOffset(), ss.getCount(), groupSort);
+ if (mergedTopGroups == null) {
+ continue;
+ }
+
+ rb.mergedSearchGroups.put(groupField, mergedTopGroups);
+ for (SearchGroup<BytesRef> mergedTopGroup : mergedTopGroups) {
+ rb.searchGroupToShard.get(groupField).put(mergedTopGroup, tempSearchGroupToShard.get(groupField).get(mergedTopGroup));
+ }
+ }
+ } catch (IOException e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
+ }
+ }
+
+}
\ No newline at end of file
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/StoredFieldsShardResponseProcessor.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/StoredFieldsShardResponseProcessor.java?rev=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/StoredFieldsShardResponseProcessor.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/StoredFieldsShardResponseProcessor.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,56 @@
+package org.apache.solr.search.grouping.distributed.responseprocessor;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.FieldDoc;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.ShardDoc;
+import org.apache.solr.handler.component.ShardRequest;
+import org.apache.solr.handler.component.ShardResponse;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.grouping.distributed.ShardResponseProcessor;
+
+/**
+ * Concrete implementation for processing the stored field values from shard responses.
+ */
+public class StoredFieldsShardResponseProcessor implements ShardResponseProcessor {
+
+ /**
+ * {@inheritDoc}
+ */
+ public void process(ResponseBuilder rb, ShardRequest shardRequest) {
+ boolean returnScores = (rb.getFieldFlags() & SolrIndexSearcher.GET_SCORES) != 0;
+ ShardResponse srsp = shardRequest.responses.get(0);
+ SolrDocumentList docs = (SolrDocumentList)srsp.getSolrResponse().getResponse().get("response");
+ String uniqueIdFieldName = rb.req.getSchema().getUniqueKeyField().getName();
+
+ for (SolrDocument doc : docs) {
+ Object id = doc.getFieldValue(uniqueIdFieldName).toString();
+ ShardDoc shardDoc = rb.resultIds.get(id);
+ FieldDoc fieldDoc = (FieldDoc) shardDoc;
+ if (shardDoc != null) {
+ if (returnScores && !Float.isNaN(fieldDoc.score)) {
+ doc.setField("score", fieldDoc.score);
+ }
+ rb.retrievedDocuments.put(id, doc);
+ }
+ }
+ }
+}
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/TopGroupsShardResponseProcessor.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/TopGroupsShardResponseProcessor.java?rev=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/TopGroupsShardResponseProcessor.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/TopGroupsShardResponseProcessor.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,141 @@
+package org.apache.solr.search.grouping.distributed.responseprocessor;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.grouping.GroupDocs;
+import org.apache.lucene.search.grouping.TopGroups;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.ShardDoc;
+import org.apache.solr.handler.component.ShardRequest;
+import org.apache.solr.handler.component.ShardResponse;
+import org.apache.solr.search.Grouping;
+import org.apache.solr.search.grouping.distributed.ShardResponseProcessor;
+import org.apache.solr.search.grouping.distributed.command.QueryCommandResult;
+import org.apache.solr.search.grouping.distributed.shardresultserializer.TopGroupsResultTransformer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Concrete implementation for merging {@link TopGroups} instances from shard responses.
+ */
+public class TopGroupsShardResponseProcessor implements ShardResponseProcessor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ public void process(ResponseBuilder rb, ShardRequest shardRequest) {
+ Sort groupSort = rb.getGroupingSpec().getGroupSort();
+ String[] fields = rb.getGroupingSpec().getFields();
+ String[] queries = rb.getGroupingSpec().getQueries();
+ Sort sortWithinGroup = rb.getGroupingSpec().getSortWithinGroup();
+
+ // If group.format=simple group.offset doesn't make sense
+ int groupOffsetDefault;
+ if (rb.getGroupingSpec().getResponseFormat() == Grouping.Format.simple || rb.getGroupingSpec().isMain()) {
+ groupOffsetDefault = 0;
+ } else {
+ groupOffsetDefault = rb.getGroupingSpec().getGroupOffset();
+ }
+ int docsPerGroupDefault = rb.getGroupingSpec().getGroupLimit();
+
+ Map<String, List<TopGroups<BytesRef>>> commandTopGroups = new HashMap<String, List<TopGroups<BytesRef>>>();
+ for (String field : fields) {
+ commandTopGroups.put(field, new ArrayList<TopGroups<BytesRef>>());
+ }
+
+ Map<String, List<QueryCommandResult>> commandTopDocs = new HashMap<String, List<QueryCommandResult>>();
+ for (String query : queries) {
+ commandTopDocs.put(query, new ArrayList<QueryCommandResult>());
+ }
+
+ TopGroupsResultTransformer serializer = new TopGroupsResultTransformer(rb);
+ for (ShardResponse srsp : shardRequest.responses) {
+ NamedList<NamedList> secondPhaseResult = (NamedList<NamedList>) srsp.getSolrResponse().getResponse().get("secondPhase");
+ Map<String, ?> result = serializer.transformToNative(secondPhaseResult, groupSort, sortWithinGroup, srsp.getShard());
+ for (String field : commandTopGroups.keySet()) {
+ TopGroups<BytesRef> topGroups = (TopGroups<BytesRef>) result.get(field);
+ if (topGroups == null) {
+ continue;
+ }
+ commandTopGroups.get(field).add(topGroups);
+ }
+ for (String query : queries) {
+ commandTopDocs.get(query).add((QueryCommandResult) result.get(query));
+ }
+ }
+ try {
+ for (String groupField : commandTopGroups.keySet()) {
+ List<TopGroups<BytesRef>> topGroups = commandTopGroups.get(groupField);
+ if (topGroups.isEmpty()) {
+ continue;
+ }
+
+ TopGroups<BytesRef>[] topGroupsArr = new TopGroups[topGroups.size()];
+ rb.mergedTopGroups.put(groupField, TopGroups.merge(topGroups.toArray(topGroupsArr), groupSort, sortWithinGroup, groupOffsetDefault, docsPerGroupDefault));
+ }
+
+ for (String query : commandTopDocs.keySet()) {
+ List<QueryCommandResult> queryCommandResults = commandTopDocs.get(query);
+ List<TopDocs> topDocs = new ArrayList<TopDocs>(queryCommandResults.size());
+ int mergedMatches = 0;
+ for (QueryCommandResult queryCommandResult : queryCommandResults) {
+ topDocs.add(queryCommandResult.getTopDocs());
+ mergedMatches += queryCommandResult.getMatches();
+ }
+
+ int topN = rb.getGroupingSpec().getOffset() + rb.getGroupingSpec().getLimit();
+ TopDocs mergedTopDocs = TopDocs.merge(sortWithinGroup, topN, topDocs.toArray(new TopDocs[topDocs.size()]));
+ rb.mergedQueryCommandResults.put(query, new QueryCommandResult(mergedTopDocs, mergedMatches));
+ }
+
+ Map<Object, ShardDoc> resultIds = new HashMap<Object, ShardDoc>();
+ int i = 0;
+ for (TopGroups<BytesRef> topGroups : rb.mergedTopGroups.values()) {
+ for (GroupDocs<BytesRef> group : topGroups.groups) {
+ for (ScoreDoc scoreDoc : group.scoreDocs) {
+ ShardDoc solrDoc = (ShardDoc) scoreDoc;
+ solrDoc.positionInResponse = i++;
+ resultIds.put(solrDoc.id, solrDoc);
+ }
+ }
+ }
+ for (QueryCommandResult queryCommandResult : rb.mergedQueryCommandResults.values()) {
+ for (ScoreDoc scoreDoc : queryCommandResult.getTopDocs().scoreDocs) {
+ ShardDoc solrDoc = (ShardDoc) scoreDoc;
+ solrDoc.positionInResponse = i++;
+ resultIds.put(solrDoc.id, solrDoc);
+ }
+ }
+
+ rb.resultIds = resultIds;
+ } catch (IOException e) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
+ }
+ }
+}
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SearchGroupsResultTransformer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SearchGroupsResultTransformer.java?rev=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SearchGroupsResultTransformer.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SearchGroupsResultTransformer.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,117 @@
+package org.apache.solr.search.grouping.distributed.shardresultserializer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.grouping.SearchGroup;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.CharsRef;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.grouping.Command;
+import org.apache.solr.search.grouping.distributed.command.SearchGroupsFieldCommand;
+
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * Implementation for transforming {@link SearchGroup} into a {@link NamedList} structure and visa versa.
+ */
+public class SearchGroupsResultTransformer implements ShardResultTransformer<List<Command>, Map<String, Collection<SearchGroup<BytesRef>>>> {
+
+ private final SolrIndexSearcher searcher;
+
+ public SearchGroupsResultTransformer(SolrIndexSearcher searcher) {
+ this.searcher = searcher;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public NamedList transform(List<Command> data) throws IOException {
+ NamedList<NamedList> result = new NamedList<NamedList>();
+ for (Command command : data) {
+ NamedList commandResult;
+ if (SearchGroupsFieldCommand.class.isInstance(command)) {
+ SearchGroupsFieldCommand fieldCommand = (SearchGroupsFieldCommand) command;
+ Collection<SearchGroup<BytesRef>> searchGroups = fieldCommand.result();
+ if (searchGroups == null) {
+ continue;
+ }
+
+ commandResult = serializeSearchGroup(searchGroups, fieldCommand.getGroupSort());
+ } else {
+ commandResult = null;
+ }
+
+ result.add(command.getKey(), commandResult);
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, Collection<SearchGroup<BytesRef>>> transformToNative(NamedList<NamedList> shardResponse, Sort groupSort, Sort sortWithinGroup, String shard) throws IOException {
+ Map<String, Collection<SearchGroup<BytesRef>>> result = new HashMap<String, Collection<SearchGroup<BytesRef>>>();
+ for (Map.Entry<String, NamedList> command : shardResponse) {
+ List<SearchGroup<BytesRef>> searchGroups = new ArrayList<SearchGroup<BytesRef>>();
+ @SuppressWarnings("unchecked")
+ NamedList<List<Comparable>> rawSearchGroups = command.getValue();
+ for (Map.Entry<String, List<Comparable>> rawSearchGroup : rawSearchGroups){
+ SearchGroup<BytesRef> searchGroup = new SearchGroup<BytesRef>();
+ searchGroup.groupValue = rawSearchGroup.getKey() != null ? new BytesRef(rawSearchGroup.getKey()) : null;
+ searchGroup.sortValues = rawSearchGroup.getValue().toArray(new Comparable[rawSearchGroup.getValue().size()]);
+ searchGroups.add(searchGroup);
+ }
+
+ result.put(command.getKey(), searchGroups);
+ }
+ return result;
+ }
+
+ private NamedList serializeSearchGroup(Collection<SearchGroup<BytesRef>> data, Sort groupSort) {
+ NamedList<Comparable[]> result = new NamedList<Comparable[]>();
+ CharsRef spare = new CharsRef();
+
+ for (SearchGroup<BytesRef> searchGroup : data) {
+ Comparable[] convertedSortValues = new Comparable[searchGroup.sortValues.length];
+ for (int i = 0; i < searchGroup.sortValues.length; i++) {
+ Comparable sortValue = (Comparable) searchGroup.sortValues[i];
+ SchemaField field = groupSort.getSort()[i].getField() != null ? searcher.getSchema().getFieldOrNull(groupSort.getSort()[i].getField()) : null;
+ if (field != null) {
+ FieldType fieldType = field.getType();
+ if (sortValue instanceof BytesRef) {
+ String indexedValue = ((BytesRef) sortValue).utf8ToChars(spare).toString();
+ sortValue = (Comparable) fieldType.toObject(field.createField(fieldType.indexedToReadable(indexedValue), 0.0f));
+ } else if (sortValue instanceof String) {
+ sortValue = (Comparable) fieldType.toObject(field.createField(fieldType.indexedToReadable((String) sortValue), 0.0f));
+ }
+ }
+ convertedSortValues[i] = sortValue;
+ }
+ String groupValue = searchGroup.groupValue != null ? searchGroup.groupValue.utf8ToString() : null;
+ result.add(groupValue, convertedSortValues);
+ }
+
+ return result;
+ }
+
+}
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/ShardResultTransformer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/ShardResultTransformer.java?rev=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/ShardResultTransformer.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/ShardResultTransformer.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,53 @@
+package org.apache.solr.search.grouping.distributed.shardresultserializer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.Sort;
+import org.apache.solr.common.util.NamedList;
+
+import java.io.IOException;
+
+/**
+ * A <code>ShardResultTransformer</code> is responsible for transforming a grouped shard result into group related
+ * structures (such as {@link org.apache.lucene.search.grouping.TopGroups} and {@link org.apache.lucene.search.grouping.SearchGroup})
+ * and visa versa.
+ */
+public interface ShardResultTransformer<T, R> {
+
+ /**
+ * Transforms data to a {@link NamedList} structure for serialization purposes.
+ *
+ * @param data The data to be transformed
+ * @return {@link NamedList} structure
+ * @throws IOException If I/O related errors occur during transforming
+ */
+ NamedList transform(T data) throws IOException;
+
+ /**
+ * Transforms the specified shard response into native structures.
+ *
+ * @param shardResponse The shard response containing data in a {@link NamedList} structure
+ * @param groupSort The group sort
+ * @param sortWithinGroup The sort inside a group
+ * @param shard The shard address where the response originated from
+ * @return native structure of the data
+ * @throws IOException If I/O related errors occur during transforming
+ */
+ R transformToNative(NamedList<NamedList> shardResponse, Sort groupSort, Sort sortWithinGroup, String shard) throws IOException;
+
+}
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/TopGroupsResultTransformer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/TopGroupsResultTransformer.java?rev=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/TopGroupsResultTransformer.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/TopGroupsResultTransformer.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,285 @@
+package org.apache.solr.search.grouping.distributed.shardresultserializer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.FieldSelector;
+import org.apache.lucene.document.FieldSelectorResult;
+import org.apache.lucene.document.FieldSelectorVisitor;
+import org.apache.lucene.search.FieldDoc;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.grouping.GroupDocs;
+import org.apache.lucene.search.grouping.TopGroups;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.CharsRef;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.ShardDoc;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.grouping.Command;
+import org.apache.solr.search.grouping.distributed.command.QueryCommand;
+import org.apache.solr.search.grouping.distributed.command.QueryCommandResult;
+import org.apache.solr.search.grouping.distributed.command.TopGroupsFieldCommand;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation for transforming {@link TopGroups} and {@link TopDocs} into a {@link NamedList} structure and
+ * visa versa.
+ */
+public class TopGroupsResultTransformer implements ShardResultTransformer<List<Command>, Map<String, ?>> {
+
+ private final ResponseBuilder rb;
+
+ public TopGroupsResultTransformer(ResponseBuilder rb) {
+ this.rb = rb;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public NamedList transform(List<Command> data) throws IOException {
+ NamedList<NamedList> result = new NamedList<NamedList>();
+ for (Command command : data) {
+ NamedList commandResult;
+ if (TopGroupsFieldCommand.class.isInstance(command)) {
+ TopGroupsFieldCommand fieldCommand = (TopGroupsFieldCommand) command;
+ SchemaField groupField = rb.req.getSearcher().getSchema().getField(fieldCommand.getKey());
+ commandResult = serializeTopGroups(fieldCommand.result(), groupField);
+ } else if (QueryCommand.class.isInstance(command)) {
+ QueryCommand queryCommand = (QueryCommand) command;
+ commandResult = serializeTopDocs(queryCommand.result());
+ } else {
+ commandResult = null;
+ }
+
+ result.add(command.getKey(), commandResult);
+ }
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Map<String, ?> transformToNative(NamedList<NamedList> shardResponse, Sort groupSort, Sort sortWithinGroup, String shard) {
+ Map<String, Object> result = new HashMap<String, Object>();
+
+ for (Map.Entry<String, NamedList> entry : shardResponse) {
+ String key = entry.getKey();
+ NamedList commandResult = entry.getValue();
+ Integer totalGroupedHitCount = (Integer) commandResult.get("totalGroupedHitCount");
+ Integer totalHits = (Integer) commandResult.get("totalHits");
+ if (totalHits != null) {
+ Integer matches = (Integer) commandResult.get("matches");
+ Float maxScore = (Float) commandResult.get("maxScore");
+ if (maxScore == null) {
+ maxScore = Float.NaN;
+ }
+
+ @SuppressWarnings("unchecked")
+ List<NamedList<Object>> documents = (List<NamedList<Object>>) commandResult.get("documents");
+ ScoreDoc[] scoreDocs = new ScoreDoc[documents.size()];
+ int j = 0;
+ for (NamedList<Object> document : documents) {
+ Object uniqueId = document.get("id").toString();
+ Float score = (Float) document.get("score");
+ if (score == null) {
+ score = Float.NaN;
+ }
+ Object[] sortValues = ((List) document.get("sortValues")).toArray();
+ scoreDocs[j++] = new ShardDoc(score, sortValues, uniqueId, shard);
+ }
+ result.put(key, new QueryCommandResult(new TopDocs(totalHits, scoreDocs, maxScore), matches));
+ continue;
+ }
+
+ Integer totalHitCount = (Integer) commandResult.get("totalHitCount");
+ Integer totalGroupCount = (Integer) commandResult.get("totalGroupCount");
+
+ List<GroupDocs<BytesRef>> groupDocs = new ArrayList<GroupDocs<BytesRef>>();
+ for (int i = totalGroupCount == null ? 2 : 3; i < commandResult.size(); i++) {
+ String groupValue = commandResult.getName(i);
+ @SuppressWarnings("unchecked")
+ NamedList<Object> groupResult = (NamedList<Object>) commandResult.getVal(i);
+ Integer totalGroupHits = (Integer) groupResult.get("totalHits");
+ Float maxScore = (Float) groupResult.get("maxScore");
+ if (maxScore == null) {
+ maxScore = Float.NaN;
+ }
+
+ @SuppressWarnings("unchecked")
+ List<NamedList<Object>> documents = (List<NamedList<Object>>) groupResult.get("documents");
+ ScoreDoc[] scoreDocs = new ScoreDoc[documents.size()];
+ int j = 0;
+ for (NamedList<Object> document : documents) {
+ Object uniqueId = document.get("id").toString();
+ Float score = (Float) document.get("score");
+ if (score == null) {
+ score = Float.NaN;
+ }
+ Object[] sortValues = ((List) document.get("sortValues")).toArray();
+ scoreDocs[j++] = new ShardDoc(score, sortValues, uniqueId, shard);
+ }
+
+ BytesRef groupValueRef = groupValue != null ? new BytesRef(groupValue) : null;
+ groupDocs.add(new GroupDocs<BytesRef>(maxScore, totalGroupHits, scoreDocs, groupValueRef, null));
+ }
+
+ @SuppressWarnings("unchecked")
+ GroupDocs<BytesRef>[] groupDocsArr = groupDocs.toArray(new GroupDocs[groupDocs.size()]);
+ TopGroups<BytesRef> topGroups = new TopGroups<BytesRef>(
+ groupSort.getSort(), sortWithinGroup.getSort(), totalHitCount, totalGroupedHitCount, groupDocsArr
+ );
+ if (totalGroupCount != null) {
+ topGroups = new TopGroups<BytesRef>(topGroups, totalGroupCount);
+ }
+
+ result.put(key, topGroups);
+ }
+
+ return result;
+ }
+
+ protected NamedList serializeTopGroups(TopGroups<BytesRef> data, SchemaField groupField) throws IOException {
+ NamedList<Object> result = new NamedList<Object>();
+ result.add("totalGroupedHitCount", data.totalGroupedHitCount);
+ result.add("totalHitCount", data.totalHitCount);
+ if (data.totalGroupCount != null) {
+ result.add("totalGroupCount", data.totalGroupCount);
+ }
+ CharsRef spare = new CharsRef();
+
+ SchemaField uniqueField = rb.req.getSearcher().getSchema().getUniqueKeyField();
+ for (GroupDocs<BytesRef> searchGroup : data.groups) {
+ NamedList<Object> groupResult = new NamedList<Object>();
+ groupResult.add("totalHits", searchGroup.totalHits);
+ if (!Float.isNaN(searchGroup.maxScore)) {
+ groupResult.add("maxScore", searchGroup.maxScore);
+ }
+
+ List<NamedList<Object>> documents = new ArrayList<NamedList<Object>>();
+ for (int i = 0; i < searchGroup.scoreDocs.length; i++) {
+ NamedList<Object> document = new NamedList<Object>();
+ documents.add(document);
+
+ Document doc = retrieveDocument(uniqueField, searchGroup.scoreDocs[i].doc);
+ document.add("id", uniqueField.getType().toObject(doc.getField(uniqueField.getName())));
+ if (!Float.isNaN(searchGroup.scoreDocs[i].score)) {
+ document.add("score", searchGroup.scoreDocs[i].score);
+ }
+ if (!(searchGroup.scoreDocs[i] instanceof FieldDoc)) {
+ continue;
+ }
+
+ FieldDoc fieldDoc = (FieldDoc) searchGroup.scoreDocs[i];
+ Object[] convertedSortValues = new Object[fieldDoc.fields.length];
+ for (int j = 0; j < fieldDoc.fields.length; j++) {
+ Object sortValue = fieldDoc.fields[j];
+ Sort sortWithinGroup = rb.getGroupingSpec().getSortWithinGroup();
+ SchemaField field = sortWithinGroup.getSort()[j].getField() != null ? rb.req.getSearcher().getSchema().getFieldOrNull(sortWithinGroup.getSort()[j].getField()) : null;
+ if (field != null) {
+ FieldType fieldType = field.getType();
+ if (sortValue instanceof BytesRef) {
+ String indexedValue = ((BytesRef) sortValue).utf8ToChars(spare).toString();
+ sortValue = fieldType.toObject(field.createField(fieldType.indexedToReadable(indexedValue), 0.0f));
+ } else if (sortValue instanceof String) {
+ sortValue = fieldType.toObject(field.createField(fieldType.indexedToReadable((String) sortValue), 0.0f));
+ }
+ }
+ convertedSortValues[j] = sortValue;
+ }
+ document.add("sortValues", convertedSortValues);
+ }
+ groupResult.add("documents", documents);
+ String groupValue = searchGroup.groupValue != null ? groupField.getType().indexedToReadable(searchGroup.groupValue.utf8ToString()): null;
+ result.add(groupValue, groupResult);
+ }
+
+ return result;
+ }
+
+ protected NamedList serializeTopDocs(QueryCommandResult result) throws IOException {
+ NamedList<Object> queryResult = new NamedList<Object>();
+ queryResult.add("matches", result.getMatches());
+ queryResult.add("totalHits", result.getTopDocs().totalHits);
+ if (rb.getGroupingSpec().isNeedScore()) {
+ queryResult.add("maxScore", result.getTopDocs().getMaxScore());
+ }
+ List<NamedList> documents = new ArrayList<NamedList>();
+ queryResult.add("documents", documents);
+
+ SchemaField uniqueField = rb.req.getSearcher().getSchema().getUniqueKeyField();
+ CharsRef spare = new CharsRef();
+ for (ScoreDoc scoreDoc : result.getTopDocs().scoreDocs) {
+ NamedList<Object> document = new NamedList<Object>();
+ documents.add(document);
+
+ Document doc = retrieveDocument(uniqueField, scoreDoc.doc);
+ document.add("id", uniqueField.getType().toObject(doc.getField(uniqueField.getName())));
+ if (rb.getGroupingSpec().isNeedScore()) {
+ document.add("score", scoreDoc.score);
+ }
+ if (!FieldDoc.class.isInstance(scoreDoc)) {
+ continue;
+ }
+
+ FieldDoc fieldDoc = (FieldDoc) scoreDoc;
+ Object[] convertedSortValues = new Object[fieldDoc.fields.length];
+ for (int j = 0; j < fieldDoc.fields.length; j++) {
+ Object sortValue = fieldDoc.fields[j];
+ Sort groupSort = rb.getGroupingSpec().getGroupSort();
+ SchemaField field = groupSort.getSort()[j].getField() != null ? rb.req.getSearcher().getSchema().getFieldOrNull(groupSort.getSort()[j].getField()) : null;
+ if (field != null) {
+ FieldType fieldType = field.getType();
+ if (sortValue instanceof BytesRef) {
+ String indexedValue = ((BytesRef) sortValue).utf8ToChars(spare).toString();
+ sortValue = fieldType.toObject(field.createField(fieldType.indexedToReadable(indexedValue), 0.0f));
+ } else if (sortValue instanceof String) {
+ sortValue = fieldType.toObject(field.createField(fieldType.indexedToReadable((String) sortValue), 0.0f));
+ }
+ }
+ convertedSortValues[j] = sortValue;
+ }
+ document.add("sortValues", convertedSortValues);
+ }
+
+ return queryResult;
+ }
+
+ private Document retrieveDocument(final SchemaField uniqueField, int doc) throws IOException {
+ FieldSelectorVisitor fieldSelectorVisitor = new FieldSelectorVisitor(new FieldSelector() {
+
+ public FieldSelectorResult accept(String fieldName) {
+ if (uniqueField.getName().equals(fieldName)) {
+ return FieldSelectorResult.LOAD_AND_BREAK;
+ }
+ return FieldSelectorResult.NO_LOAD;
+ }
+ });
+ rb.req.getSearcher().doc(doc, fieldSelectorVisitor);
+ return fieldSelectorVisitor.getDocument();
+ }
+
+}
Added: 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=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/EndResultTransformer.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/EndResultTransformer.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,52 @@
+package org.apache.solr.search.grouping.endresulttransformer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.search.grouping.GroupingSpecification;
+
+import java.util.Map;
+
+/**
+ * Responsible for transforming the grouped result into the final format for displaying purposes.
+ */
+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 solrDocumentSource The source of {@link SolrDocument} instances
+ */
+ void transform(Map<String, ?> result, SolrQueryResponse response, GroupingSpecification groupingSpecification, SolrDocumentSource solrDocumentSource);
+
+ /**
+ * Abstracts the source for {@link SolrDocument} instances.
+ * The source of documents is different for a distributed search than local search
+ */
+ public interface SolrDocumentSource {
+
+ SolrDocument retrieve(ScoreDoc doc);
+
+ }
+
+}
Added: 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=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/GroupedEndResultTransformer.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/GroupedEndResultTransformer.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,108 @@
+package org.apache.solr.search.grouping.endresulttransformer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.grouping.GroupDocs;
+import org.apache.lucene.search.grouping.TopGroups;
+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.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;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ */
+public class GroupedEndResultTransformer implements EndResultTransformer {
+
+ private final SolrIndexSearcher searcher;
+
+ public GroupedEndResultTransformer(SolrIndexSearcher searcher) {
+ this.searcher = searcher;
+ }
+
+ public void transform(Map<String, ?> result, SolrQueryResponse response, GroupingSpecification groupingSpecification, SolrDocumentSource solrDocumentSource) {
+ NamedList<Object> commands = new NamedList<Object>();
+ for (Map.Entry<String, ?> entry : result.entrySet()) {
+ Object value = entry.getValue();
+ if (TopGroups.class.isInstance(value)) {
+ @SuppressWarnings("unchecked")
+ TopGroups<BytesRef> topGroups = (TopGroups<BytesRef>) value;
+ NamedList<Object> command = new SimpleOrderedMap<Object>();
+ command.add("matches", topGroups.totalHitCount);
+ if (topGroups.totalGroupCount != null) {
+ command.add("ngroups", topGroups.totalGroupCount);
+ }
+
+ List<NamedList> groups = new ArrayList<NamedList>();
+ SchemaField groupField = searcher.getSchema().getField(entry.getKey());
+ FieldType groupFieldType = groupField.getType();
+ for (GroupDocs<BytesRef> group : topGroups.groups) {
+ SimpleOrderedMap<Object> groupResult = new SimpleOrderedMap<Object>();
+ if (group.groupValue != null) {
+ groupResult.add(
+ "groupValue", groupFieldType.toObject(groupField.createField(group.groupValue.utf8ToString(), 0.0f))
+ );
+ } else {
+ groupResult.add("groupValue", null);
+ }
+ SolrDocumentList docList = new SolrDocumentList();
+ docList.setNumFound(group.totalHits);
+ if (!Float.isNaN(group.maxScore)) {
+ docList.setMaxScore(group.maxScore);
+ }
+ docList.setStart(groupingSpecification.getGroupOffset());
+ for (ScoreDoc scoreDoc : group.scoreDocs) {
+ docList.add(solrDocumentSource.retrieve(scoreDoc));
+ }
+ groupResult.add("doclist", docList);
+ groups.add(groupResult);
+ }
+ command.add("groups", groups);
+ commands.add(entry.getKey(), command);
+ } else if (QueryCommandResult.class.isInstance(value)) {
+ QueryCommandResult queryCommandResult = (QueryCommandResult) value;
+ NamedList<Object> command = new SimpleOrderedMap<Object>();
+ command.add("matches", queryCommandResult.getMatches());
+ SolrDocumentList docList = new SolrDocumentList();
+ docList.setNumFound(queryCommandResult.getTopDocs().totalHits);
+ if (!Float.isNaN(queryCommandResult.getTopDocs().getMaxScore())) {
+ docList.setMaxScore(queryCommandResult.getTopDocs().getMaxScore());
+ }
+ docList.setStart(groupingSpecification.getGroupOffset());
+ for (ScoreDoc scoreDoc :queryCommandResult.getTopDocs().scoreDocs){
+ docList.add(solrDocumentSource.retrieve(scoreDoc));
+ }
+ command.add("doclist", docList);
+ commands.add(entry.getKey(), command);
+ }
+ }
+ response.add("grouped", commands);
+ }
+
+}
Added: 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=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/MainEndResultTransformer.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/MainEndResultTransformer.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,59 @@
+package org.apache.solr.search.grouping.endresulttransformer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.grouping.GroupDocs;
+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 java.util.Map;
+
+/**
+ *
+ */
+public class MainEndResultTransformer implements EndResultTransformer {
+
+ public void transform(Map<String, ?> result, SolrQueryResponse response, GroupingSpecification groupingSpecification, SolrDocumentSource solrDocumentSource) {
+ Object value = result.get(groupingSpecification.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);
+
+ Float maxScore = Float.NEGATIVE_INFINITY;
+ for (GroupDocs<BytesRef> group : topGroups.groups) {
+ for (ScoreDoc scoreDoc : group.scoreDocs) {
+ if (maxScore < scoreDoc.score) {
+ maxScore = scoreDoc.score;
+ }
+ docList.add(solrDocumentSource.retrieve(scoreDoc));
+ }
+ }
+ if (maxScore != Float.NEGATIVE_INFINITY) {
+ docList.setMaxScore(maxScore);
+ }
+ response.add("response", docList);
+ }
+ }
+}
Added: 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=1171970&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/SimpleEndResultTransformer.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/grouping/endresulttransformer/SimpleEndResultTransformer.java Sat Sep 17 12:48:27 2011
@@ -0,0 +1,73 @@
+package org.apache.solr.search.grouping.endresulttransformer;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.grouping.GroupDocs;
+import org.apache.lucene.search.grouping.TopGroups;
+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 java.util.Map;
+
+/**
+ *
+ */
+public class SimpleEndResultTransformer implements EndResultTransformer {
+
+ @Override
+ public void transform(Map<String, ?> result, SolrQueryResponse response, GroupingSpecification groupingSpecification, SolrDocumentSource solrDocumentSource) {
+ NamedList<Object> commands = new SimpleOrderedMap<Object>();
+ for (Map.Entry<String, ?> entry : result.entrySet()) {
+ Object value = entry.getValue();
+ if (TopGroups.class.isInstance(value)) {
+ @SuppressWarnings("unchecked")
+ TopGroups<BytesRef> topGroups = (TopGroups<BytesRef>) value;
+ NamedList<Object> command = new SimpleOrderedMap<Object>();
+ command.add("matches", topGroups.totalHitCount);
+ if (topGroups.totalGroupCount != null) {
+ command.add("ngroups", topGroups.totalGroupCount);
+ }
+ SolrDocumentList docList = new SolrDocumentList();
+ docList.setStart(groupingSpecification.getOffset());
+ docList.setNumFound(topGroups.totalHitCount);
+
+ Float maxScore = Float.NEGATIVE_INFINITY;
+ for (GroupDocs<BytesRef> group : topGroups.groups) {
+ for (ScoreDoc scoreDoc : group.scoreDocs) {
+ if (maxScore < scoreDoc.score) {
+ maxScore = scoreDoc.score;
+ }
+ docList.add(solrDocumentSource.retrieve(scoreDoc));
+ }
+ }
+ if (maxScore != Float.NEGATIVE_INFINITY) {
+ docList.setMaxScore(maxScore);
+ }
+ command.add("doclist", docList);
+ commands.add(entry.getKey(), command);
+ }
+ }
+
+ response.add("grouped", commands);
+ }
+}