2015-04-15 35 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