pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / facet / src / java / org / apache / lucene / facet / index / CategoryDocumentBuilder.java
1 package org.apache.lucene.facet.index;
2
3 import java.io.IOException;
4 import java.util.ArrayList;
5 import java.util.HashMap;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Map.Entry;
9
10 import org.apache.lucene.analysis.TokenStream;
11 import org.apache.lucene.document.Document;
12 import org.apache.lucene.document.Field;
13
14 import org.apache.lucene.facet.index.attributes.CategoryAttribute;
15 import org.apache.lucene.facet.index.attributes.CategoryAttributesIterable;
16 import org.apache.lucene.facet.index.categorypolicy.OrdinalPolicy;
17 import org.apache.lucene.facet.index.categorypolicy.PathPolicy;
18 import org.apache.lucene.facet.index.params.DefaultFacetIndexingParams;
19 import org.apache.lucene.facet.index.params.FacetIndexingParams;
20 import org.apache.lucene.facet.index.streaming.CategoryAttributesStream;
21 import org.apache.lucene.facet.index.streaming.CategoryListTokenizer;
22 import org.apache.lucene.facet.index.streaming.CategoryParentsStream;
23 import org.apache.lucene.facet.index.streaming.CategoryTokenizer;
24 import org.apache.lucene.facet.index.streaming.CountingListTokenizer;
25 import org.apache.lucene.facet.taxonomy.CategoryPath;
26 import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
27
28 /**
29  * Licensed to the Apache Software Foundation (ASF) under one or more
30  * contributor license agreements.  See the NOTICE file distributed with
31  * this work for additional information regarding copyright ownership.
32  * The ASF licenses this file to You under the Apache License, Version 2.0
33  * (the "License"); you may not use this file except in compliance with
34  * the License.  You may obtain a copy of the License at
35  *
36  *     http://www.apache.org/licenses/LICENSE-2.0
37  *
38  * Unless required by applicable law or agreed to in writing, software
39  * distributed under the License is distributed on an "AS IS" BASIS,
40  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41  * See the License for the specific language governing permissions and
42  * limitations under the License.
43  */
44
45 /**
46  * A utility class which allows attachment of {@link CategoryPath}s or
47  * {@link CategoryAttribute}s to a given document using a taxonomy.<br>
48  * Construction could be done with either a given {@link FacetIndexingParams} or
49  * the default implementation {@link DefaultFacetIndexingParams}.<br>
50  * A CategoryDocumentBuilder can be reused by repeatedly setting the categories
51  * and building the document. Categories are provided either as
52  * {@link CategoryAttribute} elements through {@link #setCategories(Iterable)},
53  * or as {@link CategoryPath} elements through
54  * {@link #setCategoryPaths(Iterable)}.
55  * <p>
56  * Note that both {@link #setCategories(Iterable)} and
57  * {@link #setCategoryPaths(Iterable)} return this
58  * {@link CategoryDocumentBuilder}, allowing the following pattern: {@code new
59  * CategoryDocumentBuilder(taxonomy,
60  * params).setCategories(categories).build(doc)}.
61  * 
62  * @lucene.experimental
63  */
64 public class CategoryDocumentBuilder {
65
66   /**
67    * A {@link TaxonomyWriter} for adding categories and retrieving their
68    * ordinals.
69    */
70   protected final TaxonomyWriter taxonomyWriter;
71
72   /**
73    * Parameters to be used when indexing categories.
74    */
75   protected final FacetIndexingParams indexingParams;
76
77   /**
78    * A list of fields which is filled at ancestors' construction and used
79    * during {@link CategoryDocumentBuilder#build(Document)}.
80    */
81   protected final ArrayList<Field> fieldList = new ArrayList<Field>();
82
83   protected Map<String, List<CategoryAttribute>> categoriesMap;
84
85   /**
86    * Creating a facets document builder with default facet indexing
87    * parameters.<br>
88    * See:
89    * {@link #CategoryDocumentBuilder(TaxonomyWriter, FacetIndexingParams)}
90    * 
91    * @param taxonomyWriter
92    *            to which new categories will be added, as well as translating
93    *            known categories to ordinals
94    * @throws IOException
95    * 
96    */
97   public CategoryDocumentBuilder(TaxonomyWriter taxonomyWriter)
98       throws IOException {
99     this(taxonomyWriter, new DefaultFacetIndexingParams());
100   }
101
102   /**
103    * Creating a facets document builder with a given facet indexing parameters
104    * object.<br>
105    * 
106    * @param taxonomyWriter
107    *            to which new categories will be added, as well as translating
108    *            known categories to ordinals
109    * @param params
110    *            holds all parameters the indexing process should use such as
111    *            category-list parameters
112    * @throws IOException
113    */
114   public CategoryDocumentBuilder(TaxonomyWriter taxonomyWriter,
115       FacetIndexingParams params) throws IOException {
116     this.taxonomyWriter = taxonomyWriter;
117     this.indexingParams = params;
118     this.categoriesMap = new HashMap<String, List<CategoryAttribute>>();
119   }
120
121   /**
122    * Set the categories of the document builder from an {@link Iterable} of
123    * {@link CategoryPath} objects.
124    * 
125    * @param categoryPaths
126    *            An iterable of CategoryPath objects which holds the categories
127    *            (facets) which will be added to the document at
128    *            {@link #build(Document)}
129    * @return This CategoryDocumentBuilder, to enable this one line call:
130    *         {@code new} {@link #CategoryDocumentBuilder(TaxonomyWriter)}.
131    *         {@link #setCategoryPaths(Iterable)}.{@link #build(Document)}.
132    * @throws IOException
133    */
134   public CategoryDocumentBuilder setCategoryPaths(
135       Iterable<CategoryPath> categoryPaths) throws IOException {
136     if (categoryPaths == null) {
137       fieldList.clear();
138       return this;
139     }
140     return setCategories(new CategoryAttributesIterable(categoryPaths));
141   }
142
143   /**
144    * Set the categories of the document builder from an {@link Iterable} of
145    * {@link CategoryAttribute} objects.
146    * 
147    * @param categories
148    *            An iterable of {@link CategoryAttribute} objects which holds
149    *            the categories (facets) which will be added to the document at
150    *            {@link #build(Document)}
151    * @return This CategoryDocumentBuilder, to enable this one line call:
152    *         {@code new} {@link #CategoryDocumentBuilder(TaxonomyWriter)}.
153    *         {@link #setCategories(Iterable)}.{@link #build(Document)}.
154    * @throws IOException
155    */
156   public CategoryDocumentBuilder setCategories(
157       Iterable<CategoryAttribute> categories) throws IOException {
158     fieldList.clear();
159     if (categories == null) {
160       return this;
161     }
162
163     // get field-name to a list of facets mapping as different facets could
164     // be added to different category-lists on different fields
165     fillCategoriesMap(categories);
166
167     // creates a different stream for each different field
168     for (Entry<String, List<CategoryAttribute>> e : categoriesMap
169         .entrySet()) {
170       // create a category attributes stream for the array of facets
171       CategoryAttributesStream categoryAttributesStream = new CategoryAttributesStream(
172           e.getValue());
173
174       // Set a suitable {@link TokenStream} using
175       // CategoryParentsStream, followed by CategoryListTokenizer and
176       // CategoryTokenizer composition (the ordering of the last two is
177       // not mandatory).
178       CategoryParentsStream parentsStream = (CategoryParentsStream) getParentsStream(categoryAttributesStream);
179       CategoryListTokenizer categoryListTokenizer = getCategoryListTokenizer(parentsStream);
180       CategoryTokenizer stream = getCategoryTokenizer(categoryListTokenizer);
181
182       // Finally creating a suitable field with stream and adding it to a
183       // master field-list, used during the build process (see
184       // super.build())
185       fieldList.add(new Field(e.getKey(), stream));
186     }
187
188     return this;
189   }
190
191   /**
192    * Get a stream of categories which includes the parents, according to
193    * policies defined in indexing parameters.
194    * 
195    * @param categoryAttributesStream
196    *            The input stream
197    * @return The parents stream.
198    * @see OrdinalPolicy OrdinalPolicy (for policy of adding category tokens for parents)
199    * @see PathPolicy PathPolicy (for policy of adding category <b>list</b> tokens for parents)
200    */
201   protected TokenStream getParentsStream(
202       CategoryAttributesStream categoryAttributesStream) {
203     return new CategoryParentsStream(categoryAttributesStream,
204         taxonomyWriter, indexingParams);
205   }
206
207   /**
208    * Fills the categories mapping between a field name and a list of
209    * categories that belongs to it according to this builder's
210    * {@link FacetIndexingParams} object
211    * 
212    * @param categories
213    *            Iterable over the category attributes
214    */
215   protected void fillCategoriesMap(Iterable<CategoryAttribute> categories)
216       throws IOException {
217     categoriesMap.clear();
218
219     // for-each category
220     for (CategoryAttribute category : categories) {
221       // extracting the field-name to which this category belongs
222       String fieldName = indexingParams.getCategoryListParams(
223           category.getCategoryPath()).getTerm().field();
224
225       // getting the list of categories which belongs to that field
226       List<CategoryAttribute> list = categoriesMap.get(fieldName);
227
228       // if no such list exists
229       if (list == null) {
230         // adding a new one to the map
231         list = new ArrayList<CategoryAttribute>();
232         categoriesMap.put(fieldName, list);
233       }
234
235       // adding the new category to the list
236       list.add(category.clone());
237     }
238   }
239
240   /**
241    * Get a category list tokenizer (or a series of such tokenizers) to create
242    * the <b>category list tokens</b>.
243    * 
244    * @param categoryStream
245    *            A stream containing {@link CategoryAttribute} with the
246    *            relevant data.
247    * @return The category list tokenizer (or series of tokenizers) to be used
248    *         in creating category list tokens.
249    */
250   protected CategoryListTokenizer getCategoryListTokenizer(
251       TokenStream categoryStream) {
252     return getCountingListTokenizer(categoryStream);
253   }
254
255   /**
256    * Get a {@link CountingListTokenizer} for creating counting list token.
257    * 
258    * @param categoryStream
259    *            A stream containing {@link CategoryAttribute}s with the
260    *            relevant data.
261    * @return A counting list tokenizer to be used in creating counting list
262    *         token.
263    */
264   protected CountingListTokenizer getCountingListTokenizer(
265       TokenStream categoryStream) {
266     return new CountingListTokenizer(categoryStream, indexingParams);
267   }
268
269   /**
270    * Get a {@link CategoryTokenizer} to create the <b>category tokens</b>.
271    * This method can be overridden for adding more attributes to the category
272    * tokens.
273    * 
274    * @param categoryStream
275    *            A stream containing {@link CategoryAttribute} with the
276    *            relevant data.
277    * @return The {@link CategoryTokenizer} to be used in creating category
278    *         tokens.
279    * @throws IOException
280    */
281   protected CategoryTokenizer getCategoryTokenizer(TokenStream categoryStream)
282       throws IOException {
283     return new CategoryTokenizer(categoryStream, indexingParams);
284   }
285
286   /** Adds the fields created in one of the "set" methods to the document */
287   public Document build(Document doc) {
288     for (Field f : fieldList) {
289       f.setOmitNorms(true);
290       doc.add(f);
291     }
292     return doc;
293   }
294
295 }