lucene学习
1. 非结构化数据: 指不定长或无固定格式的数据,如邮件,word文档等。非结构化数据又叫全文数据。
结构化数据: 指具有固定格式或有限长度的数据,如数据库,元数据等。
2. 全文检索的基本思路,即将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对有一定结构的数据进行搜索,从而达到快速搜索非结构化数据的目的
3. 索引流程:采集原始内容数据->创建文档->分析文档(分词)->创建索引
搜索流程:用户通过搜索界面->创建查询->执行搜索->从索引库搜索->渲染搜索结果
采集原始内容数据: 创建若干个文件.txt
创建文档:一个文件可以生成一个document,Document中包括一些Field(file_name文件名称、file_path文件路径、file_size文件大小、file_content文件内容),不同的文档其field的个数可以不同,建议相同类型的文档包括相同的field。
分析文档(分词):分词+过滤,去除停用词,大小写转换等
创建索引:创建索引是对语汇单元索引,通过词语找文档,这种索引的结构叫倒排索引结构。为磁盘上的文本文件创建索引,然后进行查找,凡是文件名或文件内容包括关键字的文件都要找出来,这里要对文件名和文件内容创建索引
4.Field是文档中的域,包括Field名和Field值两部分,一个文档可以包括多个Field,Document只是Field的一个承载体,Field值即为要索引的内容,也是要搜索的内容。
是否分词(tokenized)
是:作分词处理,即将Field值进行分词,分词的目的是为了索引。
比如:商品名称、商品简介等,这些内容用户要输入关键字搜索,由于搜索的内容格式大、内容多需要分词后将语汇单元索引。
否:不作分词处理
比如:商品id、订单号、身份证号等
是否索引(indexed)
是:进行索引。将Field分词后的词或整个Field值进行索引,索引的目的是为了搜索。
比如:商品名称、商品简介分析后进行索引,订单号、身份证号不用分析但也要索引,这些将来都要作为查询条件。
否:不索引。该域的内容无法搜索到
比如:商品id、文件路径、图片路径等,不用作为查询条件的不用索引。
是否存储(stored)
是:将Field值存储在文档中,存储在文档中的Field才可以从Document中获取。
比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。
否:不存储Field值,不存储的Field无法通过Document获取
比如:商品简介,内容较大不用存储。如果要向用户展示商品简介可以从系统的关系数据库中获取商品简介。
String file_name = file.getName(); Field fileNameField = new TextField("fileName", file_name, Field.Store.YES); // 文件内容 String file_content = FileUtils.readFileToString(file, "UTF-8"); Field fileContentField = new TextField("fileContent", file_content, Field.Store.NO);
5.实现
package com.albertyy.com; import org.apache.commons.io.FileUtils; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.*; 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; import java.nio.file.Paths; /** * 为磁盘上的文本文件创建索引,然后进行查找, * 凡是文件名或文件内容包括关键字(albert)的文件都要找出来 * @author albert */ public class LuceneTest1 {
// 创建索引 public void testIndex() throws Exception { /* * 第一步:创建一个indexwriter对象 * 1指定索引库的存放位置Directory对象 * 2指定一个分析器,对文档内容进行分析。 */ Directory directory = FSDirectory.open(Paths.get("D:\\temp\\index")); // Directory directory = new RAMDirectory();//保存索引到内存中 (内存索引库) // 官方推荐分词器,对中文不友好 Analyzer analyzer = new StandardAnalyzer(); IndexWriterConfig config = new IndexWriterConfig(analyzer); IndexWriter indexWriter = new IndexWriter(directory, config); // 第二步:通过IO读取磁盘上的文件信息 File f = new File("D:\\学习\\Lucene"); File[] listFiles = f.listFiles(); if(listFiles != null){ for (File file : listFiles) { if(file.isFile()){ // 第三步:创建document对象, 并把文件信息添加到document对象中 Document document = new Document(); // 文件名称 String file_name = file.getName(); Field fileNameField = new TextField("fileName", file_name, Field.Store.YES); // 文件路径 String file_path = file.getPath(); Field filePathField = new StoredField("filePath", file_path); // 文件大小 long file_size = FileUtils.sizeOf(file); //索引 Field fileSizeField1 = new LongPoint("fileSize", file_size); //存储 Field fileSizeField2 = new StoredField("fileSize", file_size); // 文件内容 String file_content = FileUtils.readFileToString(file, "UTF-8"); Field fileContentField = new TextField("fileContent", file_content, Field.Store.NO); document.add(fileNameField); document.add(fileSizeField1); document.add(fileSizeField2); document.add(filePathField); document.add(fileContentField); // 第四步:使用indexwriter对象将document对象写入索引库,此过程进行索引创建。并将索引和document对象写入索引库。 indexWriter.addDocument(document); } } // 第五步:关闭IndexWriter对象。 indexWriter.close(); } } // 搜索 public void testSearch() throws Exception { // 第一步:创建一个Directory对象,也就是索引库存放的位置。 Directory directory = FSDirectory.open(Paths.get("D:\\temp\\index")); // 第二步:创建一个indexReader对象,需要指定Directory对象。 IndexReader indexReader = DirectoryReader.open(directory); // 第三步:创建一个indexsearcher对象,需要指定IndexReader对象 IndexSearcher indexSearcher = new IndexSearcher(indexReader); // 第四步:创建一个TermQuery对象,指定查询的域和查询的关键词。 Query query = new TermQuery(new Term("fileName", "albert")); // 第五步:执行查询。 TopDocs topDocs = indexSearcher.search(query, 10); // 第六步:返回查询结果。遍历查询结果并输出。 ScoreDoc[] scoreDocs = topDocs.scoreDocs; for (ScoreDoc scoreDoc : scoreDocs) { int doc = scoreDoc.doc; Document document = indexSearcher.doc(doc); // 文件名称 String fileName = document.get("fileName"); System.out.println(fileName); // 文件内容 String fileContent = document.get("fileContent"); System.out.println(fileContent); // 文件大小 String fileSize = document.get("fileSize"); System.out.println(fileSize); // 文件路径 String filePath = document.get("filePath"); System.out.println(filePath); System.out.println("------------"); } // 第七步:关闭IndexReader对象 indexReader.close(); } }
删除索引:
//删除fileName为albert的索引 @Test public void testDelete() throws Exception { IndexWriter indexWriter = getIndexWriter(); Query query = new TermQuery(new Term("fileName","albert")); indexWriter.deleteDocuments(query); indexWriter.close(); }
修改索引:
//修改索引,把fileName为AlbertLucene.text的索引进行修改 @Test public void testUpdate() throws Exception { Directory directory = FSDirectory.open(Paths.get("D:\\temp\\index")); Analyzer analyzer = new StandardAnalyzer(); IndexWriterConfig config = new IndexWriterConfig(analyzer); IndexWriter indexWriter = new IndexWriter(directory, config); Document doc = new Document(); doc.add(new TextField("fileN", "testFileName111", Field.Store.YES)); doc.add(new TextField("fileC", "testFileContent111", Field.Store.YES)); indexWriter.updateDocument(new Term("fileName","AlbertLucene.text"), doc); indexWriter.close(); }
6.相关度排序
Lucene是通过打分来进行相关度排序的。
打分分两步:
- 根据词计算词的权重
- 根据词的权重进行打分
影响词的权重的方式有两种:Tf和Df
词在同一个文档中出现的频率, Tf越高,说明词的权重越高
词在多个文档中出现的频率,Df越高,说明词的权重越低
设置boost值来影响打分
创建索引时设置boost值
// 文件内容 String file_content = FileUtils.readFileToString(file, "UTF-8"); Field fileContentField = new TextField("fileContent", "测试设置BOOST值 lucene", Field.Store.NO); // 设置boost fileContentField.setBoost(10.0f);
搜索时设置boost值
IndexSearcher indexSearcher = getIndexSearcher();
String[] fields = {"fileName","fileContent"}; Map<String, Float> boosts = new HashMap<String, Float>(); boosts.put("fileName", 100f); //参数1: 默认查询的域 //参数2:采用的分析器 MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields,new StandardAnalyzer(), boosts); // *:* 域:值 Query query = queryParser.parse("apache1.0 lucene"); printResult(indexSearcher, query);