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, IndexWriterConfig.OpenMode.CREATE);
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        DrillDownQuery q = new DrillDownQuery(fsp.indexingParams, new MatchAllDocsQuery());
123        q.add(new CategoryPath("Publish Date/2010", '/'));
124        FacetsCollector fc = FacetsCollector.create(fsp, searcher.getIndexReader(), taxoReader);
125        searcher.search(q, fc);
126    
127        // Retrieve results
128        List<FacetResult> facetResults = fc.getFacetResults();
129        
130        indexReader.close();
131        taxoReader.close();
132        
133        return facetResults;
134      }
135    
136      /** Runs the search example. */
137      public List<FacetResult> runSearch() throws IOException {
138        index();
139        return search();
140      }
141      
142      /** Runs the drill-down example. */
143      public List<FacetResult> runDrillDown() throws IOException {
144        index();
145        return drillDown();
146      }
147    
148      /** Runs the search and drill-down examples and prints the results. */
149      public static void main(String[] args) throws Exception {
150        System.out.println("Facet counting example:");
151        System.out.println("-----------------------");
152        List<FacetResult> results = new SimpleFacetsExample().runSearch();
153        for (FacetResult res : results) {
154          System.out.println(res);
155        }
156    
157        System.out.println("\n");
158        System.out.println("Facet drill-down example (Publish Date/2010):");
159        System.out.println("---------------------------------------------");
160        results = new SimpleFacetsExample().runDrillDown();
161        for (FacetResult res : results) {
162          System.out.println(res);
163        }
164      }
165      
166    }