pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / facet / src / test / org / apache / lucene / facet / search / params / MultiIteratorsPerCLParamsTest.java
1 package org.apache.lucene.facet.search.params;
2
3 import java.io.IOException;
4 import java.util.Arrays;
5 import java.util.List;
6
7 import org.apache.lucene.analysis.MockAnalyzer;
8 import org.apache.lucene.analysis.MockTokenizer;
9 import org.apache.lucene.document.Document;
10 import org.apache.lucene.index.CorruptIndexException;
11 import org.apache.lucene.index.IndexReader;
12 import org.apache.lucene.index.RandomIndexWriter;
13 import org.apache.lucene.store.Directory;
14 import org.junit.Test;
15
16 import org.apache.lucene.util.LuceneTestCase;
17 import org.apache.lucene.facet.index.CategoryDocumentBuilder;
18 import org.apache.lucene.facet.index.params.CategoryListParams;
19 import org.apache.lucene.facet.index.params.DefaultFacetIndexingParams;
20 import org.apache.lucene.facet.index.params.FacetIndexingParams;
21 import org.apache.lucene.facet.search.CategoryListIterator;
22 import org.apache.lucene.facet.search.FacetArrays;
23 import org.apache.lucene.facet.search.FacetResultsHandler;
24 import org.apache.lucene.facet.search.FacetsAccumulator;
25 import org.apache.lucene.facet.search.ScoredDocIDs;
26 import org.apache.lucene.facet.search.StandardFacetsAccumulator;
27 import org.apache.lucene.facet.search.TopKFacetResultsHandler;
28 import org.apache.lucene.facet.search.cache.CategoryListCache;
29 import org.apache.lucene.facet.search.results.FacetResult;
30 import org.apache.lucene.facet.search.results.FacetResultNode;
31 import org.apache.lucene.facet.search.results.IntermediateFacetResult;
32 import org.apache.lucene.facet.taxonomy.CategoryPath;
33 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
34 import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
35 import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
36 import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
37 import org.apache.lucene.facet.util.ScoredDocIdsUtils;
38
39 /**
40  * Licensed to the Apache Software Foundation (ASF) under one or more
41  * contributor license agreements.  See the NOTICE file distributed with
42  * this work for additional information regarding copyright ownership.
43  * The ASF licenses this file to You under the Apache License, Version 2.0
44  * (the "License"); you may not use this file except in compliance with
45  * the License.  You may obtain a copy of the License at
46  *
47  *     http://www.apache.org/licenses/LICENSE-2.0
48  *
49  * Unless required by applicable law or agreed to in writing, software
50  * distributed under the License is distributed on an "AS IS" BASIS,
51  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
52  * See the License for the specific language governing permissions and
53  * limitations under the License.
54  */
55
56 /**
57  * Test faceted search with creation of multiple category list iterators by the
58  * same CLP, depending on the provided facet request
59  */
60 public class MultiIteratorsPerCLParamsTest extends LuceneTestCase {
61
62   CategoryPath[][] perDocCategories = new CategoryPath[][] {
63       { new CategoryPath("author", "Mark Twain"),
64           new CategoryPath("date", "2010") },
65       { new CategoryPath("author", "Robert Frost"),
66           new CategoryPath("date", "2009") },
67       { new CategoryPath("author", "Artur Miller"),
68           new CategoryPath("date", "2010") },
69       { new CategoryPath("author", "Edgar Allan Poe"),
70           new CategoryPath("date", "2009") },
71       { new CategoryPath("author", "Henry James"),
72           new CategoryPath("date", "2010") } };
73   
74   String countForbiddenDimension;
75
76   @Test
77   public void testCLParamMultiIteratorsByRequest() throws Exception {
78     doTestCLParamMultiIteratorsByRequest(false);
79   }
80
81   @Test
82   public void testCLParamMultiIteratorsByRequestCacheCLI() throws Exception {
83     doTestCLParamMultiIteratorsByRequest(true);
84   }
85
86   private void doTestCLParamMultiIteratorsByRequest(boolean cacheCLI) throws Exception,
87       CorruptIndexException, IOException {
88     // Create a CLP which generates different CLIs according to the
89     // FacetRequest's dimension
90     CategoryListParams clp = new CategoryListParams();
91     FacetIndexingParams iParams = new DefaultFacetIndexingParams(clp);
92     Directory indexDir = newDirectory();
93     Directory taxoDir = newDirectory();
94     populateIndex(iParams, indexDir, taxoDir);
95
96     TaxonomyReader taxo = new DirectoryTaxonomyReader(taxoDir);
97     IndexReader reader = IndexReader.open(indexDir);
98
99     CategoryListCache clCache = null;
100     if (cacheCLI) {
101       // caching the iteratorr, so:
102       // 1: create the cached iterator, using original params
103       clCache = new CategoryListCache();
104       clCache.loadAndRegister(clp, reader, taxo, iParams);
105     }
106     
107     ScoredDocIDs allDocs = ScoredDocIdsUtils
108         .createAllDocsScoredDocIDs(reader);
109
110     // Search index with 'author' should filter ONLY ordinals whose parent
111     // is 'author'
112     countForbiddenDimension = "date";
113     validateFacetedSearch(iParams, taxo, reader, clCache, allDocs, "author", 5, 5);
114
115     // Search index with 'date' should filter ONLY ordinals whose parent is
116     // 'date'
117     countForbiddenDimension = "author";
118     validateFacetedSearch(iParams, taxo, reader, clCache, allDocs, "date", 5, 2);
119
120     // Search index with both 'date' and 'author'
121     countForbiddenDimension = null;
122     validateFacetedSearch(iParams, taxo, reader, clCache, allDocs, new String[] {
123             "author", "date" }, new int[] { 5, 5 }, new int[] { 5, 2 });
124     taxo.close();
125     reader.close();
126     indexDir.close();
127     taxoDir.close();
128   }
129
130   private void validateFacetedSearch(FacetIndexingParams iParams,
131       TaxonomyReader taxo, IndexReader reader, CategoryListCache clCache,
132       ScoredDocIDs allDocs, String dimension, int expectedValue, int expectedNumDescendants) throws IOException {
133     validateFacetedSearch(iParams, taxo, reader, clCache, allDocs,
134         new String[] { dimension }, new int[] { expectedValue },
135         new int[] { expectedNumDescendants });
136   }
137
138   private void validateFacetedSearch(FacetIndexingParams iParams,
139       TaxonomyReader taxo, IndexReader reader,  CategoryListCache clCache, ScoredDocIDs allDocs,
140       String[] dimension, int[] expectedValue,
141       int[] expectedNumDescendants)
142       throws IOException {
143     FacetSearchParams sParams = new FacetSearchParams(iParams);
144     sParams.setClCache(clCache);
145     for (String dim : dimension) {
146       sParams.addFacetRequest(new PerDimCountFacetRequest(
147           new CategoryPath(dim), 10));
148     }
149     FacetsAccumulator acc = new StandardFacetsAccumulator(sParams, reader, taxo);
150     
151     // no use to test this with complement since at that mode all facets are taken
152     acc.setComplementThreshold(FacetsAccumulator.DISABLE_COMPLEMENT);
153
154     List<FacetResult> results = acc.accumulate(allDocs);
155     assertEquals("Wrong #results", dimension.length, results.size());
156
157     for (int i = 0; i < results.size(); i++) {
158       FacetResult res = results.get(i);
159       assertEquals("wrong num-descendants for dimension " + dimension[i],
160           expectedNumDescendants[i], res.getNumValidDescendants());
161       FacetResultNode resNode = res.getFacetResultNode();
162       assertEquals("wrong value for dimension " + dimension[i],
163           expectedValue[i], (int) resNode.getValue());
164     }
165   }
166
167   private void populateIndex(FacetIndexingParams iParams, Directory indexDir,
168       Directory taxoDir) throws Exception {
169     RandomIndexWriter writer = new RandomIndexWriter(random, indexDir, 
170         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random, MockTokenizer.KEYWORD, false)));
171     TaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(taxoDir);
172
173     for (CategoryPath[] categories : perDocCategories) {
174       writer.addDocument(new CategoryDocumentBuilder(taxoWriter, iParams)
175           .setCategoryPaths(Arrays.asList(categories)).build(
176               new Document()));
177
178     }
179     taxoWriter.commit();
180     writer.commit();
181     taxoWriter.close();
182     writer.close();
183   }
184
185   private class PerDimCountFacetRequest extends CountFacetRequest {
186     
187     public PerDimCountFacetRequest(CategoryPath path, int num) {
188       super(path, num);
189     }
190
191     @Override
192     public CategoryListIterator createCategoryListIterator(IndexReader reader, 
193         TaxonomyReader taxo, FacetSearchParams sParams, int partition) throws IOException {
194       // categories of certain dimension only
195       return new PerDimensionCLI(taxo, super.createCategoryListIterator(
196           reader, taxo, sParams, partition), getCategoryPath());
197     }
198     
199     @Override
200     /** Override this method just for verifying that only specified facets are iterated.. */
201     public FacetResultsHandler createFacetResultsHandler(
202         TaxonomyReader taxonomyReader) {
203       return new TopKFacetResultsHandler(taxonomyReader, this) {
204         @Override
205         public IntermediateFacetResult fetchPartitionResult(
206             FacetArrays facetArrays, int offset) throws IOException {
207           final IntermediateFacetResult res = super.fetchPartitionResult(facetArrays, offset);
208           if (countForbiddenDimension!=null) {
209             int ord = taxonomyReader.getOrdinal(new CategoryPath(countForbiddenDimension));
210             assertEquals("Should not have accumulated for dimension '"+countForbiddenDimension+"'!",0,facetArrays.getIntArray()[ord]);
211           }
212           return res;
213         }
214       };
215     }
216   }
217
218   /**
219    * a CLI which filters another CLI for the dimension of the provided
220    * category-path
221    */
222   private static class PerDimensionCLI implements CategoryListIterator {
223     private final CategoryListIterator superCLI;
224     private final int[] parentArray;
225     private final int parentOrdinal;
226
227     PerDimensionCLI(TaxonomyReader taxo, CategoryListIterator superCLI,
228         CategoryPath requestedPath) throws IOException {
229       this.superCLI = superCLI;
230       if (requestedPath == null) {
231         parentOrdinal = 0;
232       } else {
233         CategoryPath cp = new CategoryPath(requestedPath.getComponent(0));
234         parentOrdinal = taxo.getOrdinal(cp);
235       }
236       parentArray = taxo.getParentArray();
237     }
238
239     public boolean init() throws IOException {
240       return superCLI.init();
241     }
242
243     public long nextCategory() throws IOException {
244       long next;
245       while ((next = superCLI.nextCategory()) <= Integer.MAX_VALUE
246           && !isInDimension((int) next)) {
247       }
248
249       return next;
250     }
251
252     /** look for original parent ordinal, meaning same dimension */
253     private boolean isInDimension(int ordinal) {
254       while (ordinal > 0) {
255         if (ordinal == parentOrdinal) {
256           return true;
257         }
258         ordinal = parentArray[ordinal];
259       }
260       return false;
261     }
262
263     public boolean skipTo(int docId) throws IOException {
264       return superCLI.skipTo(docId);
265     }
266   }
267 }