Java 使用Lucene解析搜索查询并基于此构建Hibernate条件
需求是在单独一个表中保留的有限数量的字段上构建简化的搜索功能。使用Solr或类似工具目前还不是一个选项,所有东西都必须在一个webapp中工作。数据库是MSSQL。我试图做的是利用Lucene查询解析器并从中构建Hibernate标准。尽管我最初的印象是这不会太难,但我不知道如何为复杂的查询构建条件 下面是我创建的一个快速测试,用于使用Lucene(4.7.2)解析查询字符串Java 使用Lucene解析搜索查询并基于此构建Hibernate条件,java,hibernate,lucene,hibernate-search,Java,Hibernate,Lucene,Hibernate Search,需求是在单独一个表中保留的有限数量的字段上构建简化的搜索功能。使用Solr或类似工具目前还不是一个选项,所有东西都必须在一个webapp中工作。数据库是MSSQL。我试图做的是利用Lucene查询解析器并从中构建Hibernate标准。尽管我最初的印象是这不会太难,但我不知道如何为复杂的查询构建条件 下面是我创建的一个快速测试,用于使用Lucene(4.7.2)解析查询字符串 Lucene first thing将搜索字符串转换为(+姓名:\'Luke Skywalker\'+父亲:未知fna
Lucene first thing将搜索字符串转换为
(+姓名:\'Luke Skywalker\'+父亲:未知fname:Luke)姓名:yoda
。基本上,然后它通过为每个术语设置isRequired()来迭代术语。Hibernate的工作方式不同——您创建一个criteria对象并不断添加带有成对值的Criterian。我不知道如何把它们转换成另一个。我想我需要的是一个通用的连接对象来附加标准。最终解决了它,我将在这里分享我的解决方案,以防有人面临同样的问题
正确方向的第一步是认识到QueryParser
不能很好地处理布尔逻辑。例如,(+姓名:\'Luke Skywalker\'+父亲:未知fname:Luke)姓名:yoda
是与(姓名:\'Luke Skywalker\'和父亲:未知或fname:Luke)或(姓名:yoda)
不同的搜索。我不知道为什么QueryParser甚至接受布尔逻辑,这很容易混淆
解决方案是使用precencequeryparser
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)
);
}
希望有人会觉得这个有用
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_47);
PrecedenceQueryParser luceneParser = new PrecedenceQueryParser(analyzer);
luceneParser.setAllowLeadingWildcard(true);
Query luceneQuery = luceneParser.parse(searchQuery, "name");
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)
);
}