~slub.team/goobi-indexserver/3.x

« back to all changes in this revision

Viewing changes to solr/core/src/java/org/apache/solr/handler/component/StatsComponent.java

  • Committer: Sebastian Meyer
  • Date: 2012-08-03 09:12:40 UTC
  • Revision ID: sebastian.meyer@slub-dresden.de-20120803091240-x6861b0vabq1xror
Remove Lucene and Solr source code and add patches instead
Fix Bug #985487: Auto-suggestion for the search interface

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
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
8
 
 *
9
 
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 
 *
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.
16
 
 */
17
 
 
18
 
package org.apache.solr.handler.component;
19
 
 
20
 
import java.io.IOException;
21
 
import java.util.ArrayList;
22
 
import java.util.HashMap;
23
 
import java.util.List;
24
 
import java.util.Map;
25
 
 
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;
43
 
 
44
 
/**
45
 
 * Stats component calculates simple statistics on numeric field values
46
 
 * 
47
 
 * @version $Id$
48
 
 * @since solr 1.4
49
 
 */
50
 
public class StatsComponent extends SearchComponent {
51
 
 
52
 
  public static final String COMPONENT_NAME = "stats";
53
 
  
54
 
  @Override
55
 
  public void prepare(ResponseBuilder rb) throws IOException {
56
 
    if (rb.req.getParams().getBool(StatsParams.STATS,false)) {
57
 
      rb.setNeedDocSet( true );
58
 
      rb.doStats = true;
59
 
    }
60
 
  }
61
 
 
62
 
  @Override
63
 
  public void process(ResponseBuilder rb) throws IOException {
64
 
    if (rb.doStats) {
65
 
      SolrParams params = rb.req.getParams();
66
 
      SimpleStats s = new SimpleStats(rb.req,
67
 
              rb.getResults().docSet,
68
 
              params );
69
 
 
70
 
      // TODO ???? add this directly to the response, or to the builder?
71
 
      rb.rsp.add( "stats", s.getStatsCounts() );
72
 
    }
73
 
  }
74
 
 
75
 
  @Override
76
 
  public int distributedProcess(ResponseBuilder rb) throws IOException {
77
 
    return ResponseBuilder.STAGE_DONE;
78
 
  }
79
 
 
80
 
  @Override
81
 
  public void modifyRequest(ResponseBuilder rb, SearchComponent who, ShardRequest sreq) {
82
 
    if (!rb.doStats) return;
83
 
 
84
 
    if ((sreq.purpose & ShardRequest.PURPOSE_GET_TOP_IDS) != 0) {
85
 
        sreq.purpose |= ShardRequest.PURPOSE_GET_STATS;
86
 
 
87
 
        StatsInfo si = rb._statsInfo;
88
 
        if (si == null) {
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");
93
 
        }
94
 
    } else {
95
 
      // turn off stats on other requests
96
 
      sreq.params.set(StatsParams.STATS, "false");
97
 
      // we could optionally remove stats params
98
 
    }
99
 
  }
100
 
 
101
 
  @Override
102
 
  public void handleResponses(ResponseBuilder rb, ShardRequest sreq) {
103
 
    if (!rb.doStats || (sreq.purpose & ShardRequest.PURPOSE_GET_STATS) == 0) return;
104
 
 
105
 
    StatsInfo si = rb._statsInfo;
106
 
 
107
 
    for (ShardResponse srsp : sreq.responses) {
108
 
      NamedList stats = (NamedList) srsp.getSolrResponse().getResponse().get("stats");
109
 
 
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);
117
 
        }
118
 
      }
119
 
    }
120
 
  }
121
 
 
122
 
  @Override
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)
127
 
 
128
 
    StatsInfo si = rb._statsInfo;
129
 
 
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);
137
 
      } else {
138
 
        stats_fields.add(field, null);
139
 
      }
140
 
    }
141
 
 
142
 
    rb.rsp.add("stats", stats);
143
 
 
144
 
    rb._statsInfo = null;
145
 
  }
146
 
 
147
 
 
148
 
  /////////////////////////////////////////////
149
 
  ///  SolrInfoMBean
150
 
  ////////////////////////////////////////////
151
 
 
152
 
  @Override
153
 
  public String getDescription() {
154
 
    return "Calculate Statistics";
155
 
  }
156
 
 
157
 
  @Override
158
 
  public String getVersion() {
159
 
    return "$Revision$";
160
 
  }
161
 
 
162
 
  @Override
163
 
  public String getSourceId() {
164
 
    return "$Id$";
165
 
  }
166
 
 
167
 
  @Override
168
 
  public String getSource() {
169
 
    return "$URL$";
170
 
  }
171
 
 
172
 
}
173
 
 
174
 
class StatsInfo {
175
 
  Map<String, StatsValues> statsFields;
176
 
 
177
 
  void parse(SolrParams params, ResponseBuilder rb) {
178
 
    statsFields = new HashMap<String, StatsValues>();
179
 
 
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));
185
 
      }
186
 
    }
187
 
  }
188
 
}
189
 
 
190
 
 
191
 
class SimpleStats {
192
 
 
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;
200
 
 
201
 
  public SimpleStats(SolrQueryRequest req,
202
 
                      DocSet docs,
203
 
                      SolrParams params) {
204
 
    this.req = req;
205
 
    this.searcher = req.getSearcher();
206
 
    this.docs = docs;
207
 
    this.params = params;
208
 
  }
209
 
 
210
 
  public NamedList<Object> getStatsCounts() throws IOException {
211
 
    NamedList<Object> res = new SimpleOrderedMap<Object>();
212
 
    res.add("stats_fields", getStatsFields());
213
 
    return res;
214
 
  }
215
 
 
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...
225
 
        }
226
 
        SchemaField sf = searcher.getSchema().getField(f);
227
 
        FieldType ft = sf.getType();
228
 
        NamedList stv;
229
 
 
230
 
        // Currently, only UnInvertedField can deal with multi-part trie fields
231
 
        String prefix = TrieField.getMainValuePrefix(ft);
232
 
 
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();
237
 
        } else {
238
 
          stv = getFieldCacheStats(f, facets);
239
 
        }
240
 
        if (isShard == true || (Long) stv.get("count") > 0) {
241
 
          res.add(f, stv);
242
 
        } else {
243
 
          res.add(f, null);
244
 
        }
245
 
      }
246
 
    }
247
 
    return res;
248
 
  }
249
 
  
250
 
  public NamedList getFieldCacheStats(String fieldName, String[] facet ) {
251
 
    FieldType ft = searcher.getSchema().getFieldType(fieldName);
252
 
 
253
 
    FieldCache.StringIndex si;
254
 
    try {
255
 
      si = FieldCache.DEFAULT.getStringIndex(searcher.getReader(), fieldName);
256
 
    } 
257
 
    catch (IOException e) {
258
 
      throw new RuntimeException( "failed to open field cache for: "+fieldName, e );
259
 
    }
260
 
    StatsValues allstats = StatsValuesFactory.createStatsValues(ft);
261
 
    final int nTerms = si.lookup.length - 1;
262
 
    if ( nTerms <= 0 || docs.size() <= 0 ) return allstats.getStatsValues();
263
 
 
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);
269
 
 
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 + "]");
274
 
        }
275
 
      try {
276
 
        facetTermsIndex = FieldCache.DEFAULT.getStringIndex(searcher.getReader(), facetField);
277
 
      }
278
 
      catch (IOException e) {
279
 
        throw new RuntimeException( "failed to open field cache for: "
280
 
          + facetField, e );
281
 
      }
282
 
      facetStats.add(new FieldFacetStats(facetField, facetTermsIndex, facetFieldType, nTerms, ft));
283
 
    }
284
 
    
285
 
    
286
 
    DocIterator iter = docs.iterator();
287
 
    while (iter.hasNext()) {
288
 
      int docID = iter.nextDoc();
289
 
      String raw = si.lookup[si.order[docID]];
290
 
      String v;
291
 
      if( raw != null ) {
292
 
        v = ft.indexedToReadable(raw);
293
 
        allstats.accumulate(v);
294
 
      } else {
295
 
        v = null;
296
 
        allstats.missing();
297
 
      }
298
 
 
299
 
      // now update the facets
300
 
      for (FieldFacetStats f : facetStats) {
301
 
        f.facet(docID, v);
302
 
      }
303
 
    }
304
 
 
305
 
    for (FieldFacetStats f : facetStats) {
306
 
      allstats.addFacet(f.name, f.facetStatsValues);
307
 
    }
308
 
    return allstats.getStatsValues();
309
 
  }
310
 
 
311
 
 
312
 
}