Index: lucene/CHANGES.txt =================================================================== --- lucene/CHANGES.txt (revision 1462883) +++ 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 1462883) +++ 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 1462883) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (working copy) @@ -146,6 +146,14 @@ } return clps; } + + private 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); + } /** * Used by {@link FacetsCollector} to build the list of {@link FacetResult @@ -156,6 +164,14 @@ * the documents that matched the query, per-segment. */ public List accumulate(List matchingDocs) throws IOException { + if (matchingDocs.isEmpty()) { + List res = new ArrayList(); + for (FacetRequest fr : searchParams.facetRequests) { + res.add(emptyResult(taxonomyReader.getOrdinal(fr.categoryPath), fr)); + } + return res; + } + // aggregate facets per category list (usually onle one category list) FacetsAggregator aggregator = getAggregator(); for (CategoryListParams clp : getCategoryLists()) { @@ -173,12 +189,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 1462883) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java (working copy) @@ -204,7 +204,11 @@ */ public final List getFacetResults() throws IOException { finish(); - return accumulator.accumulate(matchingDocs); + List result = accumulator.accumulate(matchingDocs); + // clear matchingDocs so that subsequent calls get an empty result, until a + // new search is performed. + matchingDocs.clear(); + return result; } /** Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java (revision 1462883) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java (working copy) @@ -230,4 +230,44 @@ IOUtils.close(taxo, taxoDir, r, indexDir); } + @Test + public void testGetFacetResultsTwice() throws Exception { + // LUCENE-4893: if getFacetResults was called twice, counts we doubled (and trippled if 3 times...) + 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 res = fc.getFacetResults(); + assertEquals(2, res.size()); + assertEquals(1, (int) res.get(0).getFacetResultNode().subResults.get(0).value); + assertEquals(1, (int) res.get(1).getFacetResultNode().subResults.get(0).value); + + // second time should return an empty result for each request + res = fc.getFacetResults(); + assertEquals(2, res.size()); + assertEquals(0, fc.getFacetResults().get(0).getFacetResultNode().subResults.size()); + assertEquals(0, fc.getFacetResults().get(1).getFacetResultNode().subResults.size()); + + IOUtils.close(taxo, taxoDir, r, indexDir); + } + }