全文搜索Lucene
什么是全文检索
-这种先建立索引,再对索引进行搜索的过程就叫全文检索(Full-text Search) 。
什么是Lucene
-是Apache开源的一个专门用来实现全文检索的一个套类库,就是一些jar包
全文检索的应用场景
1、搜索引擎 百度 谷歌 360 搜狗
2、站内搜索 微博 京东 猫扑 天涯
3、垂直搜索 在优酷中搜索有可能数据是从芒果TV 搜狐视频 腾讯视频来的
数据的分类
1、结构化数据 :有固定格式和有限长度 举例:mysql数据库表中的的数据
查询方式:写sql
数据量特别大时:
使用SQL语句不可以:1、查询效率低
2、查询的关键字有可能需要分词 小米6X 128G
使用全文检索的方案
2、非结构化数据:没有固定格式和长度 举例:电脑上的word Excel txt
查询方式:目测
数据量比较多时:使用流
数据量特别大时:使用全文检索的方案
如何使用Lucene(理论)
创建索引的步骤
1、获取原始文档
2、构建文档对象 Document
3、文档分词
4、构建索引
查询索引的步骤
1、创建用户查询接口 创建一个能让用户输入关键字的位置
2、创建查询
3、执行查询
4、渲染查询结果 高亮显示结果
准备:依赖
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!-- Junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- lucene核心库 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.10.2</version>
</dependency>
<!-- Lucene的查询解析器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.10.2</version>
</dependency>
<!-- lucene的默认分词器库 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.10.2</version>
</dependency>
<!--ik分词器-->
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
<!-- lucene的高亮显示 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>4.10.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
创建索引
//创建索引
@Test
public void testCreat() throws Exception{
//指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\lucene\\1"));
//创建分词器 StandardAnalyzer()
IKAnalyzer analyzer = new IKAnalyzer();
//配置 版本(Version.LATEST 最新) 分词器
IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
//创建索引的写出工具类。参数:索引的目录和配置信息
IndexWriter indexWriter = new IndexWriter(directory,config);
// 修改索引。参数:
// * 词条:根据这个词条匹配到的所有文档都会被修改
// * 文档信息:要修改的新的文档数据
//indexWriter.updateDocument(new Term("fileName","1"),new Document().add(new TextField("id","1", Field.Store.YES)));更新
//indexWriter.deleteAll();//删除索引库
//indexWriter.deleteDocuments(new Term("fileName","spring"));根据词条进行删除, 词条的数据类型必须是字符串
//根据query对象删除,如果ID是数值类型,那么我们可以用数值范围查询锁定一个具体的ID
// NumericRangeQuery<Long> fileSize1 = NumericRangeQuery.newLongRange("fileSize", 100L, 500L, true, true);
// indexWriter.deleteDocuments(fileSize1);
/*读取文件 创建文档对象*/
//读取文件夹
File fileDirectoryPath = new File("D:\\lucene\\资料\\查询资料searchsource");
//获得文件夹的文件
File[] files = fileDirectoryPath.listFiles();
for (File file : files) {
//创建文档对象
Document document = new Document();
//得到文件名
String fileName = file.getName();
// 创建并添加字段信息。参数:字段的名称、字段的值、是否存储,这里选Store.YES代表存储到文档列表。Store.NO代表不存储
document.add(new TextField("fileName",fileName, Field.Store.YES));//存文件名
//内容fileContent file转string
String fileContent = FileUtils.readFileToString(file, "utf-8");
document.add(new TextField("fileContent",fileContent, Field.Store.YES));
//文件大小 fileSize
long fileSize = FileUtils.sizeOf(file);//单位是byte字节
document.add(new LongField("fileSize",fileSize, Field.Store.YES));
//文件路径 filePath StringField不分词
String filePath = file.getPath();
document.add(new StringField("filePath",filePath, Field.Store.YES));
// 把文档交给IndexWriter
indexWriter.addDocument(document);
}
indexWriter.close();
}
查询索引 以及分页 排序 高亮
//查询索引
@Test
public void testSearch() throws Exception{
//指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\lucene\\1"));
//创建用户读取索引的对象
IndexReader indexReader = DirectoryReader.open(directory);
// 索引搜索工具
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
/*使用term查询*/
//Query query = new TermQuery(new Term("fileName","简介"));
/*查询所有*/
//MatchAllDocsQuery query = new MatchAllDocsQuery();
/*通配符查询*/
//WildcardQuery query = new WildcardQuery(new Term("fileName", "*介*"));
/*组合查询*/
// BooleanQuery query = new BooleanQuery();
// query.add(new TermQuery(new Term("fileName","apache")), BooleanClause.Occur.MUST);//必须有的
// query.add(new TermQuery(new Term("fileContent","apache")), BooleanClause.Occur.SHOULD);//不必须有的
/*范围查询*/ //ture是闭区间的意思
// NumericRangeQuery<Long> query = NumericRangeQuery.newLongRange("fileSize", 100L, 500L, true, true);
/*分词查询*/
// QueryParser queryParser = new QueryParser("fileName", new IKAnalyzer());
// Query query = queryParser.parse("spring is a good project");
/*多域查询*/
MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(new String[]{"fileName", "fileContent"}, new IKAnalyzer());
Query query = multiFieldQueryParser.parse("spring is a good project");
query.setBoost(10);//竞价排序 打分 默认是1
TopDocs topDocs = indexSearcher.search(query, 100);//按照匹配度排名得分前N名的文档信息
//获得命中的文档对象
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;//获取文档id
Document document = indexSearcher.doc(doc);//根据id查出文档
System.out.println( "fileName:"+ document.get("fileName"));
System.out.println( "fileSize:"+ document.get("fileSize"));
System.out.println( "filePath:"+ document.get("filePath"));
System.out.println( "fileContent:"+ document.get("fileContent"));
System.out.println("------------------------------------------------------------------------");
}
indexReader.close();
}
//实现高亮
@Test
public void testHighlighter() throws Exception{
//指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\lucene\\1"));
//创建用户读取索引的对象
IndexReader indexReader = DirectoryReader.open(directory);
// 索引搜索工具
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
/*使用term查询*/
Query query = new TermQuery(new Term("fileName","简介"));
/*高亮*/
//格式化器
SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<em>", "</em>");
QueryScorer queryScorer = new QueryScorer(query);
//准备高亮工具
Highlighter highlighter = new Highlighter(formatter,queryScorer);
//搜索
TopDocs topDocs = indexSearcher.search(query, 100);//按照匹配度排名得分前N名的文档信息
//个数
System.out.println("本次共搜索到:"+topDocs.totalHits);
//获得命中的文档对象
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;//获取文档编号
Document document = indexSearcher.doc(doc);//根据id查出文档
// 用高亮工具处理普通的查询结果,参数:分词器,要高亮的字段的名称,高亮字段的原始值
String fileName = document.get("fileName");
highlighter.getBestFragment(new IKAnalyzer(),"fileName",fileName);
System.out.println( "fileName:"+fileName);
System.out.println( "fileSize:"+ document.get("fileSize"));
System.out.println( "filePath:"+ document.get("filePath"));
System.out.println( "fileContent:"+ document.get("fileContent"));
System.out.println("------------------------------------------------------------------------");
}
indexReader.close();
}
//实现排序
@Test
public void testSortQuery() throws Exception{
//指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\lucene\\1"));
//创建用户读取索引的对象
IndexReader indexReader = DirectoryReader.open(directory);
// 索引搜索工具
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
/*使用term查询*/
Query query = new TermQuery(new Term("fileName","spring"));
// 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转如果是false,升序。true降序
Sort sort = new Sort(new SortField("fileName", SortField.Type.STRING, false));
//搜索
TopDocs topDocs = indexSearcher.search(query, 100,sort);//按照匹配度排名得分前N名的文档信息
//个数
System.out.println("本次共搜索到:"+topDocs.totalHits);
//获得命中的文档对象
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int doc = scoreDoc.doc;//获取文档编号
Document document = indexSearcher.doc(doc);//根据id查出文档
String fileName = document.get("fileName");
System.out.println( "fileName:"+fileName);
System.out.println("------------------------------------------------------------------------");
}
indexReader.close();
}
//实现分页
@Test
public void testPageQuery() throws Exception{
// 实际上Lucene本身不支持分页。因此我们需要自己进行逻辑分页。我们要准备分页参数:
int pageSize = 2;// 每页条数
int pageNum = 3;// 当前页码
int start = (pageNum - 1) * pageSize;// 当前页的起始条数
int end = start + pageSize;// 当前页的结束条数(不能包含)
//指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\lucene\\1"));
//创建用户读取索引的对象
IndexReader indexReader = DirectoryReader.open(directory);
// 索引搜索工具
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
/*分词查询*/
QueryParser queryParser = new QueryParser("fileName", new IKAnalyzer());
Query query = queryParser.parse("spring is a good project");
// 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转如果是false,升序。true降序
Sort sort = new Sort(new SortField("fileName", SortField.Type.STRING, false));
//搜索,查询0~end条
TopDocs topDocs = indexSearcher.search(query, 100,sort);//按照匹配度排名得分前N名的文档信息
//个数
System.out.println("本次共搜索到:"+topDocs.totalHits);
//获得命中的文档对象
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (int i=start;i<end&&i<scoreDocs.length;i++) {
ScoreDoc doc = scoreDocs[i];//获取文档
int docID = doc.doc;//获得id
Document document = indexSearcher.doc(docID);//根据id查出文档
String fileName = document.get("fileName");
System.out.println( "fileName:"+fileName);
System.out.println("------------------------------------------------------------------------");
}
indexReader.close();
}
可以使用luke查看添加的索引。
以上代码看不到具体体现