Lucene学习笔记
#
- 概念
- lucene本质上是一个jar包,专注于底层,提供了很多API供java实现对索引库的维护
- 索引库采用的是倒排索引的的技术:先查索引,如果有就会获得文档ID;拿到文档ID去文档数据中找到文档数据
2.相关依赖:
<dependencies>
<!-- Junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- lucene核心库 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>4.10.2</version>
</dependency>
<!-- Lucene的查询解析器 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>4.10.2</version>
</dependency>
<!-- lucene的默认分词器库 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.10.2</version>
</dependency>
<!-- lucene的高亮显示 -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>4.10.2</version>
</dependency>
<!-- IK分词器,适用于中文分词 -->
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
</dependencies>
3.索引写入
相关API
- Document:文档对象,原始数据,对象中的每一个记录,就是一个Field
- Store:枚举类型
- Store.No : 当前字段不在文档数据去存储,但是建立索引
- Store.YES : 当前字段在文档区存储,且建立索引
- Field:字段,即document中的记录,常用的有StringField(不分词),TextField(分词)等
- Directory:指定索引要存储的位置
- FSDirectory:文件系统目录:会把索引库保存到本地磁盘。特点:速度略慢,但是比较安全
- RAMDirectory:内存目录,将索引库保存到内存。特点:速度快,但是不安全
- IndexWriterConfig:设置配置信息,lucene的版本和分词器类型
- IndexWriter:索引写入器:
- Analyzer:分词器
代码实现
`public void indexCreate() throws IOException { // 创建文档对象 Document document = new Document(); // 添加字段,参数Field是一个接口,要new实现类的对象(StringField, TextField) // StringField的实例化需要3个参数:1-字段名,2-字段值,3-是否保存到文档,Store.YES存储,NO不存储 document.add(new StringField("id", "1", Store.YES)); // TextField:创建索引并提供分词,StringField创建索引但不分词 document.add(new TextField("title", "谷歌地图之父跳槽FaceBook", Store.YES)); // 创建目录对象,指定索引库的存放位置;FSDirectory文件系统;RAMDirectory内存 Directory directory = FSDirectory.open(new File("C:\\tmp\\indexDir")); // 创建分词器对象 Analyzer analyzer = new StandardAnalyzer(); // 创建索引写入器配置对象,第一个参数版本VerSion.LATEST,第一个参数分词器 IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, analyzer); // 创建索引写入器 IndexWriter indexWriter = new IndexWriter(directory , conf); // 向索引库写入文档对象 indexWriter.addDocument(document); // 提交 indexWriter.commit(); // 关闭 indexWriter.close(); }`
4.查询
- 相关API:
- QueryParser:单一字段解析器
- MultiFieldQueryParser:多域字段解析器
MultiFieldQueryParser pasrer =new MultiFieldQueryParser(new String[]{"id","content"},new IKAnalyzer);
Query query = parser.parse("2");
- Query:查询对象
- IndexSearcher:索引搜索对象
- IndexReader:索引读取对象
- TopDocs:查询结果对象
- ScoreDoc:得分文档
代码实现
public void testSearcher() throws IOException, ParseException{ // 初始化索引库对象 Directory directory = FSDirectory.open(new File("C:\\tmp\\index")); // 索引读取工具 IndexReader indexReader = DirectoryReader.open(directory); // 索引搜索对象 IndexSearcher indexSearcher = new IndexSearcher(indexReader); // 创建查询解析器对象 QueryParser parser = new QueryParser("title", new IKAnalyzer()); // 创建查询对象 Query query = parser.parse("谷歌"); // 执行搜索操作,返回值topDocs包含命中数,得分文档 TopDocs topDocs = indexSearcher.search(query, Integer.MAX_VALUE); // 打印命中数 System.out.println("一共命中:"+topDocs.totalHits+"条数据"); // 获得得分文档数组对象,得分文档对象包含得分和文档编号 ScoreDoc[] scoreDocs = topDocs.scoreDocs; for (ScoreDoc scoreDoc : scoreDocs) { System.out.println("得分:"+scoreDoc.score); // 文档的编号 int doc = scoreDoc.doc; System.out.println("编号:"+doc); // 获取文档对象,通过索引读取工具 Document document = indexReader.document(doc); System.out.println("id:"+document.get("id")); System.out.println("title:"+document.get("title")); }
}
- 几种特殊查询query。
- 字段查询
Query query = new TermQuery(new Term("title", "谷歌"));
// 执行搜索操作
searcher(query);
- 通配符查询
Query query = new WildcardQuery(new Term("title", "*歌*"));
search(query); - 模糊查询
Query query = new FuzzyQuery(new Term("title", "facebook"), 1);
search(query); - 数值范围查询
Query query = NumericRangeQuery.newLongRange("id", 2l, 4l, true, true);
search(query); - 组合查询
BooleanQuery query = new BooleanQuery();
// 交集: Occur.MUST + Occur.MUST
// 并集:Occur.SHOULD + Occur.SHOULD
// 非:Occur.MUST_NOT
query.add(query1, Occur.SHOULD);
query.add(query2, Occur.SHOULD);
- 字段查询
5.修改删除索引
修改索引:实质上是先删后加
代码实现
`// 创建文档对象 Document document = new Document(); document.add(new StringField("id", "9", Store.YES)); document.add(new TextField("title", "谷歌地图之父跳槽FaceBook", Store.YES)); // 索引库对象 Directory directory = FSDirectory.open(new File("C:\\tmp\\index")); // 索引写入器配置对象 IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, new IKAnalyzer()); // 索引写入器对象 IndexWriter indexWriter = new IndexWriter(directory, conf); // 执行更新操作 indexWriter.updateDocument(new Term("id", "1"), document); // 提交 indexWriter.commit(); // 关闭 indexWriter.close();
===========================
查询到所有 id 符合 1 的文档,并删除,然后将新的文档添加进去。`
删除索引:
代码实现
// 创建目录对象 Directory directory = FSDirectory.open(new File("C:\\tmp\\indexDir")); // 创建索引写入器配置对象 IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, new IKAnalyzer()); // 创建索引写入器对象 IndexWriter indexWriter = new IndexWriter(directory, conf); // 执行删除操作(根据词条),要求id字段必须是字符串类型 // indexWriter.deleteDocuments(new Term("id", "5")); // 根据查询条件删除 // indexWriter.deleteDocuments(NumericRangeQuery.newLongRange("id", 2l, 4l, true, false)); // 删除所有 indexWriter.deleteAll(); indexWriter.commit(); indexWriter.close();
6.其他功能
高亮显示:本质是给查询结果添加一个前后坠
代码实现
.... // 格式化器 设置查询结果的前缀后缀 Formatter formatter = new SimpleHTMLFormatter("<em>", "</em>"); Scorer scorer = new QueryScorer(query); // 准备高亮工具 Highlighter highlighter = new Highlighter(formatter, scorer); .... ScoreDoc[] scoreDocs = topDocs.scoreDocs; for (ScoreDoc scoreDoc : scoreDocs) { ... String title = doc.get("title"); // 用高亮工具处理普通的查询结果,参数:分词器,要高亮的字段的名称,高亮字段的原始值 String hTitle = highlighter.getBestFragment(new IKAnalyzer(), "title", title); System.out.println("title: " + hTitle); }
排序搜索:
代码实现
// 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转:如果是false,升序,true降序 Sort sort = new Sort(new SortField("id", Type.LONG, true)); // 搜索,搜索结果即为指定排序的顺序 TopDocs topDocs = searcher.search(query, 10,sort);
分页查询
代码实现
// 实际上Lucene本身不支持分页。因此我们需要自己进行逻辑分页。我们要准备分页参数: int pageSize = 2; // 每页条数 int pageNum = 3; // 当前页码 int start = (pageNum - 1) * pageSize; // 当前页的起始条数 int end = start + pageSize; // 当前页的结束条数(不能包含) // 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转如果是false,升序。true降序 Sort sort = new Sort(new SortField("id", Type.LONG, false)); // 搜索数据,查询0~end条 TopDocs topDocs = searcher.search(query, end,sort);
得分算法
代码实现
//lucene会对搜索结果打分,用来表示文档与词条关联性的强弱,得分越高,表示查询的匹配度越高,结果中出现的位置越靠前 //创建文档对象 Document document1 = new Document(); //添加字段,参数1-字段名,2-字段值,3-是否保存,Store枚举, document1.add(new StringField("id","1",Store.YES)); TextField field = newTextField("title","谷歌致富"); field.setBoost(100); document1.add(field);
7.IK分词器配置
在resource资源路径下新建: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">ext.dic;</entry> <!--用户可以在这里配置自己的扩展停止词字典--> <entry key="ext_stopwords">stopword.dic;</entry> </properties>`
- 扩展词典配置:按cfg.xml文件中配置的文件地址创建,直接在内容中写明需要识别为分词的词语即可,停止词词典同。