001    package org.apache.lucene.demo.facet;
002    
003    import java.io.IOException;
004    import java.util.ArrayList;
005    import java.util.List;
006    
007    import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
008    import org.apache.lucene.document.Document;
009    import org.apache.lucene.facet.index.FacetFields;
010    import org.apache.lucene.facet.params.FacetSearchParams;
011    import org.apache.lucene.facet.search.CountFacetRequest;
012    import org.apache.lucene.facet.search.DrillDownQuery;
013    import org.apache.lucene.facet.search.FacetResult;
014    import org.apache.lucene.facet.search.FacetsCollector;
015    import org.apache.lucene.facet.taxonomy.CategoryPath;
016    import org.apache.lucene.facet.taxonomy.TaxonomyReader;
017    import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
018    import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
019    import org.apache.lucene.index.DirectoryReader;
020    import org.apache.lucene.index.IndexWriter;
021    import org.apache.lucene.index.IndexWriterConfig;
022    import org.apache.lucene.search.IndexSearcher;
023    import org.apache.lucene.search.MatchAllDocsQuery;
024    import org.apache.lucene.store.Directory;
025    import org.apache.lucene.store.RAMDirectory;
026    
027    /*
028     * Licensed to the Apache Software Foundation (ASF) under one or more
029     * contributor license agreements.  See the NOTICE file distributed with
030     * this work for additional information regarding copyright ownership.
031     * The ASF licenses this file to You under the Apache License, Version 2.0
032     * (the "License"); you may not use this file except in compliance with
033     * the License.  You may obtain a copy of the License at
034     *
035     *     http://www.apache.org/licenses/LICENSE-2.0
036     *
037     * Unless required by applicable law or agreed to in writing, software
038     * distributed under the License is distributed on an "AS IS" BASIS,
039     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
040     * See the License for the specific language governing permissions and
041     * limitations under the License.
042     */
043    
044    /** Shows simple usage of faceted indexing and search. */
045    public class SimpleFacetsExample {
046    
047      private final Directory indexDir = new RAMDirectory();
048      private final Directory taxoDir = new RAMDirectory();
049    
050      /** Empty constructor */
051      public SimpleFacetsExample() {}
052      
053      private void add(IndexWriter indexWriter, FacetFields facetFields, String ... categoryPaths) throws IOException {
054        Document doc = new Document();
055        
056        List<CategoryPath> paths = new ArrayList<CategoryPath>();
057        for (String categoryPath : categoryPaths) {
058          paths.add(new CategoryPath(categoryPath, '/'));
059        }
060        facetFields.addFields(doc, paths);
061        indexWriter.addDocument(doc);
062      }
063    
064      /** Build the example index. */
065      private void index() throws IOException {
066        IndexWriter indexWriter = new IndexWriter(indexDir, new IndexWriterConfig(FacetExamples.EXAMPLES_VER, 
067            new WhitespaceAnalyzer(FacetExamples.EXAMPLES_VER)));
068    
069        // Writes facet ords to a separate directory from the main index
070        DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(taxoDir);
071    
072        // Reused across documents, to add the necessary facet fields
073        FacetFields facetFields = new FacetFields(taxoWriter);
074    
075        add(indexWriter, facetFields, "Author/Bob", "Publish Date/2010/10/15");
076        add(indexWriter, facetFields, "Author/Lisa", "Publish Date/2010/10/20");
077        add(indexWriter, facetFields, "Author/Lisa", "Publish Date/2012/1/1");
078        add(indexWriter, facetFields, "Author/Susan", "Publish Date/2012/1/7");
079        add(indexWriter, facetFields, "Author/Frank", "Publish Date/1999/5/5");
080        
081        indexWriter.close();
082        taxoWriter.close();
083      }
084    
085      /** User runs a query and counts facets. */
086      private List<FacetResult> search() throws IOException {
087        DirectoryReader indexReader = DirectoryReader.open(indexDir);
088        IndexSearcher searcher = new IndexSearcher(indexReader);
089        TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir);
090    
091        // Count both "Publish Date" and "Author" dimensions
092        FacetSearchParams fsp = new FacetSearchParams(
093            new CountFacetRequest(new CategoryPath("Publish Date"), 10), 
094            new CountFacetRequest(new CategoryPath("Author"), 10));
095    
096        // Aggregatses the facet counts
097        FacetsCollector fc = FacetsCollector.create(fsp, searcher.getIndexReader(), taxoReader);
098    
099        // MatchAllDocsQuery is for "browsing" (counts facets
100        // for all non-deleted docs in the index); normally
101        // you'd use a "normal" query, and use MultiCollector to
102        // wrap collecting the "normal" hits and also facets:
103        searcher.search(new MatchAllDocsQuery(), fc);
104    
105        // Retrieve results
106        List<FacetResult> facetResults = fc.getFacetResults();
107        
108        indexReader.close();
109        taxoReader.close();
110        
111        return facetResults;
112      }
113      
114      /** User drills down on 'Publish Date/2010'. */
115      private List<FacetResult> drillDown() throws IOException {
116        DirectoryReader indexReader = DirectoryReader.open(indexDir);
117        IndexSearcher searcher = new IndexSearcher(indexReader);
118        TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir);
119    
120        // Now user drills down on Publish Date/2010:
121        FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(new CategoryPath("Author"), 10));
122    
123        // Passing no baseQuery means we drill down on all
124        // documents ("browse only"):
125        DrillDownQuery q = new DrillDownQuery(fsp.indexingParams);
126        q.add(new CategoryPath("Publish Date/2010", '/'));
127        FacetsCollector fc = FacetsCollector.create(fsp, searcher.getIndexReader(), taxoReader);
128        searcher.search(q, fc);
129    
130        // Retrieve results
131        List<FacetResult> facetResults = fc.getFacetResults();
132        
133        indexReader.close();
134        taxoReader.close();
135        
136        return facetResults;
137      }
138    
139      /** Runs the search example. */
140      public List<FacetResult> runSearch() throws IOException {
141        index();
142        return search();
143      }
144      
145      /** Runs the drill-down example. */
146      public List<FacetResult> runDrillDown() throws IOException {
147        index();
148        return drillDown();
149      }
150    
151      /** Runs the search and drill-down examples and prints the results. */
152      public static void main(String[] args) throws Exception {
153        System.out.println("Facet counting example:");
154        System.out.println("-----------------------");
155        List<FacetResult> results = new SimpleFacetsExample().runSearch();
156        for (FacetResult res : results) {
157          System.out.println(res);
158        }
159    
160        System.out.println("\n");
161        System.out.println("Facet drill-down example (Publish Date/2010):");
162        System.out.println("---------------------------------------------");
163        results = new SimpleFacetsExample().runDrillDown();
164        for (FacetResult res : results) {
165          System.out.println(res);
166        }
167      }
168      
169    }