您的位置:首页 > 其它

全文检索之lucene的优化篇--查询篇

2017-01-16 14:07 309 查看
全文检索的查询是很重要的,里面的很多的查询方式,就像是Google和Baidu中的高级查找。

    首先,还是上目录。新建一个query,建立一个QueryTest的类。里面的代码就是各种查询方法。一段代码一段代码分析.

    第一个是根据"关键字"查询,这个就是用Term,上篇的删除索引用的就是这个.要想知道Term认不认这种写法,用这个查查就知道了.当然用上上篇的分词器查就更好了.

[java] view
plain copy

 





package com.lucene.query;  

  

import java.util.Date;  

  

import org.apache.lucene.document.DateTools;  

import org.apache.lucene.document.DateTools.Resolution;  

import org.apache.lucene.document.Document;  

import org.apache.lucene.document.NumberTools;  

import org.apache.lucene.index.Term;  

import org.apache.lucene.search.BooleanClause.Occur;  

import org.apache.lucene.search.BooleanQuery;  

import org.apache.lucene.search.PhraseQuery;  

import org.apache.lucene.search.Query;  

import org.apache.lucene.search.RangeQuery;  

import org.apache.lucene.search.TermQuery;  

import org.apache.lucene.search.WildcardQuery;  

import org.junit.Test;  

  

import com.lucene.IndexDao;  

import com.lucene.QueryResult;  

import com.lucene.units.File2DocumentUtils;  

  

public class QueryTest {  

  

    //indexDao,实现增删改查(分词器:MMAnalyzer,索引路径:***\luceneIndex)  

    IndexDao indexDao = new IndexDao();  

  

    /** 

     * 关键词查询 

     *  

     * name:room 

     */  

    @Test  

    public void testTermQuery() {  

        // Term term = new Term("name", "房间");  //在name中查找关键字房间的  

        // Term term = new Term("name", "Room"); //查不出来的,要英文关键词全是小写字符,  

        Term term = new Term("name", "s");         

        Query query = new TermQuery(term);  

  

        queryAndPrintResult(query);  

    }  

      

    /** 

     * 查询并且打印结果 

     * @param query Query对象 

     */  

    public void queryAndPrintResult(Query query) {  

        System.out.println("对应的查询字符串:" + query);  

        //调用indexDao的查询,查询luceneIndex索引路径下的索引文件,从0开始查最多100个索引文件,  

        QueryResult qr = indexDao.search(query, 0, 100);  

        //打印总记录数和索引信息  

        System.out.println("总共有【" + qr.getRecordCount() + "】条匹配结果");  

        for (Document doc : qr.getRecordList()) {  

            File2DocumentUtils.printDocumentInfo(doc);  

        }  

    }  

      

}  

    这里用了几种查询Term关键字查询.为了一次性显示所有效果所以进行了一些修改.当前测试之前先删了索引,并且用com.lucene中的IndexDaoTest的testSave生成索引.然后生成索引的效果如下.第二种没有结果,因为对于大写是不认的;还有对于path这种不是Index.ANALYZED的,也是查不出来的.



    然后是根据范围查询,还是设置好两个根据size的Term,然后用RangeQuery组合起来,查询200~1000size的索引文件,并且不包含边界.

[java] view
plain copy

 





/** 

* 范围查询 

 *  

 * 包含边界:size:[0000000000001e TO 000000000000rs] 

 * 不包含边界:size:{0000000000001e TO 000000000000rs} 

 */  

@Test  

public void testRangeQuery() {  

    Term lowerTerm = new Term("size", NumberTools.longToString(200));  

    Term upperTerm = new Term("size", NumberTools.longToString(1000));  

    Query query = new RangeQuery(lowerTerm, upperTerm, false);  

            queryAndPrintResult(query);  

}  

    结果就是这样,顺便可以看到NumberTools.longToString()转换的效果.



    还有根据通配符查询.

[java] view
plain copy

 





/** 

 * 通配符查询 

 *  

 * '?' 代表一个字符, '*' 代表0个或多个字符 

 */  

@Test  

public void testWildcardQuery() {  

    Term term = new Term("name", "roo?");  

    // Term term = new Term("name", "ro*"); // 前缀查询 PrefixQuery  

    // Term term = new Term("name", "*o*");  

    // Term term = new Term("name", "房*");  

    Query query = new WildcardQuery(term);  

  

    queryAndPrintResult(query);  

}  

    效果就是如下,在Term中指定的name中查找roo+任意一个字符,所以room就被查到了.



    还有根据短语查询,同样是Term先指定在content内容中查找绅士和饭店,并且要求这两个词之间的间隔不大于2个,或者是一个在索引1一个在索引4的位子,但是这个1~4是相对位置,写成21~24效果是一样的.

[java] view
plain copy

 





/** 

 * 短语查询 

 *  

 * content:"? 绅士 ? ? 饭店" 

 *  

 * content:"绅士 饭店"~2 

 */  

@Test  

public void testPhraseQuery() {  

    PhraseQuery phraseQuery = new PhraseQuery();  

    // phraseQuery.add(new Term("content", "绅士"), 1);  

    // phraseQuery.add(new Term("content", "饭店"), 4);  

  

    phraseQuery.add(new Term("content", "绅士"));  

    phraseQuery.add(new Term("content", "饭店"));  

    phraseQuery.setSlop(2);  

  

    queryAndPrintResult(phraseQuery);  

}  

    查询改了点代码,是显示的三种查询的效果,1是设置为21~24的效果,同样可以查询出效果;第二种是按照代码的设置“绅士”和“饭店”最大间隔距离为2,可以得到结果;而第三种,间距设置为1,则不行,因为“绅士”和“饭店”之间的距离是2.(就是要求查询到最大间距不能超过1的,但是实际上是2个间距)



    用MMAnalyzer分词这句话“一位绅士到旅游胜地的一家饭店要开个房间”的效果,可以看出他们就是隔了2个分词。 



    接下来是一种组合类型,Boolean查询,可以将其他查询方式进行组合,然后变成高级查找。如设置短语查询,查询“绅士”和“饭店”这两个关键字之间不超过2的距离的结果,以及根据范围查找,size在500~1000之间的数据,这两种查询,前面的是Must必须的,后面随意,符合不符合都成。所以只要满足条件1就可以查出来了。

[java] view
plain copy

 





/** 

 * 测试Boolean查询 

 *  

 * +content:"绅士 饭店"~2 -size:[000000000000dw TO 000000000000rs] 

 * +content:"绅士 饭店"~2 +size:[000000000000dw TO 000000000000rs] 

 * content:"绅士 饭店"~2 size:[000000000000dw TO 000000000000rs] 

 * +content:"绅士 饭店"~2 size:[000000000000dw TO 000000000000rs] 

 */  

@Test  

public void testBooleanQuery() {  

    // 条件1,在content中的绅士和饭店最多间隔不能超过2个,  

    PhraseQuery query1 = new PhraseQuery();  

    query1.add(new Term("content", "绅士"));  

    query1.add(new Term("content", "饭店"));  

    query1.setSlop(2);  

  

    // 条件2,大小在500~1000之间  

    Term lowerTerm = new Term("size", NumberTools.longToString(500));  

    Term upperTerm = new Term("size", NumberTools.longToString(1000));  

    Query query2 = new RangeQuery(lowerTerm, upperTerm, true);  

  

    // 组合  

    BooleanQuery boolQuery = new BooleanQuery();  

    //条件1必须存在,条件2随意  

    boolQuery.add(query1, Occur.MUST);  

    boolQuery.add(query2, Occur.SHOULD);  

  

    queryAndPrintResult(boolQuery);  

}  

    运行结果是,顺便可以看下查询字符串,+表示必须



    还有就是这种的反查询,用查询字符串来查询。如

[java] view
plain copy

 





/** 

 * 测试用query的查询字符串来查询 +表示Must,-表示Not,AND,OR就是且或.~2,表示最多间隔2个字符. 

 */  

@Test  

public void testQueryString() {  

    // 对于500~1000之间,文件size分别为169和304,是没有符合条件的;对于绅士和饭店间距不超过2,是有1条记录符合条件的。  

    // 查询绅士和饭店间距不超过2,并且size不在500~1000的文件,1条匹配  

    String queryString = "+content:\"绅士 饭店\"~2 -size:[000000000000dw TO 000000000000rs]";  

    // 查询绅士和饭店间距不超过2,并且size不在500~1000的文件,1条匹配 (NOT和-是一样的)  

    // String queryString = "(content:\"绅士 饭店\"~2 NOT size:[000000000000dw TO 000000000000rs])";  

    // 查询绅士和饭店间距不超过2,并且size必须在500~1000的文件,0条匹配  

    // String queryString = "content:\"绅士 饭店\"~2 AND size:[000000000000dw TO 000000000000rs]";  

    // 查询绅士和饭店间距不超过2,或者size在500~1000的文件,1条匹配  

    // String queryString ="content:\"绅士 饭店\"~2 OR size:[000000000000dw TO 000000000000rs]";  

  

    System.out.println("对应的查询字符串:" + queryString);  

    QueryResult qr = indexDao.search(queryString, 0, 10);  

    System.out.println("总共有【" + qr.getRecordCount() + "】条匹配结果");  

    for (Document doc : qr.getRecordList()) {  

        File2DocumentUtils.printDocumentInfo(doc);  

    }  

}  

    查询结果和Boolean的查询是一样一样的.



    还有其中用到的NumberTools.longToString和stringToLong的效果,和DateTools的dataToString和stringToDate的效果.

[java] view
plain copy

 





public static void main(String[] args) {  

    //long型的最大值  

    System.out.println(Long.MAX_VALUE);  

    //1000long转为String为  

    System.out.println(NumberTools.longToString(1000));  

    //000000000000rs字符串转为long型为  

    System.out.println(NumberTools.stringToLong("000000000000rs"));  

  

    //DateTools,转换日期效果,精度分别精确到Day,Minute和Second  

    try {  

        System.out.println(DateTools.stringToDate("20141231"));  

    } catch (ParseException e) {  

        e.printStackTrace();  

    }  

    System.out.println(DateTools.dateToString(new Date(), Resolution.DAY));  

    System.out.println(DateTools.dateToString(new Date(), Resolution.MINUTE));  

    System.out.println(DateTools.dateToString(new Date(), Resolution.SECOND));  

}  

    效果就是这样



    以上就是lucene的各种查询方式的代码和效果.这是我在学习lucene上,通过能够运行一份代码,然后在上面加注释,慢慢理解.不写这些博客前,我对lucene只有特别简单的理解,写博客促进了我的学习,继续努力.但是写这样的博客,也不利于我提高,所以以后博客还是要有自己的理解,深刻的理解.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  lucenne