Index: lucene/CHANGES.txt =================================================================== --- lucene/CHANGES.txt (revision 1463038) +++ lucene/CHANGES.txt (working copy) @@ -185,6 +185,9 @@ IndexDeletionPolicy and InfoStream in order to make an IndexWriterConfig and its clone fully independent. (Adrien Grand) +* LUCENE-4893: Facet counts were multiplied as many times as + FacetsCollector.getFacetResults() is called. (Shai Erera) + Documentation * LUCENE-4841: Added example SimpleSortedSetFacetsExample to show how Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java (revision 1463038) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java (working copy) @@ -1,6 +1,5 @@ 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 Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (revision 1463038) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (working copy) @@ -81,6 +81,14 @@ return new FacetsAccumulator(fsp, indexReader, taxoReader); } + private static FacetResult emptyResult(int ordinal, FacetRequest fr) { + FacetResultNode root = new FacetResultNode(); + root.ordinal = ordinal; + root.label = fr.categoryPath; + root.value = 0; + return new FacetResult(fr, root, 0); + } + /** * Initializes the accumulator with the given parameters as well as * {@link FacetArrays}. Note that the accumulator doesn't call @@ -173,12 +181,8 @@ for (FacetRequest fr : searchParams.facetRequests) { int rootOrd = taxonomyReader.getOrdinal(fr.categoryPath); if (rootOrd == TaxonomyReader.INVALID_ORDINAL) { // category does not exist - // Add empty FacetResult: - FacetResultNode root = new FacetResultNode(); - root.ordinal = TaxonomyReader.INVALID_ORDINAL; - root.label = fr.categoryPath; - root.value = 0; - res.add(new FacetResult(fr, root, 0)); + // Add empty FacetResult + res.add(emptyResult(rootOrd, fr)); continue; } CategoryListParams clp = searchParams.indexingParams.getCategoryListParams(fr.categoryPath); Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java (revision 1463038) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java (working copy) @@ -183,6 +183,7 @@ } private final FacetsAccumulator accumulator; + private List cachedResults; protected final List matchingDocs = new ArrayList(); @@ -198,13 +199,17 @@ /** * Returns a {@link FacetResult} per {@link FacetRequest} set in - * {@link FacetSearchParams}. Note that if one of the {@link FacetRequest - * requests} is for a {@link CategoryPath} that does not exist in the taxonomy, - * no matching {@link FacetResult} will be returned. + * {@link FacetSearchParams}. Note that if a {@link FacetRequest} defines a + * {@link CategoryPath} which does not exist in the taxonomy, an empty + * {@link FacetResult} will be returned for it. */ public final List getFacetResults() throws IOException { - finish(); - return accumulator.accumulate(matchingDocs); + if (cachedResults == null) { + finish(); + cachedResults = accumulator.accumulate(matchingDocs); + } + + return cachedResults; } /** @@ -224,6 +229,7 @@ public final void reset() { finish(); matchingDocs.clear(); + cachedResults = null; } } Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java (revision 1463038) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java (working copy) @@ -230,4 +230,70 @@ IOUtils.close(taxo, taxoDir, r, indexDir); } + @Test + public void testGetFacetResultsTwice() throws Exception { + // LUCENE-4893: counts were multiplied as many times as getFacetResults was called. + Directory indexDir = newDirectory(); + Directory taxoDir = newDirectory(); + + TaxonomyWriter taxonomyWriter = new DirectoryTaxonomyWriter(taxoDir); + IndexWriter iw = new IndexWriter(indexDir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random()))); + + FacetFields facetFields = new FacetFields(taxonomyWriter); + Document doc = new Document(); + facetFields.addFields(doc, Arrays.asList(new CategoryPath("a/1", '/'), new CategoryPath("b/1", '/'))); + iw.addDocument(doc); + taxonomyWriter.close(); + iw.close(); + + DirectoryReader r = DirectoryReader.open(indexDir); + DirectoryTaxonomyReader taxo = new DirectoryTaxonomyReader(taxoDir); + + FacetSearchParams fsp = new FacetSearchParams( + new CountFacetRequest(new CategoryPath("a"), 10), + new CountFacetRequest(new CategoryPath("b"), 10)); + final FacetsAccumulator fa = random().nextBoolean() ? new FacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); + final FacetsCollector fc = FacetsCollector.create(fa); + new IndexSearcher(r).search(new MatchAllDocsQuery(), fc); + + List res1 = fc.getFacetResults(); + List res2 = fc.getFacetResults(); + assertSame("calling getFacetResults twice should return the exact same result", res1, res2); + + IOUtils.close(taxo, taxoDir, r, indexDir); + } + + @Test + public void testReset() throws Exception { + Directory indexDir = newDirectory(); + Directory taxoDir = newDirectory(); + + TaxonomyWriter taxonomyWriter = new DirectoryTaxonomyWriter(taxoDir); + IndexWriter iw = new IndexWriter(indexDir, newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random()))); + + FacetFields facetFields = new FacetFields(taxonomyWriter); + Document doc = new Document(); + facetFields.addFields(doc, Arrays.asList(new CategoryPath("a/1", '/'), new CategoryPath("b/1", '/'))); + iw.addDocument(doc); + taxonomyWriter.close(); + iw.close(); + + DirectoryReader r = DirectoryReader.open(indexDir); + DirectoryTaxonomyReader taxo = new DirectoryTaxonomyReader(taxoDir); + + FacetSearchParams fsp = new FacetSearchParams( + new CountFacetRequest(new CategoryPath("a"), 10), + new CountFacetRequest(new CategoryPath("b"), 10)); + final FacetsAccumulator fa = random().nextBoolean() ? new FacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); + final FacetsCollector fc = FacetsCollector.create(fa); + new IndexSearcher(r).search(new MatchAllDocsQuery(), fc); + + List res1 = fc.getFacetResults(); + fc.reset(); + List res2 = fc.getFacetResults(); + assertNotSame("reset() should clear the cached results", res1, res2); + + IOUtils.close(taxo, taxoDir, r, indexDir); + } + }