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
输出统计信息:
<lst name="stats">
<lst name="stats_fields">
<lst name="price">
<double name="min">0.0</double>
<double name="max">2199.0</double>
<long name="count">16</long>
<long name="missing">16</long>
<double name="sum">5251.270030975342</double>
<double name="sumOfSquares">6038619.175900028</double>
<double name="mean">328.20437693595886</double>
<double name="stddev">536.3536996709846</double>
<lst name="facets"/>
</lst>
<lst name="popularity">
<double name="min">0.0</double>
<double name="max">10.0</double>
<long name="count">15</long>
<long name="missing">17</long>
<double name="sum">85.0</double>
<double name="sumOfSquares">603.0</double>
<double name="mean">5.666666666666667</double>
<double name="stddev">2.943920288775949</double>
<lst name="facets"/>
</lst>
</lst>
</lst>
(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<String, StatsValues> 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<NamedList<NamedList<?>>> convertToResponse
(Map<String,StatsValues> statsValues) {
NamedList<NamedList<NamedList<?>>> stats = new SimpleOrderedMap<>();
NamedList<NamedList<?>> stats_fields = new SimpleOrderedMap<>();
stats.add("stats_fields", stats_fields);
'''遍历每一个StatsValues,调用getStatsValues计算统计值'''
for (Map.Entry<String,StatsValues> entry : statsValues.entrySet()) {
String key = entry.getKey();
NamedList stv = entry.getValue().getStatsValues();
stats_fields.add(key, stv);
}
return stats;
}
}
'''(2)AbstractStatsValues类负责各统计值的计算'''
abstract class AbstractStatsValues<T> implements StatsValues {
'''返回key,value对,比如: min 0 max 2199.0 ...'''
'''此处的min/max/count...等是所有StatsValues子类公共输出'''
public NamedList<?> getStatsValues() {
NamedList<Object> 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<NamedList<?>> nl = new SimpleOrderedMap<>();
for (Map.Entry<String,Map<String,StatsValues>> entry : facets.entrySet()) {
NamedList<NamedList<?>> nl2 = new SimpleOrderedMap<>();
nl.add(entry.getKey(), nl2);
for (Map.Entry<String,StatsValues> e2 : entry.getValue().entrySet()) {
nl2.add(e2.getKey(), e2.getValue().getStatsValues());
}
}
res.add(FACETS, nl);
}
return res;
}
}
'''(3)StatsValues子类数字型NumericStatsValues类'''
class NumericStatsValues extends AbstractStatsValues<Number> {
'''输出数字型特殊的特有的统计值sum, sumOfSquares, mean, stddev, and percentiles'''
@Override
protected void addTypeSpecificStats(NamedList<Object> 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<Object> percentileNameList = new NamedList<Object>();
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);
}
}
}
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/zhufenglonglove/article/details/51736052
内容来源于网络,如有侵权,请联系作者删除!