1 /* 2 * Copyright (c) 2008, 2017, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package ensemble.search; 33 34 import ensemble.generated.Samples; 35 import java.io.BufferedReader; 36 import java.io.IOException; 37 import java.io.InputStreamReader; 38 import java.util.ArrayList; 39 import java.util.EnumMap; 40 import java.util.List; 41 import java.util.Map; 42 import javafx.application.ConditionalFeature; 43 import javafx.application.Platform; 44 import org.apache.lucene.analysis.Analyzer; 45 import org.apache.lucene.analysis.standard.StandardAnalyzer; 46 import org.apache.lucene.document.Document; 47 import org.apache.lucene.index.DirectoryReader; 48 import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; 49 import org.apache.lucene.queryparser.classic.ParseException; 50 import org.apache.lucene.search.Query; 51 import org.apache.lucene.search.ScoreDoc; 52 import org.apache.lucene.search.Sort; 53 import org.apache.lucene.search.grouping.GroupDocs; 54 import org.apache.lucene.search.grouping.SearchGroup; 55 import org.apache.lucene.search.grouping.TermGroupSelector; 56 import org.apache.lucene.search.grouping.TopGroups; 57 import org.apache.lucene.search.grouping.TopGroupsCollector; 58 import org.apache.lucene.util.BytesRef; 59 60 /** 61 * Class for searching the index 62 */ 63 public class IndexSearcher { 64 private final static List<SearchGroup<BytesRef>> searchGroups = new ArrayList<>(); 65 static { 66 for (DocumentType dt: DocumentType.values()){ 67 SearchGroup<BytesRef> searchGroup = new SearchGroup(); 68 searchGroup.groupValue = new BytesRef(dt.toString()); 69 searchGroup.sortValues = new Comparable[]{5f}; 70 searchGroups.add(searchGroup); 71 } 72 } 73 private org.apache.lucene.search.IndexSearcher searcher; 74 private final Analyzer analyzer; 75 private final MultiFieldQueryParser parser; 76 77 public IndexSearcher() { 78 try { 79 searcher = new org.apache.lucene.search.IndexSearcher(DirectoryReader.open(new ClasspathDirectory())); 80 } catch (IOException e) { 81 e.printStackTrace(); 82 } 83 analyzer = new StandardAnalyzer(); 84 parser = new MultiFieldQueryParser(new String[]{"name","bookTitle","chapter","description"}, analyzer); 85 } 86 87 public Map<DocumentType, List<SearchResult>> search(String searchString) throws ParseException { 88 Map<DocumentType, List<SearchResult>> resultMap = new EnumMap<>(DocumentType.class); 89 try { 90 Query query = parser.parse(searchString); 91 final TopGroupsCollector<BytesRef> collector = new TopGroupsCollector( 92 new TermGroupSelector("documentType"), searchGroups, 93 Sort.RELEVANCE, Sort.RELEVANCE, 10, true, false, true); 94 searcher.search(query, collector); 95 final TopGroups<BytesRef> groups = collector.getTopGroups(0); 96 for (GroupDocs<BytesRef> groupDocs : groups.groups) { 97 DocumentType docType = DocumentType.valueOf(groupDocs.groupValue.utf8ToString()); 98 List<SearchResult> results = new ArrayList<>(); 99 for (ScoreDoc scoreDoc : groupDocs.scoreDocs) { 100 if ((Platform.isSupported(ConditionalFeature.WEB)) || (docType != DocumentType.DOC)) { 101 Document doc = searcher.doc(scoreDoc.doc); 102 SearchResult result = new SearchResult( 103 docType, 104 doc.get("name"), 105 doc.get("url"), 106 doc.get("className"), 107 doc.get("package"), 108 doc.get("ensemblePath"), 109 docType == DocumentType.DOC 110 ? doc.get("bookTitle") == null ? doc.get("chapter") : doc.get("bookTitle") 111 : doc.get("shortDescription").trim() 112 ); 113 /* If the result is a sample, then filter out the samples that 114 * the runtime platform does not support. We really want to show 115 * just 5 results, but we search for 10 and filter out unsupported 116 * samples and show just 5. 117 */ 118 if (docType == DocumentType.SAMPLE) { 119 if (Samples.ROOT.sampleForPath(result.getEnsemblePath().substring(9).trim()) == null) { 120 121 // Skip unsupported (not existing) samples 122 continue; 123 } 124 if (results.size() == 5) { 125 126 // 5 samples is enough 127 break; 128 } 129 } 130 results.add(result); 131 } 132 } 133 resultMap.put(docType, results); 134 } 135 } catch (IOException e) { 136 e.printStackTrace(); 137 } 138 return resultMap; 139 } 140 141 /** 142 * Simple command line test application 143 * @param args command line arguments 144 * @throws Exception for maps errors 145 */ 146 public static void main(String[] args) throws Exception { 147 BufferedReader in = new BufferedReader(new InputStreamReader(System.in, "UTF-8")); 148 IndexSearcher indexSearcher = new IndexSearcher(); 149 while (true) { 150 System.out.println("Enter query: "); 151 String line = in.readLine(); 152 if (line == null || line.length() == -1) break; 153 line = line.trim(); 154 if (line.length() == 0) break; 155 Map<DocumentType, List<SearchResult>> results = indexSearcher.search(line); 156 for (Map.Entry<DocumentType, List<SearchResult>> entry : results.entrySet()) { 157 System.out.println("--------- "+entry.getKey()+" ["+entry.getValue().size()+"] --------------------------------"); 158 for(SearchResult result: entry.getValue()) { 159 System.out.println(result.toString()); 160 } 161 } 162 } 163 } 164 }