Lucene自定义扩展QueryParser

Lucene版本:4.10.2

 

在使用lucene的时候,不可避免的需要扩展lucene的相关功能来实现业务的需要,比如搜索时,需要在满足一个特定范围内的document进行搜索,如年龄在20和30岁之间的document中搜索并排序。其实lucene自带的NumericRangeQuery类已经能实现这个功能了,如下:

public void testInclusive() throws Exception {

    Directory dir = FSDirectory(---);

    IndexSearcher searcher = new IndexSearcher(dir);

 

    NumericRangeQuery query = NumericRangeQuery.newIntRange("age",20,30,true,true);

    TopDocs matches = searcher.search(query,10);

}

 

这个是通过lucene内置的query类型来进行搜索,但是这样使用起来的缺点就是无法再搜索期间生成对应的NumericRangeQuery实例,如比如直接在搜索的时候传入搜索text: age:[20 TO 30],这样就可以动态的使用范围搜索,更为灵活,那么要是实现这个功能,我们就需要自定义QueryParser了。

QueryParser,故名思议,就是讲前台传入的查询条件,解析为Query实例,从而进行查询,所以简单的说,我们需要自定义一个NumericRangeQueryParser,用来实现,从 age:[20 TO 30] 转化为

NumericRangeQuery query = NumericRangeQuery.newIntRange("age",20,30,true,true); 

的一个Query实例。

 

在lucene中扩展QueryParser是比较简单的,我们可以直接继承QueryParser类,然后实现特定的方法就可以了(查询age在20至30岁的记录):

 

        //自定义queryParser
    class NumericRangeQueryParser extends QueryParser {

        /**
         * @param arg0
         */
        protected NumericRangeQueryParser(String field,Analyzer analyzer) {
            //调用父类的构造方法
            super(field,analyzer);
            // TODO Auto-generated constructor stub
        }
        
        //获取query实例的方法
        public Query getRangeQuery(String field,String part1,String part2,boolean startInclusive,boolean endInclusive) throws ParseException {
            //调用父类的getRangeQuery方法获取query实例
            TermRangeQuery query = (TermRangeQuery) super.getRangeQuery方法获取query实例(field, part1, part2, startInclusive,endInclusive);
            //如果是搜索age Field
            if("age".equals(field)) {
                System.out.println(query.getLowerTerm().utf8ToString());
                System.out.println(query.getUpperTerm().utf8ToString());

                //按照业务需求处理query生成新的query实例并返回
                return NumericRangeQuery.newDoubleRange("price", Double.parseDouble(query.getLowerTerm().utf8ToString()),
                        Double.parseDouble(query.getUpperTerm().utf8ToString()), query.includesLower(), query.includesUpper());
            } else {
                return query;
            }
        }
        
    }

 

其实代码很简单,因为我只是将范围查询的上下限从Int类型格式化成Double类型。其中最重要的一条代码是:

NumericRangeQuery.newDoubleRange("price", Double.parseDouble(query.getLowerTerm().utf8ToString()),

                        Double.parseDouble(query.getUpperTerm().utf8ToString()), query.includesLower(), query.includesUpper());

 

NumericRangeQuery.newDoubleRange是调用了lucene自带的方法来生成相应的query实例,在这里是浮点数范围查询Query实例。其中参数的解释:

query.getLowerTerm().utf8ToString():getLowerTerm是指获取范围查询中的下限,在这里是20。getLowerTerm()返回一个BytesRef对象,lucene自定义的一种对象,我们需要转换成String类型再转成Double类型的对象,传给NumericRangeQuery.newDoubleRange方法,在这里我一开始想到的是toString方法,结果老是报错,最后查看源码才知道应该使用utf8ToString()方法,它会将BytesRef转成utf8编码的String对象。

public String utf8ToString()

Interprets stored bytes as UTF8 bytes, returning the resulting string

 

query.getUpperTerm().utf8ToString():与query.getLowerTerm().utf8ToString()同理,获取范围查询中的上限,在这里是30

 

query.includesLower()和query.includesUpper():是否包含临界值,在这里值是否包含年龄为20和30岁的记录,即[20,30]或[20,30)…

 

下面我没来测试NumericRangeQueryParser:

    public static void main(String[] args) throws IOException, ParseException {
        String expression = "age:[10 TO 20]";
        Analyzer analyzer  = new StandardAnalyzer();
        QueryParser parser = new QueryParser("age",analyzer);
        Query query = parser.parse(expression);
        
        QueryParser testParser = kscSearch.new NumericRangeQueryParser("age",analyzer);
        Query testQuery = testParser.parse(expression);
        
        System.out.println(expression + "  parser to  " + query);
        System.out.println(expression + "  parser to  " + testQuery);
    }

 

测试结果如下:

age:[10 TO 20] parser to age:[10 TO 20]

age:[10 TO 20] parser to price:[10.0 TO 20.0]

 

可以看到使用NumericRangeQueryParser后,10和20都变成了浮点数。

 

相应的我们可以自定义日期QueryParser或者是更为复杂的QueryParser。不过从这个简单的NumericRangeQueryParser中我们可以看到QueryParser的原理,就是讲给定的查询字符串解析生成Lucene能识别的Query实例,从而进行查询,我们这里是使用了Lucene内置的NumericRangeQuery扩展QueryParser,如果有需要,甚至可以自定义NumericRangeQuery。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。