in solr/core/src/java/org/apache/solr/request/SimpleFacets.java [437:674]
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);
boolean missing = params.getFieldBool(field, FacetParams.FACET_MISSING, false);
// when limit=0 and missing=false then return empty list
if (limit == 0 && !missing) return new NamedList<>();
if (mincount == null) {
Boolean zeros = params.getFieldBool(field, FacetParams.FACET_ZEROS);
// mincount = (zeros!=null && zeros) ? 0 : 1;
mincount = (zeros != null && !zeros) ? 1 : 0;
// current default is to include zeros.
}
// default to sorting if there is a limit.
String sort =
params.getFieldParam(
field,
FacetParams.FACET_SORT,
limit > 0 ? FacetParams.FACET_SORT_COUNT : FacetParams.FACET_SORT_INDEX);
String prefix = params.getFieldParam(field, FacetParams.FACET_PREFIX);
final Predicate<BytesRef> termFilter = newBytesRefFilter(field, params);
boolean exists = params.getFieldBool(field, FacetParams.FACET_EXISTS, false);
NamedList<Integer> counts;
SchemaField sf = searcher.getSchema().getField(field);
if (sf.getType().isPointField() && !sf.hasDocValues()) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST, "Can't facet on a PointField without docValues");
}
FieldType ft = sf.getType();
// determine what type of faceting method to use
final String methodStr = params.getFieldParam(field, FacetParams.FACET_METHOD);
final FacetMethod requestedMethod;
if (FacetParams.FACET_METHOD_enum.equals(methodStr)) {
requestedMethod = FacetMethod.ENUM;
} else if (FacetParams.FACET_METHOD_fcs.equals(methodStr)) {
requestedMethod = FacetMethod.FCS;
} else if (FacetParams.FACET_METHOD_fc.equals(methodStr)) {
requestedMethod = FacetMethod.FC;
} else if (FacetParams.FACET_METHOD_uif.equals(methodStr)) {
requestedMethod = FacetMethod.UIF;
} else {
requestedMethod = null;
}
final boolean multiToken = sf.multiValued() || ft.multiValuedFieldCache();
FacetMethod appliedFacetMethod =
selectFacetMethod(field, sf, requestedMethod, mincount, exists);
RTimer timer = null;
if (fdebug != null) {
fdebug.putInfoItem(
"requestedMethod", requestedMethod == null ? "not specified" : requestedMethod.name());
fdebug.putInfoItem("appliedMethod", appliedFacetMethod.name());
fdebug.putInfoItem("inputDocSetSize", docs.size());
fdebug.putInfoItem("field", field);
timer = new RTimer();
}
if (params.getFieldBool(field, GroupParams.GROUP_FACET, false)) {
counts =
getGroupedCounts(
searcher,
docs,
field,
multiToken,
offset,
limit,
mincount,
missing,
sort,
prefix,
termFilter);
} else {
assert appliedFacetMethod != null;
switch (appliedFacetMethod) {
case ENUM:
assert TrieField.getMainValuePrefix(ft) == null;
counts =
getFacetTermEnumCounts(
searcher,
docs,
field,
offset,
limit,
mincount,
missing,
sort,
prefix,
termFilter,
exists);
break;
case FCS:
assert ft.isPointField() || !multiToken;
if (ft.isPointField() || (ft.getNumberType() != null && !sf.multiValued())) {
if (prefix != null) {
throw new SolrException(
ErrorCode.BAD_REQUEST,
FacetParams.FACET_PREFIX + " is not supported on numeric types");
}
if (termFilter != null) {
throw new SolrException(
ErrorCode.BAD_REQUEST,
"BytesRef term filters ("
+ FacetParams.FACET_MATCHES
+ ", "
+ FacetParams.FACET_CONTAINS
+ ", "
+ FacetParams.FACET_EXCLUDETERMS
+ ") are not supported on numeric types");
}
if (ft.isPointField()
&& mincount <= 0) { // default is mincount=0. See SOLR-10033 & SOLR-11174.
String warningMessage =
"Raising facet.mincount from "
+ mincount
+ " to 1, because field "
+ field
+ " is Points-based.";
log.warn(warningMessage);
@SuppressWarnings({"unchecked"})
List<String> warnings = (List<String>) rb.rsp.getResponseHeader().get("warnings");
if (null == warnings) {
warnings = new ArrayList<>();
rb.rsp.getResponseHeader().add("warnings", warnings);
}
warnings.add(warningMessage);
mincount = 1;
}
counts =
NumericFacets.getCounts(
searcher, docs, field, offset, limit, mincount, missing, sort);
} else {
PerSegmentSingleValuedFaceting ps =
new PerSegmentSingleValuedFaceting(
searcher,
docs,
field,
offset,
limit,
mincount,
missing,
sort,
prefix,
termFilter);
Executor executor = threads == 0 ? directExecutor : facetExecutor;
ps.setNumThreads(threads);
counts = ps.getFacetCounts(executor);
}
break;
case UIF:
// Emulate the JSON Faceting structure so we can use the same parsing classes
Map<String, Object> jsonFacet = CollectionUtil.newHashMap(13);
jsonFacet.put("type", "terms");
jsonFacet.put("field", field);
jsonFacet.put("offset", offset);
jsonFacet.put("limit", limit);
jsonFacet.put("mincount", mincount);
jsonFacet.put("missing", missing);
jsonFacet.put("prefix", prefix);
jsonFacet.put("numBuckets", params.getFieldBool(field, "numBuckets", false));
jsonFacet.put("allBuckets", params.getFieldBool(field, "allBuckets", false));
jsonFacet.put("method", "uif");
jsonFacet.put("cacheDf", 0);
jsonFacet.put("perSeg", false);
final String sortVal;
switch (sort) {
case FacetParams.FACET_SORT_COUNT_LEGACY:
sortVal = FacetParams.FACET_SORT_COUNT;
break;
case FacetParams.FACET_SORT_INDEX_LEGACY:
sortVal = FacetParams.FACET_SORT_INDEX;
break;
default:
sortVal = sort;
}
jsonFacet.put(SORT, sortVal);
// TODO do we handle debug? Should probably already be handled by the legacy code
Object resObj = FacetRequest.parseOneFacetReq(req, jsonFacet).process(req, docs);
// Go through the response to build the expected output for SimpleFacets
counts = new NamedList<>();
if (resObj != null) {
@SuppressWarnings({"unchecked"})
NamedList<Object> res = (NamedList<Object>) resObj;
@SuppressWarnings({"unchecked"})
List<NamedList<Object>> buckets = (List<NamedList<Object>>) res.get("buckets");
for (NamedList<Object> b : buckets) {
counts.add(b.get("val").toString(), ((Number) b.get("count")).intValue());
}
if (missing) {
@SuppressWarnings({"unchecked"})
NamedList<Object> missingCounts = (NamedList<Object>) res.get("missing");
counts.add(null, ((Number) missingCounts.get("count")).intValue());
}
}
break;
case FC:
counts =
DocValuesFacets.getCounts(
searcher,
docs,
field,
offset,
limit,
mincount,
missing,
sort,
prefix,
termFilter,
fdebug);
break;
default:
throw new AssertionError();
}
}
if (fdebug != null) {
long timeElapsed = (long) timer.getTime();
fdebug.setElapse(timeElapsed);
}
return counts;
}