2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
18
package org.apache.solr.handler.component;
20
import java.io.IOException;
21
import java.util.ArrayList;
22
import java.util.HashMap;
23
import java.util.List;
26
import org.apache.lucene.search.FieldCache;
27
import org.apache.solr.common.SolrException;
28
import org.apache.solr.common.params.SolrParams;
29
import org.apache.solr.common.params.StatsParams;
30
import org.apache.solr.common.params.ShardParams;
31
import org.apache.solr.common.util.NamedList;
32
import org.apache.solr.common.util.SimpleOrderedMap;
33
import org.apache.solr.handler.component.StatsValues;
34
import org.apache.solr.handler.component.FieldFacetStats;
35
import org.apache.solr.request.SolrQueryRequest;
36
import org.apache.solr.schema.FieldType;
37
import org.apache.solr.schema.SchemaField;
38
import org.apache.solr.schema.TrieField;
39
import org.apache.solr.search.DocIterator;
40
import org.apache.solr.search.DocSet;
41
import org.apache.solr.search.SolrIndexSearcher;
42
import org.apache.solr.request.UnInvertedField;
45
* Stats component calculates simple statistics on numeric field values
50
public class StatsComponent extends SearchComponent {
52
public static final String COMPONENT_NAME = "stats";
55
public void prepare(ResponseBuilder rb) throws IOException {
56
if (rb.req.getParams().getBool(StatsParams.STATS,false)) {
57
rb.setNeedDocSet( true );
63
public void process(ResponseBuilder rb) throws IOException {
65
SolrParams params = rb.req.getParams();
66
SimpleStats s = new SimpleStats(rb.req,
67
rb.getResults().docSet,
70
// TODO ???? add this directly to the response, or to the builder?
71
rb.rsp.add( "stats", s.getStatsCounts() );
76
public int distributedProcess(ResponseBuilder rb) throws IOException {
77
return ResponseBuilder.STAGE_DONE;
81
public void modifyRequest(ResponseBuilder rb, SearchComponent who, ShardRequest sreq) {
82
if (!rb.doStats) return;
84
if ((sreq.purpose & ShardRequest.PURPOSE_GET_TOP_IDS) != 0) {
85
sreq.purpose |= ShardRequest.PURPOSE_GET_STATS;
87
StatsInfo si = rb._statsInfo;
89
rb._statsInfo = si = new StatsInfo();
90
si.parse(rb.req.getParams(), rb);
91
// should already be true...
92
// sreq.params.set(StatsParams.STATS, "true");
95
// turn off stats on other requests
96
sreq.params.set(StatsParams.STATS, "false");
97
// we could optionally remove stats params
102
public void handleResponses(ResponseBuilder rb, ShardRequest sreq) {
103
if (!rb.doStats || (sreq.purpose & ShardRequest.PURPOSE_GET_STATS) == 0) return;
105
StatsInfo si = rb._statsInfo;
107
for (ShardResponse srsp : sreq.responses) {
108
NamedList stats = (NamedList) srsp.getSolrResponse().getResponse().get("stats");
110
NamedList stats_fields = (NamedList) stats.get("stats_fields");
111
if (stats_fields != null) {
112
for (int i = 0; i < stats_fields.size(); i++) {
113
String field = stats_fields.getName(i);
114
StatsValues stv = si.statsFields.get(field);
115
NamedList shardStv = (NamedList) stats_fields.get(field);
116
stv.accumulate(shardStv);
123
public void finishStage(ResponseBuilder rb) {
124
if (!rb.doStats || rb.stage != ResponseBuilder.STAGE_GET_FIELDS) return;
125
// wait until STAGE_GET_FIELDS
126
// so that "result" is already stored in the response (for aesthetics)
128
StatsInfo si = rb._statsInfo;
130
NamedList stats = new SimpleOrderedMap();
131
NamedList stats_fields = new SimpleOrderedMap();
132
stats.add("stats_fields", stats_fields);
133
for (String field : si.statsFields.keySet()) {
134
NamedList stv = si.statsFields.get(field).getStatsValues();
135
if ((Long) stv.get("count") != 0) {
136
stats_fields.add(field, stv);
138
stats_fields.add(field, null);
142
rb.rsp.add("stats", stats);
144
rb._statsInfo = null;
148
/////////////////////////////////////////////
150
////////////////////////////////////////////
153
public String getDescription() {
154
return "Calculate Statistics";
158
public String getVersion() {
163
public String getSourceId() {
168
public String getSource() {
175
Map<String, StatsValues> statsFields;
177
void parse(SolrParams params, ResponseBuilder rb) {
178
statsFields = new HashMap<String, StatsValues>();
180
String[] statsFs = params.getParams(StatsParams.STATS_FIELD);
181
if (statsFs != null) {
182
for (String field : statsFs) {
183
FieldType ft = rb.req.getSchema().getFieldType(field);
184
statsFields.put(field, StatsValuesFactory.createStatsValues(ft));
193
/** The main set of documents */
194
protected DocSet docs;
195
/** Configuration params behavior should be driven by */
196
protected SolrParams params;
197
/** Searcher to use for all calculations */
198
protected SolrIndexSearcher searcher;
199
protected SolrQueryRequest req;
201
public SimpleStats(SolrQueryRequest req,
205
this.searcher = req.getSearcher();
207
this.params = params;
210
public NamedList<Object> getStatsCounts() throws IOException {
211
NamedList<Object> res = new SimpleOrderedMap<Object>();
212
res.add("stats_fields", getStatsFields());
216
public NamedList getStatsFields() throws IOException {
217
NamedList<NamedList<Number>> res = new SimpleOrderedMap<NamedList<Number>>();
218
String[] statsFs = params.getParams(StatsParams.STATS_FIELD);
219
boolean isShard = params.getBool(ShardParams.IS_SHARD, false);
220
if (null != statsFs) {
221
for (String f : statsFs) {
222
String[] facets = params.getFieldParams(f, StatsParams.STATS_FACET);
223
if (facets == null) {
224
facets = new String[0]; // make sure it is something...
226
SchemaField sf = searcher.getSchema().getField(f);
227
FieldType ft = sf.getType();
230
// Currently, only UnInvertedField can deal with multi-part trie fields
231
String prefix = TrieField.getMainValuePrefix(ft);
233
if (sf.multiValued() || ft.multiValuedFieldCache() || prefix!=null) {
234
//use UnInvertedField for multivalued fields
235
UnInvertedField uif = UnInvertedField.getUnInvertedField(f, searcher);
236
stv = uif.getStats(searcher, docs, facets).getStatsValues();
238
stv = getFieldCacheStats(f, facets);
240
if (isShard == true || (Long) stv.get("count") > 0) {
250
public NamedList getFieldCacheStats(String fieldName, String[] facet ) {
251
FieldType ft = searcher.getSchema().getFieldType(fieldName);
253
FieldCache.StringIndex si;
255
si = FieldCache.DEFAULT.getStringIndex(searcher.getReader(), fieldName);
257
catch (IOException e) {
258
throw new RuntimeException( "failed to open field cache for: "+fieldName, e );
260
StatsValues allstats = StatsValuesFactory.createStatsValues(ft);
261
final int nTerms = si.lookup.length - 1;
262
if ( nTerms <= 0 || docs.size() <= 0 ) return allstats.getStatsValues();
264
// don't worry about faceting if no documents match...
265
List<FieldFacetStats> facetStats = new ArrayList<FieldFacetStats>();
266
FieldCache.StringIndex facetTermsIndex;
267
for( String facetField : facet ) {
268
FieldType facetFieldType = searcher.getSchema().getFieldType(facetField);
270
if (facetFieldType.isTokenized() || facetFieldType.isMultiValued()) {
271
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
272
"Stats can only facet on single-valued fields, not: " + facetField
273
+ "[" + facetFieldType + "]");
276
facetTermsIndex = FieldCache.DEFAULT.getStringIndex(searcher.getReader(), facetField);
278
catch (IOException e) {
279
throw new RuntimeException( "failed to open field cache for: "
282
facetStats.add(new FieldFacetStats(facetField, facetTermsIndex, facetFieldType, nTerms, ft));
286
DocIterator iter = docs.iterator();
287
while (iter.hasNext()) {
288
int docID = iter.nextDoc();
289
String raw = si.lookup[si.order[docID]];
292
v = ft.indexedToReadable(raw);
293
allstats.accumulate(v);
299
// now update the facets
300
for (FieldFacetStats f : facetStats) {
305
for (FieldFacetStats f : facetStats) {
306
allstats.addFacet(f.name, f.facetStatsValues);
308
return allstats.getStatsValues();