001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.lucene.demo.facet;
018
019
020import java.io.IOException;
021import java.util.ArrayList;
022import java.util.List;
023
024import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
025import org.apache.lucene.document.Document;
026import org.apache.lucene.facet.DrillDownQuery;
027import org.apache.lucene.facet.DrillSideways.DrillSidewaysResult;
028import org.apache.lucene.facet.DrillSideways;
029import org.apache.lucene.facet.FacetField;
030import org.apache.lucene.facet.FacetResult;
031import org.apache.lucene.facet.Facets;
032import org.apache.lucene.facet.FacetsCollector;
033import org.apache.lucene.facet.FacetsConfig;
034import org.apache.lucene.facet.taxonomy.FastTaxonomyFacetCounts;
035import org.apache.lucene.facet.taxonomy.TaxonomyReader;
036import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader;
037import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter;
038import org.apache.lucene.index.DirectoryReader;
039import org.apache.lucene.index.IndexWriter;
040import org.apache.lucene.index.IndexWriterConfig;
041import org.apache.lucene.index.IndexWriterConfig.OpenMode;
042import org.apache.lucene.search.IndexSearcher;
043import org.apache.lucene.search.MatchAllDocsQuery;
044import org.apache.lucene.store.Directory;
045import org.apache.lucene.store.RAMDirectory;
046
047/** Shows simple usage of faceted indexing and search. */
048public class SimpleFacetsExample {
049
050  private final Directory indexDir = new RAMDirectory();
051  private final Directory taxoDir = new RAMDirectory();
052  private final FacetsConfig config = new FacetsConfig();
053
054  /** Empty constructor */
055  public SimpleFacetsExample() {
056    config.setHierarchical("Publish Date", true);
057  }
058  
059  /** Build the example index. */
060  private void index() throws IOException {
061    IndexWriter indexWriter = new IndexWriter(indexDir, new IndexWriterConfig(
062        new WhitespaceAnalyzer()).setOpenMode(OpenMode.CREATE));
063
064    // Writes facet ords to a separate directory from the main index
065    DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(taxoDir);
066
067    Document doc = new Document();
068    doc.add(new FacetField("Author", "Bob"));
069    doc.add(new FacetField("Publish Date", "2010", "10", "15"));
070    indexWriter.addDocument(config.build(taxoWriter, doc));
071
072    doc = new Document();
073    doc.add(new FacetField("Author", "Lisa"));
074    doc.add(new FacetField("Publish Date", "2010", "10", "20"));
075    indexWriter.addDocument(config.build(taxoWriter, doc));
076
077    doc = new Document();
078    doc.add(new FacetField("Author", "Lisa"));
079    doc.add(new FacetField("Publish Date", "2012", "1", "1"));
080    indexWriter.addDocument(config.build(taxoWriter, doc));
081
082    doc = new Document();
083    doc.add(new FacetField("Author", "Susan"));
084    doc.add(new FacetField("Publish Date", "2012", "1", "7"));
085    indexWriter.addDocument(config.build(taxoWriter, doc));
086
087    doc = new Document();
088    doc.add(new FacetField("Author", "Frank"));
089    doc.add(new FacetField("Publish Date", "1999", "5", "5"));
090    indexWriter.addDocument(config.build(taxoWriter, doc));
091    
092    indexWriter.close();
093    taxoWriter.close();
094  }
095
096  /** User runs a query and counts facets. */
097  private List<FacetResult> facetsWithSearch() throws IOException {
098    DirectoryReader indexReader = DirectoryReader.open(indexDir);
099    IndexSearcher searcher = new IndexSearcher(indexReader);
100    TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir);
101
102    FacetsCollector fc = new FacetsCollector();
103
104    // MatchAllDocsQuery is for "browsing" (counts facets
105    // for all non-deleted docs in the index); normally
106    // you'd use a "normal" query:
107    FacetsCollector.search(searcher, new MatchAllDocsQuery(), 10, fc);
108
109    // Retrieve results
110    List<FacetResult> results = new ArrayList<>();
111
112    // Count both "Publish Date" and "Author" dimensions
113    Facets facets = new FastTaxonomyFacetCounts(taxoReader, config, fc);
114    results.add(facets.getTopChildren(10, "Author"));
115    results.add(facets.getTopChildren(10, "Publish Date"));
116    
117    indexReader.close();
118    taxoReader.close();
119    
120    return results;
121  }
122  
123  /** User runs a query and counts facets only without collecting the matching documents.*/
124  private List<FacetResult> facetsOnly() throws IOException {
125    DirectoryReader indexReader = DirectoryReader.open(indexDir);
126    IndexSearcher searcher = new IndexSearcher(indexReader);
127    TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir);
128
129    FacetsCollector fc = new FacetsCollector();
130
131    // MatchAllDocsQuery is for "browsing" (counts facets
132    // for all non-deleted docs in the index); normally
133    // you'd use a "normal" query:
134    searcher.search(new MatchAllDocsQuery(), fc);
135
136    // Retrieve results
137    List<FacetResult> results = new ArrayList<>();
138
139    // Count both "Publish Date" and "Author" dimensions
140    Facets facets = new FastTaxonomyFacetCounts(taxoReader, config, fc);
141   
142    results.add(facets.getTopChildren(10, "Author"));
143    results.add(facets.getTopChildren(10, "Publish Date"));
144    
145    indexReader.close();
146    taxoReader.close();
147    
148    return results;
149  }
150  
151  /** User drills down on 'Publish Date/2010', and we
152   *  return facets for 'Author' */
153  private FacetResult drillDown() throws IOException {
154    DirectoryReader indexReader = DirectoryReader.open(indexDir);
155    IndexSearcher searcher = new IndexSearcher(indexReader);
156    TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir);
157
158    // Passing no baseQuery means we drill down on all
159    // documents ("browse only"):
160    DrillDownQuery q = new DrillDownQuery(config);
161
162    // Now user drills down on Publish Date/2010:
163    q.add("Publish Date", "2010");
164    FacetsCollector fc = new FacetsCollector();
165    FacetsCollector.search(searcher, q, 10, fc);
166
167    // Retrieve results
168    Facets facets = new FastTaxonomyFacetCounts(taxoReader, config, fc);
169    FacetResult result = facets.getTopChildren(10, "Author");
170
171    indexReader.close();
172    taxoReader.close();
173    
174    return result;
175  }
176
177  /** User drills down on 'Publish Date/2010', and we
178   *  return facets for both 'Publish Date' and 'Author',
179   *  using DrillSideways. */
180  private List<FacetResult> drillSideways() throws IOException {
181    DirectoryReader indexReader = DirectoryReader.open(indexDir);
182    IndexSearcher searcher = new IndexSearcher(indexReader);
183    TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir);
184
185    // Passing no baseQuery means we drill down on all
186    // documents ("browse only"):
187    DrillDownQuery q = new DrillDownQuery(config);
188
189    // Now user drills down on Publish Date/2010:
190    q.add("Publish Date", "2010");
191
192    DrillSideways ds = new DrillSideways(searcher, config, taxoReader);
193    DrillSidewaysResult result = ds.search(q, 10);
194
195    // Retrieve results
196    List<FacetResult> facets = result.facets.getAllDims(10);
197
198    indexReader.close();
199    taxoReader.close();
200    
201    return facets;
202  }
203
204  /** Runs the search example. */
205  public List<FacetResult> runFacetOnly() throws IOException {
206    index();
207    return facetsOnly();
208  }
209  
210  /** Runs the search example. */
211  public List<FacetResult> runSearch() throws IOException {
212    index();
213    return facetsWithSearch();
214  }
215  
216  /** Runs the drill-down example. */
217  public FacetResult runDrillDown() throws IOException {
218    index();
219    return drillDown();
220  }
221
222  /** Runs the drill-sideways example. */
223  public List<FacetResult> runDrillSideways() throws IOException {
224    index();
225    return drillSideways();
226  }
227
228  /** Runs the search and drill-down examples and prints the results. */
229  public static void main(String[] args) throws Exception {
230    System.out.println("Facet counting example:");
231    System.out.println("-----------------------");
232    SimpleFacetsExample example = new SimpleFacetsExample();
233    List<FacetResult> results1 = example.runFacetOnly();
234    System.out.println("Author: " + results1.get(0));
235    System.out.println("Publish Date: " + results1.get(1));
236    
237    System.out.println("Facet counting example (combined facets and search):");
238    System.out.println("-----------------------");
239    List<FacetResult> results = example.runSearch();
240    System.out.println("Author: " + results.get(0));
241    System.out.println("Publish Date: " + results.get(1));
242    
243    System.out.println("Facet drill-down example (Publish Date/2010):");
244    System.out.println("---------------------------------------------");
245    System.out.println("Author: " + example.runDrillDown());
246
247    System.out.println("Facet drill-sideways example (Publish Date/2010):");
248    System.out.println("---------------------------------------------");
249    for(FacetResult result : example.runDrillSideways()) {
250      System.out.println(result);
251    }
252  }
253  
254}