全文检索之lucene的优化篇--查询篇
2017-01-16 14:07
309 查看
全文检索的查询是很重要的,里面的很多的查询方式,就像是Google和Baidu中的高级查找。
首先,还是上目录。新建一个query,建立一个QueryTest的类。里面的代码就是各种查询方法。一段代码一段代码分析.
第一个是根据"关键字"查询,这个就是用Term,上篇的删除索引用的就是这个.要想知道Term认不认这种写法,用这个查查就知道了.当然用上上篇的分词器查就更好了.
[java] view
plain copy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
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的,也是查不出来的.
![](https://img-blog.csdn.net/20150111214216435?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1eWFubGluZ2xhbnE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
然后是根据范围查询,还是设置好两个根据size的Term,然后用RangeQuery组合起来,查询200~1000size的索引文件,并且不包含边界.
[java] view
plain copy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
/**
* 范围查询
*
* 包含边界: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()转换的效果.
![](https://img-blog.csdn.net/20150111214259828?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1eWFubGluZ2xhbnE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
还有根据通配符查询.
[java] view
plain copy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
/**
* 通配符查询
*
* '?' 代表一个字符, '*' 代表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就被查到了.
![](https://img-blog.csdn.net/20150111214419660?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1eWFubGluZ2xhbnE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
还有根据短语查询,同样是Term先指定在content内容中查找绅士和饭店,并且要求这两个词之间的间隔不大于2个,或者是一个在索引1一个在索引4的位子,但是这个1~4是相对位置,写成21~24效果是一样的.
[java] view
plain copy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
/**
* 短语查询
*
* 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个间距)
![](https://img-blog.csdn.net/20150111214710839?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1eWFubGluZ2xhbnE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
用MMAnalyzer分词这句话“一位绅士到旅游胜地的一家饭店要开个房间”的效果,可以看出他们就是隔了2个分词。
![](https://img-blog.csdn.net/20150111214756110?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1eWFubGluZ2xhbnE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
接下来是一种组合类型,Boolean查询,可以将其他查询方式进行组合,然后变成高级查找。如设置短语查询,查询“绅士”和“饭店”这两个关键字之间不超过2的距离的结果,以及根据范围查找,size在500~1000之间的数据,这两种查询,前面的是Must必须的,后面随意,符合不符合都成。所以只要满足条件1就可以查出来了。
[java] view
plain copy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
/**
* 测试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);
}
运行结果是,顺便可以看下查询字符串,+表示必须
![](https://img-blog.csdn.net/20150111214909898?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1eWFubGluZ2xhbnE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
还有就是这种的反查询,用查询字符串来查询。如
[java] view
plain copy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
/**
* 测试用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的查询是一样一样的.
![](https://img-blog.csdn.net/20150111214952500?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1eWFubGluZ2xhbnE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
还有其中用到的NumberTools.longToString和stringToLong的效果,和DateTools的dataToString和stringToDate的效果.
[java] view
plain copy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
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));
}
效果就是这样
![](https://img-blog.csdn.net/20150111215319764?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1eWFubGluZ2xhbnE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
以上就是lucene的各种查询方式的代码和效果.这是我在学习lucene上,通过能够运行一份代码,然后在上面加注释,慢慢理解.不写这些博客前,我对lucene只有特别简单的理解,写博客促进了我的学习,继续努力.但是写这样的博客,也不利于我提高,所以以后博客还是要有自己的理解,深刻的理解.
首先,还是上目录。新建一个query,建立一个QueryTest的类。里面的代码就是各种查询方法。一段代码一段代码分析.
第一个是根据"关键字"查询,这个就是用Term,上篇的删除索引用的就是这个.要想知道Term认不认这种写法,用这个查查就知道了.当然用上上篇的分词器查就更好了.
[java] view
plain copy
![](https://code.csdn.net/assets/CODE_ico.png)
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
![](https://code.csdn.net/assets/CODE_ico.png)
/**
* 范围查询
*
* 包含边界: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
![](https://code.csdn.net/assets/CODE_ico.png)
/**
* 通配符查询
*
* '?' 代表一个字符, '*' 代表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
![](https://code.csdn.net/assets/CODE_ico.png)
/**
* 短语查询
*
* 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
![](https://code.csdn.net/assets/CODE_ico.png)
/**
* 测试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
![](https://code.csdn.net/assets/CODE_ico.png)
/**
* 测试用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
![](https://code.csdn.net/assets/CODE_ico.png)
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只有特别简单的理解,写博客促进了我的学习,继续努力.但是写这样的博客,也不利于我提高,所以以后博客还是要有自己的理解,深刻的理解.
相关文章推荐
- JAVAWEB开发之Lucene详解——Lucene入门及使用场景、全文检索、索引CRUD、优化索引库、分词器、高亮、相关度排序、各种查询
- 全文检索之lucene的优化篇--查询篇
- 关于Mysql模糊查询的优化-全文检索和Like的使用
- 全文检索Lucene(三)----查询,分词器,排序,过滤,高亮
- Lucene.Net, SQL Server 2008全文检索, Like模糊查询的一点心得(转)
- 全文检索之lucene的优化篇--分词器
- 全文检索之lucene的优化篇--分词器
- lucene学习六:lucene全文检索与数据库查询的比较
- java实现搜索引擎,全文检索,超大数据量查询,lucene
- 全文检索之lucene的优化篇--增删改查
- 全文检索Lucene使用与优化
- 全文检索之lucene的优化篇--创建索引库
- 基于Lucene/XML的站内全文检索解决方案_搜索引擎优化SEO165
- lucene全文检索多种查询检索简单案例
- java Lucene全文检索优化方法
- 关于Mysql模糊查询的优化-全文检索和Like的使用
- 全文检索之lucene的优化篇--增删改查
- 全文检索之lucene的优化篇--创建索引库
- Lucene.Net, SQL Server 2008全文检索, Like模糊查询的一点心得
- lucene全文检索应用示例及代码简析