搜索引擎之Lucene
1.搜索引擎
1.1什么是搜索引擎
搜索引擎指的是通过一定的策略, 从互联网中获取到数据, 将这些数据保存到自己的服务器当中, 然后提供用户一个页面, 用来做查询的, 这个就被称为搜索引擎 例如: 百度 或者 谷歌
1.2 原始数据库查询的弊端
- 当数据量很大的时候, 当用户输入一个内容的时候, 我们很难在很快的时间进行返回
- 原始的数据库中模糊查询, 只能进行首尾的匹配
- 当用户输入错误, 那么原始的数据库将会和用户的基本需求相差太远
1.3 倒排索引
倒排索引, 指定的将一句话,或者是一段话进行分词, 然后将分词的后的内容保存到索引库当中, 建立索引, 这样当用户输入一些内容的时候, 将用户输入的内容进行分词, 将分词后的数据到索引库中进行匹配, 如果有对应的数据将其对应的内容进行返回
2. lucene
lucene是Apache旗下的开源的全文检索的工具包, 使用lucene来构建一个搜索引擎
官方网址: http://lucene.apache.org/
2.1 lucene和solr的基本区别:
lucene: 是一个工具包
solr: 是基于lucene进行开发的一个企业级的搜索引擎
2.2 lucene的基本入门(索引库的创建)
2.2.1导包
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queries</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-test-framework</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
2.2.2添加索引的操作
public void indexWriterToLucene() throws IOException {
Directory directory = FSDirectory.open(new File("H:\\xx"));
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST,analyzer);
IndexWriter indexWriter = new IndexWriter(directory,indexWriterConfig);
Document document = new Document();
document.add(new LongField("id",1L, Field.Store.YES));
document.add(new TextField("content","welcome to beijing, how are you", Field.Store.YES));
document.add(new StringField("title","今天天气很好", Field.Store.NO));
indexWriter.addDocument(document);
indexWriter.commit();
indexWriter.close();
}
}
2.3 API详解
- IndexWriter: 索引写入器对象,其主要的作用, 添加索引, 修改索引和删除索引
- Directory: 目录类, 用来指定索引库的目录
- 常用的实现类:
- FSDirectory: 用来指定文件系统的目录, 将索引信息保存到磁盘上
- 优点: 索引可以进行长期保存, 安全系数高
- 缺点: 读取略慢
- RAMDriectory: 内存目录, 将索引库信息存放到内存中
- 优点: 读取速度快
- 缺点: 不安全, 无法长期保存, 关机后就消失了
- FSDirectory: 用来指定文件系统的目录, 将索引信息保存到磁盘上
- 常用的实现类:
- IndexWriterConfig: 索引写入器的配置类
- 创建此对象, 需要传递Lucene的版本和分词器
- 作用:
- 作用1 : 指定Lucene的版本和需要使用的分词器
- 作用2: 设置Lucene的打开索引库的方式: setOpenMode();
/**
* APPEND: 表示追加, 如果索引库存在, 就会向索引库中追加数据, 如果索引库不存在, 直接报错
* CREATE: 表示创建, 不管索引库有没有, 每一次都是重新创建一个新的索引库
* CREATE_OR_APPEND: 如果索引库有, 就会追加, 如果没有 就会创建索引库,默认值
*/
config.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
3.Document: 文档
在Lucene中, 每一条数据以文档的形式进行存储, 文档中也有其对应的属性和值, Lucene中一个文档类似数据库的一个表, 表中的字段类似于文档中的字段,只不过这个文档只能保存一条数据,Document看做是一个文件, 文件的属性就是文档的属性, 文件对应属性的值就是文档的属性的值 content.
一个文档中可以有多个字段, 每一个字段就是一个field对象,不同的文档可以有不同的属性,字段也有其对应数据类型, 故Field类也提供了各种数据类型的实现类
4 分词器
Analyzer: 分词器,用于对文档中的数据进行分词, 其分词的效果取决于分词器的选择, Lucene中根据各个国家制定了各种语言的分词器,对中文有一个ChineseAnalyzer 但是其分词的效果, 是将中文进行一个一个字的分开,针对中文分词一般只能使用第三方的分词词:一般采用IK分词器
4.1集成ik分词器
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
3.lucene的查询操作
3.1 基础查询
public void test() throws IOException, ParseException {
DirectoryReader reader = DirectoryReader.open(FSDirectory.open(new File("H:\\xx")));
IndexSearcher indexSearcher = new IndexSearcher(reader);
QueryParser queryParser = new QueryParser("content",new IKAnalyzer());
Query query = queryParser.parse("程序员");
TopDocs topDocs = indexSearcher.search(query, Integer.MAX_VALUE);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
int len = topDocs.totalHits;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;//获取文档的id
float score = scoreDoc.score;//当前这个文档的得分数
// 根据给定的id 查询对应的文档内容
Document document = indexSearcher.doc(docId);
String id = document.get("id");
String content = document.get("content");
}
}
3.2 lucene查询的api详解
- IndexSearcher: Lucene中查询对象, 用来执行查询和排序操作
- 常用方法:
- search(Query query, int n);//执行查询
- 参数1: 查询条件
- 参数2: 返回的最大条数
- search(Query query, int n,Sort sort);
- 参数1: 查询的条件
- 参数2: 返回的最大的条数
- 参数3: 排序
- doc(int id);//根据文档id查询文档对象
- search(Query query, int n);//执行查询
- 常用方法:
- IndexReader: 索引库读取工具
- 使用DirectoryReader来打开索引库
- Query:查询对象
- 获取方式:
- 通过查询解析器
- 单字段的解析器: queryParse
- 多字段的解析器: multiFieldQueryParse
- 使用Lucene自定义的实现类
- Lucene中提供了五种常用的多样化的查询
- 通过查询解析器
- 获取方式:
- TopDocs:查询结果对象
- 第一部分: 查询到的总条数
- int topDocs.totalHits
- 第二部分: 得分文档的数组
- ScoreDoc[] topDocs.scoreDocs;
- 第一部分: 查询到的总条数
- ScoreDoc: 得分文档对象
- 第一部分: 文档的id
- topDoc.doc
- 第二部分: 文档的得分
- topDoc.score
- 第一部分: 文档的id
3.3 抽取一个公共查询接口
public void queryToLucene(Query query) throws IOException {
IndexSearcher indexSearcher = new IndexSearcher(DirectoryReader.open(FSDirectory.open(new File("H:\\xx"))));
TopDocs topDocs = indexSearcher.search(query, Integer.MAX_VALUE);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
int len = topDocs.totalHits;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;//获取文档的id
float score = scoreDoc.score;//当前这个文档的得分数
Document document = indexSearcher.doc(docId);
String id = document.get("id");
String content = document.get("content");
String title = document.get("title");
}
}
3.3 词条查询 TermQuery
public void test() throws IOException {
TermQuery termQuery = new TermQuery(new Term("content","天气"));
queryToLucene(termQuery);
}
3.4 通配符查询 WildcardQuery
// WildcardQuery: 通配符查询
// ?: 占位符, 表示占用一个字符,必须缺少一个
// *: 占位符, 表示占用多个字符(0到多个)
//如果只显示一个*, 表示查询全部数据
public void wildcardQueryToLucene() throws IOException {
WildcardQuery wildcardQuery = new WildcardQuery(new Term("content","*"));
queryToLucene(wildcardQuery);
}
3.5 模糊查询 FuzzyQuery
//模糊查询:fuzzQuery
// 模糊查询指定的用户的输入的内容进行 替换, 补位, 移动
public void fuzzQueryToLucene() throws IOException {
FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term("content","气天"),2);
queryToLucene(fuzzyQuery);
}
3.6 组合查询 BooleanQuery
//组合查询
//BooleanQuery:本身没有任何的条件
//MUST: 此条件必须包含
//MUST_NOT: 不能含有此条件里面的内容
//SHOULD: 指的可选的, 如果有就返回, 如果没有就不管, 查询和其他条件的并集
public void booleanQueryToLucene() throws IOException {
BooleanQuery booleanQuery = new BooleanQuery();
FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term("content","lucene"),2);
booleanQuery.add(fuzzyQuery, BooleanClause.Occur.MUST);
NumericRangeQuery numericRangeQuery = NumericRangeQuery.newLongRange("id",10L,20L,false,true);
booleanQuery.add(numericRangeQuery, BooleanClause.Occur.MUST);
queryToLucene(booleanQuery);
}
3.7 lucene中删除修改
public void delIndexToLucene() throws IOException, ParseException {
IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File("H:\\test")),new IndexWriterConfig(Version.LATEST,new IKAnalyzer()));
// indexWriter.deleteAll();//全部删除
// indexWriter.deleteDocuments(new Term("",""));//根据对应的词条进行删除
QueryParser queryParser = new QueryParser("content",new IKAnalyzer());
Query query = queryParser.parse("天气");
indexWriter.deleteDocuments(query);
indexWriter.commit();
indexWriter.close();
}
3.8 Lucene的分页
// lucene本质上不支持分页
public void limitToLucene() throws IOException {
int page = 2 ; //当前页
int pageSize = 5; //每页显示的条数
IndexSearcher indexSearcher = new IndexSearcher(DirectoryReader.open(FSDirectory.open(new File("H:\\xx"))));
TermQuery termQuery = new TermQuery(new Term("content","xx"));
TopDocs topDocs = indexSearcher.search(termQuery, page*pageSize);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;//返回当前文档集合
int len = topDocs.totalHits;// 返回的数量
//起始的条数 : limit ? ,?
for (int i = (page-1)*pageSize; i<page*pageSize ; i++) {
int docId = scoreDocs[i].doc;
float score = scoreDocs[i].score;
Document document = indexSearcher.doc(docId);
String id = document.get("id");
String content = document.get("content");
String title = document.get("title");
}
}