You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by jb...@apache.org on 2014/03/03 16:45:59 UTC
svn commit: r1573589 - in /lucene/dev/trunk/solr:
core/src/java/org/apache/solr/core/
core/src/java/org/apache/solr/handler/component/
core/src/java/org/apache/solr/search/
core/src/test/org/apache/solr/handler/component/
solrj/src/java/org/apache/solr...
Author: jbernste
Date: Mon Mar 3 15:45:59 2014
New Revision: 1573589
URL: http://svn.apache.org/r1573589
Log:
SOLR-5720: Add ExpandComponent to expand results collapsed by the CollapsingQParserPlugin
Added:
lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java (with props)
lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/DistributedExpandComponentTest.java (with props)
lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/TestExpandComponent.java (with props)
lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/ExpandParams.java (with props)
Modified:
lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrCore.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java
lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/response/QueryResponse.java
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrCore.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrCore.java?rev=1573589&r1=1573588&r2=1573589&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrCore.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/core/SolrCore.java Mon Mar 3 15:45:59 2014
@@ -84,6 +84,7 @@ import org.apache.solr.handler.component
import org.apache.solr.handler.component.RealTimeGetComponent;
import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.handler.component.StatsComponent;
+import org.apache.solr.handler.component.ExpandComponent;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.BinaryResponseWriter;
@@ -1237,6 +1238,8 @@ public final class SolrCore implements S
addIfNotPresent(components,DebugComponent.COMPONENT_NAME,DebugComponent.class);
addIfNotPresent(components,RealTimeGetComponent.COMPONENT_NAME,RealTimeGetComponent.class);
addIfNotPresent(components,AnalyticsComponent.COMPONENT_NAME,AnalyticsComponent.class);
+ addIfNotPresent(components,ExpandComponent.COMPONENT_NAME,ExpandComponent.class);
+
return components;
}
private <T> void addIfNotPresent(Map<String ,T> registry, String name, Class<? extends T> c){
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java?rev=1573589&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java Mon Mar 3 15:45:59 2014
@@ -0,0 +1,337 @@
+/*
+ * 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.handler.component;
+
+import org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.TopDocsCollector;
+import org.apache.lucene.search.TopFieldCollector;
+import org.apache.lucene.search.TopScoreDocCollector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.CharsRef;
+import org.apache.lucene.util.FixedBitSet;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.params.ShardParams;
+import org.apache.solr.search.CollapsingQParserPlugin;
+import org.apache.solr.search.DocIterator;
+import org.apache.solr.search.DocList;
+import org.apache.solr.search.QueryParsing;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.params.ExpandParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.DocSlice;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.util.plugin.PluginInfoInitialized;
+import org.apache.solr.util.plugin.SolrCoreAware;
+import org.apache.solr.core.PluginInfo;
+import org.apache.solr.core.SolrCore;
+
+import com.carrotsearch.hppc.IntObjectOpenHashMap;
+import com.carrotsearch.hppc.IntOpenHashSet;
+import com.carrotsearch.hppc.cursors.IntObjectCursor;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+
+/**
+ * The ExpandComponent is designed to work with the CollapsingPostFilter.
+ * The CollapsingPostFilter collapses a result set on a field.
+ * <p/>
+ * The ExpandComponent expands the collapsed groups for a single page.
+ * <p/>
+ * http parameters:
+ * <p/>
+ * expand=true <br/>
+ * expand.rows=5 </br>
+ * expand.sort=field asc|desc
+ *
+ **/
+
+public class ExpandComponent extends SearchComponent implements PluginInfoInitialized, SolrCoreAware {
+ public static final String COMPONENT_NAME = "expand";
+ private PluginInfo info = PluginInfo.EMPTY_INFO;
+
+ @Override
+ public void init(PluginInfo info) {
+ this.info = info;
+ }
+
+ @Override
+ public void prepare(ResponseBuilder rb) throws IOException {
+ if (rb.req.getParams().getBool(ExpandParams.EXPAND,false)) {
+ rb.doExpand = true;
+ }
+ }
+ @Override
+ public void inform(SolrCore core) {
+
+ }
+
+ @Override
+ public void process(ResponseBuilder rb) throws IOException {
+
+ if(!rb.doExpand) {
+ return;
+ }
+
+ SolrQueryRequest req = rb.req;
+ SolrParams params = req.getParams();
+
+ boolean isShard = params.getBool(ShardParams.IS_SHARD, false);
+ String ids = params.get(ShardParams.IDS);
+
+ if(ids == null && isShard) {
+ return;
+ }
+
+ String field = null;
+ String sortParam = params.get(ExpandParams.EXPAND_SORT);
+ int limit = params.getInt(ExpandParams.EXPAND_ROWS, 5);
+
+ Sort sort = null;
+
+ if(sortParam != null) {
+ sort = QueryParsing.parseSortSpec(sortParam, rb.req).getSort();
+ }
+
+ Query query = rb.getQuery();
+ List<Query> filters = rb.getFilters();
+ List<Query> newFilters = new ArrayList();
+ for(Query q : filters) {
+ if(!(q instanceof CollapsingQParserPlugin.CollapsingPostFilter)) {
+ newFilters.add(q);
+ } else {
+ CollapsingQParserPlugin.CollapsingPostFilter cp = (CollapsingQParserPlugin.CollapsingPostFilter)q;
+ field = cp.getField();
+ }
+ }
+
+ if(field == null) {
+ throw new IOException("Expand field is null.");
+ }
+
+ SolrIndexSearcher searcher = req.getSearcher();
+ AtomicReader reader = searcher.getAtomicReader();
+ SortedDocValues values = FieldCache.DEFAULT.getTermsIndex(reader, field);
+ FixedBitSet groupBits = new FixedBitSet(values.getValueCount());
+ DocList docList = rb.getResults().docList;
+ IntOpenHashSet collapsedSet = new IntOpenHashSet(docList.size()*2);
+
+ DocIterator idit = docList.iterator();
+
+ while(idit.hasNext()) {
+ int doc = idit.nextDoc();
+ int ord = values.getOrd(doc);
+ if(ord > -1) {
+ groupBits.set(ord);
+ collapsedSet.add(doc);
+ }
+ }
+
+ Collector collector = null;
+ GroupExpandCollector groupExpandCollector = new GroupExpandCollector(values, groupBits, collapsedSet, limit, sort);
+ SolrIndexSearcher.ProcessedFilter pfilter = searcher.getProcessedFilter(null, newFilters);
+ if(pfilter.postFilter != null) {
+ pfilter.postFilter.setLastDelegate(groupExpandCollector);
+ collector = pfilter.postFilter;
+ } else {
+ collector = groupExpandCollector;
+ }
+
+ searcher.search(query, pfilter.filter, collector);
+ IntObjectOpenHashMap groups = groupExpandCollector.getGroups();
+ Iterator<IntObjectCursor> it = groups.iterator();
+ Map<String, DocSlice> outMap = new HashMap<>();
+ BytesRef bytesRef = new BytesRef();
+ CharsRef charsRef = new CharsRef();
+ FieldType fieldType = searcher.getSchema().getField(field).getType();
+
+ while(it.hasNext()) {
+ IntObjectCursor cursor = it.next();
+ int ord = cursor.key;
+ TopDocsCollector topDocsCollector = (TopDocsCollector)cursor.value;
+ TopDocs topDocs = topDocsCollector.topDocs();
+ ScoreDoc[] scoreDocs = topDocs.scoreDocs;
+ if(scoreDocs.length > 0) {
+ int[] docs = new int[scoreDocs.length];
+ float[] scores = new float[scoreDocs.length];
+ for(int i=0; i<docs.length; i++) {
+ ScoreDoc scoreDoc = scoreDocs[i];
+ docs[i] = scoreDoc.doc;
+ scores[i] = scoreDoc.score;
+ }
+ DocSlice slice = new DocSlice(0, docs.length, docs, scores, topDocs.totalHits, topDocs.getMaxScore());
+ values.lookupOrd(ord, bytesRef);
+ fieldType.indexedToReadable(bytesRef, charsRef);
+ String group = charsRef.toString();
+ outMap.put(group, slice);
+ }
+ }
+
+ rb.rsp.add("expanded", outMap);
+ }
+ @Override
+ public void modifyRequest(ResponseBuilder rb, SearchComponent who, ShardRequest sreq) {
+
+ }
+ @Override
+ public void handleResponses(ResponseBuilder rb, ShardRequest sreq) {
+
+ if(!rb.doExpand) {
+ return;
+ }
+
+ if ((sreq.purpose & ShardRequest.PURPOSE_GET_FIELDS) != 0) {
+ SolrQueryRequest req = rb.req;
+ Map expanded = (Map)req.getContext().get("expanded");
+ if(expanded == null) {
+ expanded = new HashMap();
+ req.getContext().put("expanded", expanded);
+ }
+
+ for (ShardResponse srsp : sreq.responses) {
+ NamedList response = srsp.getSolrResponse().getResponse();
+ Map ex = (Map)response.get("expanded");
+ Iterator<Map.Entry<String,SolrDocumentList>>it = ex.entrySet().iterator();
+ while(it.hasNext()) {
+ Map.Entry<String, SolrDocumentList> entry = it.next();
+ String name = entry.getKey();
+ SolrDocumentList val = entry.getValue();
+ expanded.put(name, val);
+ }
+ }
+ }
+ }
+ @Override
+ public void finishStage(ResponseBuilder rb) {
+
+ if(!rb.doExpand) {
+ return;
+ }
+
+ if (rb.stage != ResponseBuilder.STAGE_GET_FIELDS) {
+ return;
+ }
+
+ Map expanded = (Map)rb.req.getContext().get("expanded");
+ if(expanded == null) {
+ expanded = new HashMap();
+ }
+
+ rb.rsp.add("expanded", expanded);
+ }
+
+ private class GroupExpandCollector extends Collector {
+ private SortedDocValues docValues;
+ private IntObjectOpenHashMap groups;
+ private int docBase;
+ private FixedBitSet groupBits;
+ private IntOpenHashSet collapsedSet;
+ private List<Collector> collectors;
+
+ public GroupExpandCollector(SortedDocValues docValues, FixedBitSet groupBits, IntOpenHashSet collapsedSet, int limit, Sort sort) throws IOException {
+ int numGroups = collapsedSet.size();
+ groups = new IntObjectOpenHashMap(numGroups*2);
+ collectors = new ArrayList();
+ DocIdSetIterator iterator = groupBits.iterator();
+ int group = -1;
+ while((group = iterator.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
+ Collector collector = (sort == null) ? TopScoreDocCollector.create(limit, true) : TopFieldCollector.create(sort,limit, false, false,false, true);
+ groups.put(group, collector);
+ collectors.add(collector);
+ }
+
+ this.collapsedSet = collapsedSet;
+ this.groupBits = groupBits;
+ this.docValues = docValues;
+ }
+
+ public IntObjectOpenHashMap getGroups() {
+ return this.groups;
+ }
+
+ public boolean acceptsDocsOutOfOrder() {
+ return false;
+ }
+
+ public void collect(int docId) throws IOException {
+ int doc = docId+docBase;
+ int ord = docValues.getOrd(doc);
+ if(ord > -1 && groupBits.get(ord) && !collapsedSet.contains(doc)) {
+ Collector c = (Collector)groups.get(ord);
+ c.collect(docId);
+ }
+ }
+
+ public void setNextReader(AtomicReaderContext context) throws IOException {
+ this.docBase = context.docBase;
+ for(Collector c : collectors) {
+ c.setNextReader(context);
+ }
+ }
+
+ public void setScorer(Scorer scorer) throws IOException {
+ for(Collector c : collectors) {
+ c.setScorer(scorer);
+ }
+ }
+ }
+
+ ////////////////////////////////////////////
+ /// SolrInfoMBean
+ ////////////////////////////////////////////
+
+ @Override
+ public String getDescription() {
+ return "Expand Component";
+ }
+
+ @Override
+ public String getSource() {
+ return "$URL: https://svn.apache.org/repos/asf/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ExpandComponent.java $";
+ }
+
+ @Override
+ public URL[] getDocs() {
+ try {
+ return new URL[]{
+ new URL("http://wiki.apache.org/solr/ExpandComponent")
+ };
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
\ No newline at end of file
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java?rev=1573589&r1=1573588&r2=1573589&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java Mon Mar 3 15:45:59 2014
@@ -55,6 +55,7 @@ public class ResponseBuilder
public SolrQueryResponse rsp;
public boolean doHighlights;
public boolean doFacets;
+ public boolean doExpand;
public boolean doStats;
public boolean doTerms;
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java?rev=1573589&r1=1573588&r2=1573589&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java Mon Mar 3 15:45:59 2014
@@ -72,6 +72,7 @@ public class SearchHandler extends Reque
names.add( StatsComponent.COMPONENT_NAME );
names.add( DebugComponent.COMPONENT_NAME );
names.add( AnalyticsComponent.COMPONENT_NAME );
+ names.add( ExpandComponent.COMPONENT_NAME);
return names;
}
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java?rev=1573589&r1=1573588&r2=1573589&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java Mon Mar 3 15:45:59 2014
@@ -140,6 +140,11 @@ public class CollapsingQParserPlugin ext
public static final int NULL_POLICY_COLLAPSE = 1;
public static final int NULL_POLICY_EXPAND = 2;
+
+ public String getField(){
+ return this.field;
+ }
+
public void setCache(boolean cache) {
}
Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/DistributedExpandComponentTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/DistributedExpandComponentTest.java?rev=1573589&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/DistributedExpandComponentTest.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/DistributedExpandComponentTest.java Mon Mar 3 15:45:59 2014
@@ -0,0 +1,189 @@
+package org.apache.solr.handler.component;
+
+/*
+ * 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.BaseDistributedSearchTestCase;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.junit.BeforeClass;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Iterator;
+
+/**
+ * Test for QueryComponent's distributed querying
+ *
+ * @see org.apache.solr.handler.component.QueryComponent
+ */
+public class DistributedExpandComponentTest extends BaseDistributedSearchTestCase {
+
+ public DistributedExpandComponentTest() {
+ fixShardCount = true;
+ shardCount = 3;
+ stress = 0;
+ }
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ initCore("solrconfig-collapseqparser.xml", "schema11.xml");
+ }
+
+ @Override
+ public void doTest() throws Exception {
+ del("*:*");
+
+ index_specific(0,"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000");
+ index_specific(0,"id","2", "term_s", "YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200");
+ index_specific(1,"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000");
+ index_specific(1,"id","6", "term_s", "YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200");
+ index_specific(0,"id","7", "term_s", "YYYY", "group_s", "group1", "test_ti", "1", "test_tl", "100000", "test_tf", "2000");
+ index_specific(1,"id","8", "term_s", "YYYY", "group_s", "group2", "test_ti", "2", "test_tl", "100000", "test_tf", "200");
+ index_specific(2,"id","9", "term_s", "YYYY", "group_s", "group3", "test_ti", "1000", "test_tl", "1005", "test_tf", "3000");
+ index_specific(2, "id", "10", "term_s", "YYYY", "group_s", "group3", "test_ti", "1500", "test_tl", "1001", "test_tf", "3200");
+ index_specific(2,"id", "11", "term_s", "YYYY", "group_s", "group3", "test_ti", "1300", "test_tl", "1002", "test_tf", "3300");
+ index_specific(1,"id","12", "term_s", "YYYY", "group_s", "group4", "test_ti", "15", "test_tl", "10", "test_tf", "2000");
+ index_specific(1,"id","13", "term_s", "YYYY", "group_s", "group4", "test_ti", "16", "test_tl", "9", "test_tf", "2000");
+ index_specific(1,"id","14", "term_s", "YYYY", "group_s", "group4", "test_ti", "1", "test_tl", "20", "test_tf", "2000");
+
+
+ commit();
+
+
+ handle.put("explain", SKIPVAL);
+ handle.put("QTime", SKIPVAL);
+ handle.put("timestamp", SKIPVAL);
+ handle.put("score", SKIPVAL);
+ handle.put("wt", SKIP);
+ handle.put("distrib", SKIP);
+ handle.put("shards.qt", SKIP);
+ handle.put("shards", SKIP);
+ handle.put("q", SKIP);
+ handle.put("maxScore", SKIPVAL);
+ handle.put("_version_", SKIP);
+
+ query("q", "*:*", "fq", "{!collapse field=group_s}", "defType", "edismax", "bf", "field(test_ti)", "expand", "true", "fl","*,score");
+ query("q", "*:*", "fq", "{!collapse field=group_s}", "defType", "edismax", "bf", "field(test_ti)", "expand", "true", "expand.sort", "test_tl desc", "fl","*,score");
+ query("q", "*:*", "fq", "{!collapse field=group_s}", "defType", "edismax", "bf", "field(test_ti)", "expand", "true", "expand.sort", "test_tl desc", "expand.rows", "1", "fl","*,score");
+ //Test no expand results
+ query("q", "test_ti:5", "fq", "{!collapse field=group_s}", "defType", "edismax", "bf", "field(test_ti)", "expand", "true", "expand.sort", "test_tl desc", "expand.rows", "1", "fl","*,score");
+ //Test zero results
+ query("q", "test_ti:5434343", "fq", "{!collapse field=group_s}", "defType", "edismax", "bf", "field(test_ti)", "expand", "true", "expand.sort", "test_tl desc", "expand.rows", "1", "fl","*,score");
+
+ //First basic test case.
+ ModifiableSolrParams params = new ModifiableSolrParams();
+ params.add("q", "*:*");
+ params.add("fq", "{!collapse field=group_s}");
+ params.add("defType", "edismax");
+ params.add("bf", "field(test_ti)");
+ params.add("expand", "true");
+
+ setDistributedParams(params);
+ QueryResponse rsp = queryServer(params);
+ Map<String, SolrDocumentList> results = rsp.getExpandedResults();
+ assertExpandGroups(results, "group1","group2", "group3", "group4");
+ assertExpandGroupCountAndOrder("group1", 2, results, "1.0", "7.0");
+ assertExpandGroupCountAndOrder("group2", 2, results, "5.0", "8.0");
+ assertExpandGroupCountAndOrder("group3", 2, results, "11.0", "9.0");
+ assertExpandGroupCountAndOrder("group4", 2, results, "12.0", "14.0");
+
+
+ //Test expand.sort
+
+ params = new ModifiableSolrParams();
+ params.add("q", "*:*");
+ params.add("fq", "{!collapse field=group_s}");
+ params.add("defType", "edismax");
+ params.add("bf", "field(test_ti)");
+ params.add("expand", "true");
+ params.add("expand.sort", "test_tl desc");
+ setDistributedParams(params);
+ rsp = queryServer(params);
+ results = rsp.getExpandedResults();
+ assertExpandGroups(results, "group1","group2", "group3", "group4");
+ assertExpandGroupCountAndOrder("group1", 2, results, "7.0", "1.0");
+ assertExpandGroupCountAndOrder("group2", 2, results, "8.0", "5.0");
+ assertExpandGroupCountAndOrder("group3", 2, results, "9.0", "11.0");
+ assertExpandGroupCountAndOrder("group4", 2, results, "14.0", "12.0");
+
+
+ //Test expand.rows
+
+ params = new ModifiableSolrParams();
+ params.add("q", "*:*");
+ params.add("fq", "{!collapse field=group_s}");
+ params.add("defType", "edismax");
+ params.add("bf", "field(test_ti)");
+ params.add("expand", "true");
+ params.add("expand.sort", "test_tl desc");
+ params.add("expand.rows", "1");
+ setDistributedParams(params);
+ rsp = queryServer(params);
+ results = rsp.getExpandedResults();
+ assertExpandGroups(results, "group1","group2", "group3", "group4");
+ assertExpandGroupCountAndOrder("group1", 1, results, "7.0");
+ assertExpandGroupCountAndOrder("group2", 1, results, "8.0");
+ assertExpandGroupCountAndOrder("group3", 1, results, "9.0");
+ assertExpandGroupCountAndOrder("group4", 1, results, "14.0");
+
+ }
+
+ private void assertExpandGroups(Map<String, SolrDocumentList> expandedResults, String... groups) throws Exception {
+ for(int i=0; i<groups.length; i++) {
+ if(!expandedResults.containsKey(groups[i])) {
+ throw new Exception("Expanded Group Not Found:"+groups[i]+", Found:"+exportGroups(expandedResults));
+ }
+ }
+ }
+
+ private String exportGroups(Map<String, SolrDocumentList> groups) {
+ StringBuilder buf = new StringBuilder();
+ Iterator<String> it = groups.keySet().iterator();
+ while(it.hasNext()) {
+ String group = it.next();
+ buf.append(group);
+ if(it.hasNext()) {
+ buf.append(",");
+ }
+ }
+ return buf.toString();
+ }
+
+ private void assertExpandGroupCountAndOrder(String group, int count, Map<String, SolrDocumentList>expandedResults, String... docs) throws Exception {
+ SolrDocumentList results = expandedResults.get(group);
+ if(results == null) {
+ throw new Exception("Group Not Found:"+group);
+ }
+
+ if(results.size() != count) {
+ throw new Exception("Expected Count "+results.size()+" Not Found:"+count);
+ }
+
+ for(int i=0; i<docs.length;i++) {
+ String id = docs[i];
+ SolrDocument doc = results.get(i);
+ if(!doc.getFieldValue("id").toString().equals(id)) {
+ throw new Exception("Id not in results or out of order:"+id+"!="+doc.getFieldValue("id"));
+ }
+ }
+ }
+}
Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/TestExpandComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/TestExpandComponent.java?rev=1573589&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/TestExpandComponent.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/TestExpandComponent.java Mon Mar 3 15:45:59 2014
@@ -0,0 +1,193 @@
+/*
+* 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.handler.component;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.*;
+
+public class TestExpandComponent extends SolrTestCaseJ4 {
+
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ initCore("solrconfig-collapseqparser.xml", "schema11.xml");
+ }
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ // if you override setUp or tearDown, you better call
+ // the super classes version
+ super.setUp();
+ clearIndex();
+ assertU(commit());
+ }
+
+ @Test
+ public void testExpand() throws Exception {
+ String[] doc = {"id","1", "term_s", "YYYY", "group_s", "group1", "test_ti", "5", "test_tl", "10", "test_tf", "2000"};
+ assertU(adoc(doc));
+ assertU(commit());
+ String[] doc1 = {"id","2", "term_s","YYYY", "group_s", "group1", "test_ti", "50", "test_tl", "100", "test_tf", "200"};
+ assertU(adoc(doc1));
+
+ String[] doc2 = {"id","3", "term_s", "YYYY", "test_ti", "5000", "test_tl", "100", "test_tf", "200"};
+ assertU(adoc(doc2));
+ assertU(commit());
+ String[] doc3 = {"id","4", "term_s", "YYYY", "test_ti", "500", "test_tl", "1000", "test_tf", "2000"};
+ assertU(adoc(doc3));
+
+
+ String[] doc4 = {"id","5", "term_s", "YYYY", "group_s", "group2", "test_ti", "4", "test_tl", "10", "test_tf", "2000"};
+ assertU(adoc(doc4));
+ assertU(commit());
+ String[] doc5 = {"id","6", "term_s","YYYY", "group_s", "group2", "test_ti", "10", "test_tl", "100", "test_tf", "200"};
+ assertU(adoc(doc5));
+ assertU(commit());
+
+ String[] doc6 = {"id","7", "term_s", "YYYY", "group_s", "group1", "test_ti", "1", "test_tl", "100000", "test_tf", "2000"};
+ assertU(adoc(doc6));
+ assertU(commit());
+ String[] doc7 = {"id","8", "term_s","YYYY", "group_s", "group2", "test_ti", "2", "test_tl", "100000", "test_tf", "200"};
+ assertU(adoc(doc7));
+
+ assertU(commit());
+
+
+
+ //First basic test case.
+ ModifiableSolrParams params = new ModifiableSolrParams();
+ params.add("q", "*:*");
+ params.add("fq", "{!collapse field=group_s}");
+ params.add("defType", "edismax");
+ params.add("bf", "field(test_ti)");
+ params.add("expand", "true");
+ assertQ(req(params), "*[count(/response/result/doc)=2]",
+ "*[count(/response/lst[@name='expanded']/result)=2]",
+ "/response/result/doc[1]/float[@name='id'][.='2.0']",
+ "/response/result/doc[2]/float[@name='id'][.='6.0']",
+ "/response/lst[@name='expanded']/result[@name='group1']/doc[1]/float[@name='id'][.='1.0']",
+ "/response/lst[@name='expanded']/result[@name='group1']/doc[2]/float[@name='id'][.='7.0']",
+ "/response/lst[@name='expanded']/result[@name='group2']/doc[1]/float[@name='id'][.='5.0']",
+ "/response/lst[@name='expanded']/result[@name='group2']/doc[2]/float[@name='id'][.='8.0']"
+ );
+
+ //Test expand.sort
+ params = new ModifiableSolrParams();
+ params.add("q", "*:*");
+ params.add("fq", "{!collapse field=group_s}");
+ params.add("defType", "edismax");
+ params.add("bf", "field(test_ti)");
+ params.add("expand", "true");
+ params.add("expand.sort", "test_tl desc");
+ assertQ(req(params), "*[count(/response/result/doc)=2]",
+ "*[count(/response/lst[@name='expanded']/result)=2]",
+ "/response/result/doc[1]/float[@name='id'][.='2.0']",
+ "/response/result/doc[2]/float[@name='id'][.='6.0']",
+ "/response/lst[@name='expanded']/result[@name='group1']/doc[1]/float[@name='id'][.='7.0']",
+ "/response/lst[@name='expanded']/result[@name='group1']/doc[2]/float[@name='id'][.='1.0']",
+ "/response/lst[@name='expanded']/result[@name='group2']/doc[1]/float[@name='id'][.='8.0']",
+ "/response/lst[@name='expanded']/result[@name='group2']/doc[2]/float[@name='id'][.='5.0']"
+ );
+
+ //Test with nullPolicy, ExpandComponent should ignore docs with null values in the collapse fields.
+ //Main result set should include the doc with null value in the collapse field.
+ params = new ModifiableSolrParams();
+ params.add("q", "*:*");
+ params.add("fq", "{!collapse field=group_s nullPolicy=collapse}");
+ params.add("defType", "edismax");
+ params.add("bf", "field(test_ti)");
+ params.add("expand", "true");
+ params.add("expand.sort", "test_tl desc");
+ assertQ(req(params), "*[count(/response/result/doc)=3]",
+ "*[count(/response/lst[@name='expanded']/result)=2]",
+ "/response/result/doc[1]/float[@name='id'][.='3.0']",
+ "/response/result/doc[2]/float[@name='id'][.='2.0']",
+ "/response/result/doc[3]/float[@name='id'][.='6.0']",
+ "/response/lst[@name='expanded']/result[@name='group1']/doc[1]/float[@name='id'][.='7.0']",
+ "/response/lst[@name='expanded']/result[@name='group1']/doc[2]/float[@name='id'][.='1.0']",
+ "/response/lst[@name='expanded']/result[@name='group2']/doc[1]/float[@name='id'][.='8.0']",
+ "/response/lst[@name='expanded']/result[@name='group2']/doc[2]/float[@name='id'][.='5.0']"
+ );
+
+
+ //Test expand.rows
+
+ params = new ModifiableSolrParams();
+ params.add("q", "*:*");
+ params.add("fq", "{!collapse field=group_s}");
+ params.add("defType", "edismax");
+ params.add("bf", "field(test_ti)");
+ params.add("expand", "true");
+ params.add("expand.sort", "test_tl desc");
+ params.add("expand.rows", "1");
+ assertQ(req(params), "*[count(/response/result/doc)=2]",
+ "*[count(/response/lst[@name='expanded']/result)=2]",
+ "*[count(/response/lst[@name='expanded']/result[@name='group1']/doc)=1]",
+ "*[count(/response/lst[@name='expanded']/result[@name='group2']/doc)=1]",
+ "/response/result/doc[1]/float[@name='id'][.='2.0']",
+ "/response/result/doc[2]/float[@name='id'][.='6.0']",
+ "/response/lst[@name='expanded']/result[@name='group1']/doc[1]/float[@name='id'][.='7.0']",
+ "/response/lst[@name='expanded']/result[@name='group2']/doc[1]/float[@name='id'][.='8.0']"
+ );
+
+
+ //Test no group results
+
+ params = new ModifiableSolrParams();
+ params.add("q", "test_ti:5");
+ params.add("fq", "{!collapse field=group_s}");
+ params.add("defType", "edismax");
+ params.add("bf", "field(test_ti)");
+ params.add("expand", "true");
+ params.add("expand.sort", "test_tl desc");
+ params.add("expand.rows", "1");
+ assertQ(req(params), "*[count(/response/result/doc)=1]",
+ "*[count(/response/lst[@name='expanded']/result)=0]"
+ );
+
+ //Test zero results
+
+ params = new ModifiableSolrParams();
+ params.add("q", "test_ti:5532535");
+ params.add("fq", "{!collapse field=group_s}");
+ params.add("defType", "edismax");
+ params.add("bf", "field(test_ti)");
+ params.add("expand", "true");
+ params.add("expand.sort", "test_tl desc");
+ params.add("expand.rows", "1");
+ assertQ(req(params), "*[count(/response/result/doc)=0]",
+ "*[count(/response/lst[@name='expanded']/result)=0]"
+ );
+
+
+
+
+
+
+ }
+
+
+
+
+}
Modified: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/response/QueryResponse.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/response/QueryResponse.java?rev=1573589&r1=1573588&r2=1573589&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/response/QueryResponse.java (original)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/response/QueryResponse.java Mon Mar 3 15:45:59 2014
@@ -50,6 +50,9 @@ public class QueryResponse extends SolrR
private NamedList<Object> _groupedInfo = null;
private GroupResponse _groupResponse = null;
+ private NamedList<Object> _expandedInfo = null;
+ private Map<String, SolrDocumentList> _expandedResults = null;
+
// Facet stuff
private Map<String,Integer> _facetQuery = null;
private List<FacetField> _facetFields = null;
@@ -119,7 +122,10 @@ public class QueryResponse extends SolrR
_groupedInfo = (NamedList<Object>) res.getVal( i );
extractGroupedInfo( _groupedInfo );
}
- else if( "highlighting".equals( n ) ) {
+ else if("expanded".equals(n)) {
+ _expandedResults = (Map<String, SolrDocumentList>) res.getVal( i );
+ }
+ else if( "highlighting".equals( n ) ) {
_highlightingInfo = (NamedList<Object>) res.getVal( i );
extractHighlightingInfo( _highlightingInfo );
}
@@ -410,6 +416,10 @@ public class QueryResponse extends SolrR
return _facetQuery;
}
+ public Map<String, SolrDocumentList> getExpandedResults(){
+ return this._expandedResults;
+ }
+
/**
* Returns the {@link GroupResponse} containing the group commands.
* A group command can be the result of one of the following parameters:
Added: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/ExpandParams.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/ExpandParams.java?rev=1573589&view=auto
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/ExpandParams.java (added)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/common/params/ExpandParams.java Mon Mar 3 15:45:59 2014
@@ -0,0 +1,31 @@
+/*
+ * 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.common.params;
+
+/**
+ * Expand parameters
+ */
+public interface ExpandParams {
+
+ public static final String EXPAND = "expand";
+ public static final String EXPAND_SORT = EXPAND + ".sort";
+ public static final String EXPAND_ROWS = EXPAND + ".rows";
+
+
+}
+