Lucence 学习笔记
Lucence 学习笔记
什么是全文检索
数据的分类
- 结构化数据: 格式固定,长度固定,数据类型固定。例如数据库中的数据。
- 非结构化数据: Word文档,PDF文档,邮件,XML等格式不固定,长度不固定,数据类型不固定。
数据的查询
- 结构化查询。 SQL语句。简单、速度块。
- 非结构化数据的查询。 把非结构化数据变成结构化数据即可。把字符串拆分,得到一个单词列表。基于单词列表创建一个索引。
- 全文检索,就是先创建索引再查询索引的过程。索引一次创建,多次使用。
全文检索的应用场景
- 搜索引擎。
- 站内搜索。
- 电商搜索。
- 只要是有搜索的地方就可以使用全文检索。
什么是lucence
Luence是基于Java开发的全文检索工具包。
lucence实现全文检索的流程
- 创建索引
1)获得文档
2)构建文档对象
对应每个原始文档创建一个Document对象。每个Document对象中包含多个域(field),域中保存的就是原始文档的数据。域包括域的名称以及域的值。每个文档都有唯一的编号,文档ID。
3)分析文档
就是分词的过程。先根据空格进行单词的拆分。得到单词列表。再把单词全部转换为小写。去除标点符号。去除停用词(无意义的词)。每个关键词都封装成Term对象。Term包含两部分内容:关键词所在的域和关键词本身。不同的域中拆分出来的相同的关键词是不同的Term。
4)创建索引。
基于关键词列表创建一个索引,保存到索引库中。索引库中包含索引,Document对象,关键词和文档的对应关系。 - 查询索引。
1)用户查询接口
2)把关键词封装成查询对象,包括要查询的域和搜索的关键词。
3)执行查询,根据要查询单关键词到对应的域上进行搜索。
4)渲染结果
入门程序
- 引入依赖
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>8.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>8.2.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
- 创建一个Director对象,指定索引库保存的位置。
- 基于Director对象,创建一个IndexWriter对象
- 读取磁盘上的文件,对应每个文件创建一个文档对象
- 向文档对象中添加域。
- 把文档对象写入索引库。
- 关闭IndexWriter对象。
@Test
public void createIndex() throws Exception {
//2. 创建一个Director对象,指定索引库保存的位置。
Directory directory = FSDirectory.open(new File("/home/gaolei/JavaProgram/Learn-Lucene/src/main/resources/index").toPath());
//3. 基于Director对象,创建一个IndexWriter对象
IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig());
//4. 读取磁盘上的文件,对应每个文件创建一个文档对象
File dir = new File("/home/gaolei/JavaProgram/Learn-Lucene/src/main/resources/file");
System.out.println(dir.getAbsolutePath());
System.out.println(dir.isDirectory());
File[] files = dir.listFiles();
for (File file : files) {
//取文件名
String fileName = file.getName();
//取文件路径
String filePath = file.getPath();
//文件的内容
String fileContent = FileUtils.readFileToString(file, "utf-8");
//文件大小
long fileSize = FileUtils.sizeOf(file);
//创建field
//参数1:域的名称
//参数2:域的内容
//参数3:是否存储
Field filedName = 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);
//5. 向文档对象中添加域。
//创建文档对象
Document document = new Document();
document.add(filedName);
document.add(fieldPath);
document.add(fieldContent);
document.add(fieldSize);
//6. 把文档对象写入索引库。
indexWriter.addDocument(document);
}
//7. 关闭IndexWriter对象。
indexWriter.close();
}
//写resource里的文件,就这样写:"classpath:filename"
8. 使用Luke可以查看索引里的内容
9. 查询索引库,
1)创建一个Director对象,指定索引库的位置
2)创建一个IndexReader 对象
3)创建一个IndexSearcher对象
4)创建一个Query对象,TermQuery根据关键词查询
5)执行查询,得到一个TopDocs对象
6)取查询结果的总记录数。
7)取文档列表
8)打印文档中的内容
9)关闭IndexReader对象
@Test
public void searchIndex() throws Exception {
// 1)创建一个Director对象,指定索引库的位置
Directory directory = FSDirectory.open(new File("/home/gaolei/JavaProgram/Learn-Lucene/src/main/resources/index").toPath());
// 2)创建一个IndexReader 对象
IndexReader indexReader = DirectoryReader.open(directory);
// 3)创建一个IndexSearcher对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 4)创建一个Query对象,TermQuery根据关键词查询
Query query = new TermQuery(new Term("content", "string"));
// 5)执行查询,得到一个TopDocs对象
//参数1查询对象 参数2:查询结果返回的最大记录数
TopDocs topd = indexSearcher.search(query, 10);
// 6)取查询结果的总记录数。
System.out.println("总记录数" + topd.totalHits);
// 7)取文档列表
ScoreDoc[] scoreDocs = topd.scoreDocs;
// 8)打印文档中的内容
for (ScoreDoc scoreDoc : scoreDocs) {
//取文档ID
int docId = scoreDoc.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("------------------------------------------");
}
// 9)关闭IndexReader对象
indexReader.close();
}
分析器对象
默认使用标准分析器,StandardAnalyzer。
查看分析器的分析结果
使用Analyzer对象的tokenStream方法返回一个TokenStream对象,词对象中包含了最终分词结果。
实现步骤:
- 创建一个Analyzer对象,StandardAnalyzer对象
- 使用分析器对象的TokenStream方法获得一个TokenStream对象。
- 向TokenStream对象中设置一个引用,相当于一个指针。
- 调用TokenStream对象的reset方法,如果不调用抛异常
- 使用while循环来遍历TokenStream对象
- 关闭TokenStream对象。
@Test
public void testTokenStream() throws Exception {
//1. 创建一个Analyzer对象,StandardAnalyzer对象
Analyzer analyzer = new StandardAnalyzer();
//2. 使用分析器对象的TokenStream方法获得一个TokenStream对象。
TokenStream tokenStream = analyzer.tokenStream("", "i want to fuck the world~");
//3. 向TokenStream对象中设置一个引用,相当于一个指针。
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
//4. 调用TokenStream对象的reset方法,如果不调用抛异常
tokenStream.reset();
//5. 使用while循环来遍历TokenStream对象
while (tokenStream.incrementToken()) {
System.out.println(charTermAttribute.toString());
}
//6. 关闭TokenStream对象。
tokenStream.close();
}
IKAnalyzer
很棒的中文分词器
- 引入jar包
- 把配置文件,扩展词典,停用词典都添加到classpath下。
- 扩展词典严禁使用windows记事本编辑的。扩展词典可以添加一些新词。停用词词典是无意义的词或者是敏感词汇。
想使用ik分词器也很简单,在新建一个IndexWriterConfig对象的时候,传入一个ikAnalyzer对象参数进去就好。
索引库维护
删除索引
删除所有: indexWriter.deleteAll();
根据查询来删除: indexWriter.deleteDocuments(new Term(field, text));
修改索引
使用TermQuery来查询: indexWriter.updateDocument(new Term(field, text), new Document);
范围查询:
// 创建一个Query对象
Query query = LongPoint.newRangeQuery(field, lowvalue, upvalue);
//执行查询
TopDocs topDocs = indexSearcher.search(query, 10);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc;
Document document = indexSearcher.doc(docId);
...
}
使用QueryParser来查询
可以对要查询的内容先分词,然后基于分词的结果进行查询。
@Test
public void testQueryParser() throws Exception {
//创建一个QueryParser对象,两个参数,1是默认搜索域,2是分析器对象
QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
//使用QueryParser对象创建一个Query对象。
Query query = queryParser.parse("天青色等烟雨");
// 执行查询
// 1)创建一个Director对象,指定索引库的位置
Directory directory = FSDirectory.open(new File("/home/gaolei/JavaProgram/Learn-Lucene/src/main/resources/index").toPath());
// 2)创建一个IndexReader 对象
IndexReader indexReader = DirectoryReader.open(directory);
// 3)创建一个IndexSearcher对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
TopDocs topd = indexSearcher.search(query, 10);
// 6)取查询结果的总记录数。
System.out.println("总记录数" + topd.totalHits);
// 7)取文档列表
ScoreDoc[] scoreDocs = topd.scoreDocs;
// 8)打印文档中的内容
for (ScoreDoc scoreDoc : scoreDocs) {
//取文档ID
int docId = scoreDoc.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("------------------------------------------");
}
// 9)关闭IndexReader对象
indexReader.close();
}
尘嚣看不见,你沉醉了没