mirror of https://github.com/apache/lucene.git
SOLR-792: Adding PivotFacetComponent for Hierarchical faceting
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@996707 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c838bbfbd1
commit
e88daca523
|
@ -265,6 +265,8 @@ New Features
|
|||
|
||||
* SOLR-2112: Solrj API now supports streaming results. (ryan)
|
||||
|
||||
* SOLR-792: Adding PivotFacetComponent for Hierarchical faceting
|
||||
(erik, Jeremy Hinegardner, Thibaut Lassalle, ryan)
|
||||
|
||||
Optimizations
|
||||
----------------------
|
||||
|
|
|
@ -498,6 +498,9 @@
|
|||
<str name="version">2.1</str>
|
||||
-->
|
||||
</lst>
|
||||
<arr name="last-components">
|
||||
<str>pivot</str> <!-- not included by default -->
|
||||
</arr>
|
||||
</requestHandler>
|
||||
|
||||
<!--
|
||||
|
@ -665,6 +668,7 @@
|
|||
|
||||
<searchComponent name="query" class="org.apache.solr.handler.component.QueryComponent" />
|
||||
<searchComponent name="facet" class="org.apache.solr.handler.component.FacetComponent" />
|
||||
<searchComponent name="pivot" class="org.apache.solr.handler.component.PivotFacetComponent" />
|
||||
<searchComponent name="mlt" class="org.apache.solr.handler.component.MoreLikeThisComponent" />
|
||||
<searchComponent name="highlight" class="org.apache.solr.handler.component.HighlightComponent" />
|
||||
<searchComponent name="stats" class="org.apache.solr.handler.component.StatsComponent" />
|
||||
|
@ -691,7 +695,7 @@
|
|||
<str>myLastComponentName</str>
|
||||
</arr>
|
||||
-->
|
||||
|
||||
|
||||
<!-- The spell check component can return a list of alternative spelling
|
||||
suggestions. -->
|
||||
<searchComponent name="spellcheck" class="solr.SpellCheckComponent">
|
||||
|
|
|
@ -93,6 +93,22 @@ public interface FacetParams {
|
|||
*/
|
||||
public static final String FACET_MISSING = FACET + ".missing";
|
||||
|
||||
|
||||
/**
|
||||
* Comma separated list of fields to pivot
|
||||
*
|
||||
* example: author,type (for types by author / types within author)
|
||||
*/
|
||||
public static final String FACET_PIVOT = FACET + ".pivot";
|
||||
|
||||
/**
|
||||
* Minimum number of docs that need to match to be included in the sublist
|
||||
*
|
||||
* default value is 1
|
||||
*/
|
||||
public static final String FACET_PIVOT_MINCOUNT = FACET_PIVOT + ".mincount";
|
||||
|
||||
|
||||
/**
|
||||
* String option: "count" causes facets to be sorted
|
||||
* by the count, "index" results in index order.
|
||||
|
|
|
@ -833,6 +833,7 @@ public final class SolrCore implements SolrInfoMBean {
|
|||
addIfNotPresent(components,HighlightComponent.COMPONENT_NAME,HighlightComponent.class);
|
||||
addIfNotPresent(components,QueryComponent.COMPONENT_NAME,QueryComponent.class);
|
||||
addIfNotPresent(components,FacetComponent.COMPONENT_NAME,FacetComponent.class);
|
||||
addIfNotPresent(components,PivotFacetComponent.COMPONENT_NAME,PivotFacetComponent.class);
|
||||
addIfNotPresent(components,MoreLikeThisComponent.COMPONENT_NAME,MoreLikeThisComponent.class);
|
||||
addIfNotPresent(components,StatsComponent.COMPONENT_NAME,StatsComponent.class);
|
||||
addIfNotPresent(components,DebugComponent.COMPONENT_NAME,DebugComponent.class);
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
/**
|
||||
* 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.search.SolrIndexSearcher;
|
||||
import org.apache.solr.search.DocSet;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
import org.apache.solr.common.SolrException.ErrorCode;
|
||||
import org.apache.solr.common.params.SolrParams;
|
||||
import org.apache.solr.common.params.FacetParams;
|
||||
import org.apache.solr.request.SimpleFacets;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.schema.FieldType;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.index.Term;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @since solr 4.0
|
||||
*/
|
||||
public class PivotFacetComponent extends SearchComponent
|
||||
{
|
||||
public static final String COMPONENT_NAME = "pivot";
|
||||
|
||||
static final String PIVOT_KEY = "facet_pivot";
|
||||
|
||||
|
||||
/**
|
||||
* Designed to be overridden by subclasses that provide different faceting implementations.
|
||||
* TODO: Currently this is returning a SimpleFacets object, but those capabilities would
|
||||
* be better as an extracted abstract class or interface.
|
||||
*/
|
||||
protected SimpleFacets getFacetImplementation(SolrQueryRequest req,
|
||||
DocSet docs,
|
||||
SolrParams params) {
|
||||
return new SimpleFacets(req, docs, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(ResponseBuilder rb) throws IOException
|
||||
{
|
||||
if (rb.req.getParams().getBool(FacetParams.FACET,false)) {
|
||||
rb.setNeedDocSet( true );
|
||||
rb.doFacets = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void process(ResponseBuilder rb) throws IOException {
|
||||
if (!rb.doFacets) return;
|
||||
|
||||
SolrParams params = rb.req.getParams();
|
||||
String[] pivots = params.getParams(FacetParams.FACET_PIVOT); // example: author,type (for types by author / types within author)
|
||||
if (pivots == null) return;
|
||||
|
||||
int minMatch = params.getInt( FacetParams.FACET_PIVOT_MINCOUNT, 1 );
|
||||
|
||||
SimpleOrderedMap<List<NamedList<Object>>> pivotResponse = new SimpleOrderedMap<List<NamedList<Object>>>();
|
||||
for (String pivot : pivots) {
|
||||
String[] fields = pivot.split(","); // only support two levels for now
|
||||
|
||||
if( fields.length < 2 ) {
|
||||
throw new SolrException( ErrorCode.BAD_REQUEST,
|
||||
"Pivot Facet needs at least two fields: "+pivot );
|
||||
}
|
||||
|
||||
DocSet docs = rb.getResults().docSet;
|
||||
String field = fields[0];
|
||||
String subField = fields[1];
|
||||
Deque<String> fnames = new LinkedList<String>();
|
||||
for( int i=fields.length-1; i>1; i-- ) {
|
||||
fnames.push( fields[i] );
|
||||
}
|
||||
|
||||
SimpleFacets sf = getFacetImplementation(rb.req, rb.getResults().docSet, rb.req.getParams());
|
||||
NamedList<Integer> superFacets = sf.getTermCounts(field);
|
||||
|
||||
pivotResponse.add(pivot, doPivots(superFacets, field, subField, fnames, rb, docs, minMatch));
|
||||
}
|
||||
NamedList facetCounts = (NamedList) rb.rsp.getValues().get("facet_counts");
|
||||
if (facetCounts == null) {
|
||||
facetCounts = new NamedList();
|
||||
rb.rsp.add("facet_counts", facetCounts);
|
||||
}
|
||||
facetCounts.add( PIVOT_KEY, pivotResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive function to do all the pivots
|
||||
*/
|
||||
protected List<NamedList<Object>> doPivots( NamedList<Integer> superFacets, String field, String subField, Deque<String> fnames, ResponseBuilder rb, DocSet docs, int minMatch ) throws IOException
|
||||
{
|
||||
SolrIndexSearcher searcher = rb.req.getSearcher();
|
||||
// TODO: optimize to avoid converting to an external string and then having to convert back to internal below
|
||||
FieldType ftype = null;
|
||||
|
||||
// SimpleFacets sf = getFacetImplementation(rb.req, docs, rb.req.getParams());
|
||||
String nextField = fnames.poll();
|
||||
|
||||
List<NamedList<Object>> values = new ArrayList<NamedList<Object>>( superFacets.size() );
|
||||
for (Map.Entry<String, Integer> kv : superFacets) {
|
||||
// Only sub-facet if parent facet has positive count - still may not be any values for the sub-field though
|
||||
if (kv.getValue() > minMatch ) {
|
||||
SimpleOrderedMap<Object> pivot = new SimpleOrderedMap<Object>();
|
||||
pivot.add( "field", field );
|
||||
pivot.add( "value", kv.getKey() );
|
||||
pivot.add( "count", kv.getValue() );
|
||||
|
||||
if( subField == null ) {
|
||||
values.add( pivot );
|
||||
}
|
||||
else {
|
||||
String s = kv.getKey();
|
||||
if( ftype == null ) {
|
||||
ftype = searcher.getSchema().getField(field).getType();
|
||||
}
|
||||
|
||||
Query query = new TermQuery(new Term(field, ftype.toInternal(s)));
|
||||
DocSet subset = searcher.getDocSet(query, docs);
|
||||
SimpleFacets sf = getFacetImplementation(rb.req, subset, rb.req.getParams());
|
||||
|
||||
NamedList<Integer> nl = sf.getTermCounts(subField);
|
||||
if (nl.size() > minMatch ) {
|
||||
pivot.add( "pivot", doPivots( nl, subField, nextField, fnames, rb, subset, minMatch ) );
|
||||
values.add( pivot ); // only add response if there are some counts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// put the field back on the list
|
||||
fnames.push( nextField );
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int distributedProcess(ResponseBuilder rb) throws IOException {
|
||||
if (!rb.doFacets) {
|
||||
return ResponseBuilder.STAGE_DONE;
|
||||
}
|
||||
|
||||
if (rb.stage == ResponseBuilder.STAGE_GET_FIELDS) {
|
||||
SolrParams params = rb.req.getParams();
|
||||
String[] pivots = params.getParams(FacetParams.FACET_PIVOT);
|
||||
for ( ShardRequest sreq : rb.outgoing ) {
|
||||
if (( sreq.purpose & ShardRequest.PURPOSE_GET_FIELDS ) != 0
|
||||
&& sreq.shards != null && sreq.shards.length == 1 ) {
|
||||
sreq.params.set( FacetParams.FACET, "true" );
|
||||
sreq.params.set( FacetParams.FACET_PIVOT, pivots );
|
||||
sreq.params.set( FacetParams.FACET_PIVOT_MINCOUNT, 1 ); // keep this at 1 regardless so that it accumulates everything
|
||||
}
|
||||
}
|
||||
}
|
||||
return ResponseBuilder.STAGE_DONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResponses(ResponseBuilder rb, ShardRequest sreq) {
|
||||
if (!rb.doFacets) return;
|
||||
|
||||
|
||||
if ((sreq.purpose & ShardRequest.PURPOSE_GET_FACETS)!=0) {
|
||||
SimpleOrderedMap<List<NamedList<Object>>> tf = rb._pivots;
|
||||
if ( null == tf ) {
|
||||
tf = new SimpleOrderedMap<List<NamedList<Object>>>();
|
||||
rb._pivots = tf;
|
||||
}
|
||||
for (ShardResponse srsp: sreq.responses) {
|
||||
int shardNum = rb.getShardNum(srsp.getShard());
|
||||
|
||||
NamedList facet_counts = (NamedList)srsp.getSolrResponse().getResponse().get("facet_counts");
|
||||
|
||||
// handle facet trees from shards
|
||||
SimpleOrderedMap<List<NamedList<Object>>> shard_pivots =
|
||||
(SimpleOrderedMap<List<NamedList<Object>>>)facet_counts.get( PIVOT_KEY );
|
||||
|
||||
if ( shard_pivots != null ) {
|
||||
for (int j=0; j< shard_pivots.size(); j++) {
|
||||
// TODO -- accumulate the results from each shard
|
||||
// The following code worked to accumulate facets for an previous
|
||||
// two level patch... it is here for reference till someone can upgrade
|
||||
/**
|
||||
String shard_tree_name = (String) shard_pivots.getName( j );
|
||||
SimpleOrderedMap<NamedList> shard_tree = (SimpleOrderedMap<NamedList>)shard_pivots.getVal( j );
|
||||
SimpleOrderedMap<NamedList> facet_tree = tf.get( shard_tree_name );
|
||||
if ( null == facet_tree) {
|
||||
facet_tree = new SimpleOrderedMap<NamedList>();
|
||||
tf.add( shard_tree_name, facet_tree );
|
||||
}
|
||||
|
||||
for( int o = 0; o < shard_tree.size() ; o++ ) {
|
||||
String shard_outer = (String) shard_tree.getName( o );
|
||||
NamedList shard_innerList = (NamedList) shard_tree.getVal( o );
|
||||
NamedList tree_innerList = (NamedList) facet_tree.get( shard_outer );
|
||||
if ( null == tree_innerList ) {
|
||||
tree_innerList = new NamedList();
|
||||
facet_tree.add( shard_outer, tree_innerList );
|
||||
}
|
||||
|
||||
for ( int i = 0 ; i < shard_innerList.size() ; i++ ) {
|
||||
String shard_term = (String) shard_innerList.getName( i );
|
||||
long shard_count = ((Number) shard_innerList.getVal(i)).longValue();
|
||||
int tree_idx = tree_innerList.indexOf( shard_term, 0 );
|
||||
|
||||
if ( -1 == tree_idx ) {
|
||||
tree_innerList.add( shard_term, shard_count );
|
||||
} else {
|
||||
long tree_count = ((Number) tree_innerList.getVal( tree_idx )).longValue();
|
||||
tree_innerList.setVal( tree_idx, shard_count + tree_count );
|
||||
}
|
||||
} // innerList loop
|
||||
} // outer loop
|
||||
**/
|
||||
} // each tree loop
|
||||
}
|
||||
}
|
||||
}
|
||||
return ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishStage(ResponseBuilder rb) {
|
||||
if (!rb.doFacets || rb.stage != ResponseBuilder.STAGE_GET_FIELDS) return;
|
||||
// wait until STAGE_GET_FIELDS
|
||||
// so that "result" is already stored in the response (for aesthetics)
|
||||
|
||||
SimpleOrderedMap<List<NamedList<Object>>> tf = rb._pivots;
|
||||
|
||||
// get 'facet_counts' from the response
|
||||
NamedList facetCounts = (NamedList) rb.rsp.getValues().get("facet_counts");
|
||||
if (facetCounts == null) {
|
||||
facetCounts = new NamedList();
|
||||
rb.rsp.add("facet_counts", facetCounts);
|
||||
}
|
||||
facetCounts.add( PIVOT_KEY, tf );
|
||||
rb._pivots = null;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return "Handle Pivot (multi-level) Faceting";
|
||||
}
|
||||
|
||||
public String getSourceId() {
|
||||
return "$Id: $";
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return "$URL: $";
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return "$Revision: $";
|
||||
}
|
||||
}
|
|
@ -141,6 +141,7 @@ public class ResponseBuilder {
|
|||
SolrDocumentList _responseDocs;
|
||||
StatsInfo _statsInfo;
|
||||
TermsComponent.TermsHelper _termsHelper;
|
||||
SimpleOrderedMap<List<NamedList<Object>>> _pivots;
|
||||
|
||||
/**
|
||||
* Utility function to add debugging info. This will make sure a valid
|
||||
|
|
|
@ -214,6 +214,19 @@ public class SolrQuery extends ModifiableSolrParams
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Add field(s) for pivot computation.
|
||||
*
|
||||
* pivot fields are comma separated
|
||||
*
|
||||
* @param fields Array of field names from the IndexSchema
|
||||
* @return this
|
||||
*/
|
||||
public SolrQuery addFacetPivotField(String ... fields) {
|
||||
add(FacetParams.FACET_PIVOT, fields);
|
||||
this.set(FacetParams.FACET, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** get the facet fields
|
||||
*
|
||||
* @return string array of facet fields or null if not set/empty
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* 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.client.solrj.response;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class PivotField implements Serializable
|
||||
{
|
||||
final String _field;
|
||||
final Object _value;
|
||||
final int _count;
|
||||
final List<PivotField> _pivot;
|
||||
|
||||
public PivotField( String f, Object v, int count, List<PivotField> pivot )
|
||||
{
|
||||
_field = f;
|
||||
_value = v;
|
||||
_count = count;
|
||||
_pivot = pivot;
|
||||
}
|
||||
|
||||
public String getField() {
|
||||
return _field;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return _count;
|
||||
}
|
||||
|
||||
public List<PivotField> getPivot() {
|
||||
return _pivot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _field + ":" + _value + " ["+_count+"] "+_pivot;
|
||||
}
|
||||
|
||||
public void write( PrintStream out, int indent )
|
||||
{
|
||||
for( int i=0; i<indent; i++ ) {
|
||||
out.print( " " );
|
||||
}
|
||||
out.println( _field + "=" + _value + " ("+_count+")" );
|
||||
if( _pivot != null ) {
|
||||
for( PivotField p : _pivot ) {
|
||||
p.write( out, indent+1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,6 +53,7 @@ public class QueryResponse extends SolrResponseBase
|
|||
private List<FacetField> _facetFields = null;
|
||||
private List<FacetField> _limitingFacets = null;
|
||||
private List<FacetField> _facetDates = null;
|
||||
private NamedList<List<PivotField>> _facetPivot = null;
|
||||
|
||||
// Highlight Info
|
||||
private Map<String,Map<String,List<String>>> _highlighting = null;
|
||||
|
@ -241,6 +242,29 @@ public class QueryResponse extends SolrResponseBase
|
|||
_facetDates.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
//Parse pivot facets
|
||||
NamedList pf = (NamedList) info.get("facet_pivot");
|
||||
if (pf != null) {
|
||||
_facetPivot = new NamedList<List<PivotField>>();
|
||||
for( int i=0; i<pf.size(); i++ ) {
|
||||
_facetPivot.add( pf.getName(i), readPivots( (List<NamedList>)pf.getVal(i) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected List<PivotField> readPivots( List<NamedList> list )
|
||||
{
|
||||
ArrayList<PivotField> values = new ArrayList<PivotField>( list.size() );
|
||||
for( NamedList nl : list ) {
|
||||
// NOTE, this is cheating, but we know the order they are written in, so no need to check
|
||||
String f = (String)nl.getVal( 0 );
|
||||
Object v = (Object)nl.getVal( 1 );
|
||||
int cnt = ((Integer)nl.getVal( 2 )).intValue();
|
||||
List<PivotField> p = (nl.size()<4)?null:readPivots((List<NamedList>)nl.getVal(3) );
|
||||
values.add( new PivotField( f, v, cnt, p ) );
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
|
@ -302,6 +326,10 @@ public class QueryResponse extends SolrResponseBase
|
|||
public List<FacetField> getFacetDates() {
|
||||
return _facetDates;
|
||||
}
|
||||
|
||||
public NamedList<List<PivotField>> getFacetPivot() {
|
||||
return _facetPivot;
|
||||
}
|
||||
|
||||
/** get
|
||||
*
|
||||
|
|
|
@ -36,6 +36,7 @@ import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
|
|||
import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
|
||||
import org.apache.solr.client.solrj.request.AbstractUpdateRequest.ACTION;
|
||||
import org.apache.solr.client.solrj.response.LukeResponse;
|
||||
import org.apache.solr.client.solrj.response.PivotField;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.client.solrj.response.FacetField;
|
||||
import org.apache.solr.client.solrj.response.UpdateResponse;
|
||||
|
@ -565,6 +566,105 @@ abstract public class SolrExampleTests extends SolrJettyTestBase
|
|||
// System.out.println( rsp.getResults().getNumFound() + " :::: 444: "+ff.getValues() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPivotFacet() throws Exception
|
||||
{
|
||||
SolrServer server = getSolrServer();
|
||||
|
||||
// Empty the database...
|
||||
server.deleteByQuery( "*:*" );// delete everything!
|
||||
server.commit();
|
||||
assertNumFound( "*:*", 0 ); // make sure it got in
|
||||
|
||||
int id = 1;
|
||||
ArrayList<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
|
||||
docs.add( makeTestDoc( "id", id++, "features", "AAA", "cat", "a", "inStock", true ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "AAA", "cat", "a", "inStock", false ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "AAA", "cat", "a", "inStock", true ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "AAA", "cat", "b", "inStock", false ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "AAA", "cat", "b", "inStock", true ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "BBB", "cat", "a", "inStock", false ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "BBB", "cat", "a", "inStock", true ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "BBB", "cat", "b", "inStock", false ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "BBB", "cat", "b", "inStock", true ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "BBB", "cat", "b", "inStock", false ) );
|
||||
docs.add( makeTestDoc( "id", id++, "features", "BBB", "cat", "b", "inStock", true ) );
|
||||
docs.add( makeTestDoc( "id", id++ ) ); // something not matching
|
||||
server.add( docs );
|
||||
server.commit();
|
||||
|
||||
SolrQuery query = new SolrQuery( "*:*" );
|
||||
query.addFacetPivotField("features,cat", "cat,features", "features,cat,inStock" );
|
||||
query.setFacetMinCount( 0 );
|
||||
query.setRows( 0 );
|
||||
|
||||
QueryResponse rsp = server.query( query );
|
||||
assertEquals( docs.size(), rsp.getResults().getNumFound() );
|
||||
|
||||
NamedList<List<PivotField>> pivots = rsp.getFacetPivot();
|
||||
assertEquals( 3, pivots.size() );
|
||||
|
||||
// for(Map.Entry<String, List<PivotField>> entry : pivots ) {
|
||||
// System.out.println( "PIVOT: "+entry.getKey() );
|
||||
// for( PivotField p : entry.getValue() ) {
|
||||
// p.write(System.out, 0 );
|
||||
// }
|
||||
// System.out.println();
|
||||
// }
|
||||
|
||||
// Now make sure they have reasonable stuff
|
||||
List<PivotField> pivot = pivots.getVal( 0 );
|
||||
assertEquals( "features,cat", pivots.getName( 0 ) );
|
||||
assertEquals( 2, pivot.size() );
|
||||
|
||||
PivotField ff = pivot.get( 0 );
|
||||
assertEquals( "features", ff.getField() );
|
||||
assertEquals( "bbb", ff.getValue() );
|
||||
assertEquals( 6, ff.getCount() );
|
||||
List<PivotField> counts = ff.getPivot();
|
||||
assertEquals( 2, counts.size() );
|
||||
assertEquals( "cat", counts.get(0).getField() );
|
||||
assertEquals( "b", counts.get(0).getValue() );
|
||||
assertEquals( 4, counts.get(0).getCount() );
|
||||
assertEquals( "a", counts.get(1).getValue() );
|
||||
assertEquals( 2, counts.get(1).getCount() );
|
||||
|
||||
ff = pivot.get( 1 );
|
||||
assertEquals( "features", ff.getField() );
|
||||
assertEquals( "aaa", ff.getValue() );
|
||||
assertEquals( 5, ff.getCount() );
|
||||
counts = ff.getPivot();
|
||||
assertEquals( 2, counts.size() );
|
||||
assertEquals( "a", counts.get(0).getValue() );
|
||||
assertEquals( 3, counts.get(0).getCount() );
|
||||
assertEquals( "b", counts.get(1).getValue() );
|
||||
assertEquals( 2, counts.get(1).getCount() );
|
||||
|
||||
// 3 deep
|
||||
pivot = pivots.getVal( 2 );
|
||||
assertEquals( "features,cat,inStock", pivots.getName( 2 ) );
|
||||
assertEquals( 2, pivot.size() );
|
||||
PivotField p = pivot.get( 1 ).getPivot().get(0);
|
||||
assertEquals( "cat", p.getField() );
|
||||
assertEquals( "a", p.getValue() );
|
||||
counts = p.getPivot();
|
||||
// p.write(System.out, 5 );
|
||||
assertEquals( 1, counts.size() );
|
||||
assertEquals( "inStock", counts.get(0).getField() );
|
||||
assertEquals( "true", counts.get(0).getValue() );
|
||||
assertEquals( 2, counts.get(0).getCount() );
|
||||
}
|
||||
|
||||
public static SolrInputDocument makeTestDoc( Object ... kvp )
|
||||
{
|
||||
SolrInputDocument doc = new SolrInputDocument();
|
||||
for( int i=0; i<kvp.length; ) {
|
||||
String k = (String)kvp[i++];
|
||||
Object v = kvp[i++];
|
||||
doc.addField( k, v );
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamingRequest() throws Exception {
|
||||
|
|
Loading…
Reference in New Issue