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

« back to all changes in this revision

Viewing changes to solr/core/src/java/org/apache/solr/schema/TrieField.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
 
package org.apache.solr.schema;
18
 
 
19
 
import org.apache.lucene.document.Fieldable;
20
 
import org.apache.lucene.document.Field;
21
 
import org.apache.lucene.document.NumericField;
22
 
import org.apache.lucene.index.FieldInfo.IndexOptions;
23
 
import org.apache.lucene.search.*;
24
 
import org.apache.lucene.util.NumericUtils;
25
 
import org.apache.lucene.analysis.TokenStream;
26
 
import org.apache.lucene.analysis.NumericTokenStream;
27
 
import org.apache.solr.analysis.*;
28
 
import org.apache.solr.common.SolrException;
29
 
import org.apache.solr.response.TextResponseWriter;
30
 
import org.apache.solr.response.XMLWriter;
31
 
import org.apache.solr.search.QParser;
32
 
import org.apache.solr.search.function.*;
33
 
 
34
 
import java.io.IOException;
35
 
import java.util.Locale;
36
 
import java.util.Map;
37
 
import java.util.Date;
38
 
 
39
 
/**
40
 
 * Provides field types to support for Lucene's {@link NumericField}.
41
 
 * See {@link org.apache.lucene.search.NumericRangeQuery} for more details.
42
 
 * It supports integer, float, long, double and date types.
43
 
 * <p/>
44
 
 * For each number being added to this field, multiple terms are generated as per the algorithm described in the above
45
 
 * link. The possible number of terms increases dramatically with lower precision steps. For
46
 
 * the fast range search to work, trie fields must be indexed.
47
 
 * <p/>
48
 
 * Trie fields are sortable in numerical order and can be used in function queries.
49
 
 * <p/>
50
 
 * Note that if you use a precisionStep of 32 for int/float and 64 for long/double/date, then multiple terms will not be
51
 
 * generated, range search will be no faster than any other number field, but sorting will still be possible.
52
 
 *
53
 
 * @version $Id: TrieField.java 1201479 2011-11-13 19:11:52Z erick $
54
 
 * @see org.apache.lucene.search.NumericRangeQuery
55
 
 * @since solr 1.4
56
 
 */
57
 
public class TrieField extends FieldType {
58
 
  public static final int DEFAULT_PRECISION_STEP = 8;
59
 
 
60
 
  protected int precisionStepArg = TrieField.DEFAULT_PRECISION_STEP;  // the one passed in or defaulted
61
 
  protected int precisionStep;     // normalized
62
 
  protected TrieTypes type;
63
 
  protected Object missingValue;
64
 
 
65
 
 
66
 
  /**
67
 
   * Used for handling date types following the same semantics as DateField
68
 
   */
69
 
  static final DateField dateField = new DateField();
70
 
 
71
 
  @Override
72
 
  protected void init(IndexSchema schema, Map<String, String> args) {
73
 
    String p = args.remove("precisionStep");
74
 
    if (p != null) {
75
 
       precisionStepArg = Integer.parseInt(p);
76
 
    }
77
 
    // normalize the precisionStep
78
 
    precisionStep = precisionStepArg;
79
 
    if (precisionStep<=0 || precisionStep>=64) precisionStep=Integer.MAX_VALUE;
80
 
    String t = args.remove("type");
81
 
 
82
 
    if (t != null) {
83
 
      try {
84
 
        type = TrieTypes.valueOf(t.toUpperCase(Locale.ENGLISH));
85
 
      } catch (IllegalArgumentException e) {
86
 
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
87
 
                "Invalid type specified in schema.xml for field: " + args.get("name"), e);
88
 
      }
89
 
    }
90
 
  
91
 
    
92
 
    CharFilterFactory[] filterFactories = new CharFilterFactory[0];
93
 
    TokenFilterFactory[] tokenFilterFactories = new TokenFilterFactory[0];
94
 
    analyzer = new TokenizerChain(filterFactories, new TrieTokenizerFactory(type, precisionStep), tokenFilterFactories);
95
 
    // for query time we only need one token, so we use the biggest possible precisionStep:
96
 
    queryAnalyzer = new TokenizerChain(filterFactories, new TrieTokenizerFactory(type, Integer.MAX_VALUE), tokenFilterFactories);
97
 
  }
98
 
 
99
 
  @Override
100
 
  public Object toObject(Fieldable f) {
101
 
    if (f instanceof NumericField) {
102
 
      final Number val = ((NumericField) f).getNumericValue();
103
 
      if (val==null) return badFieldString(f);
104
 
      return (type == TrieTypes.DATE) ? new Date(val.longValue()) : val;
105
 
    } else {
106
 
      // the following code is "deprecated" and only to support pre-3.2 indexes using the old BinaryField encoding:
107
 
      final byte[] arr = f.getBinaryValue();
108
 
      if (arr==null) return badFieldString(f);
109
 
      switch (type) {
110
 
        case INTEGER:
111
 
          return toInt(arr);
112
 
        case FLOAT:
113
 
          return Float.intBitsToFloat(toInt(arr));
114
 
        case LONG:
115
 
          return toLong(arr);
116
 
        case DOUBLE:
117
 
          return Double.longBitsToDouble(toLong(arr));
118
 
        case DATE:
119
 
          return new Date(toLong(arr));
120
 
        default:
121
 
          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + f.name());
122
 
      }
123
 
    }
124
 
  }
125
 
 
126
 
  @Override
127
 
  public SortField getSortField(SchemaField field, boolean top) {
128
 
    field.checkSortability();
129
 
 
130
 
    Object missingValue = null;
131
 
    boolean sortMissingLast  = field.sortMissingLast();
132
 
    boolean sortMissingFirst = field.sortMissingFirst();
133
 
 
134
 
    switch (type) {
135
 
      case INTEGER:
136
 
        if( sortMissingLast ) {
137
 
          missingValue = top ? Integer.MIN_VALUE : Integer.MAX_VALUE;
138
 
        }
139
 
        else if( sortMissingFirst ) {
140
 
          missingValue = top ? Integer.MAX_VALUE : Integer.MIN_VALUE;
141
 
        }
142
 
        return new SortField( field.getName(), FieldCache.NUMERIC_UTILS_INT_PARSER, top).setMissingValue(missingValue);
143
 
 
144
 
      case FLOAT:
145
 
        if( sortMissingLast ) {
146
 
          missingValue = top ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
147
 
        }
148
 
        else if( sortMissingFirst ) {
149
 
          missingValue = top ? Float.POSITIVE_INFINITY : Float.NEGATIVE_INFINITY;
150
 
        }
151
 
        return new SortField( field.getName(), FieldCache.NUMERIC_UTILS_FLOAT_PARSER, top).setMissingValue(missingValue);
152
 
 
153
 
      case DATE: // fallthrough
154
 
      case LONG:
155
 
        if( sortMissingLast ) {
156
 
          missingValue = top ? Long.MIN_VALUE : Long.MAX_VALUE;
157
 
        }
158
 
        else if( sortMissingFirst ) {
159
 
          missingValue = top ? Long.MAX_VALUE : Long.MIN_VALUE;
160
 
        }
161
 
        return new SortField( field.getName(), FieldCache.NUMERIC_UTILS_LONG_PARSER, top).setMissingValue(missingValue);
162
 
 
163
 
      case DOUBLE:
164
 
        if( sortMissingLast ) {
165
 
          missingValue = top ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
166
 
        }
167
 
        else if( sortMissingFirst ) {
168
 
          missingValue = top ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
169
 
        }
170
 
        return new SortField( field.getName(), FieldCache.NUMERIC_UTILS_DOUBLE_PARSER, top).setMissingValue(missingValue);
171
 
 
172
 
      default:
173
 
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + field.name);
174
 
    }
175
 
  }
176
 
 
177
 
  @Override
178
 
  public ValueSource getValueSource(SchemaField field, QParser qparser) {
179
 
    field.checkFieldCacheSource(qparser);
180
 
    switch (type) {
181
 
      case INTEGER:
182
 
        return new IntFieldSource(field.getName(), FieldCache.NUMERIC_UTILS_INT_PARSER);
183
 
      case FLOAT:
184
 
        return new FloatFieldSource(field.getName(), FieldCache.NUMERIC_UTILS_FLOAT_PARSER);
185
 
      case DATE:
186
 
        return new TrieDateFieldSource(field.getName(), FieldCache.NUMERIC_UTILS_LONG_PARSER);        
187
 
      case LONG:
188
 
        return new LongFieldSource(field.getName(), FieldCache.NUMERIC_UTILS_LONG_PARSER);
189
 
      case DOUBLE:
190
 
        return new DoubleFieldSource(field.getName(), FieldCache.NUMERIC_UTILS_DOUBLE_PARSER);
191
 
      default:
192
 
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + field.name);
193
 
    }
194
 
  }
195
 
 
196
 
  @Override
197
 
  public void write(XMLWriter xmlWriter, String name, Fieldable f) throws IOException {
198
 
    xmlWriter.writeVal(name, toObject(f));
199
 
  }
200
 
 
201
 
  @Override
202
 
  public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException {
203
 
    writer.writeVal(name, toObject(f));
204
 
  }
205
 
 
206
 
  @Override
207
 
  public boolean isTokenized() {
208
 
    return true;
209
 
  }
210
 
 
211
 
  @Override
212
 
  public boolean multiValuedFieldCache() {
213
 
    return false;
214
 
  }
215
 
 
216
 
  /**
217
 
   * @return the precisionStep used to index values into the field
218
 
   */
219
 
  public int getPrecisionStep() {
220
 
    return precisionStepArg;
221
 
  }
222
 
 
223
 
  /**
224
 
   * @return the type of this field
225
 
   */
226
 
  public TrieTypes getType() {
227
 
    return type;
228
 
  }
229
 
 
230
 
  @Override
231
 
  public Query getRangeQuery(QParser parser, SchemaField field, String min, String max, boolean minInclusive, boolean maxInclusive) {
232
 
    int ps = precisionStep;
233
 
    Query query = null;
234
 
    switch (type) {
235
 
      case INTEGER:
236
 
        query = NumericRangeQuery.newIntRange(field.getName(), ps,
237
 
                min == null ? null : Integer.parseInt(min),
238
 
                max == null ? null : Integer.parseInt(max),
239
 
                minInclusive, maxInclusive);
240
 
        break;
241
 
      case FLOAT:
242
 
        query = NumericRangeQuery.newFloatRange(field.getName(), ps,
243
 
                min == null ? null : Float.parseFloat(min),
244
 
                max == null ? null : Float.parseFloat(max),
245
 
                minInclusive, maxInclusive);
246
 
        break;
247
 
      case LONG:
248
 
        query = NumericRangeQuery.newLongRange(field.getName(), ps,
249
 
                min == null ? null : Long.parseLong(min),
250
 
                max == null ? null : Long.parseLong(max),
251
 
                minInclusive, maxInclusive);
252
 
        break;
253
 
      case DOUBLE:
254
 
        query = NumericRangeQuery.newDoubleRange(field.getName(), ps,
255
 
                min == null ? null : Double.parseDouble(min),
256
 
                max == null ? null : Double.parseDouble(max),
257
 
                minInclusive, maxInclusive);
258
 
        break;
259
 
      case DATE:
260
 
        query = NumericRangeQuery.newLongRange(field.getName(), ps,
261
 
                min == null ? null : dateField.parseMath(null, min).getTime(),
262
 
                max == null ? null : dateField.parseMath(null, max).getTime(),
263
 
                minInclusive, maxInclusive);
264
 
        break;
265
 
      default:
266
 
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field");
267
 
    }
268
 
 
269
 
    return query;
270
 
  }
271
 
 
272
 
  @Deprecated
273
 
  static int toInt(byte[] arr) {
274
 
    return (arr[0]<<24) | ((arr[1]&0xff)<<16) | ((arr[2]&0xff)<<8) | (arr[3]&0xff);
275
 
  }
276
 
  
277
 
  @Deprecated
278
 
  static long toLong(byte[] arr) {
279
 
    int high = (arr[0]<<24) | ((arr[1]&0xff)<<16) | ((arr[2]&0xff)<<8) | (arr[3]&0xff);
280
 
    int low = (arr[4]<<24) | ((arr[5]&0xff)<<16) | ((arr[6]&0xff)<<8) | (arr[7]&0xff);
281
 
    return (((long)high)<<32) | (low&0x0ffffffffL);
282
 
  }
283
 
 
284
 
  @Override
285
 
  public String storedToReadable(Fieldable f) {
286
 
    return toExternal(f);
287
 
  }
288
 
 
289
 
  @Override
290
 
  public String readableToIndexed(String val) {
291
 
    switch (type) {
292
 
      case INTEGER:
293
 
        return NumericUtils.intToPrefixCoded(Integer.parseInt(val));
294
 
      case FLOAT:
295
 
        return NumericUtils.intToPrefixCoded(NumericUtils.floatToSortableInt(Float.parseFloat(val)));
296
 
      case LONG:
297
 
        return NumericUtils.longToPrefixCoded(Long.parseLong(val));
298
 
      case DOUBLE:
299
 
        return NumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(Double.parseDouble(val)));
300
 
      case DATE:
301
 
        return NumericUtils.longToPrefixCoded(dateField.parseMath(null, val).getTime());
302
 
      default:
303
 
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
304
 
    }
305
 
  }
306
 
 
307
 
  @Override
308
 
  public String toInternal(String val) {
309
 
    return readableToIndexed(val);
310
 
  }
311
 
 
312
 
 
313
 
  static String badFieldString(Fieldable f) {
314
 
    String s = f.stringValue();
315
 
    return "ERROR:SCHEMA-INDEX-MISMATCH,stringValue="+s;
316
 
  }
317
 
 
318
 
  @Override
319
 
  public String toExternal(Fieldable f) {
320
 
    return (type == TrieTypes.DATE)
321
 
      ? dateField.toExternal((Date) toObject(f)) 
322
 
      : toObject(f).toString();
323
 
  }
324
 
 
325
 
  @Override
326
 
  public String indexedToReadable(String indexedForm) {
327
 
    switch (type) {
328
 
      case INTEGER:
329
 
        return Integer.toString( NumericUtils.prefixCodedToInt(indexedForm) );
330
 
      case FLOAT:
331
 
        return Float.toString( NumericUtils.sortableIntToFloat(NumericUtils.prefixCodedToInt(indexedForm)) );
332
 
      case LONG:
333
 
        return Long.toString( NumericUtils.prefixCodedToLong(indexedForm) );
334
 
      case DOUBLE:
335
 
        return Double.toString( NumericUtils.sortableLongToDouble(NumericUtils.prefixCodedToLong(indexedForm)) );
336
 
      case DATE:
337
 
        return dateField.toExternal( new Date(NumericUtils.prefixCodedToLong(indexedForm)) );
338
 
      default:
339
 
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
340
 
    }
341
 
  }
342
 
 
343
 
  @Override
344
 
  public String storedToIndexed(Fieldable f) {
345
 
    if (f instanceof NumericField) {
346
 
      final Number val = ((NumericField) f).getNumericValue();
347
 
      if (val==null)
348
 
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Invalid field contents: "+f.name());
349
 
      switch (type) {
350
 
        case INTEGER:
351
 
          return NumericUtils.intToPrefixCoded(val.intValue());
352
 
        case FLOAT:
353
 
          return NumericUtils.intToPrefixCoded(NumericUtils.floatToSortableInt(val.floatValue()));
354
 
        case LONG: //fallthrough!
355
 
        case DATE:
356
 
          return NumericUtils.longToPrefixCoded(val.longValue());
357
 
        case DOUBLE:
358
 
          return NumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(val.doubleValue()));
359
 
        default:
360
 
          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + f.name());
361
 
      }
362
 
    } else {
363
 
      // the following code is "deprecated" and only to support pre-3.2 indexes using the old BinaryField encoding:
364
 
      final byte[] arr = f.getBinaryValue();
365
 
      if (arr==null)
366
 
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Invalid field contents: "+f.name());
367
 
      switch (type) {
368
 
        case INTEGER:
369
 
          return NumericUtils.intToPrefixCoded(toInt(arr));
370
 
        case FLOAT: {
371
 
          // WARNING: Code Duplication! Keep in sync with o.a.l.util.NumericUtils!
372
 
          // copied from NumericUtils to not convert to/from float two times
373
 
          // code in next 2 lines is identical to: int v = NumericUtils.floatToSortableInt(Float.intBitsToFloat(toInt(arr)));
374
 
          int v = toInt(arr);
375
 
          if (v<0) v ^= 0x7fffffff;
376
 
          return NumericUtils.intToPrefixCoded(v);
377
 
        }
378
 
        case LONG: //fallthrough!
379
 
        case DATE:
380
 
          return NumericUtils.longToPrefixCoded(toLong(arr));
381
 
        case DOUBLE: {
382
 
          // WARNING: Code Duplication! Keep in sync with o.a.l.util.NumericUtils!
383
 
          // copied from NumericUtils to not convert to/from double two times
384
 
          // code in next 2 lines is identical to: long v = NumericUtils.doubleToSortableLong(Double.longBitsToDouble(toLong(arr)));
385
 
          long v = toLong(arr);
386
 
          if (v<0) v ^= 0x7fffffffffffffffL;
387
 
          return NumericUtils.longToPrefixCoded(v);
388
 
        }
389
 
        default:
390
 
          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + f.name());
391
 
      }
392
 
    }
393
 
  }
394
 
 
395
 
  @Override
396
 
  public Fieldable createField(SchemaField field, String externalVal, float boost) {
397
 
    boolean indexed = field.indexed();
398
 
    boolean stored = field.stored();
399
 
 
400
 
    if (!indexed && !stored) {
401
 
      if (log.isTraceEnabled())
402
 
        log.trace("Ignoring unindexed/unstored field: " + field);
403
 
      return null;
404
 
    }
405
 
 
406
 
    final NumericField f = new NumericField(field.getName(), precisionStep, stored ? Field.Store.YES : Field.Store.NO, indexed);
407
 
    switch (type) {
408
 
      case INTEGER:
409
 
        f.setIntValue(Integer.parseInt(externalVal));
410
 
        break;
411
 
      case FLOAT:
412
 
        f.setFloatValue(Float.parseFloat(externalVal));
413
 
        break;
414
 
      case LONG:
415
 
        f.setLongValue(Long.parseLong(externalVal));
416
 
        break;
417
 
      case DOUBLE:
418
 
        f.setDoubleValue(Double.parseDouble(externalVal));
419
 
        break;
420
 
      case DATE:
421
 
        f.setLongValue(dateField.parseMath(null, externalVal).getTime());
422
 
        break;
423
 
      default:
424
 
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
425
 
    }
426
 
 
427
 
    f.setOmitNorms(field.omitNorms());
428
 
    f.setIndexOptions(getIndexOptions(field, externalVal));
429
 
    f.setBoost(boost);
430
 
    return f;
431
 
  }
432
 
 
433
 
  public enum TrieTypes {
434
 
    INTEGER,
435
 
    LONG,
436
 
    FLOAT,
437
 
    DOUBLE,
438
 
    DATE
439
 
  }
440
 
 
441
 
 
442
 
  static final String INT_PREFIX = new String(new char[]{NumericUtils.SHIFT_START_INT});
443
 
  static final String LONG_PREFIX = new String(new char[]{NumericUtils.SHIFT_START_LONG});
444
 
 
445
 
  /** expert internal use, subject to change.
446
 
   * Returns null if no prefix or prefix not needed, or the prefix of the main value of a trie field
447
 
   * that indexes multiple precisions per value.
448
 
   */
449
 
  public static String getMainValuePrefix(FieldType ft) {
450
 
    if (ft instanceof TrieDateField)
451
 
      ft = ((TrieDateField) ft).wrappedField;
452
 
    if (ft instanceof TrieField) {
453
 
      final TrieField trie = (TrieField)ft;
454
 
      if (trie.precisionStep  == Integer.MAX_VALUE)
455
 
        return null;
456
 
      switch (trie.type) {
457
 
        case INTEGER:
458
 
        case FLOAT:
459
 
          return INT_PREFIX;
460
 
        case LONG:
461
 
        case DOUBLE:
462
 
        case DATE:
463
 
          return LONG_PREFIX;
464
 
        default:
465
 
          throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + trie.type);
466
 
      }
467
 
    }
468
 
    return null;
469
 
  }
470
 
}
471
 
 
472
 
class TrieDateFieldSource extends LongFieldSource {
473
 
 
474
 
  public TrieDateFieldSource(String field, FieldCache.LongParser parser) {
475
 
    super(field, parser);
476
 
  }
477
 
 
478
 
  @Override
479
 
  public String description() {
480
 
    return "date(" + field + ')';
481
 
  }
482
 
 
483
 
  @Override
484
 
  public long externalToLong(String extVal) {
485
 
    return TrieField.dateField.parseMath(null, extVal).getTime();
486
 
  }
487
 
}
488
 
 
489