View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.search.lucene;
25  
26  import java.math.BigDecimal;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.Map;
30  import net.jcip.annotations.Immutable;
31  import net.jcip.annotations.NotThreadSafe;
32  import org.apache.lucene.document.Field;
33  import org.apache.lucene.document.Field.Index;
34  import org.apache.lucene.document.Field.Store;
35  import org.modeshape.common.util.CheckArg;
36  import org.modeshape.graph.property.Name;
37  
38  /**
39   * The set of rules that dictate how properties should be indexed.
40   */
41  @Immutable
42  public class IndexRules {
43  
44      public static enum FieldType {
45          STRING,
46          DOUBLE,
47          FLOAT,
48          INT,
49          BOOLEAN,
50          LONG,
51          DATE,
52          BINARY,
53          REFERENCE,
54          WEAK_REFERENCE,
55          DECIMAL;
56      }
57  
58      /**
59       * A single rule that dictates how a single property should be indexed.
60       * 
61       * @see IndexRules#getRule(Name)
62       */
63      @Immutable
64      public static interface Rule {
65  
66          boolean isSkipped();
67  
68          boolean canBeReference();
69  
70          boolean isFullTextSearchable();
71  
72          FieldType getType();
73  
74          Field.Store getStoreOption();
75  
76          Field.Index getIndexOption();
77      }
78  
79      @Immutable
80      public static interface NumericRule<T> extends Rule {
81          T getMinimum();
82  
83          T getMaximum();
84      }
85  
86      public static final Rule SKIP = new SkipRule();
87  
88      @Immutable
89      protected static class SkipRule implements Rule {
90  
91          /**
92           * {@inheritDoc}
93           * 
94           * @see Rule#getType()
95           */
96          public FieldType getType() {
97              return FieldType.STRING;
98          }
99  
100         /**
101          * {@inheritDoc}
102          * 
103          * @see Rule#isSkipped()
104          */
105         public boolean isSkipped() {
106             return true;
107         }
108 
109         /**
110          * {@inheritDoc}
111          * 
112          * @see org.modeshape.search.lucene.IndexRules.Rule#isFullTextSearchable()
113          */
114         @Override
115         public boolean isFullTextSearchable() {
116             return false;
117         }
118 
119         /**
120          * {@inheritDoc}
121          * 
122          * @see org.modeshape.search.lucene.IndexRules.Rule#canBeReference()
123          */
124         @Override
125         public boolean canBeReference() {
126             return false;
127         }
128 
129         /**
130          * {@inheritDoc}
131          * 
132          * @see Rule#getIndexOption()
133          */
134         public Index getIndexOption() {
135             return Field.Index.NO;
136         }
137 
138         /**
139          * {@inheritDoc}
140          * 
141          * @see Rule#getStoreOption()
142          */
143         public Store getStoreOption() {
144             return Field.Store.NO;
145         }
146     }
147 
148     @Immutable
149     protected static class TypedRule implements Rule {
150         protected final boolean canBeReference;
151         protected final boolean fullTextSearchable;
152         protected final FieldType type;
153         protected final Field.Store store;
154         protected final Field.Index index;
155 
156         protected TypedRule( FieldType type,
157                              Field.Store store,
158                              Field.Index index,
159                              boolean canBeReference,
160                              boolean fullTextSearchable ) {
161             this.type = type;
162             this.index = index;
163             this.store = store;
164             this.canBeReference = canBeReference;
165             this.fullTextSearchable = fullTextSearchable;
166             assert this.type != null;
167             assert this.index != null;
168             assert this.store != null;
169         }
170 
171         /**
172          * {@inheritDoc}
173          * 
174          * @see IndexRules.Rule#getType()
175          */
176         public FieldType getType() {
177             return type;
178         }
179 
180         /**
181          * {@inheritDoc}
182          * 
183          * @see IndexRules.Rule#isSkipped()
184          */
185         public boolean isSkipped() {
186             return false;
187         }
188 
189         /**
190          * {@inheritDoc}
191          * 
192          * @see org.modeshape.search.lucene.IndexRules.Rule#isFullTextSearchable()
193          */
194         @Override
195         public boolean isFullTextSearchable() {
196             return fullTextSearchable;
197         }
198 
199         /**
200          * {@inheritDoc}
201          * 
202          * @see org.modeshape.search.lucene.IndexRules.Rule#canBeReference()
203          */
204         @Override
205         public boolean canBeReference() {
206             return canBeReference;
207         }
208 
209         /**
210          * {@inheritDoc}
211          * 
212          * @see IndexRules.Rule#getIndexOption()
213          */
214         public Index getIndexOption() {
215             return index;
216         }
217 
218         /**
219          * {@inheritDoc}
220          * 
221          * @see IndexRules.Rule#getStoreOption()
222          */
223         public Store getStoreOption() {
224             return store;
225         }
226 
227         /**
228          * {@inheritDoc}
229          * 
230          * @see java.lang.Object#toString()
231          */
232         @Override
233         public String toString() {
234             return type.name() + " rule (" + store + "," + index + ")";
235         }
236     }
237 
238     @Immutable
239     protected static class NumericTypedRule<T> extends TypedRule implements NumericRule<T> {
240         protected final T minValue;
241         protected final T maxValue;
242 
243         protected NumericTypedRule( FieldType type,
244                                     Field.Store store,
245                                     Field.Index index,
246                                     T minValue,
247                                     T maxValue ) {
248             super(type, store, index, false, false);
249             this.minValue = minValue;
250             this.maxValue = maxValue;
251             assert this.minValue != null;
252             assert this.maxValue != null;
253         }
254 
255         /**
256          * {@inheritDoc}
257          * 
258          * @see IndexRules.NumericRule#getMaximum()
259          */
260         public T getMaximum() {
261             return maxValue;
262         }
263 
264         /**
265          * {@inheritDoc}
266          * 
267          * @see IndexRules.NumericRule#getMinimum()
268          */
269         public T getMinimum() {
270             return minValue;
271         }
272 
273         /**
274          * {@inheritDoc}
275          * 
276          * @see java.lang.Object#toString()
277          */
278         @Override
279         public String toString() {
280             return super.toString() + " with range [" + minValue + "," + maxValue + "]";
281         }
282     }
283 
284     private final Map<Name, Rule> rulesByName;
285     private final Rule defaultRule;
286 
287     protected IndexRules( Map<Name, Rule> rulesByName,
288                           Rule defaultRule ) {
289         this.rulesByName = rulesByName;
290         this.defaultRule = defaultRule != null ? defaultRule : SKIP;
291         assert this.defaultRule != null;
292     }
293 
294     /**
295      * Get the rule associated with the given property name.
296      * 
297      * @param name the property name, or null if the default rule is to be returned
298      * @return the rule; never null
299      */
300     public Rule getRule( Name name ) {
301         Rule result = rulesByName.get(name);
302         return result != null ? result : this.defaultRule;
303     }
304 
305     /**
306      * Return a new builder that can be used to create {@link IndexRules} objects.
307      * 
308      * @return a builder; never null
309      */
310     public static Builder createBuilder() {
311         return new Builder(new HashMap<Name, Rule>(), null);
312     }
313 
314     /**
315      * Return a new builder that can be used to create {@link IndexRules} objects.
316      * 
317      * @param initialRules the rules that the builder should start with
318      * @return a builder; never null
319      * @throws IllegalArgumentException if the initial rules reference is null
320      */
321     public static Builder createBuilder( IndexRules initialRules ) {
322         CheckArg.isNotNull(initialRules, "initialRules");
323         return new Builder(new HashMap<Name, Rule>(initialRules.rulesByName), initialRules.defaultRule);
324     }
325 
326     /**
327      * A builder of immutable {@link IndexRules} objects.
328      */
329     @NotThreadSafe
330     public static class Builder {
331         private final Map<Name, Rule> rulesByName;
332         private Rule defaultRule;
333 
334         Builder( Map<Name, Rule> rulesByName,
335                  Rule defaultRule ) {
336             assert rulesByName != null;
337             this.rulesByName = rulesByName;
338             this.defaultRule = defaultRule;
339         }
340 
341         /**
342          * Mark the properties with the supplied names to be skipped from indexing.
343          * 
344          * @param namesToIndex the names of the properties that are to be skipped
345          * @return this builder for convenience and method chaining; never null
346          */
347         public Builder skip( Name... namesToIndex ) {
348             if (namesToIndex != null) {
349                 for (Name name : namesToIndex) {
350                     rulesByName.put(name, SKIP);
351                 }
352             }
353             return this;
354         }
355 
356         /**
357          * Define a string-based field as the default.
358          * 
359          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
360          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
361          * @param canBeReference true if this field can contain references; or false if it cannot
362          * @param fullTextSearchable true if this field is full-text searchable, or false otherwise
363          * @return this builder for convenience and method chaining; never null
364          */
365         public Builder defaultTo( Field.Store store,
366                                   Field.Index index,
367                                   boolean canBeReference,
368                                   boolean fullTextSearchable ) {
369             if (store == null) store = Field.Store.YES;
370             if (index == null) index = Field.Index.NOT_ANALYZED;
371             defaultRule = new TypedRule(FieldType.STRING, store, index, canBeReference, fullTextSearchable);
372             return this;
373         }
374 
375         /**
376          * Define a string-based field in the indexes. This method will overwrite any existing definition in this builder.
377          * 
378          * @param name the name of the field
379          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
380          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
381          * @param canBeReference true if this field can contain references; or false if it cannot
382          * @param fullTextSearchable true if this field is full-text searchable, or false otherwise
383          * @return this builder for convenience and method chaining; never null
384          */
385         public Builder stringField( Name name,
386                                     Field.Store store,
387                                     Field.Index index,
388                                     boolean canBeReference,
389                                     boolean fullTextSearchable ) {
390             if (store == null) store = Field.Store.YES;
391             if (index == null) index = Field.Index.NOT_ANALYZED;
392             Rule rule = new TypedRule(FieldType.STRING, store, index, canBeReference, fullTextSearchable);
393             rulesByName.put(name, rule);
394             return this;
395         }
396 
397         /**
398          * Define a binary-based field in the indexes. This method will overwrite any existing definition in this builder.
399          * 
400          * @param name the name of the field
401          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
402          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
403          * @param fullTextSearchable true if this field is full-text searchable, or false otherwise
404          * @return this builder for convenience and method chaining; never null
405          */
406         public Builder binaryField( Name name,
407                                     Field.Store store,
408                                     Field.Index index,
409                                     boolean fullTextSearchable ) {
410             if (store == null) store = Field.Store.YES;
411             if (index == null) index = Field.Index.NOT_ANALYZED;
412             Rule rule = new TypedRule(FieldType.BINARY, store, index, false, fullTextSearchable);
413             rulesByName.put(name, rule);
414             return this;
415         }
416 
417         /**
418          * Define a reference-based field in the indexes. This method will overwrite any existing definition in this builder.
419          * 
420          * @param name the name of the field
421          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
422          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
423          * @return this builder for convenience and method chaining; never null
424          */
425         public Builder referenceField( Name name,
426                                        Field.Store store,
427                                        Field.Index index ) {
428             if (store == null) store = Field.Store.YES;
429             if (index == null) index = Field.Index.NOT_ANALYZED;
430             Rule rule = new TypedRule(FieldType.REFERENCE, store, index, true, false);
431             rulesByName.put(name, rule);
432             return this;
433         }
434 
435         /**
436          * Define a weak-reference-based field in the indexes. This method will overwrite any existing definition in this builder.
437          * 
438          * @param name the name of the field
439          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
440          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
441          * @param fullTextSearchable true if this field is full-text searchable, or false otherwise
442          * @return this builder for convenience and method chaining; never null
443          */
444         public Builder weakReferenceField( Name name,
445                                            Field.Store store,
446                                            Field.Index index,
447                                            boolean fullTextSearchable ) {
448             if (store == null) store = Field.Store.YES;
449             if (index == null) index = Field.Index.NOT_ANALYZED;
450             Rule rule = new TypedRule(FieldType.WEAK_REFERENCE, store, index, false, fullTextSearchable);
451             rulesByName.put(name, rule);
452             return this;
453         }
454 
455         protected <T> Builder numericField( Name name,
456                                             FieldType type,
457                                             Field.Store store,
458                                             Field.Index index,
459                                             T minValue,
460                                             T maxValue ) {
461             if (store == null) store = Field.Store.YES;
462             if (index == null) index = Field.Index.NOT_ANALYZED;
463             Rule rule = new NumericTypedRule<T>(type, store, index, minValue, maxValue);
464             rulesByName.put(name, rule);
465             return this;
466         }
467 
468         /**
469          * Define a boolean-based field in the indexes. This method will overwrite any existing definition in this builder.
470          * 
471          * @param name the name of the field
472          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
473          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
474          * @return this builder for convenience and method chaining; never null
475          */
476         public Builder booleanField( Name name,
477                                      Field.Store store,
478                                      Field.Index index ) {
479             return numericField(name, FieldType.BOOLEAN, store, index, Boolean.FALSE, Boolean.TRUE);
480         }
481 
482         /**
483          * Define a integer-based field in the indexes. This method will overwrite any existing definition in this builder.
484          * 
485          * @param name the name of the field
486          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
487          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
488          * @param minValue the minimum value for this field, or null if there is no minimum value
489          * @param maxValue the maximum value for this field, or null if there is no maximum value
490          * @return this builder for convenience and method chaining; never null
491          */
492         public Builder integerField( Name name,
493                                      Field.Store store,
494                                      Field.Index index,
495                                      Integer minValue,
496                                      Integer maxValue ) {
497             if (minValue == null) minValue = Integer.MIN_VALUE;
498             if (maxValue == null) maxValue = Integer.MAX_VALUE;
499             return numericField(name, FieldType.INT, store, index, minValue, maxValue);
500         }
501 
502         /**
503          * Define a long-based field in the indexes. This method will overwrite any existing definition in this builder.
504          * 
505          * @param name the name of the field
506          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
507          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
508          * @param minValue the minimum value for this field, or null if there is no minimum value
509          * @param maxValue the maximum value for this field, or null if there is no maximum value
510          * @return this builder for convenience and method chaining; never null
511          */
512         public Builder longField( Name name,
513                                   Field.Store store,
514                                   Field.Index index,
515                                   Long minValue,
516                                   Long maxValue ) {
517             if (minValue == null) minValue = Long.MIN_VALUE;
518             if (maxValue == null) maxValue = Long.MAX_VALUE;
519             return numericField(name, FieldType.LONG, store, index, minValue, maxValue);
520         }
521 
522         /**
523          * Define a decimal-based field in the indexes. This method will overwrite any existing definition in this builder.
524          * <p>
525          * Decimal fields can contain an exceedingly large range of values, and because Lucene is not capable of performing range
526          * queries using BigDecimal values, decimal fields are stored as lexicographically-sortable strings.
527          * 
528          * @param name the name of the field
529          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
530          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
531          * @param minValue the minimum value for this field, or null if there is no minimum value
532          * @param maxValue the maximum value for this field, or null if there is no maximum value
533          * @return this builder for convenience and method chaining; never null
534          */
535         public Builder decimalField( Name name,
536                                      Field.Store store,
537                                      Field.Index index,
538                                      BigDecimal minValue,
539                                      BigDecimal maxValue ) {
540             if (store == null) store = Field.Store.YES;
541             if (index == null) index = Field.Index.NOT_ANALYZED;
542             Rule rule = new TypedRule(FieldType.STRING, store, index, false, false);
543             rulesByName.put(name, rule);
544             return this;
545         }
546 
547         /**
548          * Define a date-based field in the indexes. This method will overwrite any existing definition in this builder.
549          * 
550          * @param name the name of the field
551          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
552          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
553          * @param minValue the minimum value for this field, or null if there is no minimum value
554          * @param maxValue the maximum value for this field, or null if there is no maximum value
555          * @return this builder for convenience and method chaining; never null
556          */
557         public Builder dateField( Name name,
558                                   Field.Store store,
559                                   Field.Index index,
560                                   Long minValue,
561                                   Long maxValue ) {
562             if (minValue == null) minValue = 0L;
563             if (maxValue == null) maxValue = Long.MAX_VALUE;
564             return numericField(name, FieldType.DATE, store, index, minValue, maxValue);
565         }
566 
567         /**
568          * Define a float-based field in the indexes. This method will overwrite any existing definition in this builder.
569          * 
570          * @param name the name of the field
571          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
572          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
573          * @param minValue the minimum value for this field, or null if there is no minimum value
574          * @param maxValue the maximum value for this field, or null if there is no maximum value
575          * @return this builder for convenience and method chaining; never null
576          */
577         public Builder floatField( Name name,
578                                    Field.Store store,
579                                    Field.Index index,
580                                    Float minValue,
581                                    Float maxValue ) {
582             if (minValue == null) minValue = Float.MIN_VALUE;
583             if (maxValue == null) maxValue = Float.MAX_VALUE;
584             return numericField(name, FieldType.FLOAT, store, index, minValue, maxValue);
585         }
586 
587         /**
588          * Define a double-based field in the indexes. This method will overwrite any existing definition in this builder.
589          * 
590          * @param name the name of the field
591          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
592          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
593          * @param minValue the minimum value for this field, or null if there is no minimum value
594          * @param maxValue the maximum value for this field, or null if there is no maximum value
595          * @return this builder for convenience and method chaining; never null
596          */
597         public Builder doubleField( Name name,
598                                     Field.Store store,
599                                     Field.Index index,
600                                     Double minValue,
601                                     Double maxValue ) {
602             if (minValue == null) minValue = Double.MIN_VALUE;
603             if (maxValue == null) maxValue = Double.MAX_VALUE;
604             return numericField(name, FieldType.DOUBLE, store, index, minValue, maxValue);
605         }
606 
607         /**
608          * Define a integer-based field in the indexes. This method will overwrite any existing definition in this builder.
609          * 
610          * @param name the name of the field
611          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
612          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
613          * @param minValue the minimum value for this field, or null if there is no minimum value
614          * @return this builder for convenience and method chaining; never null
615          */
616         public Builder integerField( Name name,
617                                      Field.Store store,
618                                      Field.Index index,
619                                      Integer minValue ) {
620             return integerField(name, store, index, minValue, null);
621         }
622 
623         /**
624          * Define a long-based field in the indexes. This method will overwrite any existing definition in this builder.
625          * 
626          * @param name the name of the field
627          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
628          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
629          * @param minValue the minimum value for this field, or null if there is no minimum value
630          * @return this builder for convenience and method chaining; never null
631          */
632         public Builder longField( Name name,
633                                   Field.Store store,
634                                   Field.Index index,
635                                   Long minValue ) {
636             return longField(name, store, index, minValue, null);
637         }
638 
639         /**
640          * Define a date-based field in the indexes. This method will overwrite any existing definition in this builder.
641          * 
642          * @param name the name of the field
643          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
644          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
645          * @param minValue the minimum value for this field, or null if there is no minimum value
646          * @return this builder for convenience and method chaining; never null
647          */
648         public Builder dateField( Name name,
649                                   Field.Store store,
650                                   Field.Index index,
651                                   Long minValue ) {
652             return dateField(name, store, index, minValue, null);
653         }
654 
655         /**
656          * Define a float-based field in the indexes. This method will overwrite any existing definition in this builder.
657          * 
658          * @param name the name of the field
659          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
660          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
661          * @param minValue the minimum value for this field, or null if there is no minimum value
662          * @return this builder for convenience and method chaining; never null
663          */
664         public Builder floatField( Name name,
665                                    Field.Store store,
666                                    Field.Index index,
667                                    Float minValue ) {
668             return floatField(name, store, index, minValue, null);
669         }
670 
671         /**
672          * Define a double-based field in the indexes. This method will overwrite any existing definition in this builder.
673          * 
674          * @param name the name of the field
675          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
676          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
677          * @param minValue the minimum value for this field, or null if there is no minimum value
678          * @return this builder for convenience and method chaining; never null
679          */
680         public Builder doubleField( Name name,
681                                     Field.Store store,
682                                     Field.Index index,
683                                     Double minValue ) {
684             return doubleField(name, store, index, minValue, null);
685         }
686 
687         /**
688          * Define a integer-based field in the indexes. This method will overwrite any existing definition in this builder.
689          * 
690          * @param name the name of the field
691          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
692          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
693          * @return this builder for convenience and method chaining; never null
694          */
695         public Builder integerField( Name name,
696                                      Field.Store store,
697                                      Field.Index index ) {
698             return integerField(name, store, index, null, null);
699         }
700 
701         /**
702          * Define a long-based field in the indexes. This method will overwrite any existing definition in this builder.
703          * 
704          * @param name the name of the field
705          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
706          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
707          * @return this builder for convenience and method chaining; never null
708          */
709         public Builder longField( Name name,
710                                   Field.Store store,
711                                   Field.Index index ) {
712             return longField(name, store, index, null, null);
713         }
714 
715         /**
716          * Define a date-based field in the indexes. This method will overwrite any existing definition in this builder.
717          * 
718          * @param name the name of the field
719          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
720          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
721          * @return this builder for convenience and method chaining; never null
722          */
723         public Builder dateField( Name name,
724                                   Field.Store store,
725                                   Field.Index index ) {
726             return dateField(name, store, index, null, null);
727         }
728 
729         /**
730          * Define a float-based field in the indexes. This method will overwrite any existing definition in this builder.
731          * 
732          * @param name the name of the field
733          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
734          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
735          * @return this builder for convenience and method chaining; never null
736          */
737         public Builder floatField( Name name,
738                                    Field.Store store,
739                                    Field.Index index ) {
740             return floatField(name, store, index, null, null);
741         }
742 
743         /**
744          * Define a double-based field in the indexes. This method will overwrite any existing definition in this builder.
745          * 
746          * @param name the name of the field
747          * @param store the storage setting, or null if the field should be {@link Store#YES stored}
748          * @param index the index setting, or null if the field should be indexed but {@link Index#NOT_ANALYZED not analyzed}
749          * @return this builder for convenience and method chaining; never null
750          */
751         public Builder doubleField( Name name,
752                                     Field.Store store,
753                                     Field.Index index ) {
754             return doubleField(name, store, index, null, null);
755         }
756 
757         /**
758          * Build the indexing rules.
759          * 
760          * @return the immutable indexing rules.
761          */
762         public IndexRules build() {
763             return new IndexRules(Collections.unmodifiableMap(new HashMap<Name, Rule>(rulesByName)), defaultRule);
764         }
765     }
766 }