全文检索的查询是很重要的,里面的很多的查询方式,就像是Google和Baidu中的高级查找。
首先,还是上目录。新建一个query,建立一个QueryTest的类。里面的代码就是各种查询方法。一段代码一段代码分析.
第一个是根据"关键字"查询,这个就是用Term,上篇的删除索引用的就是这个.要想知道Term认不认这种写法,用这个查查就知道了.当然用上上篇的分词器查就更好了.
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的索引文件,并且不包含边界.
/** * 范围查询 * * 包含边界: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()转换的效果.
还有根据通配符查询.
/** * 通配符查询 * * '?' 代表一个字符, '*' 代表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效果是一样的.
/** * 短语查询 * * 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就可以查出来了。
/** * 测试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); }
运行结果是,顺便可以看下查询字符串,+表示必须
还有就是这种的反查询,用查询字符串来查询。如
/** * 测试用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的效果.
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只有特别简单的理解,写博客促进了我的学习,继续努力.但是写这样的博客,也不利于我提高,所以以后博客还是要有自己的理解,深刻的理解.