有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java使用Lucene解析搜索查询,并在此基础上构建Hibernate条件

需求是在单独一个表中保留的有限数量的字段上构建简化的搜索功能。使用Solr或类似工具目前还不是一个选项,所有东西都必须在一个webapp中工作。数据库是MSSQL。我试图做的是利用Lucene查询解析器并从中构建Hibernate标准。尽管我最初的印象是这不会太难,但我不知道如何为复杂的查询构建条件

下面是我创建的一个快速测试,用于使用Lucene(4.7.2)解析查询字符串

Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47);
QueryParser luceneParser = new QueryParser(Version.LUCENE_47, "", analyzer);
String queryString = "(name:\"Luke Skywalker\" AND father:unknown OR fname:Luke) or (name:yoda)";
Query luceneQuery = luceneParser.parse(queryString);

public class QueryInterpreter {
    public void parse(Query query) {
        if (query instanceof TermQuery) {
            termQuery((TermQuery) query);
        } else if (query instanceof BooleanQuery) {
            booleanQuery((BooleanQuery) query);
        } else if (query instanceof PhraseQuery) {
            phraseQuery((PhraseQuery) query);
        } else {
            throw new IllegalArgumentException("");
        }
    }
    public void booleanQuery(BooleanQuery query) {
        for (BooleanClause clause : query.getClauses()) {
            parse(clause.getQuery());
        }
    }
    public void phraseQuery(PhraseQuery query) {
        StringBuilder sb = new StringBuilder();
        for (Term term : query.getTerms()) {
            sb.append(term.text());
            sb.append(" ");
        }

    }
    public void termQuery(TermQuery query) {
        Term term = query.getTerm();
    }
}

Lucene first thing将搜索字符串转换为(+name:\"Luke Skywalker\" +father:unknown fname:Luke) name:yoda。基本上,然后它通过为每个术语设置isRequired()来迭代术语。Hibernate的工作方式不同——您创建一个criteria对象并不断添加带有成对值的Criterian。我不知道如何把它们转换成另一个。我想我需要的是一个通用的连接对象来附加标准


共 (1) 个答案

  1. # 1 楼答案

    最后,我将在这里分享我的解决方案,以防有人面临同样的问题

    正确方向的第一步是认识到QueryParser不能很好地处理布尔逻辑。例如(+name:\"Luke Skywalker\" +father:unknown fname:Luke) name:yoda是与(name:\"Luke Skywalker\" AND father:unknown OR fname:Luke) or (name:yoda)不同的搜索。我不知道为什么QueryParser甚至接受布尔逻辑,这很容易混淆

    解决方法是使用PrecedenceQueryParser

    Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47);
    PrecedenceQueryParser luceneParser = new PrecedenceQueryParser(analyzer);
    luceneParser.setAllowLeadingWildcard(true);
    Query luceneQuery = luceneParser.parse(searchQuery, "name");
    

    然后从中创建一个Hibernate标准。显然,您不能在关系数据库中支持全方位的Lucene搜索功能,但这从来都不是必需的

    public Criterion buildHibernateQuery(Query luceneQuery) {
        return parse(luceneQuery);
    }
    
    private Criterion parse(Query query) {
        if (query instanceof TermQuery) {
            return parse((TermQuery) query);
        } else if (query instanceof BooleanQuery) {
            return parse((BooleanQuery) query);
        } else if (query instanceof PhraseQuery) {
            return parse((PhraseQuery) query);
        } else if (query instanceof PrefixQuery) {
            return parse((PrefixQuery) query);
        } else if (query instanceof WildcardQuery) {
            return parse((WildcardQuery) query);
        } else {
            LOG.error(String.format("%s unsupported", query.getClass()));
        }
    }
    
    private Criterion parse(TermQuery query) {
        Term term = query.getTerm();
        return createNameValueRestriction(term.field(), term.text());
    }
    
    private Criterion parse(BooleanQuery query) {
        if (query.getClauses().length == 1) {
            return parse(query.getClauses()[0].getQuery());
        }
        Junction junction = createJunction(query.getClauses()[0]);
    
        for (BooleanClause clause: query.getClauses()) {
            junction.add(parse(clause.getQuery()));
        }
        return junction;
    }
    
    private Junction createJunction(BooleanClause booleanClause) {
        if (booleanClause.isRequired()) {
            return Restrictions.conjunction();
        } else {
            return Restrictions.disjunction();
        }
    }
    
    private Criterion parse(PhraseQuery query) {
        String field = query.getTerms()[0].field();
        StringBuilder phraseBuilder = new StringBuilder();
        for (Term term : query.getTerms()) {
            phraseBuilder.append(term.text());
            phraseBuilder.append(" ");
        }
    
        return createNameValueRestriction(field, phraseBuilder.toString().trim());
    }
    
    private Criterion createNameValueRestriction(String field, String value) {
        return Restrictions.and(
                Restrictions.eq("jsonPath", field),
                Restrictions.eq("answer", value)
                );
    }
    
    private Criterion parse(PrefixQuery query) {
        Term term = query.getPrefix();
        return parseLikeQuery(term.field(), term.text(), MatchMode.START);
    }
    
    private Criterion parse(WildcardQuery query) {
        Term term = query.getTerm();
        String wildCardEscaped = Pattern.quote(String.valueOf(WildcardQuery.WILDCARD_STRING));
        String termText = term.text().replaceAll(wildCardEscaped, "");
        return parseLikeQuery(term.field(), termText, MatchMode.ANYWHERE);
    }
    
    
    private Criterion parseLikeQuery(String field, String value, MatchMode matchMode) {
        return Restrictions.and(
                Restrictions.eq("jsonPath", field),
                Restrictions.like("answer", value, matchMode)
                );
    }
    

    希望有人会觉得这个有用