LUCENE-4750: convert DrillDown to DrillDownQuery

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1444482 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Shai Erera 2013-02-10 06:57:22 +00:00
parent 91b1e22789
commit 456b429d10
11 changed files with 294 additions and 192 deletions

View File

@ -66,6 +66,9 @@ Changes in backwards compatibility policy
* LUCENE-4761: Facet packages reorganized. Should be easy to fix your import
statements, if you use an IDE such as Eclipse. (Shai Erera)
* LUCENE-4750: Convert DrillDown to DrillDownQuery, so you can initialize it
and add drill-down categories to it. (Michael McCandless, Shai Erera)
Optimizations
* LUCENE-4687: BloomFilterPostingsFormat now lazily initializes delegate

View File

@ -7,7 +7,7 @@ import org.apache.lucene.demo.facet.ExampleUtils;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.search.CountFacetRequest;
import org.apache.lucene.facet.search.DrillDown;
import org.apache.lucene.facet.search.DrillDownQuery;
import org.apache.lucene.facet.search.FacetRequest;
import org.apache.lucene.facet.search.FacetResult;
import org.apache.lucene.facet.search.FacetResultNode;
@ -16,7 +16,6 @@ import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MultiCollector;
import org.apache.lucene.search.Query;
@ -156,7 +155,8 @@ public class SimpleSearcher {
CategoryPath categoryOfInterest = resIterator.next().label;
// drill-down preparation: turn the base query into a drill-down query for the category of interest
Query q2 = DrillDown.query(indexingParams, baseQuery, Occur.MUST, categoryOfInterest);
DrillDownQuery q2 = new DrillDownQuery(indexingParams, baseQuery);
q2.add(categoryOfInterest);
// that's it - search with the new query and we're done!
// only documents both matching the base query AND containing the

View File

@ -612,7 +612,8 @@ data set into a portion of it by specifying a certain category, is what we call
We now show the required code lines for implementing such a drill-down.
<pre class="prettyprint lang-java linenums">
Query baseQuery = queryParser.parse("tennis racquet");
Query q2 = DrillDown.query(baseQuery, new CategoryPath("make", "head"), 10));
DrillDownQuery q2 = new DrillDownQuery(indexingParams, baseQuery);
q2.add(new CategoryPath("make", "head"), 10));
</pre>
<p>
In line 1 the original user query is created and then used to obtain information on

View File

@ -10,7 +10,7 @@ import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.Bits;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.search.DrillDown;
import org.apache.lucene.facet.search.DrillDownQuery;
import org.apache.lucene.facet.search.FacetResult;
import org.apache.lucene.facet.search.FacetResultNode;
import org.apache.lucene.facet.search.ScoredDocIDs;
@ -105,7 +105,7 @@ class TakmiSampleFixer implements SampleFixer {
}
CategoryPath catPath = fresNode.label;
Term drillDownTerm = DrillDown.term(searchParams, catPath);
Term drillDownTerm = DrillDownQuery.term(searchParams.indexingParams, catPath);
// TODO (Facet): avoid Multi*?
Bits liveDocs = MultiFields.getLiveDocs(indexReader);
int updatedCount = countIntersection(MultiFields.getTermDocsEnum(indexReader, liveDocs,

View File

@ -1,114 +0,0 @@
package org.apache.lucene.facet.search;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.facet.params.CategoryListParams;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.taxonomy.CategoryPath;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Utility class for creating drill-down {@link Query queries} or {@link Term
* terms} over {@link CategoryPath}. This can be used to e.g. narrow down a
* user's search to selected categories.
* <p>
* <b>NOTE:</b> if you choose to create your own {@link Query} by calling
* {@link #term}, it is recommended to wrap it with {@link ConstantScoreQuery}
* and set the {@link ConstantScoreQuery#setBoost(float) boost} to {@code 0.0f},
* so that it does not affect the scores of the documents.
*
* @lucene.experimental
*/
public final class DrillDown {
/**
* @see #term(FacetIndexingParams, CategoryPath)
*/
public static final Term term(FacetSearchParams sParams, CategoryPath path) {
return term(sParams.indexingParams, path);
}
/** Return a drill-down {@link Term} for a category. */
public static final Term term(FacetIndexingParams iParams, CategoryPath path) {
CategoryListParams clp = iParams.getCategoryListParams(path);
char[] buffer = new char[path.fullPathLength()];
iParams.drillDownTermText(path, buffer);
return new Term(clp.field, String.valueOf(buffer));
}
/**
* Wraps a given {@link Query} by a drill-down query over the given
* categories. {@link Occur} defines the relationship between the cateories
* (e.g. {@code OR} or {@code AND}. If you need to construct a more
* complicated relationship, e.g. {@code AND} of {@code ORs}), call this
* method with every group of categories with the same relationship and then
* construct a {@link BooleanQuery} which will wrap all returned queries. It
* is advised to construct that boolean query with coord disabled, and also
* wrap the final query with {@link ConstantScoreQuery} and set its boost to
* {@code 0.0f}.
* <p>
* <b>NOTE:</b> {@link Occur} only makes sense when there is more than one
* {@link CategoryPath} given.
* <p>
* <b>NOTE:</b> {@code baseQuery} can be {@code null}, in which case only the
* {@link Query} over the categories will is returned.
*/
public static final Query query(FacetIndexingParams iParams, Query baseQuery, Occur occur, CategoryPath... paths) {
if (paths == null || paths.length == 0) {
throw new IllegalArgumentException("Empty category path not allowed for drill down query!");
}
final Query q;
if (paths.length == 1) {
q = new TermQuery(term(iParams, paths[0]));
} else {
BooleanQuery bq = new BooleanQuery(true); // disable coord
for (CategoryPath cp : paths) {
bq.add(new TermQuery(term(iParams, cp)), occur);
}
q = bq;
}
final ConstantScoreQuery drillDownQuery = new ConstantScoreQuery(q);
drillDownQuery.setBoost(0.0f);
if (baseQuery == null) {
return drillDownQuery;
} else {
BooleanQuery res = new BooleanQuery(true);
res.add(baseQuery, Occur.MUST);
res.add(drillDownQuery, Occur.MUST);
return res;
}
}
/**
* @see #query
*/
public static final Query query(FacetSearchParams sParams, Query baseQuery, Occur occur, CategoryPath... paths) {
return query(sParams.indexingParams, baseQuery, occur, paths);
}
}

View File

@ -0,0 +1,149 @@
package org.apache.lucene.facet.search;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.apache.lucene.facet.params.CategoryListParams;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
/**
* A {@link Query} for drill-down over {@link CategoryPath categories}. You
* should call {@link #add(CategoryPath...)} for every group of categories you
* want to drill-down over. Each category in the group is {@code OR'ed} with
* the others, and groups are {@code AND'ed}.
* <p>
* <b>NOTE:</b> if you choose to create your own {@link Query} by calling
* {@link #term}, it is recommended to wrap it with {@link ConstantScoreQuery}
* and set the {@link ConstantScoreQuery#setBoost(float) boost} to {@code 0.0f},
* so that it does not affect the scores of the documents.
*
* @lucene.experimental
*/
public final class DrillDownQuery extends Query {
/** Return a drill-down {@link Term} for a category. */
public static final Term term(FacetIndexingParams iParams, CategoryPath path) {
CategoryListParams clp = iParams.getCategoryListParams(path);
char[] buffer = new char[path.fullPathLength()];
iParams.drillDownTermText(path, buffer);
return new Term(clp.field, String.valueOf(buffer));
}
private final BooleanQuery query;
private final Set<String> drillDownDims = new HashSet<String>();
private final FacetIndexingParams fip;
/* Used by clone() */
private DrillDownQuery(FacetIndexingParams fip, BooleanQuery query, Set<String> drillDownDims) {
this.fip = fip;
this.query = query.clone();
this.drillDownDims.addAll(drillDownDims);
}
/**
* Creates a new {@link DrillDownQuery} without a base query, which means that
* you intend to perfor a pure browsing query (equivalent to using
* {@link MatchAllDocsQuery} as base.
*/
public DrillDownQuery(FacetIndexingParams fip) {
this(fip, null);
}
/**
* Creates a new {@link DrillDownQuery} over the given base query. Can be
* {@code null}, in which case the result {@link Query} from
* {@link #rewrite(IndexReader)} will be a pure browsing query, filtering on
* the added categories only.
*/
public DrillDownQuery(FacetIndexingParams fip, Query baseQuery) {
query = new BooleanQuery(true); // disable coord
if (baseQuery != null) {
query.add(baseQuery, Occur.MUST);
}
this.fip = fip;
}
/**
* Adds one dimension of drill downs; if you pass multiple values they are
* OR'd, and then the entire dimension is AND'd against the base query.
*/
public void add(CategoryPath... paths) {
Query q;
String dim = paths[0].components[0];
if (drillDownDims.contains(dim)) {
throw new IllegalArgumentException("dimension '" + dim + "' was already added");
}
if (paths.length == 1) {
if (paths[0].length == 0) {
throw new IllegalArgumentException("all CategoryPaths must have length > 0");
}
q = new TermQuery(term(fip, paths[0]));
} else {
BooleanQuery bq = new BooleanQuery(true); // disable coord
for (CategoryPath cp : paths) {
if (cp.length == 0) {
throw new IllegalArgumentException("all CategoryPaths must have length > 0");
}
if (!cp.components[0].equals(dim)) {
throw new IllegalArgumentException("multiple (OR'd) drill-down paths must be under same dimension; got '"
+ dim + "' and '" + cp.components[0] + "'");
}
bq.add(new TermQuery(term(fip, cp)), Occur.SHOULD);
}
q = bq;
}
drillDownDims.add(dim);
final ConstantScoreQuery drillDownQuery = new ConstantScoreQuery(q);
drillDownQuery.setBoost(0.0f);
query.add(drillDownQuery, Occur.MUST);
}
@Override
public DrillDownQuery clone() {
return new DrillDownQuery(fip, query, drillDownDims);
}
@Override
public int hashCode() {
return query.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DrillDownQuery)) {
return false;
}
DrillDownQuery other = (DrillDownQuery) obj;
return query.equals(other.query);
}
@Override
public Query rewrite(IndexReader r) throws IOException {
if (query.clauses().size() == 0) {
// baseQuery given to the ctor was null + no drill-downs were added
// note that if only baseQuery was given to the ctor, but no drill-down terms
// is fine, since the rewritten query will be the original base query.
throw new IllegalStateException("no base query or drill-down categories given");
}
return query;
}
@Override
public String toString(String field) {
return query.toString(field);
}
}

View File

@ -3,7 +3,7 @@ package org.apache.lucene.facet.params;
import org.apache.lucene.facet.FacetTestCase;
import org.apache.lucene.facet.params.CategoryListParams;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.search.DrillDown;
import org.apache.lucene.facet.search.DrillDownQuery;
import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.facet.util.PartitionsUtils;
import org.apache.lucene.index.Term;
@ -39,7 +39,7 @@ public class FacetIndexingParamsTest extends FacetTestCase {
+ dfip.getFacetDelimChar() + "b";
CategoryPath cp = new CategoryPath("a", "b");
assertEquals("wrong drill-down term", new Term("$facets",
expectedDDText), DrillDown.term(dfip,cp));
expectedDDText), DrillDownQuery.term(dfip,cp));
char[] buf = new char[20];
int numchars = dfip.drillDownTermText(cp, buf);
assertEquals("3 characters should be written", 3, numchars);

View File

@ -6,7 +6,7 @@ import org.apache.lucene.facet.FacetTestCase;
import org.apache.lucene.facet.params.CategoryListParams;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.params.PerDimensionIndexingParams;
import org.apache.lucene.facet.search.DrillDown;
import org.apache.lucene.facet.search.DrillDownQuery;
import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.facet.util.PartitionsUtils;
import org.apache.lucene.index.Term;
@ -38,7 +38,7 @@ public class PerDimensionIndexingParamsTest extends FacetTestCase {
assertEquals("Expected default category list field is $facets", "$facets", ifip.getCategoryListParams(null).field);
String expectedDDText = "a" + ifip.getFacetDelimChar() + "b";
CategoryPath cp = new CategoryPath("a", "b");
assertEquals("wrong drill-down term", new Term("$facets", expectedDDText), DrillDown.term(ifip,cp));
assertEquals("wrong drill-down term", new Term("$facets", expectedDDText), DrillDownQuery.term(ifip,cp));
char[] buf = new char[20];
int numchars = ifip.drillDownTermText(cp, buf);
assertEquals("3 characters should be written", 3, numchars);

View File

@ -1,9 +1,44 @@
package org.apache.lucene.facet.search;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.analysis.MockTokenizer;
@ -23,12 +58,13 @@ import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.IOUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
@ -50,28 +86,27 @@ import org.junit.Test;
* limitations under the License.
*/
public class DrillDownTest extends FacetTestCase {
public class DrillDownQueryTest extends FacetTestCase {
private FacetIndexingParams defaultParams;
private PerDimensionIndexingParams nonDefaultParams;
private static IndexReader reader;
private static DirectoryTaxonomyReader taxo;
private static Directory dir;
private static Directory taxoDir;
public DrillDownTest() {
Map<CategoryPath,CategoryListParams> paramsMap = new HashMap<CategoryPath,CategoryListParams>();
paramsMap.put(new CategoryPath("a"), randomCategoryListParams("testing_facets_a"));
paramsMap.put(new CategoryPath("b"), randomCategoryListParams("testing_facets_b"));
nonDefaultParams = new PerDimensionIndexingParams(paramsMap);
defaultParams = new FacetIndexingParams(randomCategoryListParams(CategoryListParams.DEFAULT_FIELD));
private FacetIndexingParams defaultParams;
private PerDimensionIndexingParams nonDefaultParams;
@AfterClass
public static void afterClassDrillDownQueryTest() throws Exception {
IOUtils.close(reader, taxo, dir, taxoDir);
}
@BeforeClass
public static void createIndexes() throws IOException {
public static void beforeClassDrillDownQueryTest() throws Exception {
dir = newDirectory();
RandomIndexWriter writer = new RandomIndexWriter(random(), dir,
newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random(), MockTokenizer.KEYWORD, false)));
Random r = random();
RandomIndexWriter writer = new RandomIndexWriter(r, dir,
newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(r, MockTokenizer.KEYWORD, false)));
taxoDir = newDirectory();
TaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(taxoDir);
@ -86,7 +121,11 @@ public class DrillDownTest extends FacetTestCase {
doc.add(new TextField("content", "bar", Field.Store.NO));
}
if (i % 4 == 0) { // 25
paths.add(new CategoryPath("a"));
if (r.nextBoolean()) {
paths.add(new CategoryPath("a/1", '/'));
} else {
paths.add(new CategoryPath("a/2", '/'));
}
}
if (i % 5 == 0) { // 20
paths.add(new CategoryPath("b"));
@ -105,50 +144,66 @@ public class DrillDownTest extends FacetTestCase {
taxo = new DirectoryTaxonomyReader(taxoDir);
}
@Test
public void testTermNonDefault() {
Term termA = DrillDown.term(nonDefaultParams, new CategoryPath("a"));
assertEquals(new Term("testing_facets_a", "a"), termA);
Term termB = DrillDown.term(nonDefaultParams, new CategoryPath("b"));
assertEquals(new Term("testing_facets_b", "b"), termB);
public DrillDownQueryTest() {
Map<CategoryPath,CategoryListParams> paramsMap = new HashMap<CategoryPath,CategoryListParams>();
paramsMap.put(new CategoryPath("a"), randomCategoryListParams("testing_facets_a"));
paramsMap.put(new CategoryPath("b"), randomCategoryListParams("testing_facets_b"));
nonDefaultParams = new PerDimensionIndexingParams(paramsMap);
defaultParams = new FacetIndexingParams(randomCategoryListParams(CategoryListParams.DEFAULT_FIELD));
}
@Test
public void testDefaultField() {
String defaultField = CategoryListParams.DEFAULT_FIELD;
Term termA = DrillDown.term(defaultParams, new CategoryPath("a"));
Term termA = DrillDownQuery.term(defaultParams, new CategoryPath("a"));
assertEquals(new Term(defaultField, "a"), termA);
Term termB = DrillDown.term(defaultParams, new CategoryPath("b"));
Term termB = DrillDownQuery.term(defaultParams, new CategoryPath("b"));
assertEquals(new Term(defaultField, "b"), termB);
}
@Test
public void testAndOrs() throws Exception {
IndexSearcher searcher = newSearcher(reader);
// test (a/1 OR a/2) AND b
DrillDownQuery q = new DrillDownQuery(defaultParams);
q.add(new CategoryPath("a/1", '/'), new CategoryPath("a/2", '/'));
q.add(new CategoryPath("b"));
TopDocs docs = searcher.search(q, 100);
assertEquals(5, docs.totalHits);
}
@Test
public void testQuery() throws IOException {
IndexSearcher searcher = newSearcher(reader);
// Making sure the query yields 25 documents with the facet "a"
Query q = DrillDown.query(defaultParams, null, Occur.MUST, new CategoryPath("a"));
DrillDownQuery q = new DrillDownQuery(defaultParams);
q.add(new CategoryPath("a"));
TopDocs docs = searcher.search(q, 100);
assertEquals(25, docs.totalHits);
// Making sure the query yields 5 documents with the facet "b" and the
// previous (facet "a") query as a base query
Query q2 = DrillDown.query(defaultParams, q, Occur.MUST, new CategoryPath("b"));
DrillDownQuery q2 = new DrillDownQuery(defaultParams, q);
q2.add(new CategoryPath("b"));
docs = searcher.search(q2, 100);
assertEquals(5, docs.totalHits);
// Making sure that a query of both facet "a" and facet "b" yields 5 results
Query q3 = DrillDown.query(defaultParams, null, Occur.MUST, new CategoryPath("a"), new CategoryPath("b"));
DrillDownQuery q3 = new DrillDownQuery(defaultParams);
q3.add(new CategoryPath("a"));
q3.add(new CategoryPath("b"));
docs = searcher.search(q3, 100);
assertEquals(5, docs.totalHits);
assertEquals(5, docs.totalHits);
// Check that content:foo (which yields 50% results) and facet/b (which yields 20%)
// would gather together 10 results (10%..)
Query fooQuery = new TermQuery(new Term("content", "foo"));
Query q4 = DrillDown.query(defaultParams, fooQuery, Occur.MUST, new CategoryPath("b"));
DrillDownQuery q4 = new DrillDownQuery(defaultParams, fooQuery);
q4.add(new CategoryPath("b"));
docs = searcher.search(q4, 100);
assertEquals(10, docs.totalHits);
}
@ -158,38 +213,25 @@ public class DrillDownTest extends FacetTestCase {
IndexSearcher searcher = newSearcher(reader);
// Create the base query to start with
Query q = DrillDown.query(defaultParams, null, Occur.MUST, new CategoryPath("a"));
DrillDownQuery q = new DrillDownQuery(defaultParams);
q.add(new CategoryPath("a"));
// Making sure the query yields 5 documents with the facet "b" and the
// previous (facet "a") query as a base query
Query q2 = DrillDown.query(defaultParams, q, Occur.MUST, new CategoryPath("b"));
DrillDownQuery q2 = new DrillDownQuery(defaultParams, q);
q2.add(new CategoryPath("b"));
TopDocs docs = searcher.search(q2, 100);
assertEquals(5, docs.totalHits);
// Check that content:foo (which yields 50% results) and facet/b (which yields 20%)
// would gather together 10 results (10%..)
Query fooQuery = new TermQuery(new Term("content", "foo"));
Query q4 = DrillDown.query(defaultParams, fooQuery, Occur.MUST, new CategoryPath("b"));
DrillDownQuery q4 = new DrillDownQuery(defaultParams, fooQuery);
q4.add(new CategoryPath("b"));
docs = searcher.search(q4, 100);
assertEquals(10, docs.totalHits);
}
@AfterClass
public static void closeIndexes() throws IOException {
if (reader != null) {
reader.close();
reader = null;
}
if (taxo != null) {
taxo.close();
taxo = null;
}
dir.close();
taxoDir.close();
}
@Test
public void testScoring() throws IOException {
// verify that drill-down queries do not modify scores
@ -204,8 +246,9 @@ public class DrillDownTest extends FacetTestCase {
}
// create a drill-down query with category "a", scores should not change
q = DrillDown.query(defaultParams, q, Occur.MUST, new CategoryPath("a"));
docs = searcher.search(q, reader.maxDoc()); // fetch all available docs to this query
DrillDownQuery q2 = new DrillDownQuery(defaultParams, q);
q2.add(new CategoryPath("a"));
docs = searcher.search(q2, reader.maxDoc()); // fetch all available docs to this query
for (ScoreDoc sd : docs.scoreDocs) {
assertEquals("score of doc=" + sd.doc + " modified", scores[sd.doc], sd.score, 0f);
}
@ -216,7 +259,8 @@ public class DrillDownTest extends FacetTestCase {
// verify that drill-down queries (with no base query) returns 0.0 score
IndexSearcher searcher = newSearcher(reader);
Query q = DrillDown.query(defaultParams, null, Occur.MUST, new CategoryPath("a"));
DrillDownQuery q = new DrillDownQuery(defaultParams);
q.add(new CategoryPath("a"));
TopDocs docs = searcher.search(q, reader.maxDoc()); // fetch all available docs to this query
for (ScoreDoc sd : docs.scoreDocs) {
assertEquals(0f, sd.score, 0f);
@ -224,13 +268,36 @@ public class DrillDownTest extends FacetTestCase {
}
@Test
public void testOrQuery() throws Exception {
IndexSearcher searcher = newSearcher(reader);
// Making sure that a query of facet "a" or facet "b" yields 0 results
Query q = DrillDown.query(defaultParams, null, Occur.SHOULD, new CategoryPath("a"), new CategoryPath("b"));
TopDocs docs = searcher.search(q, 100);
assertEquals(40, docs.totalHits);
}
public void testTermNonDefault() {
Term termA = DrillDownQuery.term(nonDefaultParams, new CategoryPath("a"));
assertEquals(new Term("testing_facets_a", "a"), termA);
Term termB = DrillDownQuery.term(nonDefaultParams, new CategoryPath("b"));
assertEquals(new Term("testing_facets_b", "b"), termB);
}
@Test
public void testClone() throws Exception {
DrillDownQuery q = new DrillDownQuery(defaultParams, new MatchAllDocsQuery());
q.add(new CategoryPath("a"));
DrillDownQuery clone = q.clone();
clone.add(new CategoryPath("b"));
assertFalse("query wasn't cloned: source=" + q + " clone=" + clone, q.toString().equals(clone.toString()));
}
@Test(expected=IllegalStateException.class)
public void testNoBaseNorDrillDown() throws Exception {
DrillDownQuery q = new DrillDownQuery(defaultParams);
q.rewrite(reader);
}
public void testNoDrillDown() throws Exception {
Query base = new MatchAllDocsQuery();
DrillDownQuery q = new DrillDownQuery(defaultParams, base);
Query rewrite = q.rewrite(reader).rewrite(reader);
assertSame(base, rewrite);
}
}

View File

@ -37,8 +37,6 @@ import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.store.Directory;
public class TestDemoFacets extends FacetTestCase {
@ -110,7 +108,8 @@ public class TestDemoFacets extends FacetTestCase {
// Now user drills down on Publish Date/2010:
fsp = new FacetSearchParams(new CountFacetRequest(new CategoryPath("Author"), 10));
Query q2 = DrillDown.query(fsp, new MatchAllDocsQuery(), Occur.MUST, new CategoryPath("Publish Date/2010", '/'));
DrillDownQuery q2 = new DrillDownQuery(fsp.indexingParams, new MatchAllDocsQuery());
q2.add(new CategoryPath("Publish Date/2010", '/'));
c = FacetsCollector.create(fsp, searcher.getIndexReader(), taxoReader);
searcher.search(q2, c);
results = c.getFacetResults();

View File

@ -24,14 +24,14 @@ import org.apache.lucene.document.TextField;
import org.apache.lucene.facet.FacetTestCase;
import org.apache.lucene.facet.index.FacetFields;
import org.apache.lucene.facet.params.CategoryListParams;
import org.apache.lucene.facet.params.CategoryListParams.OrdinalPolicy;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.params.PerDimensionIndexingParams;
import org.apache.lucene.facet.params.PerDimensionOrdinalPolicy;
import org.apache.lucene.facet.params.CategoryListParams.OrdinalPolicy;
import org.apache.lucene.facet.search.CategoryListIterator;
import org.apache.lucene.facet.search.CountFacetRequest;
import org.apache.lucene.facet.search.DrillDown;
import org.apache.lucene.facet.search.DrillDownQuery;
import org.apache.lucene.facet.search.FacetRequest;
import org.apache.lucene.facet.search.FacetResult;
import org.apache.lucene.facet.search.FacetResultNode;
@ -41,8 +41,6 @@ import org.apache.lucene.facet.taxonomy.TaxonomyReader;
import org.apache.lucene.facet.taxonomy.TaxonomyWriter;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
import org.apache.lucene.facet.util.FacetsPayloadMigrationReader;
import org.apache.lucene.facet.util.PartitionsUtils;
import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.DirectoryReader;
@ -60,9 +58,7 @@ import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MultiCollector;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TotalHitCountCollector;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.IOUtils;
@ -289,7 +285,8 @@ public class TestFacetsPayloadMigrationReader extends FacetTestCase {
for (String dim : expectedCounts.keySet()) {
CategoryPath drillDownCP = new CategoryPath(dim);
FacetSearchParams fsp = new FacetSearchParams(fip, new CountFacetRequest(drillDownCP, 10));
Query drillDown = DrillDown.query(fsp, new MatchAllDocsQuery(), Occur.MUST, drillDownCP);
DrillDownQuery drillDown = new DrillDownQuery(fip, new MatchAllDocsQuery());
drillDown.add(drillDownCP);
TotalHitCountCollector total = new TotalHitCountCollector();
FacetsCollector fc = FacetsCollector.create(fsp, indexReader, taxoReader);
searcher.search(drillDown, MultiCollector.wrap(fc, total));