作者:海之蓝水之清清2011 | 来源:互联网 | 2023-06-28 11:26
简介
stats查询用于对文档中的数字型、字符型和日期型字段进行简单的统计。
stats
设置为true,启用stat统计功能
stats.field
指定产生stat统计的字段,可以提供多个字段。
stats.facet
在给定的facet字段中返回子结果
支持的统计信息
min 最小值
max 最大值
sum 所有值之和
count 值的个数
missing 空值个数
mean 平均值
查询语法
输入:
q=apple&stats=true&stats.field=price&stats.field=popularity
输出统计信息:
0.0
2199.0
16
16
5251.270030975342
6038619.175900028
328.20437693595886
536.3536996709846
0.0
10.0
15
17
85.0
603.0
5.666666666666667
2.943920288775949
源码分析
(1)stats组件
/** * Stats component calculates simple statistics on numeric field values * @since solr 1.4 */
public class StatsComponent extends SearchComponent {
public static final String COMPONENT_NAME = "stats";
@Override
'''(a)准备阶段'''
public void prepare(ResponseBuilder rb) throws IOException {
'''根据参数stats=true设置功能开关'''
if (rb.req.getParams().getBool(StatsParams.STATS,false)) {
rb.setNeedDocSet( true );
rb.doStats = true;
rb._statsInfo = new StatsInfo(rb);
}
}
@Override
'''(b)处理阶段'''
public void process(ResponseBuilder rb) throws IOException {
'''如果不需要统计,则返回'''
if (!rb.doStats) return;
'''保存统计结果的map'''
Map statsValues = new LinkedHashMap<>();
'''循环处理“stats.field=price&stats.field=popularity”中每一个field'''
for (StatsField statsField : rb._statsInfo.getStatsFields()) {
'''一般情况,返回主查询语句q=apple的结果文档集'''
DocSet docs = statsField.computeBaseDocSet();
'''getOutputKey()返回“price/popularity”作为key,computeLocalStatsValues()返回StatsValues的子类,比如NumericStatsValues/DateStatsValues/StringStatsValues/EnumStatsValues等 '''
statsValues.put(statsField.getOutputKey(), statsField.computeLocalStatsValues(docs));
}
'''convertToResponse函数返回的结果即是2~25行的内容,计算统计值'''
rb.rsp.add( "stats", convertToResponse(statsValues) );
}
}
public static NamedList>> convertToResponse
(Map statsValues) {
NamedList>> stats = new SimpleOrderedMap<>();
NamedList> stats_fields = new SimpleOrderedMap<>();
stats.add("stats_fields", stats_fields);
'''遍历每一个StatsValues,调用getStatsValues计算统计值'''
for (Map.Entry entry : statsValues.entrySet()) {
String key = entry.getKey();
NamedList stv = entry.getValue().getStatsValues();
stats_fields.add(key, stv);
}
return stats;
}
}
'''(2)AbstractStatsValues类负责各统计值的计算'''
abstract class AbstractStatsValues implements StatsValues {
'''返回key,value对,比如: min 0 max 2199.0 ...'''
'''此处的min/max/count...等是所有StatsValues子类公共输出'''
public NamedList> getStatsValues() {
NamedList res = new SimpleOrderedMap<>();
if (statsField.includeInResponse(Stat.min)) {
res.add("min", min);
}
if (statsField.includeInResponse(Stat.max)) {
res.add("max", max);
}
if (statsField.includeInResponse(Stat.count)) {
res.add("count", count);
}
if (statsField.includeInResponse(Stat.missing)) {
res.add("missing", missing);
}
if (statsField.includeInResponse(Stat.distinctValues)) {
res.add("distinctValues", distinctValues);
}
if (statsField.includeInResponse(Stat.countDistinct)) {
res.add("countDistinct", countDistinct);
}
if (statsField.includeInResponse(Stat.cardinality)) {
if (statsField.getIsShard()) {
res.add("cardinality", hll.toBytes());
} else {
res.add("cardinality", hll.cardinality());
}
}
'''此函数由各子类覆盖,定义各自特殊的输出值'''
addTypeSpecificStats(res);
'''facet输出'''
if (!facets.isEmpty()) {
// add the facet stats
NamedList> nl = new SimpleOrderedMap<>();
for (Map.Entry> entry : facets.entrySet()) {
NamedList> nl2 = new SimpleOrderedMap<>();
nl.add(entry.getKey(), nl2);
for (Map.Entry e2 : entry.getValue().entrySet()) {
nl2.add(e2.getKey(), e2.getValue().getStatsValues());
}
}
res.add(FACETS, nl);
}
return res;
}
}
'''(3)StatsValues子类数字型NumericStatsValues类'''
class NumericStatsValues extends AbstractStatsValues {
'''输出数字型特殊的特有的统计值sum, sumOfSquares, mean, stddev, and percentiles'''
@Override
protected void addTypeSpecificStats(NamedList res) {
if (statsField.includeInResponse(Stat.sum)) {
res.add("sum", sum);
}
if (statsField.includeInResponse(Stat.sumOfSquares)) {
res.add("sumOfSquares", sumOfSquares);
}
if (statsField.includeInResponse(Stat.mean)) {
res.add("mean", sum / count);
}
if (statsField.includeInResponse(Stat.stddev)) {
res.add("stddev", getStandardDeviation());
}
if (statsField.includeInResponse(Stat.percentiles)) {
if (statsField.getIsShard()) {
ByteBuffer buf = ByteBuffer.allocate(tdigest.byteSize()); // upper bound
tdigest.asSmallBytes(buf);
res.add("percentiles", Arrays.copyOf(buf.array(), buf.position()) );
} else {
NamedList percentileNameList = new NamedList();
for (Double percentile : statsField.getPercentilesList()) {
// Empty document set case
if (tdigest.size() == 0) {
percentileNameList.add(percentile.toString(), null);
} else {
Double cutoff = tdigest.quantile(percentile / 100);
percentileNameList.add(percentile.toString(), cutoff);
}
}
res.add("percentiles", percentileNameList);
}
}
}
}