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

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

动手学深度学习【数据操作+数据预处理】

import osos.makedirs(os.path.join('.', 'data'), exist_ok=True)data_file = os.path.join('.', 'data', 'house_tiny.csv')with open(data_file, 'w') as f:f.write('NumRooms,Alley,Price\n') # 列名f.write('NA

线程的四种操作

所属专栏:Java学习        1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链表中),此处的start会根据不同的系统,分别调用不同的api,创建好之后的线程,再单独去执行run(所以说,start的本质是调用系统api,系统的api

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

MySQL——表操作

目录 一、创建表 二、查看表 2.1 查看表中某成员的数据 2.2 查看整个表中的表成员 2.3 查看创建表时的句柄 三、修改表 alter 3.1 重命名 rename 3.2 新增一列 add 3.3 更改列属性 modify 3.4 更改列名称 change 3.5 删除某列 上一篇博客介绍了库的操作,接下来来看一下表的相关操作。 一、创建表 create

封装MySQL操作时Where条件语句的组织

在对数据库进行封装的过程中,条件语句应该是相对难以处理的,毕竟条件语句太过于多样性。 条件语句大致分为以下几种: 1、单一条件,比如:where id = 1; 2、多个条件,相互间关系统一。比如:where id > 10 and age > 20 and score < 60; 3、多个条件,相互间关系不统一。比如:where (id > 10 OR age > 20) AND sco

贝壳面试:什么是回表?什么是索引下推?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50+)中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题: 1.谈谈你对MySQL 索引下推 的认识? 2.在MySQL中,索引下推 是如何实现的?请简述其工作原理。 3、说说什么是 回表,什么是 索引下推 ? 最近有小伙伴在面试 贝壳、soul,又遇到了相关的

PHP7扩展开发之流操作

前言 啥是流操作?简单来讲就是对一些文件,网络的IO操作。PHP已经把这些IO操作,封装成流操作。这节,我们将使用PHP扩展实现一个目录遍历的功能。PHP示例代码如下: <?phpfunction list_dir($dir) {if (is_dir($dir) === false) {return;} $dh = opendir($dir);if ($dh == false) {ret

浙大数据结构:树的定义与操作

四种遍历 #include<iostream>#include<queue>using namespace std;typedef struct treenode *BinTree;typedef BinTree position;typedef int ElementType;struct treenode{ElementType data;BinTree left;BinTre

Mysql高级篇(中)——索引介绍

Mysql高级篇(中)——索引介绍 一、索引本质二、索引优缺点三、索引分类(1)按数据结构分类(2)按功能分类(3) 按存储引擎分类(4) 按存储方式分类(5) 按使用方式分类 四、 索引基本语法(1)创建索引(2)查看索引(3)删除索引(4)ALTER 关键字创建/删除索引 五、适合创建索引的情况思考题 六、不适合创建索引的情况 一、索引本质 索引本质 是 一种数据结构,它用