1、倒排索引
举个例子:使用新华字典查找汉字,先找到汉字的偏旁部首,再根据偏旁部首对应的目录(索引)找到目标汉字。
样例:
-
文档0(编号0): we like java java java
-
文档1(编号1): we like lucene lucene lucene
(Term 词条) | (Doc 文档,Freq 频率) | (Pos 位置) |
---|---|---|
we | (0,1) (1,1) | (0)(0) |
like | (0,1) (1,1) | (1)(1) |
java | (0,3) | (2,3,4) |
lucene | (1,3) | (2,3,4) |
存储格式:词条/词语在哪个文档出现(文档编号)、出现的频率(次数)、出现的位置(下标)
2、Lucene介绍
2.1 全文检索是什么?
-
索引流程:通过索引程序切分数据的内容成一个个词语,建立词语与每一条数据的对应关系
-
检索流程:通过检索程序,根据搜索关键词,在索引库查找目标内容
2.2 Lucene是什么?
-
Lucene是apache软件基金会下的一个子项目。是一个成熟、免费、开放源代码的全文检索引擎工具包。提供了一套简单易用的API,方便在目标系统中实现全文检索功能。
-
Lucene能够为文本类型的数据建立索引,只需要把数据转换成文本格式即可,但是像word文档、html文档、pdf文档,就需要将文档内容转换成文本格式,才能使用lucene的API建立索引数据。
2.3 Lucene&搜索引擎区别
-
Lucene是一个全文检索引擎工具包,相当于汽车发动机,搜索引擎基于全文检索实现,是一个可以独立运行的软件产品,相当于汽车。
3、全文检索流程介绍
3.1 索引和检索流程图
3.2 索引流程详细介绍
-
采集数据
-
通过JDBC操作获取到关系数据库中的业务数据
-
通过IO流获取文件上的数据
-
通过爬虫(蜘蛛)程序获取网络上的网页数据
-
-
创建文档对象【重点】
-
说明:文档对象(Document),一个文档对象包含有多个域(Field)。一个文档对象就相当于关系数据库表中的一条记录,一个域就相当于一个字段。
-
-
分析文档对象
-
把原始数据,转换成文档对象后,使用分词器把文档中域的内容切分成一个个词语,为后续建立索引做准备。
-
-
建立倒排索引
-
建立词语和文档的对应关系,词语在什么文档出现,出现了几次,在什么位置出现(倒排索引),并且保存到索引库。
-
3.3 检索流程详细介绍
-
建立查询对象【重点】
-
根据用户输入的搜索关键词,使用分析器分词以后,建立查询对象(Query),Query对象会生成具体的查询语法。
-
-
执行搜索
-
根据查询对象(Query),和Query生成的语法,在索引库中查询目标内容。
-
-
返回查询结果
-
提供一个搜索结果页面,把搜索结果友好的展示给用户(搜索关键词是高亮显示,搜索结果有排序)
-
3.4 数据库&索引库结构对比
数据库 | 表 | 行 | 列 |
---|---|---|---|
Databases实例 | Tables | Rows | column |
索引库的存储目录 | 无 | Document文档 | Field域 |
4、Lucene环境搭建
4.1配置pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion><groupId>cn.it</groupId>
<artifactId>lucene-test</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version><properties>
<mysql.version>5.1.47</mysql.version>
<lucene.version>4.10.3</lucene.version>
<junit.version>4.12</junit.version>
</properties><dependencies>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 分词器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>${lucene.version}</version>
</dependency>
<!-- 查询解析器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>${lucene.version}</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.6</version>
<scope>provided</scope>
</dependency>
</dependencies><build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
4.2原始数据采集
图书实体类
package cn.it.pojo;
import lombok.Data;
/**
* 图书实体类
*
* @Author LK
* @Date 2020/12/11
*/
@Data
public class Book {
// 图书id
private int id;
// 图书名称
private String bookName;
// 图书价格
private float bookPrice;
// 图书图片
private String bookPic;
// 图书描述
private String bookDesc;
}
图书dao接口
package cn.it.dao;
import cn.it.pojo.Book;
import java.util.List;
/**
* 图书数据访问层接口
*
* @Author LK
* @Date 2020/12/11
*/
public interface BookDao {
/**
* 查询所有图书
* @return
*/
public List<Book> findAll();
}
dao实现类
package cn.it.dao.impl;
import cn.it.dao.BookDao;
import cn.it.pojo.Book;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 图书数据访问层实现类
*
* @Author LK
* @Date 2020/12/11
*/
public class BookDaoImpl implements BookDao {
@Override
public List<Book> findAll() {
List<Book> bookList = new ArrayList<>();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
// 1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/lucenedb", "root", "123456");
// 3.编写sql语句(实际项目中不要写*)
String searchSql = "select * from book";
// 4.获取statement对象
preparedStatement = connection.prepareStatement(searchSql);
// 5.执行搜索,返回ResultSet结果集
resultSet = preparedStatement.executeQuery();
// 6.处理结果集
while (resultSet.next()) {
Book book = new Book();
book.setId(resultSet.getInt("id"));
book.setBookName(resultSet.getString("bookname"));
book.setBookPrice(resultSet.getFloat("price"));
book.setBookPic(resultSet.getString("pic"));
book.setBookDesc(resultSet.getString("bookdesc"));
bookList.add(book);
}
} catch (Exception e) {
// 里面是使用全局的输出流,往控制台输出
// e.printStackTrace();
// 一般在项目中使用log打印,或往外层继续抛出异常
throw new RuntimeException(e);
} finally {
// 7.关闭资源
try {
if(connection!=null){
connection.close();;
}
if(preparedStatement!=null){
preparedStatement.close();;
}
if(resultSet!=null){
resultSet.close();;
}
} catch (Exception e) {
// 里面是使用全局的输出流,往控制台输出
// e.printStackTrace();
// 一般在项目中使用log打印,或往外层继续抛出异常
}
}
return bookList;
}
}
4.3索引流程实现
实现步骤
-
创建文档对象(Document)
-
创建分词器(Analyzer),用于分词
-
创建索引配置对象(IndexWriterConfig),指定索引库配置信息
-
创建索引目录对象(Directory),指定索引库的位置
-
创建索引写入对象(IndexWriter),把文档对象写入索引库
-
提交事务
-
释放资源
代码
package cn.it;
import cn.it.dao.BookDao;
import cn.it.dao.impl.BookDaoImpl;
import cn.it.pojo.Book;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.junit.Test;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* 索引相关单元测试类
*
* @Author LK
* @Date 2020/12/11
*/
public class IndexTest {
/**
* 索引流程
*
* @throws Exception
*/
@Test
public void createIndex() throws Exception {
BookDao bookDao = new BookDaoImpl();
List<Book> bookList = bookDao.findAll();
System.out.println("bookList = " + bookList);
// 1.创建文档对象(Document)
List<Document> documentList = new ArrayList<>();
for (Book book : bookList) {
Document document = new Document();
/**
* 给文档对象添加域
* 方法:add()
* 参数:TextField
* TextField参数:
* 参数一:域的名称
* 参数二:域的值
* 参数三:指定是否把域值存储到文档对象中,最终保存在磁盘上
*/
document.add(new TextField("id", book.getId() + "", Field.Store.YES));
document.add(new TextField("bookName", book.getBookName(), Field.Store.YES));
document.add(new TextField("bookPrice", book.getBookPrice() + "", Field.Store.YES));
document.add(new TextField("bookPic", book.getBookPic(), Field.Store.YES));
document.add(new TextField("bookDesc", book.getBookDesc(), Field.Store.YES));
documentList.add(document);
}
// 2 创建分词器(Analyzer),用于分词
Analyzer analyzer = new StandardAnalyzer();
// 3.创建索引配置对象(IndexWriterConfig),配置索引库
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
// 3.1 设置一个打开模式
// CREATE-每次都覆盖之前的索引库,创建新的索引库
// APPEND-追加,如果存在索引库,直接追加数据,否则报错
// CREATE_OR_APPEND-创建或追加,如果存在就追加,没有就创建
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
// 4.创建索引目录对象(Directory),指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\itcast\\ee145\\luceneIndex"));
// 5.创建索引写入对象(IndexWriter),把文档对象写入索引库
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
for (Document document : documentList) {
// 5.1 写入文档
indexWriter.addDocument(document);
// 6.提交事务
indexWriter.commit();
}
// 7.释放资源
indexWriter.close();
}
}
运行结果:
5、检索流程实现
实现步骤
-
创建分词器对象(Analyzer),用于对关键字分词
-
创建查询对象(Query),相当于sql语句
-
创建索引目录对象(Directory),指定索引库的位置
-
创建索引读取对象(IndexReader),把索引数据读取到内存中
-
创建索引查询对象(IndexSearcher),执行搜索
-
执行搜索,返回结果集(TopDocs)
-
处理结果集
-
释放资源
核心代码
/**
* 检索流程
*/
@Test
public void searchIndex() throws Exception{
// 1.创建分词器对象(Analyzer),用于对关键字分词
Analyzer analyzer = new StandardAnalyzer();
// 2.创建查询对象(Query)
// 2.1 创建查询解析器
QueryParser queryParser = new QueryParser("", analyzer);
Query query = queryParser.parse("bookName:java");
// 3.创建索引目录对象(Directory),指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\itcast\\ee145\\luceneIndex"));
// 4.创建索引读取对象(IndexReader),把索引数据读取到内存中
IndexReader indexReader = DirectoryReader.open(directory);
// 5.创建索引查询对象(IndexSearcher),执行搜索
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 6.执行搜索,返回结果集(TopDocs)
TopDocs topDocs = indexSearcher.search(query, 10);
System.out.println("总命中数 = " + topDocs.totalHits);
// 7.处理结果集
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
System.out.println("--------------------------------");
System.out.println("文档分数 = " + scoreDoc.score + ",文档id = " + scoreDoc.doc);
// 根据主键id获取文档对象
Document doc = indexSearcher.doc(scoreDoc.doc);
System.out.println("图书id = " + doc.get("id"));
System.out.println("图书名称 = " + doc.get("bookName"));
System.out.println("图书价格 = " + doc.get("bookPrice"));
System.out.println("图书图片 = " + doc.get("bookPic"));
System.out.println("图书描述 = " + doc.get("bookDesc"));
}
// 8.释放资源
indexReader.close();
}
6、分词器:介绍与作用
分词器介绍
在对文档(Document)中的内容进行存储之前,需要使用分词器对域(Field)中的内容进行分词,目的是为了建立倒排索引,过程是先分词,再过滤。
-
分词:将Document中Field域的值根据分词器内部的词典切分成一个一个的单词,具体的切分方式根据使用的分词器而不同。
-
过滤:分词完毕后,通过过滤器排除一些标点符号以及一些停用词,也会将词条的大写转换小写。
-
停用词说明:停用词是指为了节省存储空间和提高搜索效率,在对field内容或搜索关键字分词时会自动排除的字或词,这些字或词被称为“stop words”。如语气助词、副词、介词、连接词等等(的、在、是、啊)
分词器作用
-
【索引流程】: 把原始数据转换成文档对象后,使用分词器把文档域中的内容切分成一个一个的词语,目的是方便后续建立索引。
-
【检索流程】: 根据用户输入的搜索关键词,将关键字进行分词,建立成查询对象(Query),在索引库中查找目标内容。
注意事项:索引流程和检索流程使用分词器要一致。
7、Field:特性与种类
Field的特性
Document(文档)是Field(域)的承载体,一个Document是由多个Field组成。Field由名称和值两部分组成,Field的值是要索引的内容,也是要搜索的内容。
- 是否分词(tokenized)
是:将Field的值进行分词处理,分词的目的是为了建立倒排索引。
比如:商品名称,商品描述,这些内容用户需要输入关键词进行查询,由于内容格式大,内容多,需要进行分词处理建立索引。否:不做分词处理
比如:订单编号,身份证号。是一个整体,分词以后没有意义,不需要分词。
- 是否索引(indexed)
是:将Field内容进行分词处理后得到的词或者整体Field内容作为一个词条去建立索引,存储到索引域。索引的目的是为了搜索。
比如:商品名称,商品描述需要分词建立索引。订单编号,身份证号作为整体建立倒排索引。只要将来要作为用户查询条件的词,都需要索引。否:不索引。
比如:商品图片路径,不作为查询条件,不需要建立索引。
是否存储(stored)
是:将Field值保存到Document中,最终保存到磁盘上面。
比如:商品名称,商品价格。凡是将来在搜索结果页面展现给用户的内容,都需要存储。否:不存储。
比如:商品描述。内容多格式大,不需要直接在搜索结果页面展现,不做存储。需要的时候可以从关系数据库取。
Field的种类
Field种类 | 数据类型 | 是否 分词 | 是否 索引 | 是否 存储 | 说明 |
---|---|---|---|---|---|
StringField(FieldName, FieldValue,Store.YES) | 字符串 | N | Y | Y或N | 字符串类型Field,不分词,作为一个整体进行索引(比如:身份证号,订单编号),是否需要存储根据Store.YES或Store.NO决定 |
LongField(FieldName, FieldValue,Store.YES) | 数值型代表 | Y | Y | Y或N | 数值型Field代表,分词并且索引(比如:价格),是否需要存储根据Store.YES或Store.NO决定 |
StoredField(FieldName, FieldValue) | 重载方法,支持多种类型 | N | N | Y | 不分词,不索引,默认存储(比如:商品图片路径) |
TextField(FieldName, FieldValue,Store.NO) | 文本类型 | Y | Y | Y或N | 文本类型Field,分词并且索引,是否需要存储根据Store.YES或Store.NO决定 |
8、索引维护
添加文档
/**
* 添加文档
*/
@Test
public void addDoc() throws Exception{
// 1.创建文档对象
Document document = new Document();
document.add(new StringField("id", "6", Field.Store.YES));
document.add(new TextField("bookName", "金瓶梅", Field.Store.YES));
document.add(new DoubleField("bookPrice", 80d, Field.Store.YES));
document.add(new StoredField("bookPic", "11.jpg"));
document.add(new TextField("bookDesc", "传闻还不错,但是我没看过", Field.Store.NO));
// 2.创建分词器
Analyzer analyzer = new IKAnalyzer();
// 3.创建索引配置对象
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
// 3.1 设置打开模式
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
// 4.创建索引目录对象
Directory directory = FSDirectory.open(new File("D:\\itcast\\ee145\\luceneIndex"));
// 5.创建索引写入对象
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
// 6.写入文档,提交事务
indexWriter.addDocument(document);
indexWriter.commit();
// 7.关闭资源
indexWriter.close();
}
删除文档
/**
* 删除文档
*/
@Test
public void deleteDoc() throws Exception{
// 2.创建分词器
Analyzer analyzer = new IKAnalyzer();
// 3.创建索引配置对象
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
// 3.1 设置打开模式
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
// 4.创建索引目录对象
Directory directory = FSDirectory.open(new File("D:\\itcast\\ee145\\luceneIndex"));
// 5.创建索引写入对象
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
// 6.删除文档,提交事务
// 6.1 根据条件删除,删除图书名称包含java的图书
// 执行流程:1、先根据关键字进行匹配(不会对关键字进行分词); 2、删除匹配到的数据
indexWriter.deleteDocuments(new Term("bookName", "java"));
// 6.2 删除全部
// indexWriter.deleteAll();
indexWriter.commit();
// 7.关闭资源
indexWriter.close();
}
修改文档
/**
* 修改文档
*/
@Test
public void updateDoc() throws Exception{
// 1.创建文档对象(修改后的数据)
Document document = new Document();
document.add(new StringField("id", "7", Field.Store.YES));
document.add(new TextField("bookName", "金瓶梅2020", Field.Store.YES));
document.add(new DoubleField("bookPrice", 90d, Field.Store.YES));
document.add(new StoredField("bookPic", "123.jpg"));
document.add(new TextField("bookDesc", "传闻还不错,但是我没看过11", Field.Store.NO));
// 2.创建分词器
Analyzer analyzer = new IKAnalyzer();
// 3.创建索引配置对象
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
// 3.1 设置打开模式
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
// 4.创建索引目录对象
Directory directory = FSDirectory.open(new File("D:\\itcast\\ee145\\luceneIndex"));
// 5.创建索引写入对象
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
// 6.修改文档,提交事务
// 修改图书名称包含java的书籍
// 执行流程:1、先根据关键字进行匹配(不会对关键字进行分词); 2、删除匹配到的数据; 3-添加新的数据
indexWriter.updateDocument(new Term("bookName", "java"), document);
indexWriter.commit();
// 7.关闭资源
indexWriter.close();
}
词条搜索
public class QueryTest {
/**
* 词条匹配:查询图书名称域中包含有java的图书
*/
@Test
public void queryByTerm() throws Exception {
// 1.创建查询对象
Query query = new TermQuery(new Term("bookName", "java"));
// 2.创建索引目录对象
Directory directory = FSDirectory.open(new File("D:\\itcast\\ee145\\luceneIndex"));
// 3.创建索引读取对象
IndexReader indexReader = DirectoryReader.open(directory);
// 4.创建索引查询对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 5.执行搜索
TopDocs topDocs = indexSearcher.search(query, 10);
System.out.println("总命中数 = " + topDocs.totalHits);
// 6.处理返回结果
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
System.out.println("----------------------------------");
System.out.println("文档分数 = " + scoreDoc.score + ",文档id = " + scoreDoc.doc);
Document doc = indexSearcher.doc(scoreDoc.doc);
System.out.println("图书id = " + doc.get("id"));
System.out.println("图书名称 = " + doc.get("bookName"));
System.out.println("图书价格 = " + doc.get("bookPrice"));
System.out.println("图书图片 = " + doc.get("bookPic"));
System.out.println("图书描述 = " + doc.get("bookDesc"));
}
// 7.关闭资源
indexReader.close();
}
}
范围查询
/**
* 范围查询:搜索图书价格大于80,小于100的图书
*/
@Test
public void queryByRange() throws Exception{
// 1.创建查询对象
// 参数1-字段名称
// 参数2-最小值
// 参数3-最大值
// 参数4-是否包含最小值
// 参数5-是否包含最大值
Query query = NumericRangeQuery.newDoubleRange("bookPrice", 80d, 100d, false, false);
search(query);
}
/**
* 搜索公共方法
* @param query
* @throws Exception
*/
private void search(Query query) throws Exception{
// 2.创建索引目录对象
Directory directory = FSDirectory.open(new File("D:\\itcast\\ee145\\luceneIndex"));
// 3.创建索引读取对象
IndexReader indexReader = DirectoryReader.open(directory);
// 4.创建索引查询对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 5.执行搜索
TopDocs topDocs = indexSearcher.search(query, 10);
System.out.println("总命中数 = " + topDocs.totalHits);
// 6.处理返回结果
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
System.out.println("----------------------------------");
System.out.println("文档分数 = " + scoreDoc.score + ",文档id = " + scoreDoc.doc);
Document doc = indexSearcher.doc(scoreDoc.doc);
System.out.println("图书id = " + doc.get("id"));
System.out.println("图书名称 = " + doc.get("bookName"));
System.out.println("图书价格 = " + doc.get("bookPrice"));
System.out.println("图书图片 = " + doc.get("bookPic"));
System.out.println("图书描述 = " + doc.get("bookDesc"));
}
// 7.关闭资源
indexReader.close();
}
组合查询
/**
* 组合查询:查询图书名称中包含有lucene,并且80<=图书价格<=100之间的图书
*/
@Test
public void queryByBool() throws Exception{
BooleanQuery booleanQuery = new BooleanQuery();
// 条件1:图书名称中包含有lucene
TermQuery termQuery = new TermQuery(new Term("bookName", "lucene"));
// 条件2:80<=图书价格<=100
NumericRangeQuery<Double> numberRangeQuery = NumericRangeQuery.newDoubleRange("bookPrice", 80d, 100d, true, true);
// 添加条件,指定组合关系为 且
booleanQuery.add(termQuery, BooleanClause.Occur.MUST);
booleanQuery.add(numberRangeQuery, BooleanClause.Occur.MUST);
search(booleanQuery);
}
条件表达式
/**
* 条件表达式搜索:查询图书名称中包含有java,并且包含lucene的图书
*/
@Test
public void queryByExpress() throws Exception{
Analyzer analyzer = new IKAnalyzer();
// 参数1:查询的field
QueryParser queryParser = new QueryParser("", analyzer);
Query query = queryParser.parse("bookName:java AND bookName:lucene");
search(query);
}
分页和排序
/**
* 分页与排序:查询图书名称包含java或lucene的图书,根据价格升序排序,每页显示2条,查询第2页的数据
*/
@Test
public void pageAndSort() throws Exception {
// 1.创建分词器对象(Analyzer),用于对关键字分词
Analyzer analyzer = new IKAnalyzer();
// 2.创建查询对象(Query)
// 2.1 创建查询解析器
QueryParser queryParser = new QueryParser("", analyzer);
Query query = queryParser.parse("bookName:java OR bookName:lucene");
// 3.创建索引目录对象(Directory),指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\itcast\\ee145\\luceneIndex"));
// 4.创建索引读取对象(IndexReader),把索引数据读取到内存中
IndexReader indexReader = DirectoryReader.open(directory);
// 5.创建索引查询对象(IndexSearcher),执行搜索
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 6.执行搜索,返回结果集(TopDocs)
// 6.1 创建排序对象,参数1-字段名,参数2-数据类型,参数3-升序或降序,false代表升序
SortField sortField = new SortField("bookPrice", SortField.Type.DOUBLE, false);
Sort sort = new Sort(sortField);
// 6.2 定义分页参数
int pageNo = 2; // 当前页
int pageSize = 2; // 每页显示条数
TopDocs topDocs = indexSearcher.search(query, pageNo * pageSize, sort);
System.out.println("总命中数 = " + topDocs.totalHits);
// 7.处理结果集
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (int i = (pageNo - 1) * pageSize; i < scoreDocs.length; i++) {
ScoreDoc scoreDoc = scoreDocs[i];
System.out.println("--------------------------------");
System.out.println("文档分数 = " + scoreDoc.score + ",文档id = " + scoreDoc.doc);
// 根据主键id获取文档对象
Document doc = indexSearcher.doc(scoreDoc.doc);
System.out.println("图书id = " + doc.get("id"));
System.out.println("图书名称 = " + doc.get("bookName"));
System.out.println("图书价格 = " + doc.get("bookPrice"));
System.out.println("图书图片 = " + doc.get("bookPic"));
System.out.println("图书描述 = " + doc.get("bookDesc"));
}
// 8.释放资源
indexReader.close();
}