pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / document / NumericField.java
1 package org.apache.lucene.document;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.Reader;
21
22 import org.apache.lucene.analysis.TokenStream;
23 import org.apache.lucene.analysis.NumericTokenStream;
24 import org.apache.lucene.index.FieldInfo.IndexOptions;
25 import org.apache.lucene.util.NumericUtils;
26 import org.apache.lucene.search.NumericRangeQuery; // javadocs
27 import org.apache.lucene.search.NumericRangeFilter; // javadocs
28 import org.apache.lucene.search.SortField; // javadocs
29 import org.apache.lucene.search.FieldCache; // javadocs
30
31 /**
32  * <p>This class provides a {@link Field} that enables indexing
33  * of numeric values for efficient range filtering and
34  * sorting.  Here's an example usage, adding an int value:
35  * <pre>
36  *  document.add(new NumericField(name).setIntValue(value));
37  * </pre>
38  *
39  * For optimal performance, re-use the
40  * <code>NumericField</code> and {@link Document} instance for more than
41  * one document:
42  *
43  * <pre>
44  *  NumericField field = new NumericField(name);
45  *  Document document = new Document();
46  *  document.add(field);
47  *
48  *  for(all documents) {
49  *    ...
50  *    field.setIntValue(value)
51  *    writer.addDocument(document);
52  *    ...
53  *  }
54  * </pre>
55  *
56  * <p>The java native types <code>int</code>, <code>long</code>,
57  * <code>float</code> and <code>double</code> are
58  * directly supported.  However, any value that can be
59  * converted into these native types can also be indexed.
60  * For example, date/time values represented by a
61  * {@link java.util.Date} can be translated into a long
62  * value using the {@link java.util.Date#getTime} method.  If you
63  * don't need millisecond precision, you can quantize the
64  * value, either by dividing the result of
65  * {@link java.util.Date#getTime} or using the separate getters
66  * (for year, month, etc.) to construct an <code>int</code> or
67  * <code>long</code> value.</p>
68  *
69  * <p>To perform range querying or filtering against a
70  * <code>NumericField</code>, use {@link NumericRangeQuery} or {@link
71  * NumericRangeFilter}.  To sort according to a
72  * <code>NumericField</code>, use the normal numeric sort types, eg
73  * {@link SortField#INT}. <code>NumericField</code> values
74  * can also be loaded directly from {@link FieldCache}.</p>
75  *
76  * <p>By default, a <code>NumericField</code>'s value is not stored but
77  * is indexed for range filtering and sorting.  You can use
78  * the {@link #NumericField(String,Field.Store,boolean)}
79  * constructor if you need to change these defaults.</p>
80  *
81  * <p>You may add the same field name as a <code>NumericField</code> to
82  * the same document more than once.  Range querying and
83  * filtering will be the logical OR of all values; so a range query
84  * will hit all documents that have at least one value in
85  * the range. However sort behavior is not defined.  If you need to sort,
86  * you should separately index a single-valued <code>NumericField</code>.</p>
87  *
88  * <p>A <code>NumericField</code> will consume somewhat more disk space
89  * in the index than an ordinary single-valued field.
90  * However, for a typical index that includes substantial
91  * textual content per document, this increase will likely
92  * be in the noise. </p>
93  *
94  * <p>Within Lucene, each numeric value is indexed as a
95  * <em>trie</em> structure, where each term is logically
96  * assigned to larger and larger pre-defined brackets (which
97  * are simply lower-precision representations of the value).
98  * The step size between each successive bracket is called the
99  * <code>precisionStep</code>, measured in bits.  Smaller
100  * <code>precisionStep</code> values result in larger number
101  * of brackets, which consumes more disk space in the index
102  * but may result in faster range search performance.  The
103  * default value, 4, was selected for a reasonable tradeoff
104  * of disk space consumption versus performance.  You can
105  * use the expert constructor {@link
106  * #NumericField(String,int,Field.Store,boolean)} if you'd
107  * like to change the value.  Note that you must also
108  * specify a congruent value when creating {@link
109  * NumericRangeQuery} or {@link NumericRangeFilter}.
110  * For low cardinality fields larger precision steps are good.
111  * If the cardinality is &lt; 100, it is fair
112  * to use {@link Integer#MAX_VALUE}, which produces one
113  * term per value.
114  *
115  * <p>For more information on the internals of numeric trie
116  * indexing, including the <a
117  * href="../search/NumericRangeQuery.html#precisionStepDesc"><code>precisionStep</code></a>
118  * configuration, see {@link NumericRangeQuery}. The format of
119  * indexed values is described in {@link NumericUtils}.
120  *
121  * <p>If you only need to sort by numeric value, and never
122  * run range querying/filtering, you can index using a
123  * <code>precisionStep</code> of {@link Integer#MAX_VALUE}.
124  * This will minimize disk space consumed. </p>
125  *
126  * <p>More advanced users can instead use {@link
127  * NumericTokenStream} directly, when indexing numbers. This
128  * class is a wrapper around this token stream type for
129  * easier, more intuitive usage.</p>
130  *
131  * @since 2.9
132  */
133 public final class NumericField extends AbstractField {
134
135   /** Data type of the value in {@link NumericField}.
136    * @since 3.2
137    */
138   public static enum DataType { INT, LONG, FLOAT, DOUBLE }
139
140   private transient NumericTokenStream numericTS;
141   private DataType type;
142   private final int precisionStep;
143
144   /**
145    * Creates a field for numeric values using the default <code>precisionStep</code>
146    * {@link NumericUtils#PRECISION_STEP_DEFAULT} (4). The instance is not yet initialized with
147    * a numeric value, before indexing a document containing this field,
148    * set a value using the various set<em>???</em>Value() methods.
149    * This constructor creates an indexed, but not stored field.
150    * @param name the field name
151    */
152   public NumericField(String name) {
153     this(name, NumericUtils.PRECISION_STEP_DEFAULT, Field.Store.NO, true);
154   }
155   
156   /**
157    * Creates a field for numeric values using the default <code>precisionStep</code>
158    * {@link NumericUtils#PRECISION_STEP_DEFAULT} (4). The instance is not yet initialized with
159    * a numeric value, before indexing a document containing this field,
160    * set a value using the various set<em>???</em>Value() methods.
161    * @param name the field name
162    * @param store if the field should be stored, {@link Document#getFieldable}
163    * then returns {@code NumericField} instances on search results.
164    * @param index if the field should be indexed using {@link NumericTokenStream}
165    */
166   public NumericField(String name, Field.Store store, boolean index) {
167     this(name, NumericUtils.PRECISION_STEP_DEFAULT, store, index);
168   }
169   
170   /**
171    * Creates a field for numeric values with the specified
172    * <code>precisionStep</code>. The instance is not yet initialized with
173    * a numeric value, before indexing a document containing this field,
174    * set a value using the various set<em>???</em>Value() methods.
175    * This constructor creates an indexed, but not stored field.
176    * @param name the field name
177    * @param precisionStep the used <a href="../search/NumericRangeQuery.html#precisionStepDesc">precision step</a>
178    */
179   public NumericField(String name, int precisionStep) {
180     this(name, precisionStep, Field.Store.NO, true);
181   }
182
183   /**
184    * Creates a field for numeric values with the specified
185    * <code>precisionStep</code>. The instance is not yet initialized with
186    * a numeric value, before indexing a document containing this field,
187    * set a value using the various set<em>???</em>Value() methods.
188    * @param name the field name
189    * @param precisionStep the used <a href="../search/NumericRangeQuery.html#precisionStepDesc">precision step</a>
190    * @param store if the field should be stored, {@link Document#getFieldable}
191    * then returns {@code NumericField} instances on search results.
192    * @param index if the field should be indexed using {@link NumericTokenStream}
193    */
194   public NumericField(String name, int precisionStep, Field.Store store, boolean index) {
195     super(name, store, index ? Field.Index.ANALYZED_NO_NORMS : Field.Index.NO, Field.TermVector.NO);
196     this.precisionStep = precisionStep;
197     setIndexOptions(IndexOptions.DOCS_ONLY);
198   }
199
200   /** Returns a {@link NumericTokenStream} for indexing the numeric value. */
201   public TokenStream tokenStreamValue()   {
202     if (!isIndexed())
203       return null;
204     if (numericTS == null) {
205       // lazy init the TokenStream as it is heavy to instantiate (attributes,...),
206       // if not needed (stored field loading)
207       numericTS = new NumericTokenStream(precisionStep);
208       // initialize value in TokenStream
209       if (fieldsData != null) {
210         assert type != null;
211         final Number val = (Number) fieldsData;
212         switch (type) {
213           case INT:
214             numericTS.setIntValue(val.intValue()); break;
215           case LONG:
216             numericTS.setLongValue(val.longValue()); break;
217           case FLOAT:
218             numericTS.setFloatValue(val.floatValue()); break;
219           case DOUBLE:
220             numericTS.setDoubleValue(val.doubleValue()); break;
221           default:
222             assert false : "Should never get here";
223         }
224       }
225     }
226     return numericTS;
227   }
228   
229   /** Returns always <code>null</code> for numeric fields */
230   @Override
231   public byte[] getBinaryValue(byte[] result){
232     return null;
233   }
234
235   /** Returns always <code>null</code> for numeric fields */
236   public Reader readerValue() {
237     return null;
238   }
239     
240   /** Returns the numeric value as a string. This format is also returned if you call {@link Document#get(String)}
241    * on search results. It is recommended to use {@link Document#getFieldable} instead
242    * that returns {@code NumericField} instances. You can then use {@link #getNumericValue}
243    * to return the stored value. */
244   public String stringValue()   {
245     return (fieldsData == null) ? null : fieldsData.toString();
246   }
247   
248   /** Returns the current numeric value as a subclass of {@link Number}, <code>null</code> if not yet initialized. */
249   public Number getNumericValue() {
250     return (Number) fieldsData;
251   }
252   
253   /** Returns the precision step. */
254   public int getPrecisionStep() {
255     return precisionStep;
256   }
257   
258   /** Returns the data type of the current value, {@code null} if not yet set.
259    * @since 3.2
260    */
261   public DataType getDataType() {
262     return type;
263   }
264   
265   /**
266    * Initializes the field with the supplied <code>long</code> value.
267    * @param value the numeric value
268    * @return this instance, because of this you can use it the following way:
269    * <code>document.add(new NumericField(name, precisionStep).setLongValue(value))</code>
270    */
271   public NumericField setLongValue(final long value) {
272     if (numericTS != null) numericTS.setLongValue(value);
273     fieldsData = Long.valueOf(value);
274     type = DataType.LONG;
275     return this;
276   }
277   
278   /**
279    * Initializes the field with the supplied <code>int</code> value.
280    * @param value the numeric value
281    * @return this instance, because of this you can use it the following way:
282    * <code>document.add(new NumericField(name, precisionStep).setIntValue(value))</code>
283    */
284   public NumericField setIntValue(final int value) {
285     if (numericTS != null) numericTS.setIntValue(value);
286     fieldsData = Integer.valueOf(value);
287     type = DataType.INT;
288     return this;
289   }
290   
291   /**
292    * Initializes the field with the supplied <code>double</code> value.
293    * @param value the numeric value
294    * @return this instance, because of this you can use it the following way:
295    * <code>document.add(new NumericField(name, precisionStep).setDoubleValue(value))</code>
296    */
297   public NumericField setDoubleValue(final double value) {
298     if (numericTS != null) numericTS.setDoubleValue(value);
299     fieldsData = Double.valueOf(value);
300     type = DataType.DOUBLE;
301     return this;
302   }
303   
304   /**
305    * Initializes the field with the supplied <code>float</code> value.
306    * @param value the numeric value
307    * @return this instance, because of this you can use it the following way:
308    * <code>document.add(new NumericField(name, precisionStep).setFloatValue(value))</code>
309    */
310   public NumericField setFloatValue(final float value) {
311     if (numericTS != null) numericTS.setFloatValue(value);
312     fieldsData = Float.valueOf(value);
313     type = DataType.FLOAT;
314     return this;
315   }
316
317 }