Index: lucene/CHANGES.txt
===================================================================
--- lucene/CHANGES.txt (revision 1444437)
+++ lucene/CHANGES.txt (working copy)
@@ -66,6 +66,9 @@
* 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
Index: lucene/demo/src/java/org/apache/lucene/demo/facet/simple/SimpleSearcher.java
===================================================================
--- lucene/demo/src/java/org/apache/lucene/demo/facet/simple/SimpleSearcher.java (revision 1444437)
+++ lucene/demo/src/java/org/apache/lucene/demo/facet/simple/SimpleSearcher.java (working copy)
@@ -7,7 +7,7 @@
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.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 @@
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
Index: lucene/facet/src/java/org/apache/lucene/facet/sampling/TakmiSampleFixer.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/sampling/TakmiSampleFixer.java (revision 1444437)
+++ lucene/facet/src/java/org/apache/lucene/facet/sampling/TakmiSampleFixer.java (working copy)
@@ -10,7 +10,7 @@
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 @@
}
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,
Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillDown.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/search/DrillDown.java (revision 1444437)
+++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillDown.java (working copy)
@@ -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);
- }
-
-}
Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java (revision 0)
+++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java (working copy)
@@ -0,0 +1,139 @@
+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.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. Every category in the group is {@code OR'ed} with
+ * the others, and every group is {@code AND'ed} with other groups.
+ *
+ * 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} over the given base query. Can be
+ * {@code null}, in which case the result {@link Query} from
+ * {@link #rewrite(IndexReader)} will filter down 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);
+ }
+
+}
Property changes on: lucene/facet/src/java/org/apache/lucene/facet/search/DrillDownQuery.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/facet/src/test/org/apache/lucene/facet/params/FacetIndexingParamsTest.java
===================================================================
--- lucene/facet/src/test/org/apache/lucene/facet/params/FacetIndexingParamsTest.java (revision 1444437)
+++ lucene/facet/src/test/org/apache/lucene/facet/params/FacetIndexingParamsTest.java (working copy)
@@ -3,7 +3,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.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 @@
+ 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);
Index: lucene/facet/src/test/org/apache/lucene/facet/params/PerDimensionIndexingParamsTest.java
===================================================================
--- lucene/facet/src/test/org/apache/lucene/facet/params/PerDimensionIndexingParamsTest.java (revision 1444437)
+++ lucene/facet/src/test/org/apache/lucene/facet/params/PerDimensionIndexingParamsTest.java (working copy)
@@ -6,7 +6,7 @@
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 @@
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);
Index: lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java
===================================================================
--- lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java (revision 0)
+++ lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java (working copy)
@@ -0,0 +1,303 @@
+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;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+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.FacetIndexingParams;
+import org.apache.lucene.facet.params.PerDimensionIndexingParams;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
+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.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.store.Directory;
+import org.apache.lucene.util.IOUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/*
+ * 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.
+ */
+
+public class DrillDownQueryTest extends FacetTestCase {
+
+ private static IndexReader reader;
+ private static DirectoryTaxonomyReader taxo;
+ private static Directory dir;
+ private static Directory taxoDir;
+
+ private FacetIndexingParams defaultParams;
+ private PerDimensionIndexingParams nonDefaultParams;
+
+ @AfterClass
+ public static void afterClassDrillDownQueryTest() throws Exception {
+ IOUtils.close(reader, taxo, dir, taxoDir);
+ }
+
+ @BeforeClass
+ public static void beforeClassDrillDownQueryTest() throws Exception {
+ dir = newDirectory();
+ 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);
+
+ for (int i = 0; i < 100; i++) {
+ ArrayList paths = new ArrayList();
+ Document doc = new Document();
+ if (i % 2 == 0) { // 50
+ doc.add(new TextField("content", "foo", Field.Store.NO));
+ }
+ if (i % 3 == 0) { // 33
+ doc.add(new TextField("content", "bar", Field.Store.NO));
+ }
+ if (i % 4 == 0) { // 25
+ 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"));
+ }
+ FacetFields facetFields = new FacetFields(taxoWriter);
+ if (paths.size() > 0) {
+ facetFields.addFields(doc, paths);
+ }
+ writer.addDocument(doc);
+ }
+
+ taxoWriter.close();
+ reader = writer.getReader();
+ writer.close();
+
+ taxo = new DirectoryTaxonomyReader(taxoDir);
+ }
+
+ 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 = DrillDownQuery.term(defaultParams, new CategoryPath("a"));
+ assertEquals(new Term(defaultField, "a"), termA);
+
+ 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, null);
+ 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"
+ DrillDownQuery q = new DrillDownQuery(defaultParams, null);
+ 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
+ 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
+ DrillDownQuery q3 = new DrillDownQuery(defaultParams, null);
+ q3.add(new CategoryPath("a"));
+ q3.add(new CategoryPath("b"));
+ docs = searcher.search(q3, 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"));
+ DrillDownQuery q4 = new DrillDownQuery(defaultParams, fooQuery);
+ q4.add(new CategoryPath("b"));
+ docs = searcher.search(q4, 100);
+ assertEquals(10, docs.totalHits);
+ }
+
+ @Test
+ public void testQueryImplicitDefaultParams() throws IOException {
+ IndexSearcher searcher = newSearcher(reader);
+
+ // Create the base query to start with
+ DrillDownQuery q = new DrillDownQuery(defaultParams, null);
+ 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
+ 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"));
+ DrillDownQuery q4 = new DrillDownQuery(defaultParams, fooQuery);
+ q4.add(new CategoryPath("b"));
+ docs = searcher.search(q4, 100);
+ assertEquals(10, docs.totalHits);
+ }
+
+ @Test
+ public void testScoring() throws IOException {
+ // verify that drill-down queries do not modify scores
+ IndexSearcher searcher = newSearcher(reader);
+
+ float[] scores = new float[reader.maxDoc()];
+
+ Query q = new TermQuery(new Term("content", "foo"));
+ TopDocs docs = searcher.search(q, reader.maxDoc()); // fetch all available docs to this query
+ for (ScoreDoc sd : docs.scoreDocs) {
+ scores[sd.doc] = sd.score;
+ }
+
+ // create a drill-down query with category "a", scores should not change
+ 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);
+ }
+ }
+
+ @Test
+ public void testScoringNoBaseQuery() throws IOException {
+ // verify that drill-down queries (with no base query) returns 0.0 score
+ IndexSearcher searcher = newSearcher(reader);
+
+ DrillDownQuery q = new DrillDownQuery(defaultParams, null);
+ 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);
+ }
+ }
+
+ @Test
+ 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, null);
+ 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);
+ }
+
+}
Property changes on: lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownQueryTest.java
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownTest.java
===================================================================
--- lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownTest.java (revision 1444437)
+++ lucene/facet/src/test/org/apache/lucene/facet/search/DrillDownTest.java (working copy)
@@ -1,236 +0,0 @@
-package org.apache.lucene.facet.search;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.lucene.analysis.MockAnalyzer;
-import org.apache.lucene.analysis.MockTokenizer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-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.FacetIndexingParams;
-import org.apache.lucene.facet.params.PerDimensionIndexingParams;
-import org.apache.lucene.facet.taxonomy.CategoryPath;
-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.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.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.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-/*
- * 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.
- */
-
-public class DrillDownTest 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));
- }
-
- @BeforeClass
- public static void createIndexes() throws IOException {
- dir = newDirectory();
- RandomIndexWriter writer = new RandomIndexWriter(random(), dir,
- newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random(), MockTokenizer.KEYWORD, false)));
-
- taxoDir = newDirectory();
- TaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(taxoDir);
-
- for (int i = 0; i < 100; i++) {
- ArrayList paths = new ArrayList();
- Document doc = new Document();
- if (i % 2 == 0) { // 50
- doc.add(new TextField("content", "foo", Field.Store.NO));
- }
- if (i % 3 == 0) { // 33
- doc.add(new TextField("content", "bar", Field.Store.NO));
- }
- if (i % 4 == 0) { // 25
- paths.add(new CategoryPath("a"));
- }
- if (i % 5 == 0) { // 20
- paths.add(new CategoryPath("b"));
- }
- FacetFields facetFields = new FacetFields(taxoWriter);
- if (paths.size() > 0) {
- facetFields.addFields(doc, paths);
- }
- writer.addDocument(doc);
- }
-
- taxoWriter.close();
- reader = writer.getReader();
- writer.close();
-
- 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);
- }
-
- @Test
- public void testDefaultField() {
- String defaultField = CategoryListParams.DEFAULT_FIELD;
-
- Term termA = DrillDown.term(defaultParams, new CategoryPath("a"));
- assertEquals(new Term(defaultField, "a"), termA);
-
- Term termB = DrillDown.term(defaultParams, new CategoryPath("b"));
- assertEquals(new Term(defaultField, "b"), termB);
- }
-
- @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"));
- 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"));
- 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"));
- docs = searcher.search(q3, 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"));
- docs = searcher.search(q4, 100);
- assertEquals(10, docs.totalHits);
- }
-
- @Test
- public void testQueryImplicitDefaultParams() throws IOException {
- IndexSearcher searcher = newSearcher(reader);
-
- // Create the base query to start with
- Query q = DrillDown.query(defaultParams, null, Occur.MUST, 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"));
- 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"));
- 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
- IndexSearcher searcher = newSearcher(reader);
-
- float[] scores = new float[reader.maxDoc()];
-
- Query q = new TermQuery(new Term("content", "foo"));
- TopDocs docs = searcher.search(q, reader.maxDoc()); // fetch all available docs to this query
- for (ScoreDoc sd : docs.scoreDocs) {
- scores[sd.doc] = sd.score;
- }
-
- // 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
- for (ScoreDoc sd : docs.scoreDocs) {
- assertEquals("score of doc=" + sd.doc + " modified", scores[sd.doc], sd.score, 0f);
- }
- }
-
- @Test
- public void testScoringNoBaseQuery() throws IOException {
- // 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"));
- TopDocs docs = searcher.search(q, reader.maxDoc()); // fetch all available docs to this query
- for (ScoreDoc sd : docs.scoreDocs) {
- assertEquals(0f, sd.score, 0f);
- }
- }
-
- @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);
- }
-
-}
Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java
===================================================================
--- lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java (revision 1444437)
+++ lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java (working copy)
@@ -37,8 +37,6 @@
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 @@
// 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();
Index: lucene/facet/src/test/org/apache/lucene/facet/util/TestFacetsPayloadMigrationReader.java
===================================================================
--- lucene/facet/src/test/org/apache/lucene/facet/util/TestFacetsPayloadMigrationReader.java (revision 1444437)
+++ lucene/facet/src/test/org/apache/lucene/facet/util/TestFacetsPayloadMigrationReader.java (working copy)
@@ -24,14 +24,14 @@
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.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.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 @@
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));