【Lucene3.6.2入门系列】第02节_针对索引文件的CRUD

[java]  view plain copy print ?
  1. package com.jadyer.lucene;  
  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.standard.StandardAnalyzer;  
  9. import org.apache.lucene.document.Document;  
  10. import org.apache.lucene.document.Field;  
  11. import org.apache.lucene.document.NumericField;  
  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.search.IndexSearcher;  
  17. import org.apache.lucene.search.Query;  
  18. import org.apache.lucene.search.ScoreDoc;  
  19. import org.apache.lucene.search.TermQuery;  
  20. import org.apache.lucene.search.TopDocs;  
  21. import org.apache.lucene.store.Directory;  
  22. import org.apache.lucene.store.FSDirectory;  
  23. import org.apache.lucene.util.Version;  
  24.   
  25. /** 
  26.  * 【Lucene3.6.2入门系列】第02节_针对索引文件的CRUD 
  27.  * @see ============================================================================================================= 
  28.  * @see Lucene官网:http://lucene.apache.org 
  29.  * @see Lucene下载:http://archive.apache.org/dist/lucene/java/ 
  30.  * @see Lucene文档:http://wiki.apache.org/lucene-java/ 
  31.  * @see ============================================================================================================= 
  32.  * @see 使用Luke查看分词信息(http://code.google.com/p/luke/) 
  33.  * @see 1)引言:每一个Lucene版本都会有一个相应的Luke文件 
  34.  * @see 2)打开:双击或java -jar lukeall-3.5.0.jar 
  35.  * @see 3)选择索引的存放目录后点击OK即可 
  36.  * @see 7)如果我们的索引有改变,可以点击右侧的Re-open按钮重新载入索引 
  37.  * @see 4)Luke界面右下角的Top ranking terms窗口中显示的就是分词信息。其中Rank列表示出现频率 
  38.  * @see 5)Luke菜单下的Documents选项卡中显示的就是文档信息,我们可以根据文档序号来浏览(点击向左和向右的方向箭头) 
  39.  * @see 6)Luke菜单下的Search选项卡中可以根据我们输入的表达式来查文档内容 
  40.  * @see   比如在Enter search expression here:输入content:my,再在右侧点击一个黑色粗体字的Search大按钮即可 
  41.  * @see ============================================================================================================= 
  42.  * @create Jun 30, 2012 4:34:09 PM 
  43.  * @author 玄玉<http://blog.csdn.net/jadyer> 
  44.  */  
  45. public class HelloIndex {  
  46.     /* 
  47.      * 定义一组数据,用来演示搜索(这里有一封邮件为例) 
  48.      * 假设每一个变量代表一个Document,这里就定义了6个Document 
  49.      */  
  50.     //邮件编号  
  51.     private String[] ids = {"1""2""3""4""5""6"};  
  52.     //邮件主题  
  53.     private String[] names = {"Michael""Scofield""Tbag""Jack""Jade""Jadyer"};  
  54.     //邮件地址  
  55.     private String[] emails = {"aa@jadyer.us""bb@jadyer.cn""cc@jadyer.cc""dd@jadyer.tw""ee@jadyer.hk""ff@jadyer.me"};  
  56.     //邮件内容  
  57.     private String[] contents = {"my blog""my website""my name""I am JavaDeveloper""I am from Haerbin""I like Lucene"};  
  58.     //邮件附件(为数字和日期加索引,与,字符串加索引的方式不同)  
  59.     private int[] attachs = {9,3,5,4,1,2};  
  60.     //邮件日期  
  61.     private Date[] dates = new Date[ids.length];  
  62.     //它的创建是比较耗时耗资源的,所以这里只让它创建一次,此时reader处于整个生命周期中,实际应用中也可能直接放到ApplicationContext里面  
  63.     private static IndexReader reader = null;  
  64.     private Directory directory = null;  
  65.   
  66.     public HelloIndex(){  
  67.         SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");  
  68.         try {  
  69.             dates[0] = sdf.parse("20120601");  
  70.             dates[1] = sdf.parse("20120603");  
  71.             dates[2] = sdf.parse("20120605");  
  72.             dates[3] = sdf.parse("20120607");  
  73.             dates[4] = sdf.parse("20120609");  
  74.             dates[5] = sdf.parse("20120611");  
  75.             directory = FSDirectory.open(new File("myExample/02_index/"));  
  76.         } catch (Exception e) {  
  77.             e.printStackTrace();  
  78.         }  
  79.     }  
  80.       
  81.       
  82.     /** 
  83.      * 获取IndexReader实例 
  84.      */  
  85.     private IndexReader getIndexReader(){  
  86.         try {  
  87.             if(reader == null){  
  88.                 reader = IndexReader.open(directory);  
  89.             }else{  
  90.                 //if the index was changed since the provided reader was opened, open and return a new reader; else,return null  
  91.                 //如果当前reader在打开期间index发生改变,则打开并返回一个新的IndexReader,否则返回null  
  92.                 IndexReader ir = IndexReader.openIfChanged(reader);  
  93.                 if(ir != null){  
  94.                     reader.close(); //关闭原reader  
  95.                     reader = ir;    //赋予新reader  
  96.                 }  
  97.             }  
  98.             return reader;  
  99.         }catch(Exception e) {  
  100.             e.printStackTrace();  
  101.         }  
  102.         return null//发生异常则返回null  
  103.     }  
  104.       
  105.       
  106.     /** 
  107.      * 通过IndexReader获取文档数量 
  108.      */  
  109.     public void getDocsCount(){  
  110.         System.out.println("maxDocs:" + this.getIndexReader().maxDoc());  
  111.         System.out.println("numDocs:" + this.getIndexReader().numDocs());  
  112.         System.out.println("deletedDocs:" + this.getIndexReader().numDeletedDocs());  
  113.     }  
  114.       
  115.       
  116.     /** 
  117.      * 创建索引 
  118.      */  
  119.     public void createIndex(){  
  120.         IndexWriter writer = null;  
  121.         Document doc = null;  
  122.         try{  
  123.             writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_36, new StandardAnalyzer(Version.LUCENE_36)));  
  124.             writer.deleteAll();              //创建索引之前,先把文档清空掉  
  125.             for(int i=0; i<ids.length; i++){ //遍历ID来创建文档  
  126.                 doc = new Document();  
  127.                 doc.add(new Field("id", ids[i], Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));  
  128.                 doc.add(new Field("name", names[i], Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));  
  129.                 doc.add(new Field("email", emails[i], Field.Store.YES, Field.Index.NOT_ANALYZED));  
  130.                 doc.add(new Field("content", contents[i], Field.Store.NO, Field.Index.ANALYZED));  
  131.                 doc.add(new NumericField("attach", Field.Store.YES, true).setIntValue(attachs[i]));        //为数字加索引(第三个参数指定是否索引)  
  132.                 doc.add(new NumericField("date", Field.Store.YES, true).setLongValue(dates[i].getTime())); //为日期加索引  
  133.                 /* 
  134.                  * 建立索引时加权 
  135.                  * 定义排名规则,即加权,这里是为指定邮件名结尾的emails加权 
  136.                  */  
  137.                 if(emails[i].endsWith("jadyer.cn")){  
  138.                     doc.setBoost(2.0f);  
  139.                 }else if(emails[i].endsWith("jadyer.me")){  
  140.                     doc.setBoost(1.5f); //为文档加权....默认为1.0,权值越高则排名越高,显示得就越靠前  
  141.                 }else{  
  142.                     doc.setBoost(0.5f); //注意它的参数类型是Float  
  143.                 }  
  144.                 writer.addDocument(doc);  
  145.             }  
  146.         }catch(Exception e) {  
  147.             e.printStackTrace();  
  148.         }finally{  
  149.             if(null != writer){  
  150.                 try {  
  151.                     writer.close();  
  152.                 } catch (IOException ce) {  
  153.                     ce.printStackTrace();  
  154.                 }  
  155.             }  
  156.         }  
  157.     }  
  158.       
  159.       
  160.     /** 
  161.      * 搜索文件 
  162.      */  
  163.     public void searchFile(){  
  164.         IndexSearcher searcher = new IndexSearcher(this.getIndexReader());  
  165.         Query query = new TermQuery(new Term("content""my")); //精确搜索:搜索"content"中包含"my"的文档  
  166.         try{  
  167.             TopDocs tds = searcher.search(query, 10);  
  168.             for(ScoreDoc sd : tds.scoreDocs){  
  169.                 Document doc = searcher.doc(sd.doc); //sd.doc得到的是文档的序号  
  170.                 //doc.getBoost()得到的权值与创建索引时设置的权值之间是不相搭的,创建索引时的权值的查看需要使用Luke工具  
  171.                 //              之所以这样,是因为这里的Document对象(是获取到的)与创建索引时的Document对象,不是同一个对象  
  172.                 //sd.score得到的是该文档的评分,该评分规则的公式是比较复杂的,它主要与文档的权值和出现次数成正比  
  173.                 System.out.print("(" + sd.doc + "|" + doc.getBoost() + "|" + sd.score + ")" + doc.get("name") + "[" + doc.get("email") + "]-->");  
  174.                 System.out.println(doc.get("id") + "," + doc.get("attach") + "," + new SimpleDateFormat("yyyyMMdd").format(new Date(Long.parseLong(doc.get("date")))));  
  175.             }  
  176.         }catch(Exception e){  
  177.             e.printStackTrace();  
  178.         }finally{  
  179.             if(null != searcher){  
  180.                 try {  
  181.                     searcher.close();  
  182.                 } catch (IOException e) {  
  183.                     e.printStackTrace();  
  184.                 }  
  185.             }  
  186.         }  
  187.     }  
  188.       
  189.       
  190.     /** 
  191.      * 更新索引 
  192.      * @see Lucene其实并未提供更新索引的方法,这里的更新操作内部是先删除再添加的方式 
  193.      * @see 因为Lucene认为更新索引的代价,与删除后重建索引的代价,二者是差不多的 
  194.      */  
  195.     public void updateIndex(){  
  196.         IndexWriter writer = null;  
  197.         Document doc = new Document();  
  198.         try{  
  199.             writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_36, new StandardAnalyzer(Version.LUCENE_36)));  
  200.             doc.add(new Field("id""1111", Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));  
  201.             doc.add(new Field("name", names[0], Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));  
  202.             doc.add(new Field("email", emails[0], Field.Store.YES, Field.Index.NOT_ANALYZED));  
  203.             doc.add(new Field("content", contents[0], Field.Store.NO, Field.Index.ANALYZED));  
  204.             doc.add(new NumericField("attach", Field.Store.YES, true).setIntValue(attachs[0]));  
  205.             doc.add(new NumericField("date", Field.Store.YES, true).setLongValue(dates[0].getTime()));  
  206.             //其实它会先删除索引文档中id为1的文档,然后再将这里的doc对象重新索引,所以即便这里的1!=1111,但它并不会报错  
  207.             //所以在执行完该方法后:maxDocs=7,numDocs=6,deletedDocs=1,就是因为Lucene会先删除再添加  
  208.             writer.updateDocument(new Term("id","1"), doc);  
  209.         }catch(Exception e) {  
  210.             e.printStackTrace();  
  211.         }finally{  
  212.             if(null != writer){  
  213.                 try {  
  214.                     writer.close();  
  215.                 } catch (IOException ce) {  
  216.                     ce.printStackTrace();  
  217.                 }  
  218.             }  
  219.         }  
  220.     }  
  221.       
  222.       
  223.     /** 
  224.      * 删除索引 
  225.      * @see ----------------------------------------------------------------------------------------------------- 
  226.      * @see 在执行完该方法后,再执行本类的searchFile()方法,得知numDocs=5,maxDocs=6,deletedDocs=1 
  227.      * @see 这说明此时删除的文档并没有被完全删除,而是存储在一个回收站中,它是可以恢复的 
  228.      * @see ----------------------------------------------------------------------------------------------------- 
  229.      * @see 从回收站中清空索引IndexWriter 
  230.      * @see 对于清空索引,Lucene3.5之前叫做优化,调用的是IndexWriter.optimize()方法,但该方法已被禁用 
  231.      * @see 因为optimize时它会全部更新索引,这一过程所涉及到的负载是很大的,于是弃用了该方法,使用forceMerge代替 
  232.      * @see 使用IndexWriter.forceMergeDeletes()方法可以强制清空回收站中的内容 
  233.      * @see 另外IndexWriter.forceMerge(3)方法会将索引合并为3段,这3段中的被删除的数据也会被清空 
  234.      * @see 但其在Lucene3.5之后不建议使用,因为其会消耗大量的开销,而Lucene会根据情况自动处理的 
  235.      * @see ----------------------------------------------------------------------------------------------------- 
  236.      */  
  237.     public void deleteIndex(){  
  238.         IndexWriter writer = null;  
  239.         try{  
  240.             writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_36, new StandardAnalyzer(Version.LUCENE_36)));  
  241.             //其参数可以传Query或Term....Query指的是可以查询出一系列的结果并将其全部删掉,而Term属于精确查找  
  242.             writer.deleteDocuments(new Term("id""1")); //删除索引文档中id为1的文档  
  243.         }catch(Exception e) {  
  244.             e.printStackTrace();  
  245.         }finally{  
  246.             if(null != writer){  
  247.                 try {  
  248.                     writer.close();  
  249.                 } catch (IOException ce) {  
  250.                     ce.printStackTrace();  
  251.                 }  
  252.             }  
  253.         }  
  254.     }  
  255.       
  256.       
  257.     /** 
  258.      * 恢复索引 
  259.      * @see 建议弃用 
  260.      */  
  261.     @Deprecated  
  262.     public void unDeleteIndex(){  
  263.         IndexReader reader = null;  
  264.         try {  
  265.             //IndexReader.open(directory)此时该IndexReader默认的readOnly=true,而在恢复索引时应该指定其为非只读的  
  266.             reader = IndexReader.open(directory, false);  
  267.             //Deprecated. Write support will be removed in Lucene 4.0. There will be no replacement for this method.  
  268.             reader.undeleteAll();  
  269.         } catch (Exception e) {  
  270.             e.printStackTrace();  
  271.         }finally{  
  272.             if(null != reader){  
  273.                 try {  
  274.                     reader.close();  
  275.                 } catch (IOException e) {  
  276.                     e.printStackTrace();  
  277.                 }  
  278.             }  
  279.         }  
  280.     }  
  281. }  


下面是用JUnit4.x写的一个小测试

[java]  view plain copy print ?
  1. package com.jadyer.test;  
  2.   
  3. import org.junit.After;  
  4. import org.junit.Before;  
  5. import org.junit.Test;  
  6.   
  7. import com.jadyer.lucene.HelloIndex;  
  8.   
  9. public class HelloIndexTest {  
  10.     private HelloIndex hello;  
  11.       
  12.     @Before  
  13.     public void init(){  
  14.         hello = new HelloIndex();  
  15.     }  
  16.       
  17.     @After  
  18.     public void destroy(){  
  19.         hello.getDocsCount();  
  20.     }  
  21.       
  22.       
  23.     @Test  
  24.     public void createIndex(){  
  25.         hello.createIndex();  
  26.     }  
  27.       
  28.       
  29.     @Test  
  30.     public void searchFile(){  
  31.         hello.searchFile();  
  32.     }  
  33.       
  34.       
  35.     @Test  
  36.     public void updateIndex(){  
  37.         hello.updateIndex();  
  38.     }  
  39.       
  40.       
  41.     @Test  
  42.     public void deleteIndex(){  
  43.         hello.deleteIndex();  
  44.     }  
  45.       
  46.       
  47.     @Test  
  48.     @SuppressWarnings("deprecation")  
  49.     public void unDeleteIndex(){  
  50.         hello.unDeleteIndex();  
  51.     }  
  52. }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值