Lucene 基于java开发全文检索工具包
小结
索引库—document(文档)—域(相当于属性,存 关键字:值)
- 创建索引
- IndexWriter:创建索引库 参数1:打开目录 参数2:配置
- IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File(“索引库磁盘路径”).toPath()),new IndexWriterConfig());
- Document:创建文档
- Document document = new Document();
- Field:创建域 参数1:域名称 参数2:域内容 参数3:是否存储
- Field field = new xxxField(“域名称”,域内容, Field.Store.YES或.NO);
- 域添加到文档:document.add(域); 文档添加到索引库:indexWriter.addDocument(document);
- IndexWriter:创建索引库 参数1:打开目录 参数2:配置
- 查询索引
- IndexReader:获取索引库
- IndexReader indexReader = DirectoryReader.open(FSDirectory.open(new File(“索引库磁盘路径”).toPath()));
- IndexSearcher:由索引库创建查询对象
- IndexSearcher indexSearcher = new IndexSearcher(indexReader);
- Query:创建具体查询
- Query query = new TermQuery(new Term(“content”,“spring”));//查询域名为content,域内容包含spring的
- TopDocs:执行查询获取topDocs即文档对象 参数1:查询对象 参数2:查询结果返回最大记录数
- TopDocs topDocs = indexSearcher.search(query, 10);
- System.out.println(“查询总记录数:”+topDocs.totalHits); //获取查询总记录数
- ScoreDoc[] scoreDocs = topDocs.scoreDocs; //获取文档列表
- 遍历scoreDocs可以获取文档编号id:int docID = doc.doc;
- 通过id可查询文档: Document document = indexSearcher.doc(docID);
- 通过文档获取域内容:document.get(“域名称”)
- TopDocs topDocs = indexSearcher.search(query, 10);
- IndexReader:获取索引库
- 索引库操作
- 优化:属性—是否分析(是否分词处理)、是否索引(索引方可查)、是否存储(存入文档,存入文档后field才可从document中取出) 根据不同数据要求创建不同的域
- 增加文档
- 创建文档document,创建域field,域加入文档,文档加入索引库
- 删除索引
- 删除全部文档:indexWriter.deleteAll();
- 选择性删除:indexWriter.deleteDocuments(new Term(“name”,“apache”));//把域名为name,其值包含apache的document删除
- 更新索引:先删除,后添加
- 查询索引:查询索引不同主要在于创建query对象,创建好相应query对象,其他操作一致
- Query子类–TermQuery: Query query = new TermQuery(new Term(“content”,“spring”));
- Query子类–RangeQuery(范围查询): Query query = LongPoint.newRangeQuery(“size”, 0l, 10000l);//最后一位为L表示long类型
- QueryPaser查询:先分词,后查询
- 导入jar包: lucene-queryparser-7.4.0.jar
- QueryParser queryParser = new QueryParser(“name”,new IKAnalyzer());
- 使用QueryPaser创建Query对象:Query query = queryParser.parse(“待检索的一句话”);
- IndexWriter和IndexReader对象使用完后都要进行关闭:.close();
lucene实现全文检索流程
环境:
1-下载lucene(就是一些jar包,需要使用那些就引入) 地址:http://lucene.apache.org/—适应的jdk—创建java项目
2-需要的jar:
- lucene-analyzers-common-7.4.0.jar
- lucene-core-7.4.0.jar
- commons-io.jar
1.创建索引
- 1–原始文档:要基于哪些数据来进行搜索,这些数据即原始文档。
- 2–构建文档对象:对应每个原始文档创建一个document对象----每个document对象包含多个域—域中保存原始文档数据(域名称、域值)
每个文档有唯一编号即文档id - 3–分析文档:分词过程:空格拆分得单词列表----单词统一转小写----去除标点----去除停用词(无意义的词)
每个关键词封装一个term对象中,Term中包含两部分内容:关键词所在的域、关键词本身
不同域拆分出相同关键词是不同Term。 - 4–创建索引:基于关键词列表创建一个索引,保存到索引库中。索引库中:索引、document对象、关键词、文档的对应关系
倒排索引结构:
通过词语找文档
2.查询索引
- 1–用户查询接口:用户输入查询条件地方
- 2–关键词封装成一个查询对象(查询的域、关键词)
- 3–执行查询:根据查询关键词----域搜索----找到关键词----根据关键词找到对应文档
- 4–渲染结果:根据文档id找到文档对象----关键词高亮----分页----展示
创建索引步骤:
1-创建director对象,指定索引库保存位置
2-基于directory对象创建一个IndexWriter对象
3-读取磁盘文件,对应每个文件创建一个文档对象
4-域添加到文档对象中
5-文档对象写入索引库
6-关闭indexwriter
package com.xiaoai.lucene;
import org.apache.commons.io.FileUtils;
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.search.FieldDoc;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;
import java.io.File;
public class LuceneFirst {
@Test
public void createIndex() throws Exception{
// 1-创建director对象,指定索引库保存位置
// Directory directory = new RAMDirectory();//把索引库保存在内存
Directory directory = FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath());//把索引库保存在磁盘
// 2-基于directory对象创建一个IndexWriter对象
IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig());
// 3-读取磁盘文件,对应每个文件创建一个文档对象
File dir = new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\02.参考资料\\searchsource");
File[] files = dir.listFiles();
for (File f : files) {
//读取文件名
String fileName = f.getName();
//文件路径
String filePath = f.getPath();
//文件内容
String fileContent = FileUtils.readFileToString(f,"utf-8");
//文件大小
long fileSize = FileUtils.sizeOf(f);
//创建field
//参数1:域名称 参数2:域内容 参数3:是否存储
Field fieldName = new TextField("name",fileName, Field.Store.YES);
Field fieldPath = new TextField("path",filePath,Field.Store.YES);
Field fieldContent = new TextField("content",fileContent,Field.Store.YES);
Field fieldSize = new TextField("size",filePath,Field.Store.YES);
//4-域添加到文档对象中
Document document = new Document();
document.add(fieldName);
document.add(fieldPath);
document.add(fieldContent);
document.add(fieldSize);
// 5-文档对象写入索引库
indexWriter.addDocument(document);
}
// 6-关闭indexwriter
indexWriter.close();
}
}
可使用luke工具查看索引库中内容
查询索引步骤:
1、创建一个Director对象,指定索引库的位置
2、创建一个IndexReader对象
3、创建一个IndexSearcher对象,构造方法中的参数indexReader对象。
4、创建一个Query对象,TermQuery
5、执行查询,得到一个TopDocs对象
6、取查询结果的总记录数
7、取文档列表
8、打印文档中的内容
9、关闭IndexReader对象
package com.xiaoai.lucene;
import org.apache.commons.io.FileUtils;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;
import java.io.File;
public class LuceneFirst {
//创建索引
@Test
public void createIndex() throws Exception{
// 1-创建director对象,指定索引库保存位置
// Directory directory = new RAMDirectory();//把索引库保存在内存
Directory directory = FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath());//把索引库保存在磁盘
// 2-基于directory对象创建一个IndexWriter对象
IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig());
// 3-读取磁盘文件,对应每个文件创建一个文档对象
File dir = new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\02.参考资料\\searchsource");
File[] files = dir.listFiles();
for (File f : files) {
//读取文件名
String fileName = f.getName();
//文件路径
String filePath = f.getPath();
//文件内容
String fileContent = FileUtils.readFileToString(f,"utf-8");
//文件大小
long fileSize = FileUtils.sizeOf(f);
//创建field
//参数1:域名称 参数2:域内容 参数3:是否存储
Field fieldName = new TextField("name",fileName, Field.Store.YES);
Field fieldPath = new TextField("path",filePath,Field.Store.YES);
Field fieldContent = new TextField("content",fileContent,Field.Store.YES);
Field fieldSize = new TextField("size",fileSize+"",Field.Store.YES);
//4-域添加到文档对象中
Document document = new Document();
document.add(fieldName);
document.add(fieldPath);
document.add(fieldContent);
document.add(fieldSize);
// 5-文档对象写入索引库
indexWriter.addDocument(document);
}
// 6-关闭indexwriter
indexWriter.close();
}
//查询索引
@Test
public void searchIndex() throws Exception{
// 1、创建一个Director对象,指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath());//把索引库保存在磁盘
// 2、创建一个IndexReader对象
IndexReader indexReader = DirectoryReader.open(directory);
// 3、创建一个IndexSearcher对象,构造方法中的参数indexReader对象。
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 4、创建一个Query对象,TermQuery
Query query = new TermQuery(new Term("content","spring"));
// 5、执行查询,得到一个TopDocs对象
TopDocs topDocs = indexSearcher.search(query, 10);//参数1:查询对象 参数2:查询结果返回最大记录数
// 6、取查询结果的总记录数
System.out.println("查询总记录数:"+topDocs.totalHits);
// 7、取文档列表
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
// 8、打印文档中的内容
for (ScoreDoc doc : scoreDocs) {
//取文档id
int docID = doc.doc;
//根据id取文档对象
Document document = indexSearcher.doc(docID);
System.out.println(document.get("name"));
System.out.println(document.get("path"));
System.out.println(document.get("size"));
// System.out.println(document.get("content"));
System.out.println("---------------------------------------------分隔线");
}
// 9、关闭IndexReader对象
indexReader.close();
}
}
分析器
默认使用标准分析器:StandardAnalyzer(父类:Analyzer)
查看分析器分析效果:使用Analyzer对象的tokenStream方法返回一个TokenStream对象。对象中包含了最终分词结果。
package com.xiaoai.lucene;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.junit.Test;
public class TestTokenStream {
@Test
public void testTokenStream() throws Exception{
// 1)创建一个Analyzer对象,StandardAnalyzer对象
Analyzer analyzer = new StandardAnalyzer();
// 2)使用分析器对象的tokenStream方法获得一个TokenStream对象
TokenStream tokenStream = analyzer.tokenStream("", "The Spring Framework provides a comprehensive programming and configuration model.");
// 3)向TokenStream对象中设置一个引用,相当于数一个指针
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
// 4)调用TokenStream对象的rest方法。如果不调用抛异常
tokenStream.reset();
// 5)使用while循环遍历TokenStream对象
while (tokenStream.incrementToken()){
System.out.println(charTermAttribute.toString());
}
// 6)关闭TokenStream对象
tokenStream.close();
}
}
中文分析器:IKAnalyze
- 1-添加jar:IK-Analyzer-1.0-SNAPSHOT.jar
- 2-配置文件、扩展词典、停用词词典添加到工程classpath下(扩展词典严禁使用windows记事本编辑,保证扩展词典编码格式是utf-8) 扩展词典:可添加新词 停用词词典:无意义的词或者敏感词
配置文件 *(IKAnalyzer.cfg.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">hotword.dic;</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords">stopword.dic;</entry>
</properties>
查看效果
package com.xiaoai.lucene;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;
public class TestTokenStream2 {
@Test
public void testTokenStream() throws Exception{
// 1)创建一个Analyzer对象,StandardAnalyzer对象
Analyzer analyzer = new IKAnalyzer();
// 2)使用分析器对象的tokenStream方法获得一个TokenStream对象
TokenStream tokenStream = analyzer.tokenStream("", "Lucene 是apache软件基金会一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎");
// 3)向TokenStream对象中设置一个引用,相当于数一个指针
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
// 4)调用TokenStream对象的rest方法。如果不调用抛异常
tokenStream.reset();
// 5)使用while循环遍历TokenStream对象
while (tokenStream.incrementToken()){
System.out.println(charTermAttribute.toString());
}
// 6)关闭TokenStream对象
tokenStream.close();
}
}
使用指定分析器 在基于directory对象创建一个IndexWriter对象时使用IndexWriterConfig()带参构造方法把分析器传入
IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig(new IKAnalyzer()));
索引库维护
域属性:是否分析(是否分词处理)、是否索引(索引方可查)、是否存储(存入文档,存入文档后field才可从document中取出)
上面创建索引可优化
//创建索引
@Test
public void createIndex() throws Exception{
// 1-创建director对象,指定索引库保存位置
// Directory directory = new RAMDirectory();//把索引库保存在内存
Directory directory = FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath());//把索引库保存在磁盘
// 2-基于directory对象创建一个IndexWriter对象
IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig(new IKAnalyzer()));
// 3-读取磁盘文件,对应每个文件创建一个文档对象
File dir = new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\02.参考资料\\searchsource");
File[] files = dir.listFiles();
for (File f : files) {
//读取文件名
String fileName = f.getName();
//文件路径
String filePath = f.getPath();
//文件内容
String fileContent = FileUtils.readFileToString(f,"utf-8");
//文件大小
long fileSize = FileUtils.sizeOf(f);
//创建field
//参数1:域名称 参数2:域内容 参数3:是否存储
Field fieldName = new TextField("name",fileName, Field.Store.YES);
// Field fieldPath = new TextField("path",filePath,Field.Store.YES);
Field fieldPath = new StoredField("path",filePath);
Field fieldContent = new TextField("content",fileContent,Field.Store.YES);
// Field fieldSize = new TextField("size",fileSize+"",Field.Store.YES);
Field fieldSizeValue = new LongPoint("size",fileSize);//分析、索引、不存储
Field fieldSizeStore = new StoredField("size",fileSize);//不分析、不索引、存储可取值
//4-域添加到文档对象中
Document document = new Document();
document.add(fieldName);
document.add(fieldPath);
document.add(fieldContent);
// document.add(fieldSize);
document.add(fieldSizeValue);
document.add(fieldSizeStore);
// 5-文档对象写入索引库
indexWriter.addDocument(document);
}
// 6-关闭indexwriter
indexWriter.close();
}
添加文档
//添加文档
@Test
public void addDocument() throws Exception{
//1--相当于创建索引库 创建indexWriter对象,需要ikanalyzer作为分析器 参数1:打开目录 参数2:配置
IndexWriter indexWriter = new IndexWriter(
FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()),
new IndexWriterConfig(new IKAnalyzer())
);
//2--document 创建一个document对象
Document document = new Document();
//3--域 向document添加域
document.add(new TextField("name","新添加的文件",Field.Store.YES));
document.add(new TextField("content","新添加的文件内容",Field.Store.NO));
document.add(new StoredField("path","d:/tem/helol"));
//把文档写入索引库
indexWriter.addDocument(document);
//关闭索引库
indexWriter.close();
}
删除文档
@Test
public void deleteAllDocument() throws Exception{
//1--相当于创建索引库 创建indexWriter对象,需要ikanalyzer作为分析器 参数1:打开目录 参数2:配置
IndexWriter indexWriter = new IndexWriter(
FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()),
new IndexWriterConfig(new IKAnalyzer())
);
//删除全部文档
indexWriter.deleteAll();
//关闭索引库
indexWriter.close();
}
根据term选择性删除文档
@Test
public void deleteDocumentByQuery() throws Exception{
//1--相当于创建索引库 创建indexWriter对象,需要ikanalyzer作为分析器 参数1:打开目录 参数2:配置
IndexWriter indexWriter = new IndexWriter(
FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()),
new IndexWriterConfig(new IKAnalyzer())
);
//删除文档
indexWriter.deleteDocuments(new Term("name","apache"));//把域名为name,其值包含apache的document删除
//关闭索引库
indexWriter.close();
}
修改文档: 原理:先删除,后添加
@Test
public void updateDocument() throws Exception{
//1--相当于创建索引库 创建indexWriter对象,需要ikanalyzer作为分析器 参数1:打开目录 参数2:配置
IndexWriter indexWriter = new IndexWriter(
FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()),
new IndexWriterConfig(new IKAnalyzer())
);
//创建一个新文档
Document document = new Document();
//向文档中添加域
document.add(new TextField("name","更新后的文档", Field.Store.YES));
document.add(new TextField("name1","更新后的文档1", Field.Store.YES));
document.add(new TextField("name2","更新后的文档2", Field.Store.YES));
//更新操作
indexWriter.updateDocument(new Term("name","spring"),document);//删除域名为name中包含spring值的document,又添加了上面新建的一个document
//关闭索引库
indexWriter.close();
}
索引库查询
1–使用query的子类
- TermQuery
- 根据关键词查询,需指定查询的域及要查询的关键词
- RangeQuery:范围查询
2–QueryPaser查询:先对查询的内容分词,然后基于分词结果进行查询 以上查询都是指定域和某一词查询,当查询只为一句话时可用QueryPaser查询,例如百度查询
- 添加jar:lucene-queryparser-7.4.0.jar
RangeQuery和QueryPaser查询代码
package com.xiaoai.lucene;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Before;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;
import java.io.File;
public class SearchIndex {
private IndexReader indexReader;
private IndexSearcher indexSearcher;
@Before
public void init() throws Exception{
//1--创建indexReader
indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()));
//2--由indexReader创建indexSearcher
indexSearcher = new IndexSearcher(indexReader);
}
//RangeQuery:范围查询
@Test
public void testRangeQuery() throws Exception{
//3--创建query对象
Query query = LongPoint.newRangeQuery("size", 0l, 100l);//最后一位为L表示long类型
//执行查询
printResult(query);
}
//查询并打印
public void printResult(Query query) throws Exception{
//4--执行查询获得topdocs对象
TopDocs topDocs = indexSearcher.search(query,10);
System.out.println("总记录数:"+topDocs.totalHits);
//获取文档数组
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc doc:scoreDocs ) {
//取文档id
int docID = doc.doc;
//根据id取文档对象
Document document = indexSearcher.doc(docID);
System.out.println(document.get("name"));
System.out.println(document.get("path"));
System.out.println(document.get("size"));
// System.out.println(document.get("content"));
System.out.println("---------------------------------------------分隔线");
}
indexReader.close();
}
//QueryPaser:查询内容先分词,后查询
@Test
public void testQueryPaser() throws Exception{
//创建一个QueryPaser对象,参数1:默认搜索域,参数2:分析器对象
QueryParser queryParser = new QueryParser("name",new IKAnalyzer());
//使用QueryPaser创建Query对象
Query query = queryParser.parse("lucene是java开发的全文检索工具包");
//执行查询
printResult(query);
}
}