全文检索的查询是很重要的,里面的很多的查询方式,就像是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只有特别简单的理解,写博客促进了我的学习,继续努力.但是写这样的博客,也不利于我提高,所以以后博客还是要有自己的理解,深刻的理解.