(先声明一下,我使用的lucene的版本是lucene4.7.2)
在lucene中,有一种类型的query叫做MultiTermQuery,故名思议,他是要涉及到很多个term的query,比如我们常用的WildcardQuery、FuzzyQuery、PrefixQuery、TermRangeQuery、NumericRangeQuery等,他们都是需要按照一个或者多个term按照一定的逻辑找到多个term,然后再重写由找到的这些term形成的TermQuery进入一个新的Query(比如BooleanQuery、或者ConstantScoreQuery),但是有个一指的注意的地方是:有些MultiTermQuery是不得分的,也就是在返回的时候不会按照得分排序,比如PrefixQuery,的不得分是由每个MultiTermQuery使用的rewriteMethod指定,也就是由重写规则指定。本文的目的不在于讨论重写规则,而是想实现一个可以得分的PrefixQuery(业务场景是我们要使用PrefixQuery做搜索框中提示词的排序,所以必须实现得分)。
实现原理很简单,在指定重写规则的时候将重写规则指定为得分的规则(当然这里涉及到重写规则的实现,这里本文不讨论),在org.apache.lucene.search.MultiTermQuery类中含有SCORING_BOOLEAN_QUERY_REWRITE这个重写规则从他的名字中就可以理解是封装为一个BooleanQuery,并且计算分数。他的逻辑很简单,将搜索到的多个termQuery封装成一个booleanQuery,每一个termQuery都是optional的,也就是对多个termQuery取并集。但是Booleanquery有个需要注意的地方,他不能有太多的clause,不然会报错,默认是1024个,所以我们需要修改这个值,做到这里就算是完成了。我的代码如下:
/** * 由于solr自带的PrefixQuery是不得分的,不能满足提示词的排序要求,所以重写这个query. */ public class ScoredPrefixQuery extends PrefixQuery { //从词典表中得到的term的限制,用于做测试的,实际中不用 private int limit = -1; static{ BooleanQuery.setMaxClauseCount(Integer.MAX_VALUE);//设置BooleanQuery的最多的子query的个数为Integer.MAX_VALUE。 } public ScoredPrefixQuery(Term prefix) { super(prefix); //重置重写规则,使用得分的booleanQuery,此处存在的问题是可能会发生BooleanQuery.TooManyClauses,所以要在得到term的时候需要做限制 setRewriteMethod(org.apache.lucene.search.MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE); } /** * 从词典表中得到前缀匹配的term的方法 */ @Override public TermsEnum getTermsEnum(Terms terms, AttributeSource atts) throws IOException { TermsEnum tenum = terms.iterator(null); if (getPrefix().bytes().length == 0) { // no prefix -- match all terms for this field: return tenum; } return new PrefixTermsEnum(tenum, getPrefix().bytes()) { int already = 0; final int termLimit = limit==-1?BooleanQuery.getMaxClauseCount():limit;//设置limit只是用于做测试的。 @Override public BytesRef next() throws IOException { BytesRef ref = super.next();//先调用父类方法,即从词典表中读取, if(ref == null){//如果真的读完了,就返回null。 return null; }else{//没有读取完,则判断是否已经读取了太多的term //最多的BooleanClause的个数 if(already++ < termLimit){//一个前缀最多从词典表中得到booleanquery的MaxClause个,这样就不会报错了。 return ref; } return null; } } }; } public int getLimit() { return limit; } //做测试用的 public void setLimit(int limit) { this.limit = limit; } //这个测试的前提是我们在索引中仅仅保存了只有id域的100个document,id为从0-99,省略了建立索引的代码。 public static void main(String[] args) throws IOException { IndexReader reader = DirectoryReader.open(getDirectory()); IndexSearcher search = new IndexSearcher(reader); ScoredPrefixQuery q = new ScoredPrefixQuery(new Term("id","1"));//这一行和下面的PrefixQuery q 这一行是区分的,如果使用这一行则只会搜到3个,并且得分不是1.0f,也就是是得分的。 q.setLimit(3);//设置最多为3个。 // PrefixQuery q = new PrefixQuery(new Term("id", "1"));//如果使用lucene中默认使用的PrefixQuery则会搜到11个,并且得分都是1.0f,也就是没有得分。 TopDocs td = search.search(q, 100); for(ScoreDoc sd:td.scoreDocs){ System.out.println(sd.score); } System.out.println(td.scoreDocs.length); } }
这样就完成了得分的前缀匹配的query,如果要在solr中使用,还需要自己定义queryparser的插件,这个留在以后再写博客。
相关推荐
总结来说,Lucene的前缀搜索功能通过`PrefixQuery`类实现,结合适当的查询解析器和分析器,为用户提供了一种快速、灵活的方式,来查找与输入前缀相关的所有文档。这在诸如在线商店、知识库、搜索引擎等应用中具有很...
3. **PrefixQuery**、**WildcardQuery** 和 **RegexQuery**:这些查询类支持模糊匹配,如前缀查询、通配符查询和正则表达式查询,适用于对多个字段进行灵活的文本匹配。 4. **DisjunctionMaxQuery**:用于在多个...
1. **Query**: Lucene支持多种查询类型,如TermQuery(单个词查询)、BooleanQuery(布尔组合查询)、PrefixQuery(前缀查询)和WildcardQuery(通配符查询)。 2. **Score**: 搜索结果中的每个文档都有一个得分,...
3. `PrefixQuery`:前缀搜索,自动在字段值的末尾添加`*`,实现前缀匹配。 4. `RangeQuery`:范围搜索,适用于数值型字段,在指定范围内查找文档。 5. `FilteredQuery`:带有过滤条件的搜索,结合`Query`和`Filter`...
4. 高级查询API:包括TermQuery、PhraseQuery、WildcardQuery、PrefixQuery等,满足不同类型的搜索需求。 5. 倒排索引:Lucene采用倒排索引机制,允许快速定位到包含特定关键词的文档,极大提高了搜索效率。 二、...
2> 全文检索的实现机制 【1】lucene学习笔记的目录如下 1. 概述 3 2. lucene 的包结构 3 3. 索引文件格式 3 4. lucene中主要的类 4 4.1. Document文档类 4 4.1.1. 常用方法 4 4.1.2. 示例 4 4.2. Field字段类 4 ...
`TopDocs` 是搜索结果的容器,包含了搜索得分最高的文档列表。 **1.2.7 ScoreDoc** `ScoreDoc` 包含了文档的 ID 和评分信息。 --- #### 二、Lucene 索引构建与管理 **2.1 Directory** - **FSDirectory**: 使用...