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

« back to all changes in this revision

Viewing changes to solr/core/src/java/org/apache/solr/spelling/AbstractLuceneSpellChecker.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
 
package org.apache.solr.spelling;
2
 
 
3
 
import org.apache.lucene.search.spell.StringDistance;
4
 
/**
5
 
 * Licensed to the Apache Software Foundation (ASF) under one or more
6
 
 * contributor license agreements.  See the NOTICE file distributed with
7
 
 * this work for additional information regarding copyright ownership.
8
 
 * The ASF licenses this file to You under the Apache License, Version 2.0
9
 
 * (the "License"); you may not use this file except in compliance with
10
 
 * the License.  You may obtain a copy of the License at
11
 
 *
12
 
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 
 *
14
 
 * Unless required by applicable law or agreed to in writing, software
15
 
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 
 * See the License for the specific language governing permissions and
18
 
 * limitations under the License.
19
 
 */
20
 
 
21
 
import java.io.File;
22
 
import java.io.IOException;
23
 
import java.util.Arrays;
24
 
import java.util.Collection;
25
 
import java.util.Collections;
26
 
import java.util.Comparator;
27
 
import java.util.List;
28
 
 
29
 
import org.apache.lucene.search.spell.SuggestMode;
30
 
import org.apache.lucene.search.spell.SuggestWord;
31
 
import org.apache.lucene.search.spell.SuggestWordFrequencyComparator;
32
 
import org.apache.lucene.search.spell.SuggestWordQueue;
33
 
import org.slf4j.Logger;
34
 
import org.slf4j.LoggerFactory;
35
 
 
36
 
import org.apache.lucene.analysis.Token;
37
 
import org.apache.lucene.analysis.WhitespaceAnalyzer;
38
 
import org.apache.lucene.index.IndexReader;
39
 
import org.apache.lucene.index.Term;
40
 
import org.apache.lucene.search.spell.Dictionary;
41
 
import org.apache.lucene.search.spell.LevensteinDistance;
42
 
import org.apache.lucene.search.spell.SpellChecker;
43
 
import org.apache.lucene.store.Directory;
44
 
import org.apache.lucene.store.FSDirectory;
45
 
import org.apache.lucene.store.RAMDirectory;
46
 
import org.apache.solr.common.params.ShardParams;
47
 
import org.apache.solr.common.params.SolrParams;
48
 
import org.apache.solr.common.util.NamedList;
49
 
import org.apache.solr.core.SolrCore;
50
 
import org.apache.solr.schema.FieldType;
51
 
import org.apache.solr.search.SolrIndexSearcher;
52
 
 
53
 
 
54
 
/**
55
 
 * Abstract base class for all Lucene-based spell checking implementations.
56
 
 * 
57
 
 * <p>
58
 
 * Refer to <a href="http://wiki.apache.org/solr/SpellCheckComponent">SpellCheckComponent</a>
59
 
 * for more details.
60
 
 * </p>
61
 
 * 
62
 
 * @since solr 1.3
63
 
 */
64
 
public abstract class AbstractLuceneSpellChecker extends SolrSpellChecker {
65
 
  public static final Logger log = LoggerFactory.getLogger(AbstractLuceneSpellChecker.class);
66
 
  
67
 
  public static final String SPELLCHECKER_ARG_NAME = "spellchecker";
68
 
  public static final String LOCATION = "sourceLocation";
69
 
  public static final String INDEX_DIR = "spellcheckIndexDir";
70
 
  public static final String ACCURACY = "accuracy";
71
 
  public static final String STRING_DISTANCE = "distanceMeasure";
72
 
  public static final String COMPARATOR_CLASS = "comparatorClass";
73
 
 
74
 
  public static final String SCORE_COMP = "score";
75
 
  public static final String FREQ_COMP = "freq";
76
 
 
77
 
  protected org.apache.lucene.search.spell.SpellChecker spellChecker;
78
 
 
79
 
  protected String sourceLocation;
80
 
  /*
81
 
  * The Directory containing the Spell checking index
82
 
  * */
83
 
  protected Directory index;
84
 
  protected Dictionary dictionary;
85
 
 
86
 
  public static final int DEFAULT_SUGGESTION_COUNT = 5;
87
 
  protected String indexDir;
88
 
  protected float accuracy = 0.5f;
89
 
  public static final String FIELD = "field";
90
 
 
91
 
  protected StringDistance sd;
92
 
 
93
 
  @Override
94
 
  public String init(NamedList config, SolrCore core) {
95
 
    super.init(config, core);
96
 
    indexDir = (String) config.get(INDEX_DIR);
97
 
    String accuracy = (String) config.get(ACCURACY);
98
 
    //If indexDir is relative then create index inside core.getDataDir()
99
 
    if (indexDir != null)   {
100
 
      if (!new File(indexDir).isAbsolute()) {
101
 
        indexDir = core.getDataDir() + File.separator + indexDir;
102
 
      }
103
 
    }
104
 
    sourceLocation = (String) config.get(LOCATION);
105
 
    String compClass = (String) config.get(COMPARATOR_CLASS);
106
 
    Comparator<SuggestWord> comp = null;
107
 
    if (compClass != null){
108
 
      if (compClass.equalsIgnoreCase(SCORE_COMP)){
109
 
        comp = SuggestWordQueue.DEFAULT_COMPARATOR;
110
 
      } else if (compClass.equalsIgnoreCase(FREQ_COMP)){
111
 
        comp = new SuggestWordFrequencyComparator();
112
 
      } else{//must be a FQCN
113
 
        comp = (Comparator<SuggestWord>) core.getResourceLoader().newInstance(compClass);
114
 
      }
115
 
    } else {
116
 
      comp = SuggestWordQueue.DEFAULT_COMPARATOR;
117
 
    }
118
 
    String strDistanceName = (String)config.get(STRING_DISTANCE);
119
 
    if (strDistanceName != null) {
120
 
      sd = (StringDistance) core.getResourceLoader().newInstance(strDistanceName);
121
 
      //TODO: Figure out how to configure options.  Where's Spring when you need it?  Or at least BeanUtils...
122
 
    } else {
123
 
      sd = new LevensteinDistance();
124
 
    }
125
 
    try {
126
 
      initIndex();
127
 
      spellChecker = new SpellChecker(index, sd, comp);
128
 
    } catch (IOException e) {
129
 
      throw new RuntimeException(e);
130
 
    }
131
 
    if (accuracy != null) {
132
 
      try {
133
 
        this.accuracy = Float.parseFloat(accuracy);
134
 
        spellChecker.setAccuracy(this.accuracy);
135
 
      } catch (NumberFormatException e) {
136
 
        throw new RuntimeException(
137
 
                "Unparseable accuracy given for dictionary: " + name, e);
138
 
      }
139
 
    }
140
 
    return name;
141
 
  }
142
 
 
143
 
  /**
144
 
   * Kept around for back compatibility purposes.
145
 
   * 
146
 
   * @param tokens          The Tokens to be spell checked.
147
 
   * @param reader          The (optional) IndexReader.  If there is not IndexReader, than extendedResults are not possible
148
 
   * @param count The maximum number of suggestions to return
149
 
   * @param onlyMorePopular  TODO
150
 
   * @param extendedResults  TODO
151
 
   * @throws IOException
152
 
   */
153
 
  @Override
154
 
  public SpellingResult getSuggestions(Collection<Token> tokens, IndexReader reader, int count, boolean onlyMorePopular, boolean extendedResults) throws IOException {
155
 
    return getSuggestions(new SpellingOptions(tokens, reader, count, onlyMorePopular, extendedResults, spellChecker.getAccuracy(), null));
156
 
  }
157
 
 
158
 
  @Override
159
 
  public SpellingResult getSuggestions(SpellingOptions options) throws IOException {
160
 
        boolean shardRequest = false;
161
 
        SolrParams params = options.customParams;
162
 
        if(params!=null)
163
 
        {
164
 
                shardRequest = "true".equals(params.get(ShardParams.IS_SHARD));
165
 
        }
166
 
    SpellingResult result = new SpellingResult(options.tokens);
167
 
    IndexReader reader = determineReader(options.reader);
168
 
    Term term = field != null ? new Term(field, "") : null;
169
 
    float theAccuracy = (options.accuracy == Float.MIN_VALUE) ? spellChecker.getAccuracy() : options.accuracy;
170
 
    
171
 
    int count = Math.max(options.count, AbstractLuceneSpellChecker.DEFAULT_SUGGESTION_COUNT);
172
 
    SuggestMode mode = options.onlyMorePopular ? SuggestMode.SUGGEST_MORE_POPULAR : SuggestMode.SUGGEST_WHEN_NOT_IN_INDEX;
173
 
    for (Token token : options.tokens) {
174
 
      String tokenText = new String(token.buffer(), 0, token.length());
175
 
      String[] suggestions = spellChecker.suggestSimilar(tokenText,
176
 
              count,
177
 
            field != null ? reader : null, //workaround LUCENE-1295
178
 
            field,
179
 
            mode, theAccuracy);
180
 
      if (suggestions.length == 1 && suggestions[0].equals(tokenText)) {
181
 
        //These are spelled the same, continue on
182
 
        continue;
183
 
      }
184
 
 
185
 
      if (options.extendedResults == true && reader != null && field != null) {
186
 
        term = term.createTerm(tokenText);
187
 
        result.addFrequency(token, reader.docFreq(term));
188
 
        int countLimit = Math.min(options.count, suggestions.length);
189
 
        if(countLimit>0)
190
 
        {
191
 
                for (int i = 0; i < countLimit; i++) {
192
 
                  term = term.createTerm(suggestions[i]);
193
 
                  result.add(token, suggestions[i], reader.docFreq(term));
194
 
                }
195
 
        } else if(shardRequest) {
196
 
                List<String> suggList = Collections.emptyList();
197
 
                result.add(token, suggList);
198
 
        }
199
 
      } else {
200
 
        if (suggestions.length > 0) {
201
 
          List<String> suggList = Arrays.asList(suggestions);
202
 
          if (suggestions.length > options.count) {
203
 
            suggList = suggList.subList(0, options.count);
204
 
          }
205
 
          result.add(token, suggList);
206
 
        } else if(shardRequest) {
207
 
                List<String> suggList = Collections.emptyList();
208
 
                result.add(token, suggList);
209
 
        }
210
 
      }
211
 
    }
212
 
    return result;
213
 
  }
214
 
 
215
 
  protected IndexReader determineReader(IndexReader reader) {
216
 
    return reader;
217
 
  }
218
 
 
219
 
  @Override
220
 
  public void reload(SolrCore core, SolrIndexSearcher searcher) throws IOException {
221
 
    spellChecker.setSpellIndex(index);
222
 
 
223
 
  }
224
 
 
225
 
  /**
226
 
   * Initialize the {@link #index} variable based on the {@link #indexDir}.  Does not actually create the spelling index.
227
 
   *
228
 
   * @throws IOException
229
 
   */
230
 
  protected void initIndex() throws IOException {
231
 
    if (indexDir != null) {
232
 
      index = FSDirectory.open(new File(indexDir));
233
 
    } else {
234
 
      index = new RAMDirectory();
235
 
    }
236
 
  }
237
 
 
238
 
  /*
239
 
  * @return the Accuracy used for the Spellchecker
240
 
  * */
241
 
  public float getAccuracy() {
242
 
    return accuracy;
243
 
  }
244
 
 
245
 
  /*
246
 
  * @return the Field used
247
 
  *
248
 
  * */
249
 
  public String getField() {
250
 
    return field;
251
 
  }
252
 
 
253
 
  /*
254
 
  *
255
 
  * @return the FieldType name.
256
 
  * */
257
 
  public String getFieldTypeName() {
258
 
    return fieldTypeName;
259
 
  }
260
 
 
261
 
 
262
 
  /*
263
 
  * @return the Index directory
264
 
  * */
265
 
  public String getIndexDir() {
266
 
    return indexDir;
267
 
  }
268
 
 
269
 
  /*
270
 
  * @return the location of the source
271
 
  * */
272
 
  public String getSourceLocation() {
273
 
    return sourceLocation;
274
 
  }
275
 
 
276
 
  public StringDistance getStringDistance() {
277
 
    return sd;
278
 
  }
279
 
 
280
 
  public SpellChecker getSpellChecker() {
281
 
    return spellChecker;
282
 
  }
283
 
}