为同一数据库列创建varargs @Query

时间:2019-04-11 20:26:51

标签: spring-data-jpa spring-data

我正在尝试实现简单的文本搜索。我想允许用户键入任意数量的单词,并对照文章标题检查所有这些单词。如果标题与搜索中的所有单词匹配(以任何顺序),则将返回结果。

一些示例数据库查询将如下所示:

SELECT * FROM Article WHERE
    lower(title) like lower(concat('%', '<term1>', '%'));

SELECT * FROM Article WHERE
   lower(title) like lower(concat('%', '<term1>', '%'))
   AND lower(title) like lower(concat('%', '<term2>', '%'));

SELECT * FROM Article WHERE
   lower(title) like lower(concat('%', '<term1>', '%'))
   AND lower(title) like lower(concat('%', '<term2>', '%'));
   AND lower(title) like lower(concat('%', '<term3>', '%'));

请注意在每种情况下如何搜索title列,并将其与1、2或3(或更多)项进行比较。

我可以创建单个JPQL查询来做到这一点吗?

类似这样的东西:

@Query("SELECT a FROM Article a WHERE lower(a.title) in %:terms%")
Iterable<Article> findAllContainingTitle(String... terms)

似乎Java的varargs起作用了,但更多地体现了集合中的特定值。

更新1

感谢this questionthis link,我学习了如何直接在Postgres查询中做到这一点:

SELECT * FROM Article WHERE ~~* ALL(ARRAY['%term1%', '%term2%', '%term3%']);

现在的挑战是尝试将其转换为Spring Data JPA查询。

注意:~~*也可以与ILIKE交换(不区分大小写的LIKE语句)。

1 个答案:

答案 0 :(得分:0)

这不能回答我的问题,但是如果其他任何人需要类似的东西,它至少提供了一种解决方法。要求的一部分是还使用JOIN FETCH在单个查询中拉回所有数据,而不会遇到N + 1性能问题。

解决方法是使用CriteriaBuilderCriteriaQuery动态生成查询。我觉得这会创建一个可读性较低的查询,但是它肯定总比没有好。

@Autowired
private EntityManager entityManager;

...

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder()
CriteriaQuery<Content> criteriaQuery = criteriaBuilder.createQuery(Article.class);

Root<Article> rootArticle = crirteriaQuery.from(Article.class);

// Provide any number of joins here
rootArticle.fetch("author", JoinType.INNER);

// This is where we dynamically add all of the 'like' statements
List<Predicate> predicates = new ArrayList<>();
// Assume that 'searchQuery' is plain text from the user, that will be split on spaces to get individual search terms.
for (String searchTerm : searchQuery.split(" ")) {
    predicates.add(criteriaBuilder.like(
        criteriaBuilder.lower(rootArticle.get("title")),
        "%"+searchTerm.toLowerCase()+"%"));
}
criteriaQuery.where(predicates.toArray(new Predicate[0]));

TypedQuery<Article> query = entityManager.createQuery(criteriaQuery);
List<Article> articles = query.getResultList();

特别感谢this answerthis page的链接,其中https://bugs.llvm.org/show_bug.cgi?id=39363#c8解释了如何将JOIN语句添加到CriteriaQuery