Lucene 全文检索
基于 lucene 8
1 Lucene简介
Lucene是apache下的一个开源的全文检索引擎工具包。
1.1 全文检索(Full-text Search)
全文检索就是先分词创建索引,再执行搜索的过程。分词就是将一段文字分成一个个单词。全文检索就将一段文字分成一个个单词去查询数据
1.2 Lucene实现全文检索的流程
全文检索的流程分为两大部分:索引流程、搜索流程。
- 索引流程:采集数据--->构建文档对象--->创建索引(将文档写入索引库)。
- 搜索流程:创建查询--->执行搜索--->渲染搜索结果。
2 入门示例
2.1 需求
使用Lucene实现电商项目中图书类商品的索引和搜索功能。
2.2 配置步骤说明
- 搭建环境
- 创建索引库
- 搜索索引库
2.3 配置步骤
2.3.1 第一部分:搭建环境(创建项目,导入包)
2.3.2 第二部分:创建索引
步骤说明:
- 采集数据
- 将数据转换成Lucene文档
- 将文档写入索引库,创建索引
2.3.2.1 第一步:采集数据
Lucene全文检索,不是直接查询数据库,所以需要先将数据采集出来。
package jdbc.dao;
import jdbc.pojo.Book;
import jdbc.util.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class BookDao {
public List<Book> listAll() {
//创建集合
List<Book> books = new ArrayList<>();
//获取数据库连接
Connection conn = JdbcUtils.getConnection();
String sql = "SELECT * FROM `BOOK`";
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//获取预编译语句
preparedStatement = conn.prepareStatement(sql);
//获取结果集
resultSet = preparedStatement.executeQuery();
//结果集解析
while (resultSet.next()) {
books.add(new Book(resultSet.getInt("id"),
resultSet.getString("name"),
resultSet.getFloat("price"),
resultSet.getString("pic"),
resultSet.getString("description")));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭资源
if (null != resultSet) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
}
}
return books;
}
}
2.3.2.2 第二步:将数据转换成Lucene文档
Lucene是使用文档类型来封装数据的,所有需要先将采集的数据转换成文档类型。其格式为:
修改BookDao,新增一个方法,转换数据
public List<Document> getDocuments(List<Book> books) {
//创建集合
List<Document> documents = new ArrayList<>();
//循环操作 books 集合
books.forEach(book -> {
//创建 Document 对象,Document 内需要设置一个个 Field 对象
Document doc = new Document();
//创建各个 Field
Field id = new TextField("id", book.getId().toString(), Field.Store.YES);
Field name = new TextField("name", book.getName(), Field.Store.YES);
Field price = new TextField("price", book.getPrice().toString(), Field.Store.YES);
Field pic = new TextField("id", book.getPic(), Field.Store.YES);
Field description = new TextField("description", book.getDescription(), Field.Store.YES);
//将 Field 添加到文档中
doc.add(id);
doc.add(name);
doc.add(price);
doc.add(pic);
doc.add(description);
documents.add(doc);
});
return documents;
}
2.3.2.3 第三步:创建索引库
Lucene是在将文档写入索引库的过程中,自动完成分词、创建索引的。因此创建索引库,从形式上看,就是将文档写入索引库!
package jdbc.test;
import jdbc.dao.BookDao;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
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.junit.Test;
import java.io.File;
import java.io.IOException;
public class LuceneTest {
/**
* 创建索引库
*/
@Test
public void createIndex() {
BookDao dao = new BookDao();
//该分词器用于逐个字符分词
StandardAnalyzer standardAnalyzer = new StandardAnalyzer();
//创建索引
//1. 创建索引库存储目录
try (Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath())) {
//2. 创建 IndexWriterConfig 对象
IndexWriterConfig ifc = new IndexWriterConfig(standardAnalyzer);
//3. 创建 IndexWriter 对象
IndexWriter indexWriter = new IndexWriter(directory, ifc);
//4. 通过 IndexWriter 对象添加文档
indexWriter.addDocuments(dao.getDocuments(dao.listAll()));
//5. 关闭 IndexWriter
indexWriter.close();
System.out.println("完成索引库创建");
} catch (IOException e) {
e.printStackTrace();
}
}
}
可以通过 luke 工具查看结果
2.3.3 第三部分:搜索索引
2.3.3.1 说明
搜索的时候,需要指定搜索哪一个域(也就是字段),并且,还要对搜索的关键词做分词处理。
2.3.3.2 执行搜索
@Test
public void searchTest() {
//1. 创建查询(Query 对象)
StandardAnalyzer standardAnalyzer = new StandardAnalyzer();
// 参数 1 指定搜索的 Field
QueryParser queryParser = new QueryParser("name", standardAnalyzer);
try {
Query query = queryParser.parse("java book");
//2. 执行搜索
//a. 指定索引库目录
Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath());
//b. 创建 IndexReader 对象
IndexReader reader = DirectoryReader.open(directory);
//c. 创建 IndexSearcher 对象
IndexSearcher searcher = new IndexSearcher(reader);
/**
* d. 通过 IndexSearcher 对象查询索引库,返回 TopDocs 对象
* 参数 1:查询对象(Query)
* 参数 2:前 n 条数据
*/
TopDocs topDocs = searcher.search(query, 10);
//e. 提取 TopDocs 对象中的的查询结果
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
System.out.println("查询结果个数为:" + topDocs.totalHits);
//循环输出数据对象
for (ScoreDoc scoreDoc : scoreDocs) {
//获得文档对象 id
int docId = scoreDoc.doc;
//通过 id 获得具体对象
Document document = searcher.doc(docId);
//输出图书的书名
System.out.println(document.get("name"));
}
//关闭 IndexReader
reader.close();
} catch (ParseException | IOException e) {
e.printStackTrace();
}
}
结果
3 分词
对Lucene分词的过程,我们可以做如下总结:
- 分词的时候,是以域为单位的。不同的域,相互独立。同一个域中,拆分出来相同的词,视为同一个词(Term)。不同的域中,拆分出来相同的词,不是同一个词。其中,Term是Lucene最小的语汇单元,不可再细分。
- 分词的时候经历了一系列的过滤器。如大小写转换、去除停用词等。
从上图中,我们发现:
- 索引库中有两个区域:索引区、文档区。
- 文档区存放的是文档。Lucene给每一个文档自动加上一个文档编号docID。
- 索引区存放的是索引。注意:
- 索引是以域为单位的,不同的域,彼此相互独立。
- 索引是根据分词规则创建出来的,根据索引就能找到对应的文档。
4 Field域
我们已经知道,Lucene是在写入文档时,完成分词、索引的。那Lucene是怎么知道如何分词的呢?Lucene是根据文档中的域的属性来确定是否要分词、是否创建索引的。所以,我们必须搞清楚域有哪些属性。
4.1 域的属性
4.1.1 三大属性
4.1.1.1 是否分词(tokenized)
只有设置了分词属性为true,lucene才会对这个域进行分词处理。
在实际的开发中,有一些字段是不需要分词的,比如商品id,商品图片等。而有一些字段是必须分词的,比如商品名称,描述信息等。
4.1.1.2 是否索引(indexed)
只有设置了索引属性为true,lucene才为这个域的Term词创建索引。
在实际的开发中,有一些字段是不需要创建索引的,比如商品的图片等。我们只需要对参与搜索的字段做索引处理。
4.1.1.3 是否存储(stored)
只有设置了存储属性为true,在查找的时候,才能从文档中获取这个域的值。
在实际开发中,有一些字段是不需要存储的。比如:商品的描述信息。因为商品描述信息,通常都是大文本数据,读的时候会造成巨大的IO开销。而描述信息是不需要经常查询的字段,这样的话就白白浪费了cpu的资源了。因此,像这种不需要经常查询,又是大文本的字段,通常不会存储到索引库。
4.1.2 特点
- 三大属性彼此独立。
- 通常分词是为了创建索引。
- 不存储这个域文本内容,也可以对这个域先分词、创建索引。
4.2 Field常用类型
域的常用类型有很多,每一个类都有自己默认的三大属性。如下:
4.3 改造入门示例中的域类型
public List<Document> getDocuments(List<Book> books) {
//创建集合
List<Document> documents = new ArrayList<>();
//循环操作 books 集合
books.forEach(book -> {
//创建 Document 对象,Document 内需要设置一个个 Field 对象
Document doc = new Document();
//创建各个 Field
//存储但不分词、不索引
Field id = new StoredField("id", book.getId());
//存储、分词、索引
Field name = new TextField("name", book.getName(), Field.Store.YES);
//存储但不分词、不索引
Field price = new StoredField("price", book.getPrice());
//存储但不分词、不索引
Field pic = new StoredField("pic", book.getPic());
//分词、索引,但不存储
Field description = new TextField("description", book.getDescription(), Field.Store.NO);
//将 Field 添加到文档中
doc.add(id);
doc.add(name);
doc.add(price);
doc.add(pic);
doc.add(description);
documents.add(doc);
});
return documents;
}
结果
5 索引库维护]
5.1 添加索引(文档)
5.1.1 需求
数据库中新上架了图书,必须把这些图书也添加到索引库中,不然就搜不到该新上架的图书了。
5.1.2 代码实现
调用 indexWriter.addDocument(doc)添加索引。(参考入门示例中的创建索引)
5.2 删除索引(文档)
5.2.1需求
某些图书不再出版销售了,我们需要从索引库中移除该图书。
5.2.2 代码实现
@Test
public void deleteIndex() throws IOException {
//1.指定索引库目录
Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath());
//2.创建 IndexWriterConfig
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(new StandardAnalyzer());
//3.创建 IndexWriter
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
//4.删除指定索引
indexWriter.deleteDocuments(new Term("name", "java"));
//5.关闭 IndexWriter
indexWriter.close();
}
5.2.3 清空索引代码实现
@Test
public void deleteAllIndex() throws IOException {
//1.指定索引库目录
Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath());
//2.创建 IndexWriterConfig
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(new StandardAnalyzer());
//3.创建 IndexWriter
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
//4.删除所有索引
indexWriter.deleteAll();
//5.关闭 IndexWriter
indexWriter.close();
}
5.3 更新索引(文档)
5.3.1 说明
Lucene更新索引比较特殊,是先删除满足条件的文档,再添加新的文档。
5.3.2 代码实现
@Test
public void updateIndex() throws IOException {
//1.指定索引库目录
Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath());
//2.创建 IndexWriterConfig
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(new StandardAnalyzer());
//3.创建 IndexWriter
IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);
//4.创建新加的文档对象
Document document = new Document();
document.add(new TextField("name", "testUpdate", Field.Store.YES));
//5.修改指定索引为新的索引
indexWriter.updateDocument(new Term("name", "java"), document);
//6.关闭 IndexWriter
indexWriter.close();
}
6 搜索
问题:我们在入门示例中,已经知道Lucene是通过IndexSearcher对象,来执行搜索的。在实际的开发中,我们的查询的业务是相对复杂的,比如我们在通过关键词查找的时候,往往进行价格、商品类别的过滤。而Lucene提供了一套查询方案,供我们实现复杂的查询。
6.1 创建查询的两种方法
执行查询之前,必须创建一个查询Query查询对象。Query自身是一个抽象类,不能实例化,必须通过其它的方式来实现初始化。在这里,Lucene提供了两种初始化Query查询对象的方式。
6.1.1 使用Lucene提供Query子类
Query是一个抽象类,lucene提供了很多查询对象,比如TermQuery项精确查询,NumericRangeQuery数字范围查询等。
6.1.2 使用QueryParse解析查询表达式
QueryParser会将用户输入的查询表达式解析成Query对象实例。如下代码:
QueryParser queryParser = new QueryParser("name", new StandardAnalyzer());
Query query = queryParser.parse("name:lucene");
6.2 常用的Query子类搜索
6.2.1 TermQuery
特点:查询的关键词不会再做分词处理,作为整体来搜索。代码如下:
@Test
public void queryByTermQuery() throws IOException {
Query query = new TermQuery(new Term("name", "java"));
doQuery(query);
}
private void doQuery(Query query) throws IOException {
//指定索引库
Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath());
//创建读取流
DirectoryReader reader = DirectoryReader.open(directory);
//创建执行搜索对象
IndexSearcher searcher = new IndexSearcher(reader);
//执行搜索
TopDocs topDocs = searcher.search(query, 10);
System.out.println("共搜索结果:" + topDocs.totalHits);
//提取文档信息
//score即相关度。即搜索的关键词和 图书名称的相关度,用来做排序处理
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
System.out.println("索引库编号:" + docId);
//提取文档信息
Document doc = searcher.doc(docId);
System.out.println(doc.get("name"));
System.out.println(doc.get("id"));
System.out.println(doc.get("priceValue"));
System.out.println(doc.get("pic"));
System.out.println(doc.get("description"));
//关闭读取流
reader.close();
}
}
6.2.2 WildCardQuery
使用通配符查询
/**
* 通过通配符查询所有文档
* @throws IOException
*/
@Test
public void queryByWildcardQuery() throws IOException {
Query query = new WildcardQuery(new Term("name", "*"));
doQuery(query);
}
private void doQuery(Query query) throws IOException {
//指定索引库
Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath());
//创建读取流
DirectoryReader reader = DirectoryReader.open(directory);
//创建执行搜索对象
IndexSearcher searcher = new IndexSearcher(reader);
//执行搜索
TopDocs topDocs = searcher.search(query, 10);
System.out.println("共搜索结果:" + topDocs.totalHits);
//提取文档信息
//score即相关度。即搜索的关键词和 图书名称的相关度,用来做排序处理
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
System.out.println("索引库编号:" + docId);
//提取文档信息
Document doc = searcher.doc(docId);
System.out.println(doc.get("name"));
System.out.println(doc.get("id"));
System.out.println(doc.get("priceValue"));
System.out.println(doc.get("pic"));
System.out.println(doc.get("description"));
}
//关闭读取流
reader.close();
}
6.2.3 数字类型的 RangeQuery
指定数字范围查询.(创建field类型时,注意与之对应),修改建立索引时的 price
/**
* 将 Book 集合封装成 Document 集合
* @param books Book集合
* @return Document 集合
*/
public List<Document> getDocuments(List<Book> books) {
//创建集合
List<Document> documents = new ArrayList<>();
//循环操作 books 集合
books.forEach(book -> {
//创建 Document 对象,Document 内需要设置一个个 Field 对象
Document doc = new Document();
//创建各个 Field
//存储但不分词、不索引
Field id = new StoredField("id", book.getId());
//存储、分词、索引
Field name = new TextField("name", book.getName(), Field.Store.YES);
//Float 数字存储、索引
Field price = new FloatPoint("price", book.getPrice()); //用于数字的区间查询,不会存储,需要额外的 StoredField
Field priceValue = new StoredField("priceValue", book.getPrice());//用于存储具体价格
//存储但不分词、不索引
Field pic = new StoredField("pic", book.getPic());
//分词、索引,但不存储
Field description = new TextField("description", book.getDescription(), Field.Store.NO);
//将 Field 添加到文档中
doc.add(id);
doc.add(name);
doc.add(price);
doc.add(priceValue);
doc.add(pic);
doc.add(description);
documents.add(doc);
});
return documents;
}
使用对应的 FloatPoint 的静态方法,获得 RangeQuery
/**
* Float 类型的范围查询
* @throws IOException
*/
@Test
public void queryByNumricRangeQuery() throws IOException {
Query query = FloatPoint.newRangeQuery("price", 60, 80);
doQuery(query);
}
private void doQuery(Query query) throws IOException {
//指定索引库
Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath());
//创建读取流
DirectoryReader reader = DirectoryReader.open(directory);
//创建执行搜索对象
IndexSearcher searcher = new IndexSearcher(reader);
//执行搜索
TopDocs topDocs = searcher.search(query, 10);
System.out.println("共搜索结果:" + topDocs.totalHits);
//提取文档信息
//score即相关度。即搜索的关键词和 图书名称的相关度,用来做排序处理
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
System.out.println("索引库编号:" + docId);
//提取文档信息
Document doc = searcher.doc(docId);
System.out.println(doc.get("name"));
System.out.println(doc.get("id"));
System.out.println(doc.get("priceValue"));
System.out.println(doc.get("pic"));
System.out.println(doc.get("description"));
}
//关闭读取流
reader.close();
}
6.2.4 BooleanQuery
BooleanQuery,布尔查询,实现组合条件查询。
@Test
public void queryByBooleanQuery() throws IOException {
Query priceQuery = FloatPoint.newRangeQuery("price", 60, 80);
Query nameQuery = new TermQuery(new Term("name", "java"));
//通过 Builder 创建 query
BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();
//至少有一个时 Occur.MUST,不然结果为空
booleanQueryBuilder.add(nameQuery, BooleanClause.Occur.MUST_NOT);
booleanQueryBuilder.add(priceQuery, BooleanClause.Occur.MUST);
BooleanQuery query = booleanQueryBuilder.build();
doQuery(query);
}
private void doQuery(Query query) throws IOException {
//指定索引库
Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath());
//创建读取流
DirectoryReader reader = DirectoryReader.open(directory);
//创建执行搜索对象
IndexSearcher searcher = new IndexSearcher(reader);
//执行搜索
TopDocs topDocs = searcher.search(query, 10);
System.out.println("共搜索结果:" + topDocs.totalHits);
//提取文档信息
//score即相关度。即搜索的关键词和 图书名称的相关度,用来做排序处理
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
System.out.println("索引库编号:" + docId);
//提取文档信息
Document doc = searcher.doc(docId);
System.out.println(doc.get("name"));
System.out.println(doc.get("id"));
System.out.println(doc.get("priceValue"));
System.out.println(doc.get("pic"));
System.out.println(doc.get("description"));
}
//关闭读取流
reader.close();
}
6.3 通过QueryParser搜索
6.3.1 特点
对搜索的关键词,做分词处理。
6.3.2 语法
6.3.2.1 基础语法
域名:关键字
如: name:java
6.3.2.2 组合条件语法
- 条件1 AND 条件2
- 条件1 OR 条件2
- 条件1 NOT 条件2
例如: Query query = queryParser.parse("java NOT 编");
6.3.3 QueryParser
@Test
public void queryByQueryParser() throws IOException, ParseException {
//创建分词器
StandardAnalyzer standardAnalyzer = new StandardAnalyzer();
/**
* 创建查询解析器
* 参数一: 默认搜索的域。
* 如果在搜索的时候,没有特别指定搜索的域,则按照默认的域进行搜索
* 指定搜索的域的方式: 域名:关键词 如: name:java
* 参数二: 分词器,对关键词做分词处理
*/
QueryParser queryParser = new QueryParser("description", standardAnalyzer);
Query query = queryParser.parse("java 教程");
doQuery(query);
}
6.3.4 MultiFieldQueryParser
通过MulitFieldQueryParse对多个域查询。
@Test
public void queryByMultiFieldQueryParser() throws ParseException, IOException {
//1.定义多个搜索的域
String[] fields = {"name", "description"};
//2.加载分词器
StandardAnalyzer standardAnalyzer = new StandardAnalyzer();
//3.创建 MultiFieldQueryParser 实例对象
MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(fields, standardAnalyzer);
Query query = multiFieldQueryParser.parse("java");
doQuery(query);
}
7 中文分词器
7.1 什么是中文分词器
学过英文的都知道,英文是以单词为单位的,单词与单词之间以空格或者逗号句号隔开。标准分词器,无法像英文那样按单词分词,只能一个汉字一个汉字来划分。所以需要一个能自动识别中文语义的分词器。
7.2 Lucene自带的中文分词器
7.2.1 StandardAnalyzer:
单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”
效果:“我”、“爱”、“中”、“国”。
7.2.2 CJKAnalyzer
二分法分词:按两个字进行切分。如:“我是中国人”
效果:“我是”、“是中”、“中国”“国人”。
7.2.3 SmartChineseAnalyzer
官方提供的智能中文识别,需要导入新的 jar 包
@Test
public void createIndexByChinese () {
BookDao dao = new BookDao();
//该分词器用于中文分词
SmartChineseAnalyzer smartChineseAnalyzer = new SmartChineseAnalyzer();
//创建索引
//1. 创建索引库存储目录
try (Directory directory = FSDirectory.open(new File("C:\\Users\\carlo\\OneDrive\\Workspace\\IdeaProjects\\lucene-demo01-start\\lucene").toPath())) {
//2. 创建 IndexWriterConfig 对象
IndexWriterConfig ifc = new IndexWriterConfig(smartChineseAnalyzer);
//3. 创建 IndexWriter 对象
IndexWriter indexWriter = new IndexWriter(directory, ifc);
//4. 通过 IndexWriter 对象添加文档
indexWriter.addDocuments(dao.getDocuments(dao.listAll()));
//5. 关闭 IndexWriter
indexWriter.close();
System.out.println("完成索引库创建");
} catch (IOException e) {
e.printStackTrace();
}
}
效果如图:
Lucene 全文检索的更多相关文章
- Apache Lucene(全文检索引擎)—创建索引
目录 返回目录:http://www.cnblogs.com/hanyinglong/p/5464604.html 本项目Demo已上传GitHub,欢迎大家fork下载学习:https://gith ...
- Lucene全文检索技术
Lucene全文检索技术 今日大纲 ● 搜索的概念.搜索引擎原理.倒排索引 ● 全文索引的概念 ● 使用Lucene对索引进行CRUD操作 ● Lucene常用API详解 ● ...
- 使用Lucene全文检索并使用中文版和高亮显示
使用Lucene全文检索并使用中文版和高亮显示 中文分词需要引入 中文分词发的jar 包,咱们从maven中获取 <!-- lucene中文分词器 --> <dependency&g ...
- lucene全文检索基础
全文检索是一种将文件中所有文本与检索项匹配的文字资料检索方法.比如用户在n个小说文档中检索某个关键词,那么所有包含该关键词的文档都返回给用户.那么应该从哪里入手去实现一个全文检索系统?相信大家都听说过 ...
- lucene 全文检索工具的介绍
Lucene:全文检索工具:这是一种思想,使用的是C语言写出来的 1.Lucene就是apache下的一个全文检索工具,一堆的jar包,我们可以使用lucene做一个谷歌和百度一样的搜索引擎系统 2. ...
- Lucene 全文检索 Lucene的使用
Lucene 全文检索 Lucene的使用 一.简介: 参考百度百科: http://baike.baidu.com/link?url=eBcEVuUL3TbUivRvtgRnMr1s44nTE7 ...
- Lucene全文检索_分词_复杂搜索_中文分词器
1 Lucene简介 Lucene是apache下的一个开源的全文检索引擎工具包. 1.1 全文检索(Full-text Search) 1.1.1 定义 全文检索就是先分词创建索引,再执行搜索的过 ...
- Apache Lucene(全文检索引擎)—分词器
目录 返回目录:http://www.cnblogs.com/hanyinglong/p/5464604.html 本项目Demo已上传GitHub,欢迎大家fork下载学习:https://gith ...
- lucene全文检索---打酱油的日子
检索内容,一般的程序员第一时间想到的是sql的like来做模糊查询,其实这样的搜索是比较耗时的.已经有lucene帮我们 封装好了,lucene采用的是分词检索等策略. 1.lucene中的类描述 I ...
随机推荐
- 使用devstack安装openstack
使用devstack安装openstack 环境介绍,宿主机器 Fedora release 29 (Twenty Nine) 40核心cpu,32g内存 设想, 在fedora中安装kvm,虚拟出U ...
- 如何通过Thread查看一个方法被调用的顺序
Test1 package com.dwz.concurrency.chapter11; public class Test1 { private Test2 test2 = new Test2(); ...
- linux密码忘记的处理步骤
linux忘记密码 centos7忘记密码如何解决?进入到单用户模式下去修改root密码step 1:开机时按住向下箭头,选择第一个,按住e进入gurb界面:step 2:在编辑模式下找ro 修改为 ...
- easyui的combobox模糊搜索
<tr> <th>测试名称:</th> <td> <select data-options="" class="ea ...
- jinja2-模版简介
一 简介 在Flask中,调用render_template来对模版进行渲染,使用render_template,只需要导入这个API就可以,from flask import render_temp ...
- Custom Roles Based Access Control (RBAC) in ASP.NET MVC Applications - Part 1 (Framework Introduction)
https://www.codeproject.com/Articles/875547/Custom-Roles-Based-Access-Control-RBAC-in-ASP-NET Introd ...
- 在Mac 搭建robotframework 环境
折腾来一下午,遇到了好多坑 坑 1.不要用pip 下载wxpython 2.不要用mac自带的python 3.不要自己下载wxpython 步骤: 1. 安装homebrew, /usr/bin/r ...
- uni-app 保持登录状态 (Vuex)
在小程序中,保持登录状态是很常见的需求,今天就把写一写使用uni-app框架的保持登录状态功能是怎样实现的. 一.场景需求 1.场景:初始打开---登陆---关闭,再次打开---(已登录)上次关闭前的 ...
- maven编译正常,运行报错:中没有主清单属性
在pom.xml添加插件 <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId&g ...
- ffprobe读取音视频元数据信息,json格式输出
命令格式: ffprobe -v quiet -show_format -show_streams -print_format json F:\temp\test1566606924822.wav 输 ...