`
suichangkele
  • 浏览: 203707 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

queryParser介绍以及自定义queryParser实现搜索提示

阅读更多

写这篇博客第一个是为了记录在solr中自定义queryParser(顺便介绍一下solr的queryParser),第二个是在 http://suichangkele.iteye.com/blog/2363599 (自定义得分的PrefixQuery)这篇博客中也说了要在solr中使用自己的query要使用自己的queryParser,第三个是公司业务需求,需要实现更加智能的搜索提示(智能是我自己给加的酷)。因为以上的原因我自己写了一个queryParser来实现我心中理想的搜索提示(先声明一下,我这里使用的是solr5.5.3的版本)。

 

1、solr中的queryParser:为什么我们在solr中设置q=*:*就能匹配所有的doc呢?为什么q=name:黄*就能匹配所有的name域是以黄开头的doc呢?原理就是solr使用queryParser将输入的q解析为了一个query,然后使用这个query进行了搜索。solr中有很多的queryParser,比如我们熟知的有lucene、dismax、edismax。我们从solr的源码中来仔细看一下吧:org.apache.solr.search.QParserPlugin在这个类中,可以发现有一个map,在加载org.apache.solr.search.QParserPlugin的时候就会忘这个map中添加很多的内容,在这个map中就能找到我们熟悉的lucene、dismax、edismax,不过他们都不是QueryParser,而是QParserPlugin,不过在QParserPlugin这个类中有createParser方法,用于产生一个QueryParser。我们在solrconfig.xml中可以配置QParserPlugin,在searchHandler中也可以配置defType表示使用的QParserPlugin,使用的名字就是在这个map中存放的内容。在org.apache.solr.search.QParserPlugin类中有一个默认的DEFAULT_QTYPE,也就是在一次查询的时候不指定defType的话默认就是使用这个QTYPE,他便是lucene,也就是使用LuceneQParserPlugin来生成要使用的QueryParser。

 

2、在solrconfig.xml中定义自己的queryParser 很简单,只要继承org.apache.solr.search.QParserPlugin这个类,实现他的createParser方法即可,然后再solrconfig.xml中配置一下。我这里先做一个最简单的,比如我们把所有的q都转化为query的value,并且需要指定一个默认的域作为query的key(加入说是id域吧),然后封装为一个TermQuery(如此一来,即使你搜q=黄*,我也给你生成一个TermQuery,即:id:黄*,注意这个并不是PrefixQuery,仍然是一个TermQuery,只不过value的部分是黄*).代码如下:

public class TermQueryParserPlugin extends QParserPlugin{
	private Logger logger = LoggerFactory.getLogger(TermQueryParserPlugin.class);
	@Override
	public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
		logger.info("解析q:{}",qstr);
		return new QParser(qstr,localParams,params,req) {//因为逻辑简单,所以直接使用一个匿名内部类,实现其parser方法即可
			SolrParams solrParams = SolrParams.wrapDefaults(localParams, params);//将客户端传来的和本地配置的参数合并
			@Override
			public Query parse() throws SyntaxError {
				String df = solrParams.get("df");//从合并后的参数中去的df参数
				try {
					return new TermQuery(new Term(df, new BytesRef(qstr.getBytes("UTF-8")))){
						public String toString(String s) {//重写的目的是为了在页面好看区别来
							return "这是一个termQuery";
						};
					};
				} catch (UnsupportedEncodingException e) {
					throw new RuntimeException(e);
				}
			}
		};
	}
}

 然后再在solrconfig.xml中配置  <queryParser name="helloword"  class="xxxxx.TermQueryParserPlugin"/>,然后再浏览器中访问你的solr,使用的url为:

 

http://localhost:8080/solr/product/select?q=黄*&wt=json&indent=true&debugQuery=true&defType=helloword&df=id  后面的参数很重要,倒数第三个是开启debug,倒数第二个是指定使用的queryParserPlugin,使用我们上面配置的helloword,第三个参数是因为我们在自定义的queryParser中要使用(不要和dismax中的df混淆了)。可以发现debug的信息:debug":{

    "rawquerystring":"黄*",
    "querystring":"黄*",
    "parsedquery":"id:黄*",
    "parsedquery_toString":"这是一个termQuery",

这样我们就能实现自己的queryParser了。

 

3、我自己实现的使用ScoredPrefixQuery的queryParser做提示(ScoredPrefixQuery参见http://suichangkele.iteye.com/blog/2363599 博客)

我们的要求是这样的:假设我要提示 特仑苏牛奶,

    1、当用于输入t时要输入,telunsuniu时也要输入,即对整个的拼音建立索引并使用前缀查询

    2、当用户输入tlsn时提示,即对整个拼音的建立索引,使用前缀搜索

    3、当用户输入niun时也要提示,即对分词后的term的拼音建立索引,使用前缀查询

    4、当用户输入tl或者nn时提示,即对分词后的term的拼音的前缀建立索引,使用前缀搜索

    5、当用户输入牛奶、牛、特伦时提示,即对分词建立索引,查询时使用前缀搜索

写到这我们便明白了要对任何一个输入词做五个域的查询,很显然这个很符合dismaxquery,所以我这里的QueryParser就直接继承了DismaxQueryParser,因为他里面有很多的方法可以直接拿来用。

 

/** 用于做提示用的QParser*/
public class SuggestQParser extends DisMaxQParser {
	private static Logger logger = LoggerFactory.getLogger(SuggestQParser.class);
	public SuggestQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
		super(qstr, localParams, params, req);
	}
	@Override
	public Query parse() throws SyntaxError {
		
		SolrParams solrParams = SolrParams.wrapDefaults(localParams, params);//将多个参数合并
		queryFields = parseQueryFields(req.getSchema(), solrParams);//获得查询的域以及boost,在这里
		/*
		 * the main query we will execute. we disable the coord because this
		 * query is an artificial construct
		 */
		BooleanQuery query = new BooleanQuery(true);
		boolean notBlank = addMainQuery(query, solrParams);
		if (!notBlank)
			return null;
		return query;
	}

	protected boolean addMainQuery(BooleanQuery query, SolrParams solrParams) throws SyntaxError {
		
		//得到tie
		float tiebreaker = solrParams.getFloat(DisMaxParams.TIE, 0.0f);
		
		// 得到用户的输入词
		String userQuery = getString();
		if (userQuery == null) {
			throw new RuntimeException("ScoredPrefixQueryParser中不接收空的query,不能使用q.alt参数");
		} else {
			
			//1、使用iK进行分词
			//2、循环所有的token,每一个token按照df形成一个ScoredPrefixQuery和termQuery,所有df的形成的query封装为一个DisjunctionMaxQuery,并添加到BooleqnQuery中,关系为optional
			Analyzer ar = new IKAnalyzer(true);
			
			try {
				int termCount = 0;
				TokenStream stream = ar.tokenStream("", userQuery);
				TermToBytesRefAttribute termAttribute = stream.addAttribute(TermToBytesRefAttribute.class);
				BytesRef bf = termAttribute.getBytesRef();//用于存放字符串的东西。
				stream.reset();
				while(stream.incrementToken()) { //循环token
					
					termAttribute.fillBytesRef();//重新放入字符串。
					termCount++;
					//每个term形成一个DisjunctionMaxQuery
					DisjunctionMaxQuery dis = new DisjunctionMaxQuery(tiebreaker);
					
					String term = bf.utf8ToString();//得到字符串。
					logger.info("分词的结果:序号:{},term:{}",new Object[]{termCount,term});
					for(String field:queryFields.keySet()){//循环qf,也就是上面中提到的五个域
						//形成一个得分的prefixQuery
						Query prefixQ = new ScoredPrefixQuery(new Term(field, term));
						dis.add(prefixQ);
					}
					
					query.add(dis,Occur.SHOULD);//添加该term的dis
				}
				
				query.setMinimumNumberShouldMatch(termCount);//这个的目的为了匹配所有的分析的term
				logger.info("最后形成的booleanQuery:{}",query);
				
			} catch (IOException e) {
				logger.error("处理分词的时候发生错误,字符串为:{}", new Object[]{userQuery},e);
				return false;
			}finally {
				if(ar != null)
					ar.close();
			}
		}
		return true;
	}

}

 

 

至此,自己实现queryParser、使用之前写的ScoredPrefixQuery以及实现提示词的queryParser便完成了。

 

分享到:
评论

相关推荐

    使用Lucene4.7实现搜索功能,分页+高亮

    标题中的“使用Lucene4.7实现搜索功能,分页+高亮”表明我们要讨论的是如何利用Apache Lucene 4.7版本来构建一个具备搜索、分页和高亮显示功能的系统。Lucene是一个高性能、全文本搜索引擎库,它提供了强大的文本...

    lucene 例子

    例如,`Analyzer`类负责文本分析,其中的`TokenStream`接口和`Tokenizer`类是实现自定义分词策略的关键。`InvertedIndex`类则实现了倒排索引的构建和查询。 至于“工具”标签,Lucene作为一个工具库,可以与其他...

    搜索篇:Struts、Lucene的Web实例

    执行查询后,获取TopDocs,从中提取匹配的文档信息,最后封装成自定义的搜索结果对象,通过转发或重定向将这些结果传递给相应的JSP页面展示。 描述中的"博文链接:https://lighter.iteye.com/blog/47592"指向了一篇...

    Lucenedemo

    4. 增加搜索提示和建议功能:利用Suggester实现自动补全和相关性建议。 5. 多线程处理:在大型索引中,可以考虑使用多线程来加速索引构建和搜索。 通过“Lucenedemo”项目的实践,你将深入理解Lucene的工作原理,为...

    Lucene-3.0.0+Tomcat集成.doc

    - **执行查询**:使用命令`java org.apache.lucene.demo.SearchFiles`,之后会提示输入查询关键字,按回车键后即可显示查询结果。 **2. Web 应用程序** - **部署 Lucene Web 应用**: - 将`lucene-core-3.0.0.jar...

    solr 需要的各种jar包

    这些插件的jar包会根据实际需求选择,如`queryparser-*.jar`用于自定义查询解析,`solr-cell.jar`支持提取库(Tika)的内容解析。 4. **DataImportHandler (DIH)**:Solr的数据导入处理程序(DIH)允许从外部数据源...

Global site tag (gtag.js) - Google Analytics