    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, 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}