【Lucene3.6.2入门系列】第03节_简述Lucene中常见的搜索功能

本文详细介绍了Lucene中各种常见的搜索功能,包括精确匹配、范围搜索、数字搜索、前缀搜索、通配符搜索、模糊搜索、多条件搜索、短语搜索及基于QueryParser的搜索等,并提供了普通分页搜索和基于searchAfter的分页搜索实现。
摘要由CSDN通过智能技术生成
[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.queryParser.ParseException;  
  17. import org.apache.lucene.queryParser.QueryParser;  
  18. import org.apache.lucene.search.BooleanQuery;  
  19. import org.apache.lucene.search.FuzzyQuery;  
  20. import org.apache.lucene.search.IndexSearcher;  
  21. import org.apache.lucene.search.NumericRangeQuery;  
  22. import org.apache.lucene.search.PhraseQuery;  
  23. import org.apache.lucene.search.PrefixQuery;  
  24. import org.apache.lucene.search.Query;  
  25. import org.apache.lucene.search.ScoreDoc;  
  26. import org.apache.lucene.search.TermQuery;  
  27. import org.apache.lucene.search.TermRangeQuery;  
  28. import org.apache.lucene.search.TopDocs;  
  29. import org.apache.lucene.search.WildcardQuery;  
  30. import org.apache.lucene.search.BooleanClause.Occur;  
  31. import org.apache.lucene.store.Directory;  
  32. import org.apache.lucene.store.FSDirectory;  
  33. import org.apache.lucene.util.Version;  
  34.   
  35. /** 
  36.  * 【Lucene3.6.2入门系列】第03节_简述Lucene中常见的搜索功能 
  37.  * @create Aug 1, 2013 3:54:27 PM 
  38.  * @author 玄玉<http://blog.csdn.net/jadyer> 
  39.  */  
  40. public class HelloSearch {  
  41.     private Directory directory;  
  42.     private IndexReader reader;  
  43.     private String[] ids = {"1""2""3""4""5""6"};  
  44.     private String[] names = {"Michael""Scofield""Tbag""Jack""Jade""Jadyer"};  
  45.     private String[] emails = {"aa@jadyer.us""bb@jadyer.cn""cc@jadyer.cc""dd@jadyer.tw""ee@jadyer.hk""ff@jadyer.me"};  
  46.     private String[] contents = {"my java blog is http://blog.csdn.net/jadyer""my website is http://www.jadyer.cn""my name is jadyer""I am JavaDeveloper""I am from Haerbin""I like Lucene"};  
  47.     private int[] attachs = {9,3,5,4,1,2};  
  48.     private Date[] dates = new Date[ids.length];  
  49.       
  50.     public HelloSearch(){  
  51.         IndexWriter writer = null;  
  52.         Document doc = null;  
  53.         SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");  
  54.         try {  
  55.             dates[0] = sdf.parse("20120601");  
  56.             dates[1] = sdf.parse("20120603");  
  57.             dates[2] = sdf.parse("20120605");  
  58.             dates[3] = sdf.parse("20120607");  
  59.             dates[4] = sdf.parse("20120609");  
  60.             dates[5] = sdf.parse("20120611");  
  61.             directory = FSDirectory.open(new File("myExample/03_index/"));  
  62.             writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_36, new StandardAnalyzer(Version.LUCENE_36)));  
  63.             writer.deleteAll();              //创建索引之前,先把文档清空掉  
  64.             for(int i=0; i<ids.length; i++){ //遍历ID来创建文档  
  65.                 doc = new Document();  
  66.                 doc.add(new Field("id", ids[i], Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));  
  67.                 doc.add(new Field("name", names[i], Field.Store.YES, Field.Index.ANALYZED_NO_NORMS));  
  68.                 doc.add(new Field("email", emails[i], Field.Store.YES, Field.Index.NOT_ANALYZED));  
  69.                 doc.add(new Field("email""test"+i+""+i+"@jadyer.com", Field.Store.YES, Field.Index.NOT_ANALYZED));  
  70.                 doc.add(new Field("content", contents[i], Field.Store.NO, Field.Index.ANALYZED));  
  71.                 doc.add(new NumericField("attach", Field.Store.YES, true).setIntValue(attachs[i]));        //为数字加索引(第三个参数指定是否索引)  
  72.                 doc.add(new NumericField("attach", Field.Store.YES, true).setIntValue((i+1)*100));         //假设有多个附件  
  73.                 doc.add(new NumericField("date", Field.Store.YES, true).setLongValue(dates[i].getTime())); //为日期加索引  
  74.                 writer.addDocument(doc);  
  75.             }  
  76.         } catch (Exception e) {  
  77.             e.printStackTrace();  
  78.         } finally {  
  79.             if(null != writer){  
  80.                 try {  
  81.                     writer.close();  
  82.                 } catch (IOException ce) {  
  83.                     ce.printStackTrace();  
  84.                 }  
  85.             }  
  86.         }  
  87.     }  
  88.       
  89.       
  90.     /** 
  91.      * 针对分页搜索创建索引 
  92.      */  
  93.     public HelloSearch(boolean pageFlag){  
  94.         String[] myNames = new String[50];  
  95.         String[] myContents = new String[50];  
  96.         for(int i=0; i<50; i++){  
  97.             myNames[i] = "file(" + i + ")";  
  98.             myContents[i] = "I love JavaSE, also love Lucene(" + i + ")";  
  99.         }  
  100.         IndexWriter writer = null;  
  101.         Document doc = null;  
  102.         try {  
  103.             directory = FSDirectory.open(new File("myExample/03_index/"));  
  104.             writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_36, new StandardAnalyzer(Version.LUCENE_36)));  
  105.             writer.deleteAll();  
  106.             for(int i=0; i<myNames.length; i++){  
  107.                 doc = new Document();  
  108.                 doc.add(new Field("myname", myNames[i], Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS));  
  109.                 doc.add(new Field("mycontent", myContents[i], Field.Store.YES, Field.Index.ANALYZED));  
  110.                 writer.addDocument(doc);  
  111.             }  
  112.         } catch (IOException e) {  
  113.             e.printStackTrace();  
  114.         } finally {  
  115.             if(null != writer){  
  116.                 try {  
  117.                     writer.close();  
  118.                 } catch (IOException ce) {  
  119.                     ce.printStackTrace();  
  120.                 }  
  121.             }  
  122.         }  
  123.     }  
  124.       
  125.       
  126.     /** 
  127.      * 获取IndexSearcher实例 
  128.      */  
  129.     private IndexSearcher getIndexSearcher(){  
  130.         try {  
  131.             if(reader == null){  
  132.                 reader = IndexReader.open(directory);  
  133.             }else{  
  134.                 //if the index was changed since the provided reader was opened, open and return a new reader; else,return null  
  135.                 //如果当前reader在打开期间index发生改变,则打开并返回一个新的IndexReader,否则返回null  
  136.                 IndexReader ir = IndexReader.openIfChanged(reader);  
  137.                 if(ir != null){  
  138.                     reader.close(); //关闭原reader  
  139.                     reader = ir;    //赋予新reader  
  140.                 }  
  141.             }  
  142.             return new IndexSearcher(reader);  
  143.         }catch(Exception e) {  
  144.             e.printStackTrace();  
  145.         }  
  146.         return null//发生异常则返回null  
  147.     }  
  148.       
  149.       
  150.     /** 
  151.      * 执行搜索操作 
  152.      * @param query 搜索的Query对象 
  153.      */  
  154.     private void doSearch(Query query){  
  155.         IndexSearcher searcher = this.getIndexSearcher();  
  156.         try {  
  157.             //第二个参数指定搜索后显示的最多的记录数,其与tds.totalHits没有联系  
  158.             TopDocs tds = searcher.search(query, 10);  
  159.             System.out.println("本次搜索到[" + tds.totalHits + "]条记录");  
  160.             for(ScoreDoc sd : tds.scoreDocs){  
  161.                 Document doc = searcher.doc(sd.doc);  
  162.                 System.out.print("文档编号=" + sd.doc + "  文档权值=" + doc.getBoost() + "  文档评分=" + sd.score + "    ");  
  163.                 System.out.print("id=" + doc.get("id") + "  email=" + doc.get("email") + "  name=" + doc.get("name") + "  ");  
  164.                 //获取多个同名域的方式  
  165.                 String[] attachValues = doc.getValues("attach");  
  166.                 for(String attach : attachValues){  
  167.                     System.out.print("attach=" + attach + "  ");  
  168.                 }  
  169.                 System.out.println();  
  170.             }  
  171.         } catch (IOException e) {  
  172.             e.printStackTrace();  
  173.         } finally {  
  174.             if(null != searcher){  
  175.                 try {  
  176.                     searcher.close(); //记得关闭IndexSearcher  
  177.                 } catch (IOException e) {  
  178.                     e.printStackTrace();  
  179.                 }  
  180.             }  
  181.         }  
  182.     }  
  183.       
  184.       
  185.     /** 
  186.      * 精确匹配搜索 
  187.      * @param fieldName 域名(相当于表的字段名) 
  188.      * @param keyWords  搜索的关键字 
  189.      */  
  190.     public void searchByTerm(String fieldName, String keyWords){  
  191.         Query query = new TermQuery(new Term(fieldName, keyWords));  
  192.         this.doSearch(query);  
  193.     }  
  194.       
  195.       
  196.     /** 
  197.      * 基于范围的搜索 
  198.      * @param fieldName 域名(相当于表的字段名) 
  199.      * @param start     开始字符 
  200.      * @param end       结束字符 
  201.      */  
  202.     public void searchByTermRange(String fieldName, String start, String end){  
  203.         Query query = new TermRangeQuery(fieldName, start, end, truetrue); //后面两个参数用于指定开区间或闭区间  
  204.         this.doSearch(query);  
  205.     }  
  206.       
  207.       
  208.     /** 
  209.      * 针对数字的搜索 
  210.      */  
  211.     public void searchByNumericRange(String fieldName, int min, int max){  
  212.         Query query = NumericRangeQuery.newIntRange(fieldName, min, max, truetrue);  
  213.         this.doSearch(query);  
  214.     }  
  215.       
  216.       
  217.     /** 
  218.      * 基于前缀的搜索 
  219.      * @see 它是对Field分词后的结果进行前缀查找的结果 
  220.      */  
  221.     public void searchByPrefix(String fieldName, String prefix){  
  222.         Query query = new PrefixQuery(new Term(fieldName, prefix));  
  223.         this.doSearch(query);  
  224.     }  
  225.       
  226.       
  227.     /** 
  228.      * 基于通配符的搜索 
  229.      * @see *-->任意多个字符 
  230.      * @see ?-->一个字符 
  231.      */  
  232.     public void searchByWildcard(String fieldName, String wildcard){  
  233.         Query query = new WildcardQuery(new Term(fieldName, wildcard));  
  234.         this.doSearch(query);  
  235.     }  
  236.       
  237.       
  238.     /** 
  239.      * 模糊搜索 
  240.      * @see 与通配符搜索不同 
  241.      */  
  242.     public void searchByFuzzy(String fieldName, String fuzzy){  
  243.         Query query = new FuzzyQuery(new Term(fieldName, fuzzy));  
  244.         this.doSearch(query);  
  245.     }  
  246.       
  247.       
  248.     /** 
  249.      * 多条件搜索 
  250.      * @see 本例中搜索name值中以Ja开头,且content中包含am的内容 
  251.      * @see Occur.MUST------表示此条件必须为true 
  252.      * @see Occur.MUST_NOT--表示此条件必须为false 
  253.      * @see Occur.SHOULD----表示此条件非必须 
  254.      */  
  255.     public void searchByBoolean(){  
  256.         BooleanQuery query = new BooleanQuery();  
  257.         query.add(new WildcardQuery(new Term("name""Ja*")), Occur.MUST);  
  258.         query.add(new TermQuery(new Term("content""am")), Occur.MUST);  
  259.         this.doSearch(query);  
  260.     }  
  261.       
  262.       
  263.     /** 
  264.      * 短语搜索 
  265.      * @see 很遗憾的是短语查询对中文搜索没有太大的作用,但对英文搜索是很好用的,但它的开销比较大,尽量少用 
  266.      */  
  267.     public void searchByPhrase(){  
  268.         PhraseQuery query = new PhraseQuery();  
  269.         query.setSlop(1);                          //设置跳数  
  270.         query.add(new Term("content""am"));      //第一个Term  
  271.         query.add(new Term("content""Haerbin")); //产生距离之后的第二个Term  
  272.         this.doSearch(query);  
  273.     }  
  274.       
  275.       
  276.     /** 
  277.      * 基于QueryParser的搜索 
  278.      */  
  279.     public void searchByQueryParse(){  
  280.         QueryParser parser = new QueryParser(Version.LUCENE_36, "content"new StandardAnalyzer(Version.LUCENE_36));  
  281.         Query query = null;  
  282.         try {  
  283. //          query = parser.parse("Haerbin");           //搜索content中包含[Haerbin]的记录  
  284. //          query = parser.parse("I AND Haerbin");     //搜索content中包含[I]和[Haerbin]的记录  
  285. //          query = parser.parse("Lucene OR Haerbin"); //搜索content中包含[Lucene]或者[Haerbin]的记录  
  286. //          query = parser.parse("Lucene Haerbin");    //搜索content中包含[Lucene]或者[Haerbin]的记录  
  287. //          parser.setDefaultOperator(Operator.AND);   //将空格的默认操作OR修改为AND  
  288. //          //1)如果name域在索引时,不进行分词,那么无论这里写成[name:Jadyer]还是[name:jadyer],最后得到的都是0条记录  
  289. //          //2)由于name原值为大写[J],若索引时不对name分词,除非修改name原值为小写[j],并且搜索[name:jadyer]才能得到记录  
  290. //          query = parser.parse("name:Jadyer");       //修改搜索域为name=Jadyer的记录  
  291. //          query = parser.parse("name:Ja*");          //支持通配符  
  292. //          query = parser.parse("\"I am\"");          //搜索content中包含[I am]的记录(注意不能使用parse("content:'I am'"))  
  293. //          parser.setAllowLeadingWildcard(true);      //设置允许[*]或[?]出现在查询字符的第一位,即[name:*de],否则[name:*de]会报异常  
  294. //          query = parser.parse("name:*de");          //Lucene默认的第一个字符不允许为通配符,因为这样效率比较低  
  295. //          //parse("+am +name:Jade")--------------搜索content中包括[am]的,并且name=Jade的记录  
  296. //          //parse("am AND NOT name:Jade")--------搜索content中包括[am]的,并且nam不是Jade的记录  
  297. //          //parse("(blog OR am) AND name:Jade")--搜索content中包括[blog]或者[am]的,并且name=Jade的记录  
  298. //          query = parser.parse("-name:Jack +I");     //搜索content中包括[I]的,并且name不是Jack的记录(加减号要放到域说明的前面)  
  299. //          query = parser.parse("id:[1 TO 3]");       //搜索id值从1到3的记录(TO必须大写,且这种方式没有办法匹配数字)  
  300. //          query = parser.parse("id:{1 TO 3}");       //搜索id=2的记录  
  301.             query = parser.parse("name:Jadk~");        //模糊搜索  
  302.         } catch (ParseException e) {  
  303.             e.printStackTrace();  
  304.         }  
  305.         this.doSearch(query);  
  306.     }  
  307.       
  308.       
  309.     /** 
  310.      * 普通的分页搜索 
  311.      * @see 适用于lucene3.5之前 
  312.      * @param expr      搜索表达式 
  313.      * @param pageIndex 页码 
  314.      * @param pageSize  分页大小 
  315.      */  
  316.     public void searchPage(String expr, int pageIndex, int pageSize){  
  317.         IndexSearcher searcher = this.getIndexSearcher();  
  318.         QueryParser parser = new QueryParser(Version.LUCENE_36, "mycontent"new StandardAnalyzer(Version.LUCENE_36));  
  319.         try {  
  320.             Query query = parser.parse(expr);  
  321.             TopDocs tds = searcher.search(query, pageIndex*pageSize);  
  322.             ScoreDoc[] sds = tds.scoreDocs;  
  323.             for(int i=(pageIndex-1)*pageSize; i<pageIndex*pageSize; i++){  
  324.                 Document doc = searcher.doc(sds[i].doc);  
  325.                 System.out.println("文档编号:" + sds[i].doc + "-->" + doc.get("myname") + "-->" + doc.get("mycontent"));  
  326.             }  
  327.         } catch (Exception e) {  
  328.             e.printStackTrace();  
  329.         } finally {  
  330.             if(null != searcher){  
  331.                 try {  
  332.                     searcher.close();  
  333.                 } catch (IOException e) {  
  334.                     e.printStackTrace();  
  335.                 }  
  336.             }  
  337.         }  
  338.     }  
  339.       
  340.       
  341.     /** 
  342.      * 基于searchAfter的分页搜索 
  343.      * @see 适用于Lucene3.5 
  344.      * @param expr      搜索表达式 
  345.      * @param pageIndex 页码 
  346.      * @param pageSize  分页大小 
  347.      */  
  348.     public void searchPageByAfter(String expr, int pageIndex, int pageSize){  
  349.         IndexSearcher searcher = this.getIndexSearcher();  
  350.         QueryParser parser = new QueryParser(Version.LUCENE_36, "mycontent"new StandardAnalyzer(Version.LUCENE_36));  
  351.         try {  
  352.             Query query = parser.parse(expr);  
  353.             TopDocs tds = searcher.search(query, (pageIndex-1)*pageSize);  
  354.             //使用IndexSearcher.searchAfter()搜索,该方法第一个参数为上一页记录中的最后一条记录  
  355.             if(pageIndex > 1){  
  356.                 tds = searcher.searchAfter(tds.scoreDocs[(pageIndex-1)*pageSize-1], query, pageSize);  
  357.             }else{  
  358.                 tds = searcher.searchAfter(null, query, pageSize);  
  359.             }  
  360.             for(ScoreDoc sd : tds.scoreDocs){  
  361.                 Document doc = searcher.doc(sd.doc);  
  362.                 System.out.println("文档编号:" + sd.doc + "-->" + doc.get("myname") + "-->" + doc.get("mycontent"));  
  363.             }  
  364.         } catch (Exception e) {  
  365.             e.printStackTrace();  
  366.         } finally {  
  367.             if(null != searcher){  
  368.                 try {  
  369.                     searcher.close();  
  370.                 } catch (IOException e) {  
  371.                     e.printStackTrace();  
  372.                 }  
  373.             }  
  374.         }  
  375.     }  
  376. }  


下面是JUnit4.x编写的测试

[java]  view plain copy print ?
  1. package com.jadyer.test;  
  2.   
  3. import java.io.File;  
  4.   
  5. import org.junit.Before;  
  6. import org.junit.Test;  
  7.   
  8. import com.jadyer.lucene.HelloSearch;  
  9.   
  10. public class HelloSearchTest {  
  11.     private HelloSearch hello;  
  12.       
  13.     @Before  
  14.     public void init(){  
  15.         hello = new HelloSearch();  
  16.     }  
  17.       
  18.       
  19.     @Test  
  20.     public void searchByTerm(){  
  21.         hello.searchByTerm("content""my");  
  22.     }  
  23.       
  24.       
  25.     @Test  
  26.     public void searchByTermRange(){  
  27.         hello.searchByTermRange("name""M""o");  
  28.     }  
  29.       
  30.       
  31.     @Test  
  32.     public void searchByNumericRange(){  
  33.         hello.searchByNumericRange("attach"25);  
  34.     }  
  35.       
  36.       
  37.     @Test  
  38.     public void searchByPrefix(){  
  39.         hello.searchByPrefix("content""b");  
  40.     }  
  41.       
  42.       
  43.     @Test  
  44.     public void searchByWildcard(){  
  45.         hello.searchByWildcard("name""Ja??er");  
  46.     }  
  47.       
  48.       
  49.     @Test  
  50.     public void searchByFuzzy(){  
  51.         hello.searchByFuzzy("name""Jadk");  
  52.     }  
  53.       
  54.       
  55.     @Test  
  56.     public void searchByBoolean(){  
  57.         hello.searchByBoolean();  
  58.     }  
  59.       
  60.       
  61.     @Test  
  62.     public void searchByPhrase(){  
  63.         hello.searchByPhrase();  
  64.     }  
  65.       
  66.       
  67.     @Test  
  68.     public void searchByQueryParse(){  
  69.         hello.searchByQueryParse();  
  70.     }  
  71.       
  72.       
  73.     @Test  
  74.     public void searchPage(){  
  75.         for(File file : new File("myExample/03_index/").listFiles()){  
  76.             file.delete();  
  77.         }  
  78.         hello = new HelloSearch(true);  
  79.         hello.searchPage("mycontent:javase"210);  
  80.     }  
  81.       
  82.       
  83.     @Test  
  84.     public void searchPageByAfter(){  
  85.         for(File file : new File("myExample/03_index/").listFiles()){  
  86.             file.delete();  
  87.         }  
  88.         hello = new HelloSearch(true);  
  89.         hello.searchPageByAfter("mycontent:javase"310);  
  90.     }  
  91. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值