Lucene索引库维护、搜索、中文分词器
删除索引(文档)
需求
某些图书不再出版销售了,我们需要从索引库中移除该图书。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @Test 2 public void deleteIndex() throws Exception { 3 // 1、指定索引库目录 4 Directory directory = FSDirectory.open(new File("F:\\lucene\\0719")); 5 // 2、创建IndexWriterConfig 6 IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, 7 new StandardAnalyzer()); 8 // 3、 创建IndexWriter 9 IndexWriter writer = new IndexWriter(directory, cfg); 10 // 4、通过IndexWriter来删除索引 11 // 删除指定索引 12 writer.deleteDocuments(new Term("name", "apache")); 13 // 5、关闭IndexWriter 14 writer.close(); 15 16 System.out.println("删除成功"); 17 18 }
清空索引库
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @Test 2 public void deleteIndex() throws Exception { 3 // 1、指定索引库目录 4 Directory directory = FSDirectory.open(new File("F:\\lucene\\0719")); 5 // 2、创建IndexWriterConfig 6 IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, 7 new StandardAnalyzer()); 8 // 3、 创建IndexWriter 9 IndexWriter writer = new IndexWriter(directory, cfg); 10 // 4、通过IndexWriter来删除索引 11 // 删除指定索引 12 writer.deleteAll(); 13 // 5、关闭IndexWriter 14 writer.close(); 15 16 System.out.println("清空索引库成功"); 17 18 }
更新索引(文档)
Lucene更新索引比较特殊,是先删除满足条件的索引,再添加新的索引。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @Test 2 public void updateIndex() throws Exception { 3 // 1、指定索引库目录 4 Directory directory = FSDirectory.open(new File("F:\\lucene\\0719")); 5 // 2、创建IndexWriterConfig 6 IndexWriterConfig cfg = new IndexWriterConfig(Version.LATEST, 7 new StandardAnalyzer()); 8 // 3、 创建IndexWriter 9 IndexWriter writer = new IndexWriter(directory, cfg); 10 // 4、通过IndexWriter来修改索引 11 // a)、创建修改后的文档对象 12 Document document = new Document(); 13 14 // 文件名称 15 Field filenameField = new StringField("name", "updateIndex", Store.YES); 16 document.add(filenameField); 17 18 // 修改指定索引为新的索引 19 writer.updateDocument(new Term("name", "apache"), document); 20 21 // 5、关闭IndexWriter 22 writer.close(); 23 24 System.out.println("更新成功"); 25 }
已经知道Lucene是通过IndexSearcher对象,来执行搜索的。那我们为什么还要继续学习Lucene呢?
答:因为在实际的开发中,我们的查询的业务是相对复杂的,比如我们在通过关键词查找的时候,往往进行价格、商品类别的过滤。
而Lucene提供了一套查询方案,供我们实现复杂的查询。
-------------------------------------------------------------------------------------------------------------------------------
创建查询的两种方法
执行查询之前,必须创建一个查询Query查询对象。
Query自身是一个抽象类,不能实例化,必须通过其它的方式来实现初始化。
在这里,Lucene提供了两种初始化Query查询对象的方式。
使用Lucene提供Query子类
Query是一个抽象类,lucene提供了很多查询对象,比如TermQuery项精确查询,NumericRangeQuery数字范围查询等。
使用TermQuery实例化
Query query = new TermQuery(new Term("name", "lucene"));
使用QueryParse解析查询表达式
QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
Query query = queryParser.parse("name:lucene");
常用的Query子类搜索
TermQuery
特点:查询的关键词不会再做分词处理,作为整体来搜索。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Query子类查询之 TermQuery 3 * 4 * 特点:不会再对查询的关键词做分词处理。 5 * 6 * 需要:查询书名与java教程相关书。 7 */ 8 @Test 9 public void queryByTermQuery(){ 10 //1、获取一个查询对象 11 Query query = new TermQuery(new Term("name", "编程思想")); 12 doSearch(query); 13 14 } 15 private void doSearch(Query query) { 16 try { 17 18 19 //2、创建一个查询的执行对象 20 //指定索引库的目录 21 Directory d = FSDirectory.open(new File("F:\\lucene\\0719")); 22 //创建流对象 23 IndexReader reader = DirectoryReader.open(d); 24 //创建搜索执行对象 25 IndexSearcher searcher = new IndexSearcher(reader); 26 27 //3、执行搜索 28 TopDocs result = searcher.search(query, 10); 29 30 //4、提出结果集,获取图书的信息 31 int totalHits = result.totalHits; 32 System.out.println("共查询到"+totalHits+"条满足条件的数据!"); 33 System.out.println("-----------------------------------------"); 34 //提取图书信息。 35 //score即相关度。即搜索的关键词和 图书名称的相关度,用来做排序处理 36 ScoreDoc[] scoreDocs = result.scoreDocs; 37 38 for (ScoreDoc scoreDoc : scoreDocs) { 39 /** 40 * scoreDoc.doc的返回值,是文档的id, 即 将文档写入索引库的时候,lucene自动给这份文档做的一个编号。 41 * 42 * 获取到这个文档id之后,即可以根据这个id,找到这份文档。 43 */ 44 int docId = scoreDoc.doc; 45 System.out.println("文档在索引库中的编号:"+docId); 46 47 //从文档中提取图书的信息 48 Document doc = searcher.doc(docId); 49 System.out.println("图书id:"+doc.get("id")); 50 System.out.println("图书name:"+doc.get("name")); 51 System.out.println("图书price:"+doc.get("price")); 52 System.out.println("图书pic:"+doc.get("pic")); 53 System.out.println("图书description:"+doc.get("description")); 54 System.out.println(); 55 System.out.println("------------------------------------"); 56 57 } 58 59 //关闭连接,释放资源 60 if(null!=reader){ 61 reader.close(); 62 } 63 } catch (Exception e) { 64 e.printStackTrace(); 65 } 66 }
NumericRangeQuery
指定数字范围查询.(创建field类型时,注意与之对应)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Query子类查询 之 NumricRangeQuery 3 * 需求:查询所有价格在[60,80)之间的书 4 * @param query 5 */ 6 @Test 7 public void queryByNumricRangeQuery(){ 8 /** 9 * 第一个参数:要搜索的域 10 * 第二个参数:最小值 11 * 第三个参数:最大值 12 * 第四个参数:是否包含最小值 13 * 第五个参数:是否包含最大值 14 */ 15 Query query = NumericRangeQuery.newFloatRange("price", 60.0f, 80.0f, true, false); 16 17 doSearch(query); 18 }
BooleanQuery
BooleanQuery,布尔查询,实现组合条件查询。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Query子类查询 之 BooelanQuery查询 组合条件查询 3 * 4 * 需求:查询书名包含java,并且价格区间在[60,80)之间的书。 5 */ 6 @Test 7 public void queryBooleanQuery(){ 8 //1、要使用BooelanQuery查询,首先要把单个创建出来,然后再通过BooelanQuery组合 9 Query price = NumericRangeQuery.newFloatRange("price", 60.0f, 80.0f, true, false); 10 Query name = new TermQuery(new Term("name", "java")); 11 12 //2、创建BooleanQuery实例对象 13 BooleanQuery query = new BooleanQuery(); 14 query.add(name, Occur.MUST_NOT); 15 query.add(price, Occur.MUST); 16 /** 17 * MSUT 表示必须满足 对应的是 + 18 * MSUT_NOT 必须不满足 应对的是 - 19 * SHOULD 可以满足也可以不满足 没有符号 20 * 21 * SHOULD 与MUST、MUST_NOT组合的时候,SHOULD就没有意义了。 22 */ 23 24 doSearch(query); 25 }
通过QueryParser搜索
特点
对搜索的关键词,做分词处理。
语法
基础语法
域名:关键字 实例:name:java |
组合条件语法
条件1 AND 条件2 条件1 OR 条件2 条件1 NOT 条件2 |
QueryParser
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * 查询解析器查询 之 QueryParser查询 3 */ 4 @Test 5 public void queryByQueryParser(){ 6 try { 7 8 //1、加载分词器 9 Analyzer analyzer = new StandardAnalyzer(); 10 11 /** 12 * 2、创建查询解析器实例对象 13 * 第一个参数:默认搜索的域。 14 * 如果在搜索的时候,没有特别指定搜索的域,则按照默认的域进行搜索 15 * 如何在搜索的时候指定搜索域呢? 16 * 答:格式 域名:关键词 即 name:java教程 17 * 18 * 第二个参数:分词器 ,对关键词做分词处理 19 */ 20 QueryParser parser = new QueryParser("description", analyzer); 21 22 Query query = parser.parse("name:java教程"); 23 24 doSearch(query); 25 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } 29 }
MultiFieldQueryParser
通过MulitFieldQueryParse对多个域查询。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * 查询解析器查询 之 MultiFieldQueryParser查询 3 * 4 * 特点:同时指定多个搜索域,并且对关键做分词处理 5 */ 6 @Test 7 public void queryByMultiFieldQueryParser(){ 8 try { 9 10 //1、定义多个搜索的 name、description 11 String[] fields = {"name","description"}; 12 //2、加载分词器 13 Analyzer analyzer = new StandardAnalyzer(); 14 15 //3、创建 MultiFieldQueryParser实例对象 16 MultiFieldQueryParser mParser = new MultiFieldQueryParser(fields, analyzer); 17 18 Query query = mParser.parse("lucene教程"); 19 20 doSearch(query); 21 } catch (Exception e) { 22 e.printStackTrace(); 23 } 24 }
中文分词器
什么是中文分词器
学过英文的都知道,英文是以单词为单位的,单词与单词之间以空格或者逗号句号隔开。
而中文的语义比较特殊,很难像英文那样,一个汉字一个汉字来划分。
所以需要一个能自动识别中文语义的分词器
使用中文分词器IKAnalyzer
添加jar包
修改分词器代码
1 / 创建中文分词器 2 Analyzer analyzer = new IKAnalyzer();
思考?
在一堆文件中,如何快速根据关键词找出对应的文件?
思路:(1)使用全文检索来解决问题
(2)数据源由数据库变成一堆文件。
(3)从一堆文件中,读出里面的内容,转成文档,创建索引库。
(4)创建索引库之后,再根据关键词搜索索引库,找出文件的名称。
问题:如何读文件的内容?
答:txt文本,直接使用IO即可。
doc|docx 使用POI读取内容。