diff --git a/lucene/grouping/src/java/org/apache/lucene/search/grouping/CollectedSearchGroup.java b/lucene/grouping/src/java/org/apache/lucene/search/grouping/CollectedSearchGroup.java index 5e4bf14cecf0..ffa1dade9ff8 100644 --- a/lucene/grouping/src/java/org/apache/lucene/search/grouping/CollectedSearchGroup.java +++ b/lucene/grouping/src/java/org/apache/lucene/search/grouping/CollectedSearchGroup.java @@ -23,6 +23,6 @@ * tracking the top doc and {@link FieldComparator} slot. * @lucene.internal */ public class CollectedSearchGroup extends SearchGroup { - int topDoc; - int comparatorSlot; + public int topDoc; + public int comparatorSlot; } diff --git a/lucene/grouping/src/java/org/apache/lucene/search/grouping/FirstPassGroupingCollector.java b/lucene/grouping/src/java/org/apache/lucene/search/grouping/FirstPassGroupingCollector.java index c363d09d1f0d..0f0b06f353af 100644 --- a/lucene/grouping/src/java/org/apache/lucene/search/grouping/FirstPassGroupingCollector.java +++ b/lucene/grouping/src/java/org/apache/lucene/search/grouping/FirstPassGroupingCollector.java @@ -132,34 +132,35 @@ public Collection> getTopGroups(int groupOffset) throws IOExcepti final Collection> result = new ArrayList<>(); int upto = 0; final int sortFieldCount = comparators.length; - assert sortFieldCount > 0; // this must always be true because fields Sort must contain at least a field for(CollectedSearchGroup group : orderedGroups) { if (upto++ < groupOffset) { continue; } // System.out.println(" group=" + (group.groupValue == null ? "null" : group.groupValue.toString())); - SearchGroup searchGroup = new SearchGroup<>(); - searchGroup.groupValue = group.groupValue; - // We pass this around so that we can get the corresponding solr id when serializing the search group to send to the federator - searchGroup.topDocLuceneId = group.topDoc; - searchGroup.sortValues = new Object[sortFieldCount]; - for(int sortFieldIDX=0;sortFieldIDX searchGroup = newSearchGroupFromCollectedSearchGroup(group, comparators, sortFieldCount); result.add(searchGroup); } //System.out.println(" return " + result.size() + " groups"); return result; } + protected SearchGroup newSearchGroup() { + return new SearchGroup<>(); + } + + protected SearchGroup newSearchGroupFromCollectedSearchGroup( + CollectedSearchGroup group, + FieldComparator[] comparators, + int sortFieldCount) { + SearchGroup searchGroup = newSearchGroup(); + searchGroup.groupValue = group.groupValue; + searchGroup.sortValues = new Object[sortFieldCount]; + for(int sortFieldIDX=0;sortFieldIDX { * been passed to {@link FirstPassGroupingCollector#getTopGroups} */ public Object[] sortValues; - /** The top doc of this group: we track the Lucene id, - * the Solr id and the score of the document */ - public Object topDocSolrId; - public float topDocScore; - - /** The topDocLuceneId will be null at the federator level because it is unique only at the shard level. - * It is used by the shard to get the corresponding solr id when serializing the search group to send to the federator - */ - public int topDocLuceneId; - @Override public String toString() { - return "SearchGroup{" + - "groupValue=" + groupValue + - ", sortValues=" + Arrays.toString(sortValues) + - ", topDocSolrId=" + topDocSolrId + - ", topDocScore=" + topDocScore + - ", topDocLuceneId=" + topDocLuceneId + - '}'; + return("SearchGroup(groupValue=" + groupValue + " sortValues=" + Arrays.toString(sortValues) + ")"); } @Override @@ -92,7 +76,12 @@ public int hashCode() { return groupValue != null ? groupValue.hashCode() : 0; } - private static class ShardIter { + /** + * Iterator for all the groups on a shard + * + * @lucene.experimental + */ + protected static class ShardIter { public final Iterator> iter; public final int shardIndex; @@ -117,8 +106,16 @@ public String toString() { } } - // Holds all shards currently on the same group - private static class MergedGroup { + protected MergedGroup newMergedGroup() { + return new MergedGroup<>(this.groupValue); + } + + /** + * Holds all shards currently on the same group + * + * @lucene.experimental + */ + protected static class MergedGroup { // groupValue may be null! public final T groupValue; @@ -129,15 +126,29 @@ private static class MergedGroup { public boolean processed; public boolean inQueue; - /** The top doc of this group: - * the Solr id and the score of the document */ - public float topDocScore; - public Object topDocSolrId; - public MergedGroup(T groupValue) { this.groupValue = groupValue; } + private SearchGroup toSearchGroup() { + final SearchGroup searchGroup = newSearchGroup(); + fillSearchGroup(searchGroup); + return searchGroup; + } + + protected SearchGroup newSearchGroup() { + return new SearchGroup(); + } + + protected void fillSearchGroup(SearchGroup searchGroup) { + searchGroup.groupValue = this.groupValue; + searchGroup.sortValues = this.topValues; + } + + protected void update(SearchGroup group) { + this.topValues = group.sortValues; + } + // Only for assert private boolean neverEquals(Object _other) { if (_other instanceof MergedGroup) { @@ -245,9 +256,7 @@ private void updateNextGroup(int topN, ShardIter shard) { if (isNew) { // Start a new group: //System.out.println(" new"); - mergedGroup = new MergedGroup<>(group.groupValue); - mergedGroup.topDocSolrId = group.topDocSolrId; - mergedGroup.topDocScore = group.topDocScore; + mergedGroup = group.newMergedGroup(); mergedGroup.minShardIndex = shard.shardIndex; assert group.sortValues != null; mergedGroup.topValues = group.sortValues; @@ -285,9 +294,7 @@ private void updateNextGroup(int topN, ShardIter shard) { if (mergedGroup.inQueue) { queue.remove(mergedGroup); } - mergedGroup.topDocScore = group.topDocScore; - mergedGroup.topDocSolrId = group.topDocSolrId; - mergedGroup.topValues = group.sortValues; + mergedGroup.update(group); mergedGroup.minShardIndex = shard.shardIndex; queue.add(mergedGroup); mergedGroup.inQueue = true; @@ -330,11 +337,7 @@ public Collection> merge(List>> shards, group.processed = true; //System.out.println(" pop: shards=" + group.shards + " group=" + (group.groupValue == null ? "null" : (((BytesRef) group.groupValue).utf8ToString())) + " sortValues=" + Arrays.toString(group.topValues)); if (count++ >= offset) { - final SearchGroup newGroup = new SearchGroup<>(); - newGroup.groupValue = group.groupValue; - newGroup.sortValues = group.topValues; - newGroup.topDocSolrId = group.topDocSolrId; - newGroup.topDocScore = group.topDocScore; + final SearchGroup newGroup = group.toSearchGroup(); newTopGroups.add(newGroup); if (newTopGroups.size() == topN) { break; diff --git a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java index 03225ea26c0f..e5be3d8360dc 100644 --- a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java +++ b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java @@ -1330,6 +1330,7 @@ private void doProcessGroupedDistributedSearchFirstPhase(ResponseBuilder rb, Que .setGroupSort(groupingSpec.getGroupSortSpec().getSort()) .setTopNGroups(cmd.getOffset() + cmd.getLen()) .setIncludeGroupCount(groupingSpec.isIncludeGroupCount()) + .setSkipSecondGroupingStep(groupingSpec.isSkipSecondGroupingStep()) .build() ); } diff --git a/solr/core/src/java/org/apache/solr/search/grouping/SkipSecondPassFirstPassGroupingCollector.java b/solr/core/src/java/org/apache/solr/search/grouping/SkipSecondPassFirstPassGroupingCollector.java new file mode 100644 index 000000000000..537c62f89952 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/grouping/SkipSecondPassFirstPassGroupingCollector.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.solr.search.grouping; + +import org.apache.lucene.search.FieldComparator; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.grouping.CollectedSearchGroup; +import org.apache.lucene.search.grouping.FirstPassGroupingCollector; +import org.apache.lucene.search.grouping.GroupSelector; +import org.apache.lucene.search.grouping.SearchGroup; + +/** + * A {@link FirstPassGroupingCollector} that gathers extra information so that (in certain scenarios) + * the second pass grouping can be skipped. + */ +public class SkipSecondPassFirstPassGroupingCollector extends FirstPassGroupingCollector { + + /** + * A {@link SearchGroup} that contains extra information so that (in certain scenarios) + * the second pass grouping can be skipped. + */ + public static class SolrSearchGroup extends org.apache.lucene.search.grouping.SearchGroup { + + public int topDocLuceneId; + public float topDocScore; + public Object topDocSolrId; + + @Override + protected MergedGroup newMergedGroup() { + SolrMergedGroup mergedGroup = new SolrMergedGroup<>(this.groupValue); + mergedGroup.topDocScore = this.topDocScore; + mergedGroup.topDocSolrId = this.topDocSolrId; + return mergedGroup; + } + + private static class SolrMergedGroup extends org.apache.lucene.search.grouping.SearchGroup.MergedGroup { + + public float topDocScore; + public Object topDocSolrId; + + public SolrMergedGroup(T groupValue) { + super(groupValue); + } + + @Override + protected SearchGroup newSearchGroup() { + return new SolrSearchGroup(); + } + + @Override + protected void fillSearchGroup(SearchGroup searchGroup) { + super.fillSearchGroup(searchGroup); + ((SolrSearchGroup)searchGroup).topDocScore = this.topDocScore; + ((SolrSearchGroup)searchGroup).topDocSolrId = this.topDocSolrId; + } + + @Override + public void update(SearchGroup searchGroup) { + super.update(searchGroup); + this.topDocScore = ((SolrSearchGroup)searchGroup).topDocScore; + this.topDocSolrId = ((SolrSearchGroup)searchGroup).topDocSolrId; + } + + } + + } + + public SkipSecondPassFirstPassGroupingCollector(GroupSelector groupSelector, Sort groupSort, int topNGroups) { + super(groupSelector, groupSort, topNGroups); + } + + @Override + protected SearchGroup newSearchGroup() { + return new SolrSearchGroup<>(); + } + + @Override + protected SearchGroup newSearchGroupFromCollectedSearchGroup( + CollectedSearchGroup group, + FieldComparator[] comparators, + int sortFieldCount) { + + final SearchGroup searchGroup = super.newSearchGroupFromCollectedSearchGroup(group, comparators, sortFieldCount); + final SolrSearchGroup solrSearchGroup = (SolrSearchGroup)searchGroup; + + solrSearchGroup.topDocLuceneId = group.topDoc; + + solrSearchGroup.topDocScore = Float.NaN; + // if there is the score comparator we want to return the score + for (FieldComparator comparator: comparators) { + if (comparator instanceof FieldComparator.RelevanceComparator) { + solrSearchGroup.topDocScore = (Float)comparator.value(group.comparatorSlot); + } + } + + return solrSearchGroup; + } + +} diff --git a/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/GroupConverter.java b/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/GroupConverter.java index 85bcf01ba62e..82657623ec41 100644 --- a/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/GroupConverter.java +++ b/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/GroupConverter.java @@ -35,6 +35,7 @@ import org.apache.solr.schema.FieldType; import org.apache.solr.schema.NumberType; import org.apache.solr.schema.SchemaField; +import org.apache.solr.search.grouping.SkipSecondPassFirstPassGroupingCollector; /** * this is a transition class: for numeric types we use function-based distributed grouping, @@ -50,10 +51,17 @@ static Collection> fromMutable(SchemaField field, Collecti FieldType fieldType = field.getType(); List> result = new ArrayList<>(values.size()); for (SearchGroup original : values) { - SearchGroup converted = new SearchGroup(); + final SearchGroup converted; + if (original instanceof SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup) { + SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup solrOriginal = (SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup)original; + SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup solrConverted = new SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup(); + solrConverted.topDocLuceneId = solrOriginal.topDocLuceneId; + solrConverted.topDocScore = solrOriginal.topDocScore; + converted = solrConverted; + } else { + converted = new SearchGroup(); + } converted.sortValues = original.sortValues; - converted.topDocLuceneId = original.topDocLuceneId; - converted.topDocScore = original.topDocScore; if (original.groupValue.exists) { BytesRefBuilder binary = new BytesRefBuilder(); fieldType.readableToIndexed(original.groupValue.toString(), binary); diff --git a/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java b/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java index 425a9877ff49..8c882be28f63 100644 --- a/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java +++ b/solr/core/src/java/org/apache/solr/search/grouping/distributed/command/SearchGroupsFieldCommand.java @@ -28,6 +28,7 @@ import org.apache.lucene.search.Sort; import org.apache.lucene.search.grouping.AllGroupsCollector; import org.apache.lucene.search.grouping.FirstPassGroupingCollector; +import org.apache.lucene.search.grouping.GroupSelector; import org.apache.lucene.search.grouping.SearchGroup; import org.apache.lucene.search.grouping.TermGroupSelector; import org.apache.lucene.search.grouping.ValueSourceGroupSelector; @@ -35,6 +36,7 @@ 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.SkipSecondPassFirstPassGroupingCollector; /** * Creates all the collectors needed for the first phase and how to handle the results. @@ -47,6 +49,7 @@ public static class Builder { private Sort groupSort; private Integer topNGroups; private boolean includeGroupCount = false; + private boolean skipSecondGroupingStep = false; public Builder setField(SchemaField field) { this.field = field; @@ -68,12 +71,17 @@ public Builder setIncludeGroupCount(boolean includeGroupCount) { return this; } + public Builder setSkipSecondGroupingStep(boolean skipSecondGroupingStep) { + this.skipSecondGroupingStep = skipSecondGroupingStep; + 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, includeGroupCount); + return new SearchGroupsFieldCommand(field, groupSort, topNGroups, includeGroupCount, skipSecondGroupingStep); } } @@ -82,15 +90,25 @@ public SearchGroupsFieldCommand build() { private final Sort groupSort; private final int topNGroups; private final boolean includeGroupCount; + private final boolean skipSecondGroupingStep; private FirstPassGroupingCollector firstPassGroupingCollector; private AllGroupsCollector allGroupsCollector; - private SearchGroupsFieldCommand(SchemaField field, Sort groupSort, int topNGroups, boolean includeGroupCount) { + private SearchGroupsFieldCommand(SchemaField field, Sort groupSort, int topNGroups, boolean includeGroupCount, boolean skipSecondGroupingStep) { this.field = field; this.groupSort = groupSort; this.topNGroups = topNGroups; this.includeGroupCount = includeGroupCount; + this.skipSecondGroupingStep = skipSecondGroupingStep; + } + + private FirstPassGroupingCollector newFirstPassGroupingCollector(GroupSelector groupSelector, Sort groupSort, int topNGroups) { + if (skipSecondGroupingStep) { + return new SkipSecondPassFirstPassGroupingCollector<>(groupSelector, groupSort, topNGroups); + } else { + return new FirstPassGroupingCollector<>(groupSelector, groupSort, topNGroups); + } } @Override @@ -101,10 +119,10 @@ public List create() throws IOException { if (fieldType.getNumberType() != null) { ValueSource vs = fieldType.getValueSource(field, null); firstPassGroupingCollector - = new FirstPassGroupingCollector<>(new ValueSourceGroupSelector(vs, new HashMap<>()), groupSort, topNGroups); + = newFirstPassGroupingCollector(new ValueSourceGroupSelector(vs, new HashMap<>()), groupSort, topNGroups); } else { firstPassGroupingCollector - = new FirstPassGroupingCollector<>(new TermGroupSelector(field.getName()), groupSort, topNGroups); + = newFirstPassGroupingCollector(new TermGroupSelector(field.getName()), groupSort, topNGroups); } collectors.add(firstPassGroupingCollector); } diff --git a/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SkipSecondStepSearchGroupShardResponseProcessor.java b/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SkipSecondStepSearchGroupShardResponseProcessor.java index d41449663872..97479f59ddab 100644 --- a/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SkipSecondStepSearchGroupShardResponseProcessor.java +++ b/solr/core/src/java/org/apache/solr/search/grouping/distributed/responseprocessor/SkipSecondStepSearchGroupShardResponseProcessor.java @@ -31,6 +31,7 @@ import org.apache.solr.handler.component.ShardResponse; import org.apache.solr.search.SolrIndexSearcher; import org.apache.solr.search.grouping.GroupingSpecification; +import org.apache.solr.search.grouping.SkipSecondPassFirstPassGroupingCollector; import org.apache.solr.search.grouping.distributed.shardresultserializer.SearchGroupsResultTransformer; import org.apache.solr.search.grouping.distributed.shardresultserializer.SkipSecondStepSearchResultResultTransformer; @@ -65,7 +66,9 @@ public void addSearchGroups(ShardResponse srsp, String field, Collection searchGroup : searchGroups) { assert(srsp.getShard() != null); - docIdToShard.put(searchGroup.topDocSolrId, srsp.getShard()); + assert(searchGroup instanceof SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup); + SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup solrSearchGroup = (SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup)searchGroup; + docIdToShard.put(solrSearchGroup.topDocSolrId, srsp.getShard()); } } @@ -89,7 +92,11 @@ public void addSearchGroupToShards(ResponseBuilder rb, String groupField, Collec float maxScore = Float.MIN_VALUE; int groupsIndex = 0; - for (SearchGroup group : mergedTopGroups) { + for (SearchGroup mergedTopGroup : mergedTopGroups) { + + assert(mergedTopGroup instanceof SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup); + SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup group = (SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup)mergedTopGroup; + if (! Float.isNaN(group.topDocScore)) { maxScore = Math.max(maxScore, group.topDocScore); } diff --git a/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SearchGroupsResultTransformer.java b/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SearchGroupsResultTransformer.java index 85dcb3119172..130d11bf8c4a 100644 --- a/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SearchGroupsResultTransformer.java +++ b/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SearchGroupsResultTransformer.java @@ -76,9 +76,13 @@ protected Object[] getSortValues(Object rawSearchGroupData) { return sortValues.toArray(new Comparable[sortValues.size()]); } + protected SearchGroup newSearchGroup() { + return new SearchGroup<>(); + } + protected SearchGroup deserializeOneSearchGroup(SchemaField groupField, String groupValue, SortField[] groupSortField, Object rawSearchGroupData) { - SearchGroup searchGroup = new SearchGroup<>(); + SearchGroup searchGroup = newSearchGroup(); searchGroup.groupValue = null; if (groupValue != null) { if (groupField != null) { diff --git a/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SkipSecondStepSearchResultResultTransformer.java b/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SkipSecondStepSearchResultResultTransformer.java index 6ce5314282b4..dd62d434bcba 100644 --- a/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SkipSecondStepSearchResultResultTransformer.java +++ b/solr/core/src/java/org/apache/solr/search/grouping/distributed/shardresultserializer/SkipSecondStepSearchResultResultTransformer.java @@ -30,6 +30,7 @@ import org.apache.solr.common.util.NamedList; import org.apache.solr.schema.SchemaField; import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.grouping.SkipSecondPassFirstPassGroupingCollector; /** * Extends {@link SearchGroupsResultTransformer} and overrides the serializeOneSearchGroup, @@ -58,19 +59,31 @@ protected Object[] getSortValues(Object rawSearchGroupData) { return sortValues.toArray(new Comparable[sortValues.size()]); } + @Override + protected SearchGroup newSearchGroup() { + return new SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup<>(); + } + @Override protected SearchGroup deserializeOneSearchGroup(SchemaField groupField, String groupValue, SortField[] groupSortField, Object rawSearchGroupData) { SearchGroup searchGroup = super.deserializeOneSearchGroup(groupField, groupValue, groupSortField, rawSearchGroupData); - NamedList groupInfo = (NamedList) rawSearchGroupData; - searchGroup.topDocLuceneId = DocIdSetIterator.NO_MORE_DOCS; - searchGroup.topDocScore = (float) groupInfo.get(TOP_DOC_SCORE_KEY); - searchGroup.topDocSolrId = groupInfo.get(TOP_DOC_SOLR_ID_KEY); + if (searchGroup instanceof SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup) { + SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup solrSearchGroup = (SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup)searchGroup; + NamedList groupInfo = (NamedList) rawSearchGroupData; + solrSearchGroup.topDocLuceneId = DocIdSetIterator.NO_MORE_DOCS; + solrSearchGroup.topDocScore = (float) groupInfo.get(TOP_DOC_SCORE_KEY); + solrSearchGroup.topDocSolrId = groupInfo.get(TOP_DOC_SOLR_ID_KEY); + } return searchGroup; } @Override - protected Object serializeOneSearchGroup(SortField[] groupSortField, SearchGroup searchGroup) { + protected Object serializeOneSearchGroup(SortField[] groupSortField, SearchGroup luceneSearchGroup) { + + assert(luceneSearchGroup instanceof SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup); + SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup searchGroup = (SkipSecondPassFirstPassGroupingCollector.SolrSearchGroup)luceneSearchGroup; + Document luceneDoc = null; /** Use the lucene id to get the unique solr id so that it can be sent to the federator. * The lucene id of a document is not unique across all shards i.e. different documents