我如何在 Lucene 中强制两个查询的组合是必须的?
How do I force the combination of two queries to be MUST in Lucene?
我正在混合使用 Hibernate Search 和 Apache 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 中,我首先构建一个 BooleanQuery,将所有可能的参与者与 Occur.SHOULD
结合起来。然后我构建另一个 BooleanQuery 将之前的 BooleanQuery 与另一个 BooleanQuery 相结合(例如包含所有类型)。
最后,我做了两次相同的操作,并将这两个 BooleanQueries 添加到一个新的 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"))
那么,我该如何结合强制执行这两个条件,这样我就不会收到只有一个演员、导演等存在的结果?我希望至少匹配 2 个参数,每个查询一个。
您的评论是正确的,您的两个子查询都可以(并且在给定查询的所有结果中,肯定会)都匹配同一个词。
有一种更简单的方法可以确保在布尔查询中至少有两个匹配的子查询,而不是创建所有可能组合的列表或类似的东西。 BooleanQuery.setMinimumNumberShouldMatch
。所以:
BooleanQuery query = getParamMatches(dto, true);
query.setMinimumShouldMatch(2);
必须至少在您的两个字段中匹配。如果您想要匹配任意两个术语,无论它们是否在不同的字段中,您都希望将它们全部添加到同一个 BooleanQuery 中。这可能意味着在接受 BooleanQuery 作为参数时修改 getMatchQuery
,然后添加到它,而不是创建一个新的。
我正在混合使用 Hibernate Search 和 Apache 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 中,我首先构建一个 BooleanQuery,将所有可能的参与者与 Occur.SHOULD
结合起来。然后我构建另一个 BooleanQuery 将之前的 BooleanQuery 与另一个 BooleanQuery 相结合(例如包含所有类型)。
最后,我做了两次相同的操作,并将这两个 BooleanQueries 添加到一个新的 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"))
那么,我该如何结合强制执行这两个条件,这样我就不会收到只有一个演员、导演等存在的结果?我希望至少匹配 2 个参数,每个查询一个。
您的评论是正确的,您的两个子查询都可以(并且在给定查询的所有结果中,肯定会)都匹配同一个词。
有一种更简单的方法可以确保在布尔查询中至少有两个匹配的子查询,而不是创建所有可能组合的列表或类似的东西。 BooleanQuery.setMinimumNumberShouldMatch
。所以:
BooleanQuery query = getParamMatches(dto, true);
query.setMinimumShouldMatch(2);
必须至少在您的两个字段中匹配。如果您想要匹配任意两个术语,无论它们是否在不同的字段中,您都希望将它们全部添加到同一个 BooleanQuery 中。这可能意味着在接受 BooleanQuery 作为参数时修改 getMatchQuery
,然后添加到它,而不是创建一个新的。