Lucene 实例教程(三)之操作索引

2024-03-16 01:32

本文主要是介绍Lucene 实例教程(三)之操作索引,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自作者:永恒の_☆ 地址:http://blog.csdn.net/chenghui0317/article/details/10281311


最近研究数据库模糊查询,发现oracle数据库中虽然可以用instr来替代like提高效率,但是这个效率提高是有瓶颈的,可以用搜索引擎技术来进一步提高查询效率

一、 前言

       前面简单介绍了如何使用Lucene将索引 写入磁盘,并且提供IKAnalyzer中文分词器操作索引和检索索引文件,地址:http://blog.csdn.net/chenghui0317/article/details/10281311

       在项目实战中,这还不能完全解决一些比较棘手的问题,比如:

       1、之前介绍了如何写入索引,但是没有提供删除索引和修改索引的操作,所以索引文件的累加会产生脏数据或者数据重复。


二、使用Lucene实战


1、使用Lucene操作索引文件

实现的思路如下:

   <1> 原先使用的IndexReader操作索引不是很方便,只有执行IndexReader.Close()时,删除操作才会被真正执行,并且这种方式的IndexReader实例方法被改为过期的方法,所以这种方式不可取;

   <2> 既然写入索引的时候使用的是IndexWriter索引写入器,同样删除和修改也一样使用它来完成;

   <3> 写入索引的实现方式:还是跟以前一样 使用IndexWriter写入器调用addDocument()即可;

   <4> 删除索引的实现方式:需要先判断一下索引文件是否存在,如果存在使用IndexWriter写入器调用deleteDocument()即可;

   <5> 修改索引的实现方式:由于IndexWriter写入器没有直接提供updateDocument(),所以可以先将要修改前的索引删除了然后再写入修改后的索引,实现效果一样;

   <6> 最后不要忘记把IndexWriter 关闭了,否则资源一直被占用,浪费内存开销。

具体代码如下:

[java]  view plain copy
print ?
  1. package com.lucene.test;  
  2.   
  3. import java.io.File;  
  4. import java.io.IOException;  
  5. import java.text.SimpleDateFormat;  
  6. import java.util.Date;  
  7.   
  8. import org.apache.lucene.analysis.Analyzer;  
  9. import org.apache.lucene.document.Document;  
  10. import org.apache.lucene.document.Field;  
  11. import org.apache.lucene.index.CorruptIndexException;  
  12. import org.apache.lucene.index.IndexReader;  
  13. import org.apache.lucene.index.IndexWriter;  
  14. import org.apache.lucene.index.IndexWriterConfig;  
  15. import org.apache.lucene.index.Term;  
  16. import org.apache.lucene.queryParser.ParseException;  
  17. import org.apache.lucene.queryParser.QueryParser;  
  18. import org.apache.lucene.search.Query;  
  19. import org.apache.lucene.store.Directory;  
  20. import org.apache.lucene.store.LockObtainFailedException;  
  21. import org.apache.lucene.store.SimpleFSDirectory;  
  22. import org.apache.lucene.util.Version;  
  23. import org.wltea.analyzer.lucene.IKAnalyzer;  
  24.   
  25. import com.lucene.entity.Article;  
  26.   
  27. /** 
  28.  * Lucene 检索和操作索引的例子 
  29.  * @author Administrator 
  30.  * 
  31.  */  
  32. public class OperateIndexDemo {  
  33.       
  34.     public static final String INDEX_DIR_PATH = "indexDir";  
  35.     /* 创建简单中文分析器 创建索引使用的分词器必须和查询时候使用的分词器一样,否则查询不到想要的结果 */  
  36.     private Analyzer analyzer = null;  
  37.     // 索引保存目录  
  38.     private File indexFile = null;  
  39.     //目录对象,因为操作索引文件都要用到它,所以定义为全局变量  
  40.     private Directory directory = null;  
  41.     //创建IndexWriter索引写入器  
  42.     IndexWriterConfig indexWriterConfig = null;  
  43.       
  44.     SimpleDateFormat simpleDateFormat = null;  
  45.       
  46.     /** 
  47.      * 初始化方法 
  48.      * @throws IOException  
  49.      */  
  50.     public void init() throws IOException{  
  51.         analyzer = new IKAnalyzer(true);  
  52.         indexFile = new File(INDEX_DIR_PATH);  
  53.         directory = new SimpleFSDirectory(indexFile);  
  54.           
  55.         simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
  56.         System.out.println("*****************初始化成功**********************");  
  57.     }  
  58.       
  59.     /** 
  60.      * 将article对象的属性全部封装成document对象,重构之后方便创建索引 
  61.      * @param article 
  62.      * @return 
  63.      */  
  64.     public Document createDocument(Article article){  
  65.         Document document = new Document();  
  66.         document.add(new Field("id", article.getId().toString(),Field.Store.YES, Field.Index.NOT_ANALYZED));  
  67.         document.add(new Field("title", article.getTitle().toString(),Field.Store.YES, Field.Index.ANALYZED));  
  68.         document.add(new Field("content", article.getContent().toString(),Field.Store.YES, Field.Index.ANALYZED));  
  69.           
  70.         return document;  
  71.     }  
  72.       
  73.     /** 
  74.      * 获得指定格式的时间字符串 
  75.      * @return 
  76.      */  
  77.     public String getDate(){  
  78.         return simpleDateFormat.format(new Date());  
  79.     }  
  80.   
  81.     /** 
  82.      * 为了如实反映操作索引文件之后的效果,每次操作之后查询索引目录下所有的索引内容 
  83.      * @throws IOException  
  84.      * @throws CorruptIndexException  
  85.      */  
  86.     public void openIndexFile() throws CorruptIndexException, IOException{  
  87.         System.out.println("*****************读取索引开始**********************");  
  88.         IndexReader indexReader = IndexReader.open(directory);  
  89.         int docLength = indexReader.maxDoc();  
  90.         for (int i = 0; i < docLength; i++) {  
  91.             Document doc = indexReader.document(i);  
  92.             Article article = new Article();  
  93.             if (doc.get("id") == null) {  
  94.                 System.out.println("id为空");  
  95.             } else {  
  96.                 article.setId(Integer.parseInt(doc.get("id")));  
  97.                 article.setTitle(doc.get("title"));  
  98.                 article.setContent(doc.get("content"));  
  99.             }  
  100.             System.out.println(article);  
  101.         }  
  102.         System.out.println("*****************读取索引结束**********************\n");  
  103.     }  
  104.       
  105.     /** 
  106.      * 创建索引到索引文件中 
  107.      * @param article 
  108.      * @throws IOException  
  109.      * @throws LockObtainFailedException  
  110.      * @throws CorruptIndexException  
  111.      */  
  112.     public void createIndex(Article article) throws CorruptIndexException, LockObtainFailedException, IOException{  
  113.         indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, analyzer);  
  114.         IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);  
  115.         indexWriter.addDocument(createDocument(article));  
  116.           
  117.         indexWriter.close();  
  118.         System.out.println("[ " + getDate() + " ] Lucene写入索引到 [" + indexFile.getAbsolutePath() + "] 成功。");  
  119.     }  
  120.       
  121.     /** 
  122.      * 根据文件中的id删除对应的索引文件 
  123.      * @param contentId 
  124.      * @throws IOException  
  125.      * @throws ParseException  
  126.      */  
  127.     public void deleteIndex(String contentId) throws IOException, ParseException{  
  128.         //判断索引文件目录内容是否有索引,有返回true ,没有返回false  
  129.         if(IndexReader.indexExists(directory)){   
  130.             indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, analyzer);  
  131.             IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);  
  132.             //封装term去检索字段名为id ,具体值为contentId的记录,如果存在就会删除,否则什么都不做  
  133.             indexWriter.deleteDocuments(new Term("id",contentId));  
  134.             /* 
  135.             QueryParser queryParser = new QueryParser(Version.LUCENE_36, "id", analyzer); 
  136.             Query query = queryParser.parse(contentId); 
  137.             indexWriter.deleteDocuments(query); 
  138.             */  
  139.             indexWriter.close();  
  140.             System.out.println("[ " + getDate() + " ] Lucene删除索引到 [" + indexFile.getAbsolutePath() + "] 成功。");  
  141.         }else{  
  142.             throw new IOException("[ " + getDate() + " ] Lucene删除索引失败,在 " + indexFile.getAbsolutePath() + "目录中没有找到索引文件。" );  
  143.         }  
  144.     }  
  145.       
  146.     /** 
  147.      * 由于实际的内容修改,所以索引文件也要跟着修改 ,具体实现方式就是先删除索引,然后重新添加 
  148.      * @param article 
  149.      * @throws IOException  
  150.      * @throws ParseException  
  151.      */  
  152.     public void updateIndex(Article article) throws IOException, ParseException{  
  153.         deleteIndex(article.getId().toString());  
  154.         createIndex(article);  
  155.     }  
  156.     /** 
  157.      * 销毁当前的操作类的实现,主要关闭资源的连接 
  158.      *  
  159.      * @throws IOException  
  160.      */  
  161.     public void destory() throws IOException{  
  162.         analyzer.close();  
  163.         directory.close();  
  164.         System.out.println("*****************销毁成功**********************");  
  165.     }  
  166.       
  167.     public static void main(String[] args) {  
  168.         OperateIndexDemo luceneInstance = new OperateIndexDemo();  
  169.         try {  
  170.             luceneInstance.init();//初始化  
  171.               
  172.             Article article = new Article(1,"标题不是很长","内容也不长。但是句号很长。。。。。。。。。。");  
  173.             luceneInstance.createIndex(article);  
  174.             luceneInstance.openIndexFile();  
  175.               
  176.             luceneInstance.deleteIndex("1");  
  177.             luceneInstance.openIndexFile();  
  178.               
  179.             //article = new Article(1,"修改之后的标题","内容变短了");  
  180.             //luceneInstance.updateIndex(article);  
  181.             //luceneInstance.openIndexFile();  
  182.         } catch (IOException e) {  
  183.             e.printStackTrace();  
  184.         } catch (ParseException e) {  
  185.             e.printStackTrace();  
  186.         }finally{  
  187.             try {  
  188.                 luceneInstance.destory();//销毁  
  189.             } catch (IOException e) {  
  190.                 e.printStackTrace();  
  191.             }  
  192.         }  
  193.     }  
  194. }  

上面的代码 做了一些重构和优化,每一个方法都只做一件事情,分工非常明确。

    init()  ------> 用于实例化全局的变量;

    createDocument(Article article)  ------> 用于将article对象的属性全部封装成document对象,重构之后方便创建索引;

    openIndexFile()  ------> 为了如实反映操作索引文件之后的效果,每次操作之后查询索引目录下所有的索引内容;

    createIndex(Article article)  ------> 用于将索引写入磁盘中;

    deleteIndex(String contentId)  ------> 用于将索引文件的索引删除;

    updateIndex(Article article)  ------> 用于修改索引文件中的索引;

    destory()  ------> 用于释放资源 关闭目录连接。

    <1>首先,调用createDocument(Article article) ,具体效果如下:

根据提示,然后看到索引文件目录,表明 索引已经写入成功了。

另外需要说明的是:

    1、每次操作的 IndexWriterConfig的实例不能被使用两次以上,否则如下错误:

Exception in thread "main" org.apache.lucene.util.SetOnce$AlreadySetException: The object cannot be set twice!

所以只能实例化后用一次,即使是同一类型的操作也不行。


    2、整个应用程序的执行周期是:(1)实例化;    (2)初始化;    (3)具体操作;    (4)销毁。    

如果directory已经关闭还继续操作directory目录对象,就会报如下错误:

Exception in thread "main" org.apache.lucene.store.AlreadyClosedException: this Directory is closed



    <2>接下里看看deleteIndex(String contentId)这个方法,先要判断索引文件内是否有索引文件,如果有 则会去索引目录下找对应的索引,有则会删除,没有则什么都不做。

具体可以实践一下,先把索引文件在索引目录中全部删除,然后执行deleteIndex(String contentId)方法,具体效果如下:



然后重新写入索引之后,并且删除是指定添加的article 的id ,再执行以下,具体效果如下:

删除之后再查询索引文件,发现已经没有对应的记录了,表明索引删除成功。另外如果删除的时候指定一个不存在的id,可以看到Lucene什么都不会做。


另外,根据eclipse的方法提示如下:

该方法支持Term 和Query的重载,并且参数个数没有上限,所以IndexWriter提供的deleteDocuments方法既支持单个索引的删除也支持多个索引的删除,只需要满足条件即可。  

现在把indexWriter.deleteDocuments(new Term("id",contentId));注释掉,替换取消下面注释的代码,然后在运行一下删除操作,具体效果和上面的一样,所以两种传参显示删除索引的方法都可以显示删除索引。  并且使用Term 的实例删除一般用于单个索引的删除,使用Query的实例删除一般用于单个以上的索引删除。


    <3>最后看看updateIndex(Article article),该方法最简单,直接调用createIndex(Article article) 和 deleteIndex(String contentId) ,为什么IndexWriter没有提供update方法,可能是因为索引写入到索引文件之后就不允许破坏其存储结构,也许数据表记录的修改操作也是一样,先删除再新增。

执行该方法的具体效果图如下:


根据图中最后打开索引的结果显示 索引已经被修改成功了。


OK,操作索引的基本功能已经全部实现了。     纸上读来终觉浅,绝知此事要躬行。

这篇关于Lucene 实例教程(三)之操作索引的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/813903

相关文章

Python调用Orator ORM进行数据库操作

《Python调用OratorORM进行数据库操作》OratorORM是一个功能丰富且灵活的PythonORM库,旨在简化数据库操作,它支持多种数据库并提供了简洁且直观的API,下面我们就... 目录Orator ORM 主要特点安装使用示例总结Orator ORM 是一个功能丰富且灵活的 python O

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

Python利用自带模块实现屏幕像素高效操作

《Python利用自带模块实现屏幕像素高效操作》这篇文章主要为大家详细介绍了Python如何利用自带模块实现屏幕像素高效操作,文中的示例代码讲解详,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、获取屏幕放缩比例2、获取屏幕指定坐标处像素颜色3、一个简单的使用案例4、总结1、获取屏幕放缩比例from

通过prometheus监控Tomcat运行状态的操作流程

《通过prometheus监控Tomcat运行状态的操作流程》文章介绍了如何安装和配置Tomcat,并使用Prometheus和TomcatExporter来监控Tomcat的运行状态,文章详细讲解了... 目录Tomcat安装配置以及prometheus监控Tomcat一. 安装并配置tomcat1、安装

Python中操作Redis的常用方法小结

《Python中操作Redis的常用方法小结》这篇文章主要为大家详细介绍了Python中操作Redis的常用方法,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解一下... 目录安装Redis开启、关闭Redisredis数据结构redis-cli操作安装redis-py数据库连接和释放增

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允