Java 在lucene中文本开头找到匹配项时如何增强文档

Java 在lucene中文本开头找到匹配项时如何增强文档,java,android,lucene,Java,Android,Lucene,我想知道这怎么可能。假设我正在搜索ka,那么Karthik的得分应该比Aakash的得分高。如何提升这些文档?。 我已经试过了 我试图使用SpanFirstQuery如下所示。但它不起作用。我正在使用lucene 4.0 //queryString is searchText. e.g ka //NAME, ORGANIZATION_NAME and ORGANIZATION_POSITION are indexed field names. Map<String, Analyzer>

我想知道这怎么可能。假设我正在搜索
ka
,那么
Karthik
的得分应该比
Aakash
的得分高。如何提升这些文档?。 我已经试过了

我试图使用
SpanFirstQuery
如下所示。但它不起作用。我正在使用lucene 4.0

//queryString is searchText. e.g ka
//NAME, ORGANIZATION_NAME and ORGANIZATION_POSITION are indexed field names.
Map<String, Analyzer> searchAnalyzers = new HashMap<String, Analyzer>();
searchAnalyzers.put(NAME, new KeywordAnalyzer());
searchAnalyzers.put(ORGANIZATION_NAME, new KeywordAnalyzer());
searchAnalyzers.put(ORGANIZATION_POSITION, new KeywordAnalyzer());
PerFieldAnalyzerWrapper perFieldAnalyzerWrapper = new  PerFieldAnalyzerWrapper(new KeywordAnalyzer(), searchAnalyzers);
MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(Version.LUCENE_40, mSearchFields, perFieldAnalyzerWrapper); //mSearchFiels is array of fiels
multiFieldQueryParser.setDefaultOperator(QueryParser.Operator.AND);
Query query = (Utils.isEmpty(queryString)) ? new MatchAllDocsQuery() : multiFieldQueryParser.parse(QueryParser.escape(queryString)); //queryString is text to be searched

Term term = new Term(NAME, queryString);
SpanFirstQuery spanFirstQuery = new SpanFirstQuery(new SpanTermQuery(term), 5);
spanFirstQuery.setBoost(5.0f);
BooleanQuery booleanQuery = new BooleanQuery();
booleanQuery.add(spanFirstQuery, BooleanClause.Occur.SHOULD);
booleanQuery.add(query, BooleanClause.Occur.MUST);
indexSearcher.search(booleanQuery, 100);
//queryString是searchText。e、 吉卡
//名称、组织名称和组织位置是索引字段名称。
Map searchAnalyzers=new HashMap();
searchAnalyzers.put(NAME,new KeywordAnalyzer());
searchAnalyzers.put(组织名称,新关键字分析器());
searchAnalyzers.put(组织位置,新关键字分析器());
perfieldanalyzerrapper perfieldanalyzerrapper=新perfieldanalyzerrapper(新关键字分析器(),搜索分析器);
MultiFieldQueryParser MultiFieldQueryParser=新的MultiFieldQueryParser(Version.LUCENE_40,mSearchFields,perFieldAnalyzerWrapper)//mSearchFiels是fiels的数组
多字段QueryParser.setDefaultOperator(QueryParser.Operator.AND);
查询=(Utils.isEmpty(queryString))?新的MatchAllDocsQuery():multifiedQueryParser.parse(QueryParser.escape(queryString))//queryString是要搜索的文本
术语=新术语(名称、查询字符串);
SpanFirstQuery SpanFirstQuery=新的SpanFirstQuery(新的SpanTermQuery(术语),5);
spanFirstQuery.setBoost(5.0f);
BooleanQuery BooleanQuery=新的BooleanQuery();
添加(spanFirstQuery,BooleanClause.occurrent.SHOULD);
添加(query,BooleanClause.occurrent.MUST);
搜索(booleanQuery,100);
我的想法,为什么SpanFirstQuery是一个坏主意-它看起来很像一个解决方案,从性能方面来说,可能会很糟糕(而且,我不知道如何让它在第一位工作),而且还需要您存储位置(额外的空间),这实际上并不需要

建议的解决方案:

警告-这是实验性的,可能不是生产就绪的解决方案,这仍然需要一些工作来完成

我使用WildcardQuery作为这个功能的基础,因为它支持像
*ka*
这样的查询,这正是您想要的。第二个问题-评分,因为WildcardQuery是MultiTermQuery的子类,所以可以指定自定义重写方法:

public class BoostPrefixScoringRewrite extends ScoringRewrite<BooleanQuery.Builder> {

    private final String text;

    public BoostPrefixScoringRewrite(String text) {
        // todo should be handled more carefully, since wildcard query supports other than * symbols
        this.text = text.replace("*", "");
    }

    @Override
    protected BooleanQuery.Builder getTopLevelBuilder() {
        BooleanQuery.Builder builder = new BooleanQuery.Builder();
        builder.setDisableCoord(true);
        return builder;
    }

    protected Query build(BooleanQuery.Builder builder) {
        return builder.build();
    }

    @Override
    protected void addClause(BooleanQuery.Builder topLevel, Term term, int docCount,
                             float boost, TermContext states) {
        final TermQuery tq = new TermQuery(term, states);
        if (term.text().startsWith(this.text)) {
            // experiment with the boost value
            topLevel.add(new BoostQuery(tq, 100f), BooleanClause.Occur.SHOULD);
        } else {
            topLevel.add(new BoostQuery(tq, boost), BooleanClause.Occur.SHOULD);
        }

    }

    @Override
    protected void checkMaxClauseCount(int count) {
        if (count > BooleanQuery.getMaxClauseCount())
            throw new BooleanQuery.TooManyClauses();
    }
}
public类BoostPrefixScoringRewrite扩展了ScoringRewrite{
私有最终字符串文本;
公共boostPrefixCorringRewrite(字符串文本){
//由于通配符查询支持*符号以外的其他符号,所以todo的处理应该更加小心
this.text=text.replace(“*”,“”);
}
@凌驾
受保护的BooleanQuery.Builder GetToLevel Builder(){
BooleanQuery.Builder=新的BooleanQuery.Builder();
builder.setDisableCoord(true);
返回生成器;
}
受保护的查询生成(BooleanQuery.Builder){
返回builder.build();
}
@凌驾
受保护的void addClause(BooleanQuery.Builder顶级、术语、int docCount、,
浮动提升,术语(上下文状态){
最终术语查询tq=新术语查询(术语、状态);
if(term.text().startsWith(this.text)){
//试验增压值
add(newboostquery(tq,100f),BooleanClause.occure.SHOULD);
}否则{
add(newboostquery(tq,boost),BooleanClause.occurrent.SHOULD);
}
}
@凌驾
受保护的无效检查MaxClauseCount(整数计数){
if(count>BooleanQuery.getMaxClauseCount())
抛出新的BooleanQuery.TooManyClauses();
}
}
请注意提升值,现在它硬编码为100,这应该足以始终将以搜索文本开头的术语放在顶部。另外,需要注意的是,如果您的术语列表很宽,使用布尔重写,您可能会遇到ToomanyCauses异常,那么您需要有一个解决方法,增加这个数字,或者以不同的方式重写此查询


对于完整测试,请看这里-

spanfirst确实是一个糟糕的建议,除了问题中列出的要求外,您还有其他要求吗?例如,如果kartnik是术语,那么每个文档都是术语,而Aakash是罕见的术语,那么行为是否应该保持不变?@Mystion。不没有这种情况。行为应该和我上面提到的一样(在每种情况下)。如果spanfirst是一个糟糕的选择,那么你能建议另一种方法来实现这一点吗?