1.1. Lucene工具类
为了后面的开发、测试方便,这里编写一个工具类:
1 import java.io.IOException; 2 3 import java.nio.file.Paths; 4 5 import java.util.List; 6 7 8 9 import org.apache.lucene.analysis.Analyzer; 10 11 import org.apache.lucene.document.Document; 12 13 import org.apache.lucene.index.DirectoryReader; 14 15 import org.apache.lucene.index.IndexReader; 16 17 import org.apache.lucene.index.IndexWriter; 18 19 import org.apache.lucene.index.IndexWriterConfig; 20 21 import org.apache.lucene.index.IndexableField; 22 23 import org.apache.lucene.search.IndexSearcher; 24 25 import org.apache.lucene.search.Query; 26 27 import org.apache.lucene.search.ScoreDoc; 28 29 import org.apache.lucene.search.TopDocs; 30 31 import org.apache.lucene.search.highlight.Formatter; 32 33 import org.apache.lucene.search.highlight.Highlighter; 34 35 import org.apache.lucene.search.highlight.QueryScorer; 36 37 import org.apache.lucene.search.highlight.Scorer; 38 39 import org.apache.lucene.search.highlight.SimpleHTMLFormatter; 40 41 import org.apache.lucene.store.Directory; 42 43 import org.apache.lucene.store.FSDirectory; 44 45 import org.slf4j.Logger; 46 47 import org.slf4j.LoggerFactory; 48 49 import org.wltea.analyzer.lucene.IKAnalyzer; 50 51 52 53 import cn.lmc.myworld.common.utils.PropertyUtil; 54 55 56 57 58 59 /** 60 61 * 全文检索工具类 62 63 * @author limingcheng 64 65 * 66 67 */ 68 69 public class LuceneUtils { 70 71 // 打印日志 72 73 private static final Logger LOGGER = LoggerFactory.getLogger(LuceneUtils.class); 74 75 76 77 private static Directory directory; // 索引文件存放目录对象 78 79 private static IndexWriter indexWriter; // 索引写对象,线程安全 80 81 private static IndexReader indexReader; // 索引读对象,线程安全 82 83 private static IndexSearcher indexSearcher; // 索引搜索对象,线程安全 84 85 private static Analyzer analyzer; // 分词器对象 86 87 public static IndexWriterConfig indexWriterConfig; // 索引配置 88 89 // public static Version matchVersion; // 索引版本(Lucene4.0之前需要用到,4.0之后被取消) 90 91 92 93 static{ 94 95 try { 96 97 //初始化索引文件存放目录对象 98 99 // directory = 100 101 // FSDirectory.open(Paths.get((String)PropertyUtil.getParamFromConfig("lucene.index.directory"))); 102 103 directory = FSDirectory.open(Paths.get("E://index")); 104 105 // 虚拟机退出时关闭 106 107 Runtime.getRuntime().addShutdownHook(new Thread(){ 108 109 @Override 110 111 public void run() { 112 113 LOGGER.info("--------Lucene释放关闭资源中...."); 114 115 try{ 116 117 //释放关闭资源 118 119 if(null!=indexWriter){ 120 121 indexWriter.close(); 122 123 } 124 125 if(null!=indexReader){ 126 127 indexReader.close(); 128 129 } 130 131 if(null!=directory){ 132 133 directory.close(); 134 135 } 136 137 if(null!=analyzer){ 138 139 analyzer.close(); 140 141 } 142 143 } catch (IOException e) { 144 145 e.printStackTrace(); 146 147 } 148 149 LOGGER.info("--------Lucene释放关闭资源成功...."); 150 151 } 152 153 }); 154 155 156 157 } catch (Exception e) { 158 159 e.printStackTrace(); 160 161 } 162 163 } 164 165 166 167 /** 168 169 * 170 171 * @return 返回用于操作索引的对象 172 173 * @throws IOException 174 175 */ 176 177 public static IndexWriter getIndexWriter() throws IOException{ 178 179 if(null==indexWriter){ 180 181 // 初始化IK分词器 182 183 Analyzer analyzer = getAnalyzer(); 184 185 // 初始化索引的写配置对象 186 187 indexWriterConfig = new IndexWriterConfig(analyzer); 188 189 // 初始化索引的写对象 190 191 indexWriter=new IndexWriter(directory, indexWriterConfig); 192 193 } 194 195 return indexWriter; 196 197 } 198 199 200 201 /** 202 203 * 204 205 * @return 返回用于操作索引的对象 206 207 * @throws IOException 208 209 */ 210 211 public static IndexReader getIndexReader() throws IOException{ 212 213 indexReader = DirectoryReader.open(directory); 214 215 return indexReader; 216 217 } 218 219 220 221 /** 222 223 * 224 225 * @return 返回用于读取索引的对象 226 227 * @throws IOException 228 229 */ 230 231 public static IndexSearcher getIndexSearcher() throws IOException{ 232 233 indexReader = DirectoryReader.open(directory); 234 235 indexSearcher = new IndexSearcher(indexReader); 236 237 return indexSearcher; 238 239 } 240 241 242 243 /** 244 245 * 246 247 * @return 返回用于读取索引的对象 248 249 * @throws IOException 250 251 */ 252 253 public static IndexSearcher getIndexSearcher(Directory directory) throws IOException{ 254 255 indexReader = DirectoryReader.open(directory); 256 257 indexSearcher = new IndexSearcher(indexReader); 258 259 return indexSearcher; 260 261 } 262 263 264 265 /** 266 267 * 268 269 * @return 返回版本信息 270 271 */ 272 273 // public static Version getMatchVersion() { 274 275 // return matchVersion; 276 277 // } 278 279 280 281 /** 282 283 * 284 285 * @return 返回分词器 286 287 */ 288 289 public static Analyzer getAnalyzer() { 290 291 // Lucene4以前的版本需要用到版本配置 292 293 // matchVersion = Version.LUCENE_44; 294 295 // 分词器 296 297 // analyzer = new StandardAnalyzer(); // 标准分词 298 299 if(analyzer == null) { 300 301 System.out.println("创建新的分析器"); 302 303 analyzer = new IKAnalyzer(); 304 305 } 306 307 return analyzer; 308 309 } 310 311 312 313 /** 314 315 * 打印一个文档的所有字段的内容 316 317 * @param 318 319 */ 320 321 public static void printDocument(Document document){ 322 323 //打印具体字段 324 325 List<IndexableField> fieldList = document.getFields(); 326 327 //遍历列表 328 329 for (IndexableField field : fieldList){ 330 331 //打印出所有的字段的名字和值(必须存储了的) 332 333 LOGGER.info(field.name()+":"+field.stringValue()); 334 335 } 336 337 //文档详情 338 339 LOGGER.info(document.toString()); 340 341 } 342 343 344 345 /** 346 347 * 打印ScoreDoc 348 349 * @param scoreDoc 350 351 * @throws IOException 352 353 */ 354 355 public static void printScoreDoc(ScoreDoc scoreDoc) throws IOException{ 356 357 //获取文档的编号(类似索引主键) 358 359 int docId = scoreDoc.doc; 360 361 LOGGER.info("======文档编号:"+docId); 362 363 // 取出文档得分 364 365 LOGGER.info("得分: " + scoreDoc.score); 366 367 //获取具体文档 368 369 Document document = indexSearcher.doc(docId); 370 371 //打印具体字段 372 373 printDocument(document); 374 375 } 376 377 378 379 /** 380 381 * 打印命中的文档(带得分)的详情 382 383 * @param topDocs 384 385 */ 386 387 public static void printTopDocs(TopDocs topDocs) throws IOException { 388 389 // 1)打印总记录数(命中数):类似于百度为您找到相关结果约100,000,000个 390 391 long totalHits = topDocs.totalHits.value; 392 393 LOGGER.info("查询(命中)总的文档条数:"+totalHits); 394 395 // LOGGER.info("查询(命中)文档最大分数:"+topDocs.getMaxScore()); 396 397 //2)获取指定的最大条数的、命中的查询结果的文档对象集合 398 399 ScoreDoc[] scoreDocs = topDocs.scoreDocs; 400 401 //打印具体文档 402 403 for (ScoreDoc scoreDoc : scoreDocs) { 404 405 printScoreDoc(scoreDoc); 406 407 } 408 409 } 410 411 412 413 public static void printTopDocsByQueryForHighlighter(Query query, int n) throws Exception{ 414 415 416 417 //=========1.创建一个高亮工具对象 418 419 // 格式化器:参数1:前置标签,参数2:后置标签 420 421 Formatter formatter = new SimpleHTMLFormatter("<em>", "</em>"); 422 423 //打分对象,参数:query里面的条件,条件里面有搜索关键词 424 425 Scorer fragmentScorer = new QueryScorer(query); 426 427 //高亮工具 428 429 //参数1.需要高亮什么颜色, 参数2.将哪些关键词进行高亮 430 431 Highlighter highlighter = new Highlighter(formatter, fragmentScorer); 432 433 //=======搜索相关 434 435 IndexSearcher indexSearcher = getIndexSearcher(); 436 437 // 搜索数据,两个参数:查询条件对象要查询的最大结果条数 438 439 // 返回的结果是 按照匹配度排名得分前N名的文档信息(包含查询到的总条数信息、所有符合条件的文档的编号信息) 440 441 TopDocs topDocs = indexSearcher.search(query, n); 442 443 // 打印命中的总条数 444 445 // LOGGER.info("本次搜索共" + topDocs.totalHits + "条数据,最高分:"+topDocs.getMaxScore()); 446 447 448 449 // 获取得分文档对象(ScoreDoc)数组.SocreDoc中包含:文档的编号、文档的得分 450 451 ScoreDoc[] scoreDocs = topDocs.scoreDocs; 452 453 454 455 //循环 456 457 for (ScoreDoc scoreDoc : scoreDocs) { 458 459 // 取出文档编号 460 461 int docID = scoreDoc.doc; 462 463 System.out.println("=========文档的编号是:"+docID); 464 465 // 取出文档得分 466 467 System.out.println("当前文档得分: " + scoreDoc.score); 468 469 // 根据编号去找文档 470 471 Document document = indexSearcher.doc(docID); 472 473 //获取文档的所有字段对象 474 475 List<IndexableField> fieldList= document.getFields(); 476 477 //遍历列表 478 479 for (IndexableField field : fieldList) { 480 481 String highlighterValue = highlighter.getBestFragment(getAnalyzer(), field.name(), field.stringValue()); 482 483 //如果没有得到高亮的值 484 485 if (null==highlighterValue) { 486 487 //则让高亮结果等不高亮的值 488 489 highlighterValue = field.stringValue(); 490 491 } 492 493 //打印出所有的字段的名字和值(必须存储了的) 494 495 LOGGER.info(field.name()+":"+highlighterValue); 496 497 } 498 499 500 501 } 502 503 } 504 505 506 507 }