1
package org.apache.lucene.facet.search.params;
3
import java.io.IOException;
4
import java.util.Arrays;
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;
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;
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
47
* http://www.apache.org/licenses/LICENSE-2.0
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.
57
* Test faceted search with creation of multiple category list iterators by the
58
* same CLP, depending on the provided facet request
60
public class MultiIteratorsPerCLParamsTest extends LuceneTestCase {
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") } };
74
String countForbiddenDimension;
77
public void testCLParamMultiIteratorsByRequest() throws Exception {
78
doTestCLParamMultiIteratorsByRequest(false);
82
public void testCLParamMultiIteratorsByRequestCacheCLI() throws Exception {
83
doTestCLParamMultiIteratorsByRequest(true);
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);
96
TaxonomyReader taxo = new DirectoryTaxonomyReader(taxoDir);
97
IndexReader reader = IndexReader.open(indexDir);
99
CategoryListCache clCache = null;
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);
107
ScoredDocIDs allDocs = ScoredDocIdsUtils
108
.createAllDocsScoredDocIDs(reader);
110
// Search index with 'author' should filter ONLY ordinals whose parent
112
countForbiddenDimension = "date";
113
validateFacetedSearch(iParams, taxo, reader, clCache, allDocs, "author", 5, 5);
115
// Search index with 'date' should filter ONLY ordinals whose parent is
117
countForbiddenDimension = "author";
118
validateFacetedSearch(iParams, taxo, reader, clCache, allDocs, "date", 5, 2);
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 });
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 });
138
private void validateFacetedSearch(FacetIndexingParams iParams,
139
TaxonomyReader taxo, IndexReader reader, CategoryListCache clCache, ScoredDocIDs allDocs,
140
String[] dimension, int[] expectedValue,
141
int[] expectedNumDescendants)
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));
149
FacetsAccumulator acc = new StandardFacetsAccumulator(sParams, reader, taxo);
151
// no use to test this with complement since at that mode all facets are taken
152
acc.setComplementThreshold(FacetsAccumulator.DISABLE_COMPLEMENT);
154
List<FacetResult> results = acc.accumulate(allDocs);
155
assertEquals("Wrong #results", dimension.length, results.size());
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());
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);
173
for (CategoryPath[] categories : perDocCategories) {
174
writer.addDocument(new CategoryDocumentBuilder(taxoWriter, iParams)
175
.setCategoryPaths(Arrays.asList(categories)).build(
185
private class PerDimCountFacetRequest extends CountFacetRequest {
187
public PerDimCountFacetRequest(CategoryPath path, int num) {
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());
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) {
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]);
219
* a CLI which filters another CLI for the dimension of the provided
222
private static class PerDimensionCLI implements CategoryListIterator {
223
private final CategoryListIterator superCLI;
224
private final int[] parentArray;
225
private final int parentOrdinal;
227
PerDimensionCLI(TaxonomyReader taxo, CategoryListIterator superCLI,
228
CategoryPath requestedPath) throws IOException {
229
this.superCLI = superCLI;
230
if (requestedPath == null) {
233
CategoryPath cp = new CategoryPath(requestedPath.getComponent(0));
234
parentOrdinal = taxo.getOrdinal(cp);
236
parentArray = taxo.getParentArray();
239
public boolean init() throws IOException {
240
return superCLI.init();
243
public long nextCategory() throws IOException {
245
while ((next = superCLI.nextCategory()) <= Integer.MAX_VALUE
246
&& !isInDimension((int) next)) {
252
/** look for original parent ordinal, meaning same dimension */
253
private boolean isInDimension(int ordinal) {
254
while (ordinal > 0) {
255
if (ordinal == parentOrdinal) {
258
ordinal = parentArray[ordinal];
263
public boolean skipTo(int docId) throws IOException {
264
return superCLI.skipTo(docId);
b'\\ No newline at end of file'