Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。人们经常提到信息检索程序库,虽然与搜索引擎有关,但不应该将信息检索程序库与搜索引擎相混淆。
在使用Lucene时有一些注意事项:
1.由于Lucene目前已经更新至6.6.0版本,所以对于JDK版本会有一些要求,建议使用低版本JDK的同学相应下载低版本Lucene。
2.Lucene版本更新迅速,所以各版本之间部分代码实现会有差异,所以在实现相关功能时,注意版本问题。
通过对于原数据的采集,并创建索引(Lucene的核心),将索引存放至索引库中,用户进行查询时,直接通过索引查询,从而实现了Lucene搜索的快捷,这也是我们学习使用Lucene的原因。相比较传统方式搜索,通过索引,无需依次扫描大量文件,提升了搜索速度。
3.1环境准备:
JDK:1.7
lucene:4.10(最新为6.6.0)
MySql:提供原数据
注意:jUnit包为项目单元测试包,具体使用方法,大家可以百度Eclipse中如何使用jUnit,这里不做过多介绍。
5.1创建Book.java实体类:
public class Book {
// 图书ID
private Integer id;
// 图书名称
private String name;
// 图书价格
private Float price;
// 图书描述
private String description;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
5.2创建数据库使用对象BookDao.java:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import com.jsoft.vo.Book;
public class BookDao{
public List queryBookList() throws Exception {
// 数据库链接
Connection connection = null;
// 预编译statement
PreparedStatement preparedStatement = null;
// 结果集
ResultSet resultSet = null;
// 图书列表
List list = new ArrayList();
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 连接数据库
connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/book", "root", "root");
// SQL语句
String sql = "SELECT * FROM book";
// 创建preparedStatement
preparedStatement = connection.prepareStatement(sql);
// 获取结果集
resultSet = preparedStatement.executeQuery();
// 结果集解析
while (resultSet.next()) {
Book book = new Book();
book.setId(resultSet.getInt("id"));
book.setName(resultSet.getString("name"));
book.setPrice(resultSet.getFloat("price"));
book.setDescription(resultSet.getString("description"));
list.add(book);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
5.3创建索引管理类IndexManager,并进行索引创建:
public class IndexManager {
@Test
public void createIndex() throws Exception {
// 采集数据
BookDao dao = new BookDao();
List list = dao.queryBookList();
// 将采集到的数据封装到Document对象中
List docList = new ArrayList<>();
Document document;
for (Book book : list) {
document = new Document();
// store:如果是yes,则说明存储到文档域中
// 图书ID
//不分词、索引、存储
Field id = new StringField("id", book.getId().toString(), Store.YES);
// 图书名称
//分词、索引、存储
Field name = new TextField("name", book.getName(), Store.YES);
// 图书价格
//分词、索引、存储
Field price = new FloatField("price", book.getPrice(),Store.YES);
// 图书描述
//不分词、索引、不存储
Field description = new TextField("description",book.getDescription(), Store.YES);
// 将field域设置到Document对象中
document.add(id);
document.add(name);
document.add(price);
document.add(description);
docList.add(document);
}
// 创建分词器,标准分词器
Analyzer analyzer = new StandardAnalyzer();
// 创建IndexWriter
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3,
analyzer);
// 指定索引库的地址
File indexFile = new File("D:/LueneIndex/");
Directory directory = FSDirectory.open(indexFile);
IndexWriter writer = new IndexWriter(directory, cfg);
// 通过IndexWriter对象将Document写入到索引库中
for (Document doc : docList) {
writer.addDocument(doc);
}
// 关闭writer
writer.close();
}
}
5.4通过jUnit进行单元测试,测试结果如下:
5.5查看自定义索引库地址,发现有新创建文件:
通过上述步骤,Lucene架构索引即创建成功。
public class IndexSearch {
@Test
public void indexSearch() throws Exception {
// 创建query对象
// 使用QueryParser搜索时,需要指定分词器,搜索时的分词器要和索引时的分词器一致
// 第一个参数:默认搜索的域的名称
QueryParser parser = new QueryParser("description",new StandardAnalyzer());
// 通过queryparser来创建query对象
// 参数:输入的lucene的查询语句(关键字一定要大写)
Query query = parser.parse("description:java OR lucene");
// 创建IndexSearcher
// 指定索引库的地址
File indexFile = new File("D:/LueneIndex/");
Directory directory = FSDirectory.open(indexFile);
IndexReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
// 通过searcher来搜索索引库
// 第二个参数:指定需要显示的顶部记录的N条
TopDocs topDocs = searcher.search(query, 10);
// 根据查询条件匹配出的记录总数
int count = topDocs.totalHits;
System.out.println("匹配出的记录总数:" + count);
// 根据查询条件匹配出的记录
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
// 获取文档的ID
int docId = scoreDoc.doc;
// 通过ID获取文档
Document doc = searcher.doc(docId);
System.out.println("商品ID:" + doc.get("id"));
System.out.println("商品名称:" + doc.get("name"));
System.out.println("商品价格:" + doc.get("price"));
// System.out.println("商品描述:" + doc.get("description"));
System.out.println("==========================");
}
// 关闭资源
reader.close();
}
}
结果如下:
7.1.根据id删除索引:
@Test
public void deleteIndex() throws Exception {
// 创建分词器,标准分词器
Analyzer analyzer = new StandardAnalyzer();
// 创建IndexWriter
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3,analyzer);
Directory directory = FSDirectory.open(new File("D:/LueneIndex/"));
// 创建IndexWriter
IndexWriter writer = new IndexWriter(directory, cfg);
// Terms
writer.deleteDocuments(new Term("id", "1"));
// 删除全部
//writer.deleteAll();
writer.close();
}
7.2根据name更新索引:
@Test
public void updateIndex() throws Exception {
// 创建分词器,标准分词器
Analyzer analyzer = new StandardAnalyzer();
// 创建IndexWriter
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
Directory directory = FSDirectory.open(new File("D:/LueneIndex/"));
// 创建IndexWriter
IndexWriter writer = new IndexWriter(directory, cfg);
// 第一个参数:指定查询条件
// 第二个参数:修改之后的对象
// 修改时如果根据查询条件,可以查询出结果,则将以前的删掉,然后覆盖新的Document对象,如果没有查询出结果,则新增一个Document
// 修改流程即:先查询,再删除,在添加
Document doc = new Document();
doc.add(new TextField("name", "Python", Store.YES));
writer.updateDocument(new Term("name", "C"), doc);
writer.close();
}
8.1创建QuerySearch类
public class QuerySearch {
private void search(Query query) {
// 创建QuerySearcher
// 指定索引库的地址
try {
File indexFile = new File("D:/LueneIndex/");
Directory directory = FSDirectory.open(indexFile);
IndexReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
// 通过searcher搜索索引库
// 第二个参数:指定需要显示的顶部记录的N条
TopDocs topDocs = searcher.search(query, 10);
// 根据查询条件匹配出的记录总数
int count = topDocs.totalHits;
System.out.println("匹配出的记录总数:" + count);
// 根据查询条件匹配出的记录
//TopDocs.totalHits:是匹配索引库中所有记录的数量
//TopDocs.scoreDocs:匹配相关度高的前边记录数组
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
// 获取文档的ID
int docId = scoreDoc.doc;
Document doc = searcher.doc(docId);
System.out.println("商品ID:" + doc.get("id"));
System.out.println("商品名称:" + doc.get("name"));
System.out.println("商品价格:" + doc.get("price"));
// System.out.println("商品描述:" + doc.get("description"));
System.out.println("==========================");
}
// 关闭资源
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
8.2通过以下两种方式创建查询对象:
(1)通过Query子类来创建查询对象
Query子类常用的有:TermQuery、NumericRangeQuery、BooleanQuery
不能输入lucene的查询语法,不需要指定分词器
(2)通过QueryParser来创建查询对象(常用)
QueryParser、MultiFieldQueryParser
可以输入lucene的查询语法、可以指定分词器
8.2.1 TermQuery:
// 创建TermQuery对象
@Test
public void termQuery() {
Query query = new TermQuery(new Term("description", "java"));
search(query);
}
结果如下:
8.2.2 NumericRangeQuery:
@Test
public void numericRangeQuery() {
// 创建NumericRangeQuery对象
// 参数:域的名称、最小值、最大值、是否包含最小值、是否包含最大值
Query query = NumericRangeQuery.newFloatRange("price", 55f, 60f, true,false);
search(query);
}
结果如下:
8.2.3 BooleanQuery:
@Test
public void booleanQuery() {
// 创建BooleanQuery
BooleanQuery query = new BooleanQuery();
// 创建TermQuery对象
Query q1 = new TermQuery(new Term("description", "oracle"));
// 创建NumericRangeQuery对象
// 参数:域的名称、最小值、最大值、是否包含最小值、是否包含最大值
Query q2 = NumericRangeQuery.newFloatRange("price", 15f, 60f, true,false);
// 组合关系代表的意思如下:
// 1、MUST和MUST表示“与”的关系,即“交集”。
// 2、MUST和MUST_NOT前者包含后者不包含。
// 3、MUST_NOT和MUST_NOT没意义
// 4、SHOULD与MUST表示MUST,SHOULD失去意义;
// 5、SHOUlD与MUST_NOT相当于MUST与MUST_NOT。
// 6、SHOULD与SHOULD表示“或”的概念。
// Occur.MUST 查询条件必须满足,相当于and (+)
// Occur.SHOULD 查询条件可选,相当于or (空)
// Occur.MUST_NOT 查询条件不能满足,相当于not (-)
query.add(q1, Occur.MUST);
query.add(q2, Occur.MUST);
search(query);
}
结果如下:
8.2.4 MultiFieldQueryParser:
@Test
public void multiFieldQueryParser() throws Exception {
// 默认搜索的多个域
String[] fields = { "name", "description" };
Analyzer analyzer = new StandardAnalyzer();
MultiFieldQueryParser parser = new MultiFieldQueryParser(fields,analyzer);
Query query = parser.parse("paradigm");
//等同于
// Query query = parser.parse("name:paradigm OR description:paradigm");
search(query);
}
结果如下:
由于Lucene标准分词器不支持汉语分词,所以建议使用第三方分词器。
10.1 创建索引时设置boost值:
10.2 搜索时设置Boost值