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

    全文检索的查询是很重要的,里面的很多的查询方式,就像是GoogleBaidu中的高级查找。

    首先,还是上目录。新建一个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 , 也是查不出来的 .

全文检索之lucene的优化篇--查询篇_第1张图片

    然后是根据范围查询,还是设置好两个根据sizeTerm,然后用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()转换的效果.

全文检索之lucene的优化篇--查询篇_第2张图片

    还有根据通配符查询.

/**
 * 通配符查询
 * 
 * '?' 代表一个字符, '*' 代表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个间距)

全文检索之lucene的优化篇--查询篇_第3张图片

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

全文检索之lucene的优化篇--查询篇_第4张图片

    接下来是一种组合类型,Boolean查询,可以将其他查询方式进行组合,然后变成高级查找。如设置短语查询,查询“绅士”和“饭店”这两个关键字之间不超过2的距离的结果,以及根据范围查找,size500~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);
}

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

全文检索之lucene的优化篇--查询篇_第5张图片

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

/**
 * 测试用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的查询是一样一样的.

全文检索之lucene的优化篇--查询篇_第6张图片

    还有其中用到的NumberTools.longToStringstringToLong的效果,DateToolsdataToStringstringToDate的效果.

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的优化篇--查询篇_第7张图片

    以上就是lucene的各种查询方式的代码和效果.这是我在学习lucene上,通过能够运行一份代码,然后在上面加注释,慢慢理解.不写这些博客前,我对lucene只有特别简单的理解,写博客促进了我的学习,继续努力.但是写这样的博客,也不利于我提高,所以以后博客还是要有自己的理解,深刻的理解.

你可能感兴趣的:(全文检索之lucene的优化篇--查询篇)