参考文档
一:什么是全文检索
数据分类
结构化数据:有固定的格式和有限的长度,比如Oracle和mysql数据库中的数据,可以利用sql语句查询,如果查询的数据量大时,可以在数据库中创建索引,但是此时不支持模糊查询
非结构化数据:没有固定的的格式和长度,比如磁盘上的文件如txt,pdf等,**)**顺序扫描法(Serial Scanning),全文检索(Full-text Search)
对数据源创建索引,在索引库中搜索
二:如何实现全文检索
使用Lucene
三:什么是Lucene
Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包
四:Lucene实现流程
获得文档对象:
应用场景:站内搜索,通过IO流
构建文档对象:
获取原始内容的目的是为了索引,在索引前需要将原始内容创建成文档(Document),文档中包括一个一个的域(Field),域中存储内容。
这里我们可以将磁盘上的一个文件当成一个document,Document中包括一些Field(file_name文件名称、file_path文件路径、file_size文件大小、file_content文件内容),如下图:
注意: (1)每个Document可以有多个Field
(2)不同的Document可以有不同的Field
(3)同一个Document可以有相同的Field(域名和域值都相同)
(4)每个文档都有一个唯一的编号,就是文档id。
分析文档:
将原始内容创建为包含域(Field)的文档(document),需要再对域中的内容进行分析,分析的过程是经过对原始文档提取单词、将字母转为小写、去除标点符号、去除停用词等过程生成最终的语汇单元,可以将语汇单元理解为一个一个的单词。
比如下边的文档经过分析如下:
原文档内容:
Lucene is a Java full-text search engine.
分析后得到的语汇单元:
lucene、java、full、search、engine
每个单词叫做一个Term,不同的域中拆分出来的相同的单词是不同的term。term中包含两部分一部分是文档的域名,另一部分是单词的内容。
例如:文件名中包含apache和文件内容中包含的apache是不同的term。
创建索引:

根据不同的term找到对应的Document
注意: (1)创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构。
(2)传统方法是根据文件找到该文件的内容,在文件内容中匹配搜索关键字,这种方法是顺序扫描方法,数据量大、搜索慢。
用户查询接口:
搜索框输入关键字
五:入门案例
导入相关jar包

IndexWriterTest.java
1 1 package com.it.lucene; 2 2 3 3 import java.io.File; 4 4 import java.io.IOException; 5 5 6 6 import org.apache.commons.io.FileUtils; 7 7 import org.apache.lucene.analysis.Analyzer; 8 8 import org.apache.lucene.analysis.standard.StandardAnalyzer; 9 9 import org.apache.lucene.document.Document; 1010 import org.apache.lucene.document.Field; 1111 import org.apache.lucene.document.Field.Store; 1212 import org.apache.lucene.document.TextField; 1313 import org.apache.lucene.index.IndexWriter; 1414 import org.apache.lucene.index.IndexWriterConfig; 1515 import org.apache.lucene.store.Directory; 1616 import org.apache.lucene.store.FSDirectory; 1717 1818 public class lucene_first { 1919 public static void main(String[] args) throws Exception { 2020 //1,指定索引库位置 2121 Directory directory =FSDirectory.open(new File("D:\\BaiduNetdiskDownload\\lucene\\indexDatebase").toPath()); 2222 //指定分词器 2323 Analyzer analyzer=new StandardAnalyzer(); 2424 IndexWriterConfig config=new IndexWriterConfig(analyzer); 2525 2626 //2,创建写入索引的对象 2727 IndexWriter indexWriter=new IndexWriter(directory, config); 2828 2929 //3获取原文档 3030 File scrFile=new File("D:\\BaiduNetdiskDownload\\lucene\\searchSource"); 3131 //遍历 3232 File[] listFiles = scrFile.listFiles(); 3333 for (File file : listFiles) { 3434 Document doc=new Document(); 3535 //将域写入到文档中 3636 //1),文件名称 3737 String name = file.getName(); 3838 Field fileName=new TextField("name",name, Store.YES); 3939 doc.add(fileName); 4040 //2),文件大小 4141 long size = FileUtils.sizeOf(file); 4242 Field fileSize=new TextField("size",size+"", Store.YES); 4343 doc.add(fileSize); 4444 //3),文件路径 4545 String path = file.getPath(); 4646 Field filePath=new TextField("path",path+"", Store.YES); 4747 doc.add(filePath); 4848 //4),文件内容 4949 String content = FileUtils.readFileToString(file); 5050 Field fileContent=new TextField("content",content, Store.YES); 5151 doc.add(fileContent); 5252 5353 //4,将文档写入索引库 5454 indexWriter.addDocument(doc); 5555 } 5656 //5关闭资源 5757 indexWriter.close(); 5858 } 5959 }
运行程序后,在索引库中可以查看到索引文件,通过luke可视化工具查看到
IndexReaderTest.java
1 1 package com.it.lucene; 2 2 3 3 import java.io.File; 4 4 import java.io.IOException; 5 5 6 6 import org.apache.commons.io.FileUtils; 7 7 import org.apache.lucene.analysis.Analyzer; 8 8 import org.apache.lucene.analysis.standard.StandardAnalyzer; 9 9 import org.apache.lucene.document.Document; 1010 import org.apache.lucene.document.Field; 1111 import org.apache.lucene.document.Field.Store; 1212 import org.apache.lucene.document.TextField; 1313 import org.apache.lucene.index.DirectoryReader; 1414 import org.apache.lucene.index.IndexReader; 1515 import org.apache.lucene.index.IndexWriter; 1616 import org.apache.lucene.index.IndexWriterConfig; 1717 import org.apache.lucene.index.Term; 1818 import org.apache.lucene.search.IndexSearcher; 1919 import org.apache.lucene.search.Query; 2020 import org.apache.lucene.search.ScoreDoc; 2121 import org.apache.lucene.search.TermQuery; 2222 import org.apache.lucene.search.TopDocs; 2323 import org.apache.lucene.store.Directory; 2424 import org.apache.lucene.store.FSDirectory; 2525 2626 public class IndexReaderTest { 2727 public static void main(String[] args) throws Exception { 2828 //1,指定索引库位置 2929 Directory directory =FSDirectory.open(new File("D:\\BaiduNetdiskDownload\\lucene\\indexDatebase").toPath()); 3030 //2,创建索引读取对象 3131 IndexReader indexReader=DirectoryReader.open(directory); 3232 //3,创建索引查询对象 3333 IndexSearcher indexSearcher=new IndexSearcher(indexReader); 3434 //4,查询条件 3535 Query query=new TermQuery(new Term("content","spring")); 3636 //5,返回查询结果 3737 TopDocs result = indexSearcher.search(query, 100);//100指最多返回100个Document 3838 System.out.println("总记录数:"+result.totalHits); 3939 ScoreDoc[] scoreDocs = result.scoreDocs; 4040 for (ScoreDoc scoreDoc : scoreDocs) { 4141 int docId = scoreDoc.doc; 4242 //获取文件 4343 Document doc = indexSearcher.doc(docId); 4444 System.out.println("文件名"+doc.get("name")); 4545 System.out.println("文件路径"+doc.get("path")); 4646 } 4747 //6,关闭资源 4848 indexReader.close(); 4949 } 5050 }
六:分词器(Aanlyzer)
每个分词器都有tokenStream()方法
中文一般使用第三方分词器IK-Aanlyzer(需要导入相应的包)
下载地址: https://pan.baidu.com/s/1BAujr36FozHuwt6JyVFpHQ 提取码: m3mt
**注意:**搜索使用的分析器要和索引使用的分析器一致,不然搜索出来结果可能会错乱。
七:Field域的属性概述
是否分析:即是否分词
是否索引:即是否添加到索引库中用来检索
是否存储:即是否用来展示出来
如下图:
Field类
数据类型
Analyzed
是否分析
Indexed
是否索引
Stored
是否存储
说明
StringField(FieldName, FieldValue,Store.YES))
字符串
N
Y
Y或N
这个Field用来构建一个字符串Field,但是不会进行分析,会将整个串存储在索引中,比如(订单号,姓名等)
是否存储在文档中用Store.YES或Store.NO决定
LongField(FieldName, FieldValue,Store.YES)
Long型
Y
Y
Y或N
这个Field用来构建一个Long数字型Field,进行分析和索引,比如(价格)
是否存储在文档中用Store.YES或Store.NO决定
StoredField(FieldName, FieldValue)
重载方法,支持多种类型
N
N
Y
这个Field用来构建不同类型Field
不分析,不索引,但要Field存储在文档中
TextField(FieldName, FieldValue, Store.NO)
或
TextField(FieldName, reader)
字符串
或
流
Y
Y
Y或N
如果是一个Reader, lucene猜测内容比较多,会采用Unstored的策略.
八:索引查询
1,MatchAllDocsQuery(查询索引库中的全部Document)
2,TermQuery(精准查询)
3,NumericRangeQuery(根据数值范围查询)
示例代码:
1 1 //数值范围查询 2 2 @Test 3 3 public void testNumericRangeQuery() throws Exception { 4 4 //创建一个Directory对象,指定索引库存放的路径 5 5 Directory directory = FSDirectory.open(new File("E:\\programme\\test")); 6 6 //创建IndexReader对象,需要指定Directory对象 7 7 IndexReader indexReader = DirectoryReader.open(directory); 8 8 //创建Indexsearcher对象,需要指定IndexReader对象 9 9 IndexSearcher indexSearcher = new IndexSearcher(indexReader); 1010 1111 //创建查询 1212 //参数: 1313 //1.域名 1414 //2.最小值 1515 //3.最大值 1616 //4.是否包含最小值 1717 //5.是否包含最大值 1818 Query query = NumericRangeQuery.newLongRange("fileSize", 41L, 2055L, true, true); 1919 //执行查询 2020 2121 //第一个参数是查询对象,第二个参数是查询结果返回的最大值 2222 TopDocs topDocs = indexSearcher.search(query, 10); 2323 2424 //查询结果的总条数 2525 System.out.println("查询结果的总条数:"+ topDocs.totalHits); 2626 //遍历查询结果 2727 //topDocs.scoreDocs存储了document对象的id 2828 //ScoreDoc[] scoreDocs = topDocs.scoreDocs; 2929 for (ScoreDoc scoreDoc : topDocs.scoreDocs) { 3030 //scoreDoc.doc属性就是document对象的id 3131 //int doc = scoreDoc.doc; 3232 //根据document的id找到document对象 3333 Document document = indexSearcher.doc(scoreDoc.doc); 3434 //文件名称 3535 System.out.println(document.get("fileName")); 3636 //文件内容 3737 System.out.println(document.get("fileContent")); 3838 //文件大小 3939 System.out.println(document.get("fileSize")); 4040 //文件路径 4141 System.out.println(document.get("filePath")); 4242 System.out.println("----------------------------------"); 4343 } 4444 //关闭indexreader对象 4545 indexReader.close(); 4646 }
4,BooleanQuery(组合条件查询)
示例代码:
1 1 //组合条件查询 2 2 2 @Test 3 3 3 public void testBooleanQuery() throws Exception { 4 4 4 //创建一个Directory对象,指定索引库存放的路径 5 5 5 Directory directory = FSDirectory.open(new File("E:\\programme\\test")); 6 6 6 //创建IndexReader对象,需要指定Directory对象 7 7 7 IndexReader indexReader = DirectoryReader.open(directory); 8 8 8 //创建Indexsearcher对象,需要指定IndexReader对象 9 9 9 IndexSearcher indexSearcher = new IndexSearcher(indexReader); 1010 10 1111 11 //创建一个布尔查询对象 1212 12 BooleanQuery query = new BooleanQuery(); 1313 13 //创建第一个查询条件 1414 14 Query query1 = new TermQuery(new Term("fileName", "apache")); 1515 15 Query query2 = new TermQuery(new Term("fileName", "lucene")); 1616 16 //组合查询条件 1717 17 /* 1818 18 Occur.MUST:必须满足此条件,相当于and 1919 19 2020 20 Occur.SHOULD:应该满足,但是不满足也可以,相当于or 2121 21 2222 22 Occur.MUST_NOT:必须不满足。相当于not*/ 2323 23 2424 17 query.add(query1, Occur.MUST); 2525 18 query.add(query2, Occur.MUST); 2626 19 //执行查询 2727 20 2828 21 //第一个参数是查询对象,第二个参数是查询结果返回的最大值 2929 22 TopDocs topDocs = indexSearcher.search(query, 10); 3030 23 3131 24 //查询结果的总条数 3232 25 System.out.println("查询结果的总条数:"+ topDocs.totalHits); 3333 26 //遍历查询结果 3434 27 //topDocs.scoreDocs存储了document对象的id 3535 28 //ScoreDoc[] scoreDocs = topDocs.scoreDocs; 3636 29 for (ScoreDoc scoreDoc : topDocs.scoreDocs) { 3737 30 //scoreDoc.doc属性就是document对象的id 3838 31 //int doc = scoreDoc.doc; 3939 32 //根据document的id找到document对象 4040 33 Document document = indexSearcher.doc(scoreDoc.doc); 4141 34 //文件名称 4242 35 System.out.println(document.get("fileName")); 4343 36 //文件内容 4444 37 System.out.println(document.get("fileContent")); 4545 38 //文件大小 4646 39 System.out.println(document.get("fileSize")); 4747 40 //文件路径 4848 41 System.out.println(document.get("filePath")); 4949 42 System.out.println("----------------------------------"); 5050 43 } 5151 44 //关闭indexreader对象 5252 45 indexReader.close(); 5353 46 }
5,queryparser(更具查询语法查询)
查询语法
1、基础的查询语法,关键词查询:
域名+“:”+搜索的关键字
例如:content:java
2、范围查询
域名+“:”+[最小值 TO 最大值]
例如:size:[1 TO 1000]
范围查询在lucene中支持数值类型,不支持字符串类型。在solr中支持字符串类型。
3、组合条件查询
1)+条件1 +条件2:两个条件之间是并且的关系and
例如:+filename:apache +content:apache
2)+条件1 条件2:必须满足第一个条件,应该满足第二个条件
例如:+filename:apache content:apache
3)条件1 条件2:两个条件满足其一即可。
例如:filename:apache content:apache
4)-条件1 条件2:必须不满足条件1,要满足条件2
例如:-filename:apache content:apache
示例代码:
1 1 @Test 2 2 public void testQueryParser() throws Exception { 3 3 //创建一个Directory对象,指定索引库存放的路径 4 4 Directory directory = FSDirectory.open(new File("E:\\programme\\test")); 5 5 //创建IndexReader对象,需要指定Directory对象 6 6 IndexReader indexReader = DirectoryReader.open(directory); 7 7 //创建Indexsearcher对象,需要指定IndexReader对象 8 8 IndexSearcher indexSearcher = new IndexSearcher(indexReader); 9 9 1010 //创建queryparser对象 1111 //第一个参数默认搜索的域 1212 //第二个参数就是分析器对象 1313 QueryParser queryParser = new QueryParser("fileName", new IKAnalyzer()); 1414 //使用默认的域,这里用的是语法,下面会详细讲解一下 1515 Query query = queryParser.parse("apache"); 1616 //不使用默认的域,可以自己指定域 1717 //Query query = queryParser.parse("fileContent:apache"); 1818 //执行查询 1919 2020 2121 //第一个参数是查询对象,第二个参数是查询结果返回的最大值 2222 TopDocs topDocs = indexSearcher.search(query, 10); 2323 2424 //查询结果的总条数 2525 System.out.println("查询结果的总条数:"+ topDocs.totalHits); 2626 //遍历查询结果 2727 //topDocs.scoreDocs存储了document对象的id 2828 //ScoreDoc[] scoreDocs = topDocs.scoreDocs; 2929 for (ScoreDoc scoreDoc : topDocs.scoreDocs) { 3030 //scoreDoc.doc属性就是document对象的id 3131 //int doc = scoreDoc.doc; 3232 //根据document的id找到document对象 3333 Document document = indexSearcher.doc(scoreDoc.doc); 3434 //文件名称 3535 System.out.println(document.get("fileName")); 3636 //文件内容 3737 System.out.println(document.get("fileContent")); 3838 //文件大小 3939 System.out.println(document.get("fileSize")); 4040 //文件路径 4141 System.out.println(document.get("filePath")); 4242 System.out.println("----------------------------------"); 4343 } 4444 //关闭indexreader对象 4545 indexReader.close(); 4646 }
6,MultiFieldQueryParser(指定多个默认域)
示例代码:
1 1 @Test 2 2 public void testMultiFiledQueryParser() throws Exception { 3 3 //创建一个Directory对象,指定索引库存放的路径 4 4 Directory directory = FSDirectory.open(new File("E:\\programme\\test")); 5 5 //创建IndexReader对象,需要指定Directory对象 6 6 IndexReader indexReader = DirectoryReader.open(directory); 7 7 //创建Indexsearcher对象,需要指定IndexReader对象 8 8 IndexSearcher indexSearcher = new IndexSearcher(indexReader); 9 9 1010 //可以指定默认搜索的域是多个 1111 String[] fields = {"fileName", "fileContent"}; 1212 //创建一个MulitFiledQueryParser对象 1313 MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields, new IKAnalyzer()); 1414 Query query = queryParser.parse("apache"); 1515 System.out.println(query); 1616 //执行查询 1717 1818 1919 //第一个参数是查询对象,第二个参数是查询结果返回的最大值 2020 TopDocs topDocs = indexSearcher.search(query, 10); 2121 2222 //查询结果的总条数 2323 System.out.println("查询结果的总条数:"+ topDocs.totalHits); 2424 //遍历查询结果 2525 //topDocs.scoreDocs存储了document对象的id 2626 //ScoreDoc[] scoreDocs = topDocs.scoreDocs; 2727 for (ScoreDoc scoreDoc : topDocs.scoreDocs) { 2828 //scoreDoc.doc属性就是document对象的id 2929 //int doc = scoreDoc.doc; 3030 //根据document的id找到document对象 3131 Document document = indexSearcher.doc(scoreDoc.doc); 3232 //文件名称 3333 System.out.println(document.get("fileName")); 3434 //文件内容 3535 System.out.println(document.get("fileContent")); 3636 //文件大小 3737 System.out.println(document.get("fileSize")); 3838 //文件路径 3939 System.out.println(document.get("filePath")); 4040 System.out.println("----------------------------------"); 4141 } 4242 //关闭indexreader对象 4343 indexReader.close(); 4444 }
7:IndexSearcher.search()查询方法
方法
说明
indexSearcher.search(query, n)
根据Query搜索,返回评分最高的n条记录
indexSearcher.search(query, filter, n)
根据Query搜索,添加过滤策略,返回评分最高的n条记录
indexSearcher.search(query, n, sort)
根据Query搜索,添加排序策略,返回评分最高的n条记录
indexSearcher.search(booleanQuery, filter, n, sort)
根据Query搜索,添加过滤策略,添加排序策略,返回评分最高的n条记录
8:TopDocs(返回的查询结果)
TopDocs topDocs.totalHits 查询到的总 条数
TopDocs topDocs.scoreDocs 匹配度较高的Document 集合数组
九:索引库的修改
1,删除全部索引(不建议使用)
1 1 //删除全部索引 2 2 @Test 3 3 public void testDeleteAllIndex() throws Exception { 4 4 Directory directory = FSDirectory.open(new File("E:\\programme\\test")); 5 5 Analyzer analyzer = new IKAnalyzer(); 6 6 IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer); 7 7 IndexWriter indexWriter = new IndexWriter(directory, config); 8 8 //删除全部索引 9 9 indexWriter.deleteAll(); 1010 //关闭indexwriter 1111 indexWriter.close(); 1212 }
2,根据条件删除索引
1 1 //根据查询条件删除索引 2 2 @Test 3 3 public void deleteIndexByQuery() throws Exception { 4 4 Directory directory = FSDirectory.open(new File("E:\\programme\\test")); 5 5 Analyzer analyzer = new IKAnalyzer(); 6 6 IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer); 7 7 IndexWriter indexWriter = new IndexWriter(directory, config); 8 8 //创建一个查询条件 9 9 Query query = new TermQuery(new Term("fileContent", "apache")); 1010 //根据查询条件删除 1111 indexWriter.deleteDocuments(query); 1212 //关闭indexwriter 1313 indexWriter.close(); 1414 }
3,update索引
1 1 //修改索引库 2 2 @Test 3 3 public void updateIndex() throws Exception { 4 4 Directory directory = FSDirectory.open(new File("E:\\programme\\test")); 5 5 Analyzer analyzer = new IKAnalyzer(); 6 6 IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer); 7 7 IndexWriter indexWriter = new IndexWriter(directory, config); 8 8 //创建一个Document对象 9 9 Document document = new Document(); 1010 //向document对象中添加域。 1111 //不同的document可以有不同的域,同一个document可以有相同的域。 1212 document.add(new TextField("fileXXX", "要更新的文档", Store.YES)); 1313 document.add(new TextField("contentYYY", "简介 Lucene 是一个基于 Java 的全文信息检索工具包。", Store.YES)); 1414 indexWriter.updateDocument(new Term("fileName", "apache"), document); 1515 //关闭indexWriter 1616 indexWriter.close(); 1717 }
十:相关排序
对域进行打分设置,分数越高,排名越靠前(默认分数是1)
11 Field fileName=new TextField("name","这是该域的内容", Store.YES); 22 fileName.setBoost(10);//设置为10,将提高排名
十一:什么是Solr
Solr是基于Lucene开发的一个项目