2015-04-15 78 views
0

我正在使用Hibernate Search和Apache Lucene的混合。我所做的事情應該是相當直接和容易的,但我無法實現我的目標。如何強制兩個查詢的組合必須在Lucene中?

我有我要查詢的字段的字符串(短語)的列表。該字段可以包含任何這些字符串。在每個領域之間,只有其中一個必須完全匹配。

在MySQL中,它看起來像這樣

select * from movies where (genres = 'name' or genres = 'name2') OR (actors = 'name' or actors = 'name2)' AND (actors = 'name' or actors = 'name2)

因此,如果一部電影至少包含1個流派給出1級給出的演員或2個演員,條件將滿足。現在在Lucene中,我首先構建一個布爾查詢,將所有可能的角色與Occur.SHOULD結合在一起。然後,我構建了另一個布爾查詢,將前一個布爾查詢與另一個布爾查詢(例如,包含所有類型)結合起來。

最後,我做同樣的兩次,這兩個BooleanQueries與Occur.MUST添加到新的一個,兩個。然而,如果我的條件中只有一個條件滿足,我至少得到了2個結果。我該如何解決這個問題?

private BooleanQuery getMatchQuery(List<String> list, String field) { 
     BooleanQuery bq = new BooleanQuery(); 
     QueryBuilder qb = getFullTextEntityManager().getSearchFactory().buildQueryBuilder().forEntity(Movie.class).get(); 
     for (String string : list) { 
      bq.add(qb.phrase().onField(field).sentence(string).createQuery(), Occur.SHOULD); 
     } 
     return bq; 
    } 

private BooleanQuery getParamMatches(MovieDto dto, boolean genres){ 
     BooleanQuery bq = new BooleanQuery(); 
     bq.add(getMatchQuery(dto.getActors(), "actors"), Occur.SHOULD); 
     bq.add(getMatchQuery(dto.getDirectors(), "directors"), Occur.SHOULD); 
     bq.add(getMatchQuery(dto.getWriters(), "writers"), Occur.SHOULD); 
     if(genres){ 
      bq.add(getMatchQuery(dto.getGenres(), "genres"), Occur.SHOULD); 
     } 
     return bq; 

    } 
public List<Movie> test(MovieDto dto){ 
     QueryBuilder qb = getFullTextEntityManager().getSearchFactory().buildQueryBuilder().forEntity(Movie.class).get(); 
     log.info(getMatches(dto.getActors())); 
     BooleanQuery bq = new BooleanQuery(); 
     bq.add(getParamMatches(dto, true), Occur.MUST); 
     bq.add(getParamMatches(dto, false), Occur.MUST); 
     javax.persistence.Query query = getFullTextEntityManager().createFullTextQuery(bq, Movie.class); 
     List<Movie> result = query.getResultList(); 
     return result; 
    } 

這是我按照上述方法進行操作的順序。雖然呼叫從下到上完成。結果查詢是這一個:

+((actors:"marlon brando" actors:"al pacino" actors:"james caan" actors:"richard s castellano") 
(directors:"francis ford coppola") (writers:"mario puzo screenplay" writers:"francis ford coppola screenplay" writers:"mario puzo novel") 
(genres:crime genres:drama)) 
+((actors:"marlon brando" actors:"al pacino" actors:"james caan" actors:"richard s castellano") 
(directors:"francis ford coppola") (writers:"mario puzo screenplay" writers:"francis ford coppola screenplay" writers:"mario puzo novel")) 

所以,我怎麼去讓這兩個條件結合強制性的,所以我不會接受,其中只有一個演員的結果,導演等存在?我希望至少有兩個參數匹配,每個查詢一個。

+0

我才意識到在詳細打字出這一切,那我可能已經做正確,但它並不完全適用於我的情況,因爲QUERY1和QUERY2既可以匹配相同的演員和整個事情會是真的。任何人都可以證實這一點?如果有人對我的問題有一個解決方案,我沒有正確識別,那甚至會更好。 – Schaka

回答

1

您的評論是正確的,無論你的子查詢可以(在給定查詢的所有結果,肯定會)都匹配了同一個術語。

有一種簡單的方法可以確保在布爾查詢中至少有兩個匹配的子查詢,而不是創建所有可能的組合列表或類似的東西。 BooleanQuery.setMinimumNumberShouldMatch。所以:

BooleanQuery query = getParamMatches(dto, true); 
query.setMinimumShouldMatch(2); 

至少有兩個字段必須匹配。如果您希望匹配任何兩個匹配項,無論它們是否在不同的字段中,您都希望將它們全部添加到同一個布爾查詢中。這可能意味着修改getMatchQuery接受BooleanQuery作爲參數,並添加到它,而不是創建一個新的。

+0

據我瞭解,我應該將所有條件,一個BooleanQuery,然後(對於演員,導演,流派等一個條件)setMinimumShouldMatch(2)該查詢,從而使的條件中至少2始終是真實的。這似乎應該解決我的問題。 這樣一來,「演員:X風格:Y」將是一個比賽,但「流派:X風格:Y」將不匹配,是否正確?我會盡力做到這一點,並接受你的問題作爲解決方案。謝謝! – Schaka