001package org.apache.lucene.demo.facet; 002 003import java.io.IOException; 004import java.text.ParseException; 005import java.util.Collections; 006import java.util.List; 007 008import org.apache.lucene.analysis.core.WhitespaceAnalyzer; 009import org.apache.lucene.document.Document; 010import org.apache.lucene.document.Field.Store; 011import org.apache.lucene.document.NumericDocValuesField; 012import org.apache.lucene.document.TextField; 013import org.apache.lucene.expressions.Expression; 014import org.apache.lucene.expressions.SimpleBindings; 015import org.apache.lucene.expressions.js.JavascriptCompiler; 016import org.apache.lucene.facet.index.FacetFields; 017import org.apache.lucene.facet.params.FacetSearchParams; 018import org.apache.lucene.facet.search.FacetResult; 019import org.apache.lucene.facet.search.FacetsCollector; 020import org.apache.lucene.facet.search.SumValueSourceFacetRequest; 021import org.apache.lucene.facet.taxonomy.CategoryPath; 022import org.apache.lucene.facet.taxonomy.TaxonomyReader; 023import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader; 024import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter; 025import org.apache.lucene.index.DirectoryReader; 026import org.apache.lucene.index.IndexWriter; 027import org.apache.lucene.index.IndexWriterConfig; 028import org.apache.lucene.search.IndexSearcher; 029import org.apache.lucene.search.MatchAllDocsQuery; 030import org.apache.lucene.search.SortField; 031import org.apache.lucene.store.Directory; 032import org.apache.lucene.store.RAMDirectory; 033 034/* 035 * Licensed to the Apache Software Foundation (ASF) under one or more 036 * contributor license agreements. See the NOTICE file distributed with 037 * this work for additional information regarding copyright ownership. 038 * The ASF licenses this file to You under the Apache License, Version 2.0 039 * (the "License"); you may not use this file except in compliance with 040 * the License. You may obtain a copy of the License at 041 * 042 * http://www.apache.org/licenses/LICENSE-2.0 043 * 044 * Unless required by applicable law or agreed to in writing, software 045 * distributed under the License is distributed on an "AS IS" BASIS, 046 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 047 * See the License for the specific language governing permissions and 048 * limitations under the License. 049 */ 050 051/** Shows facets aggregation by an expression. */ 052public class ExpressionAggregationFacetsExample { 053 054 private final Directory indexDir = new RAMDirectory(); 055 private final Directory taxoDir = new RAMDirectory(); 056 057 /** Empty constructor */ 058 public ExpressionAggregationFacetsExample() {} 059 060 private void add(IndexWriter indexWriter, FacetFields facetFields, String text, String category, long popularity) throws IOException { 061 Document doc = new Document(); 062 doc.add(new TextField("c", text, Store.NO)); 063 doc.add(new NumericDocValuesField("popularity", popularity)); 064 facetFields.addFields(doc, Collections.singletonList(new CategoryPath(category, '/'))); 065 indexWriter.addDocument(doc); 066 } 067 068 /** Build the example index. */ 069 private void index() throws IOException { 070 IndexWriter indexWriter = new IndexWriter(indexDir, new IndexWriterConfig(FacetExamples.EXAMPLES_VER, 071 new WhitespaceAnalyzer(FacetExamples.EXAMPLES_VER))); 072 073 // Writes facet ords to a separate directory from the main index 074 DirectoryTaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(taxoDir); 075 076 // Reused across documents, to add the necessary facet fields 077 FacetFields facetFields = new FacetFields(taxoWriter); 078 079 add(indexWriter, facetFields, "foo bar", "A/B", 5L); 080 add(indexWriter, facetFields, "foo foo bar", "A/C", 3L); 081 082 indexWriter.close(); 083 taxoWriter.close(); 084 } 085 086 /** User runs a query and aggregates facets. */ 087 private List<FacetResult> search() throws IOException, ParseException { 088 DirectoryReader indexReader = DirectoryReader.open(indexDir); 089 IndexSearcher searcher = new IndexSearcher(indexReader); 090 TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir); 091 092 // Aggregate categories by an expression that combines the document's score 093 // and its popularity field 094 Expression expr = JavascriptCompiler.compile("_score * sqrt(popularity)"); 095 SimpleBindings bindings = new SimpleBindings(); 096 bindings.add(new SortField("_score", SortField.Type.SCORE)); // the score of the document 097 bindings.add(new SortField("popularity", SortField.Type.LONG)); // the value of the 'popularity' field 098 099 FacetSearchParams fsp = new FacetSearchParams( 100 new SumValueSourceFacetRequest(new CategoryPath("A"), 10, expr.getValueSource(bindings), true)); 101 102 // Aggregates the facet values 103 FacetsCollector fc = FacetsCollector.create(fsp, searcher.getIndexReader(), taxoReader); 104 105 // MatchAllDocsQuery is for "browsing" (counts facets 106 // for all non-deleted docs in the index); normally 107 // you'd use a "normal" query, and use MultiCollector to 108 // wrap collecting the "normal" hits and also facets: 109 searcher.search(new MatchAllDocsQuery(), fc); 110 111 // Retrieve results 112 List<FacetResult> facetResults = fc.getFacetResults(); 113 114 indexReader.close(); 115 taxoReader.close(); 116 117 return facetResults; 118 } 119 120 /** Runs the search example. */ 121 public List<FacetResult> runSearch() throws IOException, ParseException { 122 index(); 123 return search(); 124 } 125 126 /** Runs the search and drill-down examples and prints the results. */ 127 public static void main(String[] args) throws Exception { 128 System.out.println("Facet counting example:"); 129 System.out.println("-----------------------"); 130 List<FacetResult> results = new ExpressionAggregationFacetsExample().runSearch(); 131 for (FacetResult res : results) { 132 System.out.println(res); 133 } 134 } 135 136}