001package org.apache.lucene.demo.facet;
002
003import java.io.IOException;
004import java.util.ArrayList;
005import java.util.List;
006
007import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
008import org.apache.lucene.document.Document;
009import org.apache.lucene.facet.index.FacetFields;
010import org.apache.lucene.facet.params.FacetSearchParams;
011import org.apache.lucene.facet.search.CountFacetRequest;
012import org.apache.lucene.facet.search.DrillDownQuery;
013import org.apache.lucene.facet.search.FacetResult;
014import org.apache.lucene.facet.search.FacetsCollector;
015import org.apache.lucene.facet.taxonomy.CategoryPath;
016import org.apache.lucene.facet.taxonomy.TaxonomyReader;
017import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
018import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
019import org.apache.lucene.index.DirectoryReader;
020import org.apache.lucene.index.IndexWriter;
021import org.apache.lucene.index.IndexWriterConfig;
022import org.apache.lucene.search.IndexSearcher;
023import org.apache.lucene.search.MatchAllDocsQuery;
024import org.apache.lucene.store.Directory;
025import 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. */
045public 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}