SOLR-6686: facet.threads can return wrong results when using facet.prefix multiple times on same field

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1687791 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Shalin Shekhar Mangar 2015-06-26 16:00:22 +00:00
parent 42fdbbeb95
commit 878881a77a
4 changed files with 190 additions and 159 deletions

View File

@ -186,6 +186,9 @@ Bug Fixes
* SOLR-7664: Throw correct exception (RemoteSolrException) on receiving a HTTP 413.
(Ramkumar Aiyengar, Eirik Lygre)
* SOLR-6686: facet.threads can return wrong results when using facet.prefix multiple
times on same field. (Michael Ryan, Tim Underwood via shalin)
Optimizations
----------------------

View File

@ -76,13 +76,14 @@ public class PivotFacetProcessor extends SimpleFacets
SimpleOrderedMap<List<NamedList<Object>>> pivotResponse = new SimpleOrderedMap<>();
for (String pivotList : pivots) {
final ParsedParams parsed;
try {
// NOTE: this sets localParams (SimpleFacets is stateful)
this.parseParams(FacetParams.FACET_PIVOT, pivotList);
parsed = this.parseParams(FacetParams.FACET_PIVOT, pivotList);
} catch (SyntaxError e) {
throw new SolrException(ErrorCode.BAD_REQUEST, e);
}
List<String> pivotFields = StrUtils.splitSmart(facetValue, ",", true);
List<String> pivotFields = StrUtils.splitSmart(parsed.facetValue, ",", true);
if( pivotFields.size() < 1 ) {
throw new SolrException( ErrorCode.BAD_REQUEST,
"Pivot Facet needs at least one field name: " + pivotList);
@ -101,11 +102,11 @@ public class PivotFacetProcessor extends SimpleFacets
String refineKey = null; // no local => no refinement
List<StatsField> statsFields = Collections.emptyList(); // no local => no stats
if (null != localParams) {
if (null != parsed.localParams) {
// we might be refining..
refineKey = localParams.get(PivotFacet.REFINE_PARAM);
refineKey = parsed.localParams.get(PivotFacet.REFINE_PARAM);
String statsLocalParam = localParams.get(StatsParams.STATS);
String statsLocalParam = parsed.localParams.get(StatsParams.STATS);
if (null != refineKey
&& null != statsLocalParam
&& null == statsInfo) {
@ -123,10 +124,10 @@ public class PivotFacetProcessor extends SimpleFacets
= params.getParams(PivotFacet.REFINE_PARAM + refineKey);
for(String refinements : refinementValuesByField){
pivotResponse.addAll(processSingle(pivotFields, refinements, statsFields));
pivotResponse.addAll(processSingle(pivotFields, refinements, statsFields, parsed));
}
} else{
pivotResponse.addAll(processSingle(pivotFields, null, statsFields));
pivotResponse.addAll(processSingle(pivotFields, null, statsFields, parsed));
}
}
return pivotResponse;
@ -141,7 +142,8 @@ public class PivotFacetProcessor extends SimpleFacets
private SimpleOrderedMap<List<NamedList<Object>>> processSingle
(List<String> pivotFields,
String refinements,
List<StatsField> statsFields) throws IOException {
List<StatsField> statsFields,
final ParsedParams parsed) throws IOException {
SolrIndexSearcher searcher = rb.req.getSearcher();
SimpleOrderedMap<List<NamedList<Object>>> pivotResponse = new SimpleOrderedMap<>();
@ -170,18 +172,18 @@ public class PivotFacetProcessor extends SimpleFacets
facetCounts = new NamedList<>();
facetCounts.add(firstFieldsValues,
getSubsetSize(this.docs, sfield, firstFieldsValues));
getSubsetSize(parsed.docs, sfield, firstFieldsValues));
} else {
// no refinements needed
facetCounts = this.getTermCountsForPivots(field, this.docs);
facetCounts = this.getTermCountsForPivots(field, parsed);
}
if(pivotFields.size() > 1) {
String subField = pivotFields.get(1);
pivotResponse.add(key,
doPivots(facetCounts, field, subField, fnames, vnames, this.docs, statsFields));
pivotResponse.add(parsed.key,
doPivots(facetCounts, field, subField, fnames, vnames, parsed, statsFields));
} else {
pivotResponse.add(key, doPivots(facetCounts, field, null, fnames, vnames, this.docs, statsFields));
pivotResponse.add(parsed.key, doPivots(facetCounts, field, null, fnames, vnames, parsed, statsFields));
}
return pivotResponse;
}
@ -223,7 +225,7 @@ public class PivotFacetProcessor extends SimpleFacets
protected List<NamedList<Object>> doPivots(NamedList<Integer> superFacets,
String field, String subField,
Deque<String> fnames, Deque<String> vnames,
DocSet docs, List<StatsField> statsFields)
ParsedParams parsed, List<StatsField> statsFields)
throws IOException {
boolean isShard = rb.req.getParams().getBool(ShardParams.IS_SHARD, false);
@ -255,7 +257,7 @@ public class PivotFacetProcessor extends SimpleFacets
}
pivot.add( "count", pivotCount );
DocSet subset = getSubset(docs, sfield, fieldValue);
final DocSet subset = getSubset(parsed.docs, sfield, fieldValue);
if( subField != null ) {
NamedList<Integer> facetCounts;
@ -266,11 +268,11 @@ public class PivotFacetProcessor extends SimpleFacets
searcher.getSchema().getField(subField),
val));
} else {
facetCounts = this.getTermCountsForPivots(subField, subset);
facetCounts = this.getTermCountsForPivots(subField, parsed.withDocs(subset));
}
if (facetCounts.size() >= 1) {
pivot.add( "pivot", doPivots( facetCounts, subField, nextField, fnames, vnames, subset, statsFields ) );
pivot.add( "pivot", doPivots( facetCounts, subField, nextField, fnames, vnames, parsed.withDocs(subset), statsFields ) );
}
}
if ((isShard || 0 < pivotCount) && ! statsFields.isEmpty()) {

View File

@ -115,7 +115,7 @@ public class SimpleFacets {
/** The main set of documents all facet counts should be relative to */
protected DocSet docsOrig;
/** Configuration params behavior should be driven by */
protected final SolrParams orig;
protected final SolrParams global;
/** Searcher to use for all calculations */
protected final SolrIndexSearcher searcher;
protected final SolrQueryRequest req;
@ -124,14 +124,36 @@ public class SimpleFacets {
protected SimpleOrderedMap<Object> facetResponse;
// per-facet values
protected SolrParams localParams; // localParams on this particular facet command
protected SolrParams params; // local+original
protected SolrParams required; // required version of params
protected String facetValue; // the field to or query to facet on (minus local params)
protected DocSet docs; // the base docset for this particular facet
protected String key; // what name should the results be stored under
protected int threads;
protected final static class ParsedParams {
final public SolrParams localParams; // localParams on this particular facet command
final public SolrParams params; // local+original
final public SolrParams required; // required version of params
final public String facetValue; // the field to or query to facet on (minus local params)
final public DocSet docs; // the base docset for this particular facet
final public String key; // what name should the results be stored under
final public int threads;
public ParsedParams(final SolrParams localParams, // localParams on this particular facet command
final SolrParams params, // local+original
final SolrParams required, // required version of params
final String facetValue, // the field to or query to facet on (minus local params)
final DocSet docs, // the base docset for this particular facet
final String key, // what name should the results be stored under
final int threads) {
this.localParams = localParams;
this.params = params;
this.required = required;
this.facetValue = facetValue;
this.docs = docs;
this.key = key;
this.threads = threads;
}
public ParsedParams withDocs(final DocSet docs) {
return new ParsedParams(localParams, params, required, facetValue, docs, key, threads);
}
}
public SimpleFacets(SolrQueryRequest req,
DocSet docs,
SolrParams params) {
@ -144,9 +166,8 @@ public class SimpleFacets {
ResponseBuilder rb) {
this.req = req;
this.searcher = req.getSearcher();
this.docs = this.docsOrig = docs;
this.params = orig = params;
this.required = new RequiredSolrParams(params);
this.docsOrig = docs;
this.global = params;
this.rb = rb;
}
@ -170,20 +191,21 @@ public class SimpleFacets {
}
protected void parseParams(String type, String param) throws SyntaxError, IOException {
localParams = QueryParsing.getLocalParams(param, req.getParams());
docs = docsOrig;
facetValue = param;
key = param;
threads = -1;
protected ParsedParams parseParams(String type, String param) throws SyntaxError, IOException {
SolrParams localParams = QueryParsing.getLocalParams(param, req.getParams());
DocSet docs = docsOrig;
String facetValue = param;
String key = param;
int threads = -1;
if (localParams == null) {
params = orig;
required = new RequiredSolrParams(params);
return;
SolrParams params = global;
SolrParams required = new RequiredSolrParams(params);
return new ParsedParams(localParams, params, required, facetValue, docs, key, threads);
}
params = SolrParams.wrapDefaults(localParams, orig);
required = new RequiredSolrParams(params);
SolrParams params = SolrParams.wrapDefaults(localParams, global);
SolrParams required = new RequiredSolrParams(params);
// remove local params unless it's a query
if (type != FacetParams.FACET_QUERY) { // TODO Cut over to an Enum here
@ -203,7 +225,7 @@ public class SimpleFacets {
// figure out if we need a new base DocSet
String excludeStr = localParams.get(CommonParams.EXCLUDE);
if (excludeStr == null) return;
if (excludeStr == null) return new ParsedParams(localParams, params, required, facetValue, docs, key, threads);
Map<?,?> tagMap = (Map<?,?>)req.getContext().get("tags");
if (tagMap != null && rb != null) {
@ -220,7 +242,7 @@ public class SimpleFacets {
excludeSet.put(qp.getQuery(), Boolean.TRUE);
}
}
if (excludeSet.size() == 0) return;
if (excludeSet.size() == 0) return new ParsedParams(localParams, params, required, facetValue, docs, key, threads);
List<Query> qlist = new ArrayList<>();
@ -248,17 +270,18 @@ public class SimpleFacets {
} else if (rb.getGroupingSpec().getFunctions().length > 0) {
grouping.addFunctionCommand(rb.getGroupingSpec().getFunctions()[0], req);
} else {
this.docs = base;
return;
docs = base;
return new ParsedParams(localParams, params, required, facetValue, docs, key, threads);
}
AbstractAllGroupHeadsCollector allGroupHeadsCollector = grouping.getCommands().get(0).createAllGroupCollector();
searcher.search(base.getTopFilter(), allGroupHeadsCollector);
this.docs = new BitDocSet(allGroupHeadsCollector.retrieveGroupHeads(searcher.maxDoc()));
docs = new BitDocSet(allGroupHeadsCollector.retrieveGroupHeads(searcher.maxDoc()));
} else {
this.docs = base;
docs = base;
}
}
return new ParsedParams(localParams, params, required, facetValue, docs, key, threads);
}
@ -277,7 +300,7 @@ public class SimpleFacets {
public NamedList<Object> getFacetCounts() {
// if someone called this method, benefit of the doubt: assume true
if (!params.getBool(FacetParams.FACET,true))
if (!global.getBool(FacetParams.FACET,true))
return null;
facetResponse = new SimpleOrderedMap<>();
@ -313,21 +336,21 @@ public class SimpleFacets {
*/
// SolrQueryParser qp = searcher.getSchema().getSolrQueryParser(null);
String[] facetQs = params.getParams(FacetParams.FACET_QUERY);
String[] facetQs = global.getParams(FacetParams.FACET_QUERY);
if (null != facetQs && 0 != facetQs.length) {
for (String q : facetQs) {
parseParams(FacetParams.FACET_QUERY, q);
final ParsedParams parsed = parseParams(FacetParams.FACET_QUERY, q);
// TODO: slight optimization would prevent double-parsing of any localParams
Query qobj = QParser.getParser(q, null, req).getQuery();
if (qobj == null) {
res.add(key, 0);
} else if (params.getBool(GroupParams.GROUP_FACET, false)) {
res.add(key, getGroupedFacetQueryCount(qobj));
res.add(parsed.key, 0);
} else if (parsed.params.getBool(GroupParams.GROUP_FACET, false)) {
res.add(parsed.key, getGroupedFacetQueryCount(qobj, parsed));
} else {
res.add(key, searcher.numDocs(qobj, docs));
res.add(parsed.key, searcher.numDocs(qobj, parsed.docs));
}
}
}
@ -340,8 +363,8 @@ public class SimpleFacets {
*
* @see FacetParams#FACET_QUERY
*/
public int getGroupedFacetQueryCount(Query facetQuery) throws IOException {
String groupField = params.get(GroupParams.GROUP_FIELD);
public int getGroupedFacetQueryCount(Query facetQuery, ParsedParams parsed) throws IOException {
String groupField = parsed.params.get(GroupParams.GROUP_FIELD);
if (groupField == null) {
throw new SolrException (
SolrException.ErrorCode.BAD_REQUEST,
@ -350,7 +373,7 @@ public class SimpleFacets {
}
TermAllGroupsCollector collector = new TermAllGroupsCollector(groupField);
Filter mainQueryFilter = docs.getTopFilter(); // This returns a filter that only matches documents matching with q param and fq params
Filter mainQueryFilter = parsed.docs.getTopFilter(); // This returns a filter that only matches documents matching with q param and fq params
Query filteredFacetQuery = new BooleanQuery.Builder()
.add(facetQuery, Occur.MUST)
.add(mainQueryFilter, Occur.FILTER)
@ -367,9 +390,9 @@ public class SimpleFacets {
* Term counts for use in pivot faceting that resepcts the appropriate mincount
* @see FacetParams#FACET_PIVOT_MINCOUNT
*/
public NamedList<Integer> getTermCountsForPivots(String field, DocSet docs) throws IOException {
Integer mincount = params.getFieldInt(field, FacetParams.FACET_PIVOT_MINCOUNT, 1);
return getTermCounts(field, mincount, docs);
public NamedList<Integer> getTermCountsForPivots(String field, ParsedParams parsed) throws IOException {
Integer mincount = parsed.params.getFieldInt(field, FacetParams.FACET_PIVOT_MINCOUNT, 1);
return getTermCounts(field, mincount, parsed);
}
/**
@ -377,18 +400,9 @@ public class SimpleFacets {
*
* @see FacetParams#FACET_MINCOUNT
*/
public NamedList<Integer> getTermCounts(String field) throws IOException {
return getTermCounts(field, this.docs);
}
/**
* Term counts for use in field faceting that resepects the appropriate mincount
*
* @see FacetParams#FACET_MINCOUNT
*/
public NamedList<Integer> getTermCounts(String field, DocSet base) throws IOException {
Integer mincount = params.getFieldInt(field, FacetParams.FACET_MINCOUNT);
return getTermCounts(field, mincount, base);
public NamedList<Integer> getTermCounts(String field, ParsedParams parsed) throws IOException {
Integer mincount = parsed.params.getFieldInt(field, FacetParams.FACET_MINCOUNT);
return getTermCounts(field, mincount, parsed);
}
/**
@ -398,7 +412,10 @@ public class SimpleFacets {
*
* @see FacetParams#FACET_ZEROS
*/
private NamedList<Integer> getTermCounts(String field, Integer mincount, DocSet base) throws IOException {
private NamedList<Integer> getTermCounts(String field, Integer mincount, ParsedParams parsed) throws IOException {
final SolrParams params = parsed.params;
final DocSet docs = parsed.docs;
final int threads = parsed.threads;
int offset = params.getFieldInt(field, FacetParams.FACET_OFFSET, 0);
int limit = params.getFieldInt(field, FacetParams.FACET_LIMIT, 100);
if (limit == 0) return new NamedList<>();
@ -465,13 +482,13 @@ public class SimpleFacets {
}
if (params.getFieldBool(field, GroupParams.GROUP_FACET, false)) {
counts = getGroupedCounts(searcher, base, field, multiToken, offset,limit, mincount, missing, sort, prefix, contains, ignoreCase);
counts = getGroupedCounts(searcher, docs, field, multiToken, offset,limit, mincount, missing, sort, prefix, contains, ignoreCase);
} else {
assert method != null;
switch (method) {
case ENUM:
assert TrieField.getMainValuePrefix(ft) == null;
counts = getFacetTermEnumCounts(searcher, base, field, offset, limit, mincount, missing, sort, prefix, contains, ignoreCase);
counts = getFacetTermEnumCounts(searcher, docs, field, offset, limit, mincount, missing, sort, prefix, contains, ignoreCase);
break;
case FCS:
assert !multiToken;
@ -483,16 +500,16 @@ public class SimpleFacets {
if (contains != null && !contains.isEmpty()) {
throw new SolrException(ErrorCode.BAD_REQUEST, FacetParams.FACET_CONTAINS + " is not supported on numeric types");
}
counts = NumericFacets.getCounts(searcher, base, field, offset, limit, mincount, missing, sort);
counts = NumericFacets.getCounts(searcher, docs, field, offset, limit, mincount, missing, sort);
} else {
PerSegmentSingleValuedFaceting ps = new PerSegmentSingleValuedFaceting(searcher, base, field, offset,limit, mincount, missing, sort, prefix, contains, ignoreCase);
PerSegmentSingleValuedFaceting ps = new PerSegmentSingleValuedFaceting(searcher, docs, field, offset, limit, mincount, missing, sort, prefix, contains, ignoreCase);
Executor executor = threads == 0 ? directExecutor : facetExecutor;
ps.setNumThreads(threads);
counts = ps.getFacetCounts(executor);
}
break;
case FC:
counts = DocValuesFacets.getCounts(searcher, base, field, offset,limit, mincount, missing, sort, prefix, contains, ignoreCase);
counts = DocValuesFacets.getCounts(searcher, docs, field, offset,limit, mincount, missing, sort, prefix, contains, ignoreCase);
break;
default:
throw new AssertionError();
@ -598,7 +615,7 @@ public class SimpleFacets {
throws IOException, SyntaxError {
NamedList<Object> res = new SimpleOrderedMap<>();
String[] facetFs = params.getParams(FacetParams.FACET_FIELD);
String[] facetFs = global.getParams(FacetParams.FACET_FIELD);
if (null == facetFs) {
return res;
}
@ -614,11 +631,11 @@ public class SimpleFacets {
try {
//Loop over fields; submit to executor, keeping the future
for (String f : facetFs) {
parseParams(FacetParams.FACET_FIELD, f);
final ParsedParams parsed = parseParams(FacetParams.FACET_FIELD, f);
final SolrParams localParams = parsed.localParams;
final String termList = localParams == null ? null : localParams.get(CommonParams.TERMS);
final String workerKey = key;
final String workerFacetValue = facetValue;
final DocSet workerBase = this.docs;
final String key = parsed.key;
final String facetValue = parsed.facetValue;
Callable<NamedList> callable = new Callable<NamedList>() {
@Override
public NamedList call() throws Exception {
@ -626,16 +643,16 @@ public class SimpleFacets {
NamedList<Object> result = new SimpleOrderedMap<>();
if(termList != null) {
List<String> terms = StrUtils.splitSmart(termList, ",", true);
result.add(workerKey, getListedTermCounts(workerFacetValue, workerBase, terms));
result.add(key, getListedTermCounts(facetValue, parsed, terms));
} else {
result.add(workerKey, getTermCounts(workerFacetValue, workerBase));
result.add(key, getTermCounts(facetValue, parsed));
}
return result;
} catch (SolrException se) {
throw se;
} catch (Exception e) {
throw new SolrException(ErrorCode.SERVER_ERROR,
"Exception during facet.field: " + workerFacetValue, e);
"Exception during facet.field: " + facetValue, e);
} finally {
semaphore.release();
}
@ -668,30 +685,18 @@ public class SimpleFacets {
return res;
}
/**
* Computes the term-&gt;count counts for the specified termList relative to the
* @param field the name of the field to compute term counts against
* @param termList a comma seperated (and backslash escaped) list of term values (in the specified field) to compute the counts for
* @see StrUtils#splitSmart
*/
private NamedList<Integer> getListedTermCounts(String field, String termList) throws IOException {
List<String> terms = StrUtils.splitSmart(termList, ",", true);
return getListedTermCounts(field, this.docs, terms);
}
/**
* Computes the term-&gt;count counts for the specified term values relative to the
* @param field the name of the field to compute term counts against
* @param base the docset to compute term counts relative to
* @param parsed contains the docset to compute term counts relative to
* @param terms a list of term values (in the specified field) to compute the counts for
*/
protected NamedList<Integer> getListedTermCounts(String field, DocSet base, List<String> terms) throws IOException {
protected NamedList<Integer> getListedTermCounts(String field, final ParsedParams parsed, List<String> terms) throws IOException {
FieldType ft = searcher.getSchema().getFieldType(field);
NamedList<Integer> res = new NamedList<>();
for (String term : terms) {
String internal = ft.toInternal(term);
int count = searcher.numDocs(new TermQuery(new Term(field, internal)), base);
int count = searcher.numDocs(new TermQuery(new Term(field, internal)), parsed.docs);
res.add(term, count);
}
return res;
@ -731,7 +736,7 @@ public class SimpleFacets {
*/
// Minimum term docFreq in order to use the filterCache for that term.
int minDfFilterCache = params.getFieldInt(field, FacetParams.FACET_ENUM_CACHE_MINDF, 0);
int minDfFilterCache = global.getFieldInt(field, FacetParams.FACET_ENUM_CACHE_MINDF, 0);
// make sure we have a set that is fast for random access, if we will use it for that
DocSet fastForRandomSet = docs;
@ -893,7 +898,7 @@ public class SimpleFacets {
throws IOException, SyntaxError {
final NamedList<Object> resOuter = new SimpleOrderedMap<>();
final String[] fields = params.getParams(FacetParams.FACET_DATE);
final String[] fields = global.getParams(FacetParams.FACET_DATE);
if (null == fields || 0 == fields.length) return resOuter;
@ -913,9 +918,11 @@ public class SimpleFacets {
final IndexSchema schema = searcher.getSchema();
parseParams(FacetParams.FACET_DATE, dateFacet);
String f = facetValue;
final ParsedParams parsed = parseParams(FacetParams.FACET_DATE, dateFacet);
final SolrParams params = parsed.params;
final SolrParams required = parsed.required;
final String key = parsed.key;
final String f = parsed.facetValue;
final NamedList<Object> resInner = new SimpleOrderedMap<>();
resOuter.add(key, resInner);
@ -1000,7 +1007,7 @@ public class SimpleFacets {
(include.contains(FacetRangeInclude.UPPER) ||
(include.contains(FacetRangeInclude.EDGE) && high.equals(end)));
final int count = rangeCount(sf,low,high,includeLower,includeUpper);
final int count = rangeCount(parsed,sf,low,high,includeLower,includeUpper);
if (count >= minCount) {
resInner.add(label, count);
}
@ -1036,7 +1043,7 @@ public class SimpleFacets {
if (all || others.contains(FacetRangeOther.BEFORE)) {
// include upper bound if "outer" or if first gap doesn't already include it
resInner.add(FacetRangeOther.BEFORE.toString(),
rangeCount(sf,null,start,
rangeCount(parsed,sf,null,start,
false,
(include.contains(FacetRangeInclude.OUTER) ||
(! (include.contains(FacetRangeInclude.LOWER) ||
@ -1045,7 +1052,7 @@ public class SimpleFacets {
if (all || others.contains(FacetRangeOther.AFTER)) {
// include lower bound if "outer" or if last gap doesn't already include it
resInner.add(FacetRangeOther.AFTER.toString(),
rangeCount(sf,end,null,
rangeCount(parsed,sf,end,null,
(include.contains(FacetRangeInclude.OUTER) ||
(! (include.contains(FacetRangeInclude.UPPER) ||
include.contains(FacetRangeInclude.EDGE)))),
@ -1053,7 +1060,7 @@ public class SimpleFacets {
}
if (all || others.contains(FacetRangeOther.BETWEEN)) {
resInner.add(FacetRangeOther.BETWEEN.toString(),
rangeCount(sf,start,end,
rangeCount(parsed,sf,start,end,
(include.contains(FacetRangeInclude.LOWER) ||
include.contains(FacetRangeInclude.EDGE)),
(include.contains(FacetRangeInclude.UPPER) ||
@ -1074,7 +1081,7 @@ public class SimpleFacets {
public NamedList<Object> getFacetRangeCounts() throws IOException, SyntaxError {
final NamedList<Object> resOuter = new SimpleOrderedMap<>();
final String[] fields = params.getParams(FacetParams.FACET_RANGE);
final String[] fields = global.getParams(FacetParams.FACET_RANGE);
if (null == fields || 0 == fields.length) return resOuter;
@ -1090,11 +1097,12 @@ public class SimpleFacets {
final IndexSchema schema = searcher.getSchema();
parseParams(FacetParams.FACET_RANGE, facetRange);
String f = facetValue;
String methodStr = params.get(FacetParams.FACET_RANGE_METHOD);
final ParsedParams parsed = parseParams(FacetParams.FACET_RANGE, facetRange);
final String key = parsed.key;
final String f = parsed.facetValue;
String methodStr = parsed.params.get(FacetParams.FACET_RANGE_METHOD);
FacetRangeMethod method = (methodStr==null?FacetRangeMethod.getDefault():FacetRangeMethod.get(methodStr));
boolean groupFacet = params.getBool(GroupParams.GROUP_FACET, false);
boolean groupFacet = parsed.params.getBool(GroupParams.GROUP_FACET, false);
if (groupFacet && method.equals(FacetRangeMethod.DV)) {
// the user has explicitly selected the FacetRangeMethod.DV method
log.warn("Range facet method '" + FacetRangeMethod.DV + "' is not supported together with '" +
@ -1146,17 +1154,20 @@ public class SimpleFacets {
}
if (method.equals(FacetRangeMethod.DV)) {
assert ft instanceof TrieField;
resOuter.add(key, getFacetRangeCountsDocValues(sf, calc));
resOuter.add(key, getFacetRangeCountsDocValues(sf, calc, parsed));
} else {
resOuter.add(key, getFacetRangeCounts(sf, calc));
resOuter.add(key, getFacetRangeCounts(sf, calc, parsed));
}
}
private <T extends Comparable<T>> NamedList<Object> getFacetRangeCounts
(final SchemaField sf,
final RangeEndpointCalculator<T> calc) throws IOException {
final RangeEndpointCalculator<T> calc,
final ParsedParams parsed) throws IOException {
final String f = sf.getName();
final SolrParams params = parsed.params;
final SolrParams required = parsed.required;
final NamedList<Object> res = new SimpleOrderedMap<>();
final NamedList<Integer> counts = new NamedList<>();
res.add("counts", counts);
@ -1214,7 +1225,7 @@ public class SimpleFacets {
final String lowS = calc.formatValue(low);
final String highS = calc.formatValue(high);
final int count = rangeCount(sf, lowS, highS,
final int count = rangeCount(parsed, sf, lowS, highS,
includeLower,includeUpper);
if (count >= minCount) {
counts.add(lowS, count);
@ -1249,7 +1260,7 @@ public class SimpleFacets {
if (all || others.contains(FacetRangeOther.BEFORE)) {
// include upper bound if "outer" or if first gap doesn't already include it
res.add(FacetRangeOther.BEFORE.toString(),
rangeCount(sf,null,startS,
rangeCount(parsed,sf,null,startS,
false,
(include.contains(FacetRangeInclude.OUTER) ||
(! (include.contains(FacetRangeInclude.LOWER) ||
@ -1259,7 +1270,7 @@ public class SimpleFacets {
if (all || others.contains(FacetRangeOther.AFTER)) {
// include lower bound if "outer" or if last gap doesn't already include it
res.add(FacetRangeOther.AFTER.toString(),
rangeCount(sf,endS,null,
rangeCount(parsed,sf,endS,null,
(include.contains(FacetRangeInclude.OUTER) ||
(! (include.contains(FacetRangeInclude.UPPER) ||
include.contains(FacetRangeInclude.EDGE)))),
@ -1267,7 +1278,7 @@ public class SimpleFacets {
}
if (all || others.contains(FacetRangeOther.BETWEEN)) {
res.add(FacetRangeOther.BETWEEN.toString(),
rangeCount(sf,startS,endS,
rangeCount(parsed,sf,startS,endS,
(include.contains(FacetRangeInclude.LOWER) ||
include.contains(FacetRangeInclude.EDGE)),
(include.contains(FacetRangeInclude.UPPER) ||
@ -1280,15 +1291,15 @@ public class SimpleFacets {
}
private <T extends Comparable<T>> NamedList<Object> getFacetRangeCountsDocValues(final SchemaField sf,
final RangeEndpointCalculator<T> calc) throws IOException {
final RangeEndpointCalculator<T> calc, ParsedParams parsed) throws IOException {
final String f = sf.getName();
final NamedList<Object> res = new SimpleOrderedMap<>();
final NamedList<Integer> counts = new NamedList<>();
res.add("counts", counts);
String globalStartS = required.getFieldParam(f,FacetParams.FACET_RANGE_START);
String globalEndS = required.getFieldParam(f,FacetParams.FACET_RANGE_END);
String globalStartS = parsed.required.getFieldParam(f,FacetParams.FACET_RANGE_START);
String globalEndS = parsed.required.getFieldParam(f,FacetParams.FACET_RANGE_END);
final T start = calc.getValue(globalStartS);
// not final, hardend may change this
@ -1299,19 +1310,19 @@ public class SimpleFacets {
"range facet 'end' comes before 'start': "+end+" < "+start);
}
final String gap = required.getFieldParam(f, FacetParams.FACET_RANGE_GAP);
final String gap = parsed.required.getFieldParam(f, FacetParams.FACET_RANGE_GAP);
// explicitly return the gap. compute this early so we are more
// likely to catch parse errors before attempting math
res.add("gap", calc.getGap(gap));
final int minCount = params.getFieldInt(f,FacetParams.FACET_MINCOUNT, 0);
final int minCount = parsed.params.getFieldInt(f,FacetParams.FACET_MINCOUNT, 0);
final EnumSet<FacetRangeInclude> include = FacetRangeInclude.parseParam
(params.getFieldParams(f,FacetParams.FACET_RANGE_INCLUDE));
(parsed.params.getFieldParams(f,FacetParams.FACET_RANGE_INCLUDE));
ArrayList<IntervalFacets.FacetInterval> intervals = new ArrayList<>();
final String[] othersP =
params.getFieldParams(f,FacetParams.FACET_RANGE_OTHER);
parsed.params.getFieldParams(f,FacetParams.FACET_RANGE_OTHER);
boolean includeBefore = false;
boolean includeBetween = false;
@ -1353,7 +1364,7 @@ public class SimpleFacets {
while (low.compareTo(end) < 0) {
T high = calc.addGap(low, gap);
if (end.compareTo(high) < 0) {
if (params.getFieldBool(f,FacetParams.FACET_RANGE_HARD_END,false)) {
if (parsed.params.getFieldBool(f,FacetParams.FACET_RANGE_HARD_END,false)) {
high = end;
} else {
end = high;
@ -1418,7 +1429,7 @@ public class SimpleFacets {
// don't use the ArrayList anymore
intervals = null;
new IntervalFacets(sf, searcher, docs, intervalsArray);
new IntervalFacets(sf, searcher, parsed.docs, intervalsArray);
int intervalIndex = 0;
int lastIntervalIndex = intervalsArray.length - 1;
@ -1461,13 +1472,13 @@ public class SimpleFacets {
* @see SolrIndexSearcher#numDocs
* @see TermRangeQuery
*/
protected int rangeCount(SchemaField sf, String low, String high,
protected int rangeCount(ParsedParams parsed, SchemaField sf, String low, String high,
boolean iLow, boolean iHigh) throws IOException {
Query rangeQ = sf.getType().getRangeQuery(null, sf, low, high, iLow, iHigh);
if (params.getBool(GroupParams.GROUP_FACET, false)) {
return getGroupedFacetQueryCount(rangeQ);
if (parsed.params.getBool(GroupParams.GROUP_FACET, false)) {
return getGroupedFacetQueryCount(rangeQ, parsed);
} else {
return searcher.numDocs(rangeQ , docs);
return searcher.numDocs(rangeQ , parsed.docs);
}
}
@ -1475,10 +1486,10 @@ public class SimpleFacets {
* @deprecated Use rangeCount(SchemaField,String,String,boolean,boolean) which is more generalized
*/
@Deprecated
protected int rangeCount(SchemaField sf, Date low, Date high,
protected int rangeCount(ParsedParams parsed, SchemaField sf, Date low, Date high,
boolean iLow, boolean iHigh) throws IOException {
Query rangeQ = ((TrieDateField)(sf.getType())).getRangeQuery(null, sf, low, high, iLow, iHigh);
return searcher.numDocs(rangeQ, docs);
return searcher.numDocs(rangeQ, parsed.docs);
}
/**
@ -1731,20 +1742,20 @@ public class SimpleFacets {
*/
public NamedList<Object> getFacetIntervalCounts() throws IOException, SyntaxError {
NamedList<Object> res = new SimpleOrderedMap<Object>();
String[] fields = params.getParams(FacetParams.FACET_INTERVAL);
String[] fields = global.getParams(FacetParams.FACET_INTERVAL);
if (fields == null || fields.length == 0) return res;
for (String field : fields) {
parseParams(FacetParams.FACET_INTERVAL, field);
String[] intervalStrs = required.getFieldParams(facetValue, FacetParams.FACET_INTERVAL_SET);
SchemaField schemaField = searcher.getCore().getLatestSchema().getField(facetValue);
if (params.getBool(GroupParams.GROUP_FACET, false)) {
final ParsedParams parsed = parseParams(FacetParams.FACET_INTERVAL, field);
String[] intervalStrs = parsed.required.getFieldParams(parsed.facetValue, FacetParams.FACET_INTERVAL_SET);
SchemaField schemaField = searcher.getCore().getLatestSchema().getField(parsed.facetValue);
if (parsed.params.getBool(GroupParams.GROUP_FACET, false)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Interval Faceting can't be used with " + GroupParams.GROUP_FACET);
}
SimpleOrderedMap<Integer> fieldResults = new SimpleOrderedMap<Integer>();
res.add(key, fieldResults);
IntervalFacets intervalFacets = new IntervalFacets(schemaField, searcher, docs, intervalStrs, params);
res.add(parsed.key, fieldResults);
IntervalFacets intervalFacets = new IntervalFacets(schemaField, searcher, parsed.docs, intervalStrs, parsed.params);
for (FacetInterval interval : intervalFacets) {
fieldResults.add(interval.getKey(), interval.getCount());
}
@ -1759,14 +1770,14 @@ public class SimpleFacets {
if (unparsedFields == null || unparsedFields.length == 0) {
return resOuter;
}
if (params.getBool(GroupParams.GROUP_FACET, false)) {
if (global.getBool(GroupParams.GROUP_FACET, false)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"Heatmaps can't be used with " + GroupParams.GROUP_FACET);
}
for (String unparsedField : unparsedFields) {
parseParams(FacetParams.FACET_HEATMAP, unparsedField); // populates facetValue, rb, params, docs
final ParsedParams parsed = parseParams(FacetParams.FACET_HEATMAP, unparsedField); // populates facetValue, rb, params, docs
resOuter.add(key, SpatialHeatmapFacets.getHeatmapForField(key, facetValue, rb, params, docs));
resOuter.add(parsed.key, SpatialHeatmapFacets.getHeatmapForField(parsed.key, parsed.facetValue, rb, parsed.params, parsed.docs));
}
return resOuter;
}

View File

@ -107,7 +107,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
}
static void indexSimpleFacetCounts() {
add_doc("id", "42",
add_doc("id", "42",
"range_facet_f", "35.3",
"range_facet_f1", "35.3",
"trait_s", "Tool", "trait_s", "Obnoxious",
@ -127,7 +127,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
"range_facet_mv_f", "7.5",
"range_facet_mv_f", "12.0"
);
add_doc("id", "44",
add_doc("id", "44",
"range_facet_f", "15.97",
"range_facet_f1", "15.97",
"trait_s", "Tool",
@ -137,7 +137,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
"range_facet_mv_f", "5",
"range_facet_mv_f", "74"
);
add_doc("id", "45",
add_doc("id", "45",
"range_facet_f", "30.0",
"range_facet_f1", "30.0",
"trait_s", "Chauvinist",
@ -148,7 +148,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
"range_facet_mv_f", "32.77",
"range_facet_mv_f", "0.123"
);
add_doc("id", "46",
add_doc("id", "46",
"range_facet_f", "20.0",
"range_facet_f1", "20.0",
"trait_s", "Obnoxious",
@ -159,14 +159,16 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
"range_facet_mv_f", "7.3",
"range_facet_mv_f", "0.123"
);
add_doc("id", "47",
"range_facet_f", "28.62",
"range_facet_f1", "28.62",
add_doc("id", "47",
"range_facet_f", "28.62",
"range_facet_f1", "28.62",
"trait_s", "Pig",
"text", "line up and fly directly at the enemy death cannons, clogging them with wreckage!",
"zerolen_s","",
"foo_s","A", "foo_s","B", "foo_s","C"
);
add_doc("id", "101", "myfield_s", "foo");
add_doc("id", "102", "myfield_s", "bar");
}
static void indexSimpleGroupedFacetCounts() {
@ -388,7 +390,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.limit", facetLimit,
"facet.limit", facetLimit,
"facet.field", "airport_s1"
),
"//lst[@name='facet_fields']/lst[@name='airport_s1']",
@ -405,8 +407,8 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.offset", "1",
"facet.limit", facetLimit,
"facet.offset", "1",
"facet.limit", facetLimit,
"facet.field", "airport_s1"
),
"//lst[@name='facet_fields']/lst[@name='airport_s1']",
@ -423,7 +425,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
"group.facet", "true",
"group.field", "hotel_s1",
"facet", "true",
"facet.limit", facetLimit,
"facet.limit", facetLimit,
"facet.field", "airport_s1"
),
"//lst[@name='facet_fields']/lst[@name='airport_s1']",
@ -441,7 +443,7 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
"group.field", "hotel_s1",
"facet", "true",
"facet.field", "airport_s1",
"facet.limit", facetLimit,
"facet.limit", facetLimit,
"facet.prefix", "a"
),
"//lst[@name='facet_fields']/lst[@name='airport_s1']",
@ -2882,6 +2884,19 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
}
public void testFacetPrefixWithFacetThreads() throws Exception {
assertQ("Test facet.prefix with facet.thread",
req("q", "id:[101 TO 102]"
,"facet","true"
,"facet.field", "{!key=key1 facet.prefix=foo}myfield_s"
,"facet.field", "{!key=key2 facet.prefix=bar}myfield_s"
,"facet.threads", "1"
)
,"*[count(//lst[@name='facet_fields']/lst[@name='key1']/int[@name='foo'])=1]"
,"*[count(//lst[@name='facet_fields']/lst[@name='key2']/int[@name='bar'])=1]"
);
}
private String getRandomQuery() {
if (rarely()) {