diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 40155a3a221..d9413329339 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -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
diff --git a/lucene/demo/src/java/org/apache/lucene/demo/facet/simple/SimpleSearcher.java b/lucene/demo/src/java/org/apache/lucene/demo/facet/simple/SimpleSearcher.java
index a3ca72683d7..326094dad60 100644
--- a/lucene/demo/src/java/org/apache/lucene/demo/facet/simple/SimpleSearcher.java
+++ b/lucene/demo/src/java/org/apache/lucene/demo/facet/simple/SimpleSearcher.java
@@ -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
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/doc-files/userguide.html b/lucene/facet/src/java/org/apache/lucene/facet/doc-files/userguide.html
index 051aad0870e..ca68aca72ea 100755
--- a/lucene/facet/src/java/org/apache/lucene/facet/doc-files/userguide.html
+++ b/lucene/facet/src/java/org/apache/lucene/facet/doc-files/userguide.html
@@ -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.
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));
In line 1 the original user query is created and then used to obtain information on
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/sampling/TakmiSampleFixer.java b/lucene/facet/src/java/org/apache/lucene/facet/sampling/TakmiSampleFixer.java
index 0b3e3f1f777..83536e26a31 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/sampling/TakmiSampleFixer.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/sampling/TakmiSampleFixer.java
@@ -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,
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/search/DrillDown.java b/lucene/facet/src/java/org/apache/lucene/facet/search/DrillDown.java
deleted file mode 100644
index 670df7e796d..00000000000
--- a/lucene/facet/src/java/org/apache/lucene/facet/search/DrillDown.java
+++ /dev/null
@@ -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.
- *
- * NOTE: 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}.
- *
- * NOTE: {@link Occur} only makes sense when there is more than one
- * {@link CategoryPath} given.
- *
- * NOTE: {@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);
- }
-
-}
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java b/lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java
new file mode 100644
index 00000000000..2851e3147ab
--- /dev/null
+++ b/lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java
@@ -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}.
+ *
+ * NOTE: 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 drillDownDims = new HashSet();
+
+ private final FacetIndexingParams fip;
+
+ /* Used by clone() */
+ private DrillDownQuery(FacetIndexingParams fip, BooleanQuery query, Set 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);
+ }
+
+}
diff --git a/lucene/facet/src/test/org/apache/lucene/facet/params/FacetIndexingParamsTest.java b/lucene/facet/src/test/org/apache/lucene/facet/params/FacetIndexingParamsTest.java
index 3f58bb509a3..4f475250737 100644
--- a/lucene/facet/src/test/org/apache/lucene/facet/params/FacetIndexingParamsTest.java
+++ b/lucene/facet/src/test/org/apache/lucene/facet/params/FacetIndexingParamsTest.java
@@ -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);
diff --git a/lucene/facet/src/test/org/apache/lucene/facet/params/PerDimensionIndexingParamsTest.java b/lucene/facet/src/test/org/apache/lucene/facet/params/PerDimensionIndexingParamsTest.java
index 62d9cfe76ac..62aec7c1c04 100644
--- a/lucene/facet/src/test/org/apache/lucene/facet/params/PerDimensionIndexingParamsTest.java
+++ b/lucene/facet/src/test/org/apache/lucene/facet/params/PerDimensionIndexingParamsTest.java
@@ -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);
diff --git a/lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownTest.java b/lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java
similarity index 60%
rename from lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownTest.java
rename to lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java
index 1c620485afb..b539bf9b5a6 100644
--- a/lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownTest.java
+++ b/lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java
@@ -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 paramsMap = new HashMap();
- 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 paramsMap = new HashMap();
+ 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);
+ }
+
}
diff --git a/lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java b/lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java
index ac70655af01..284da50ef91 100644
--- a/lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java
+++ b/lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java
@@ -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();
diff --git a/lucene/facet/src/test/org/apache/lucene/facet/util/TestFacetsPayloadMigrationReader.java b/lucene/facet/src/test/org/apache/lucene/facet/util/TestFacetsPayloadMigrationReader.java
index 71d39082a86..3e11e025a7c 100644
--- a/lucene/facet/src/test/org/apache/lucene/facet/util/TestFacetsPayloadMigrationReader.java
+++ b/lucene/facet/src/test/org/apache/lucene/facet/util/TestFacetsPayloadMigrationReader.java
@@ -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));