2015-01-21 65 views
0

需求是在保留在單獨的單個表中的有限數量的字段上構建簡化的搜索功能。使用Solr或類似的東西目前不是一種選擇,所有東西都必須在一個webapp中工作。數據庫是MSSQL。我想要做的就是利用Lucene查詢解析器並從中構建Hibernate條件。儘管我最初的印象是它不會太難,但我無法弄清楚如何爲複雜查詢構建標準。使用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的第一件事覆羽的搜索字符串(+name:\"Luke Skywalker\" +father:unknown fname:Luke) name:yoda 。基本上,它通過爲每個條件設置isRequired()來迭代條件。 Hibernate的工作方式不同 - 您創建一個標準對象並繼續添加具有值對的Criterions。我無法弄清楚如何將一個轉換爲另一個。我認爲我需要的是一個通用的Junction對象來附加Criterions。

回答

0

終於搞清楚了,在這裏會分享我的解決方案,以防有人會面臨同樣的問題。

向正確方向邁出的第一步是認識到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 Criterion。顯然你不能在關係型數據庫中支持全系列的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) 
      ); 
} 

希望有人會發現這個有用。