如何在JPA(Spring Data JPA)中实现简单的全文搜索?

6jjcrrmo  于 2023-10-19  发布在  Spring
关注(0)|答案(2)|浏览(117)

我正在使用JPA 2.1(Hibernate 4作为impl)和Spring Data JPA 1.9.0。如何实现全文搜索?
我的情况如下。我有一个User实体和用户界面上有一个显示的用户属性的大部分表,我希望用户给予文本框输入有一个搜索词和搜索的所有属性。
我看到两个选项来做到这一点:
1.从数据库加载所有用户并在Java中过滤它们
1.编写包含多个ORsLIKE % :searchString %的JPQL查询

选项1对性能不好,但写起来很好。
选项2是高性能的,因为在数据库端执行,但写起来很麻烦。

现在我起诉选项1,因为我需要将布尔转换为"yes"/"no",也有一个配置文件枚举,我想通过它的字段描述搜索,而不是通过实际枚举值。
在User实体中,我有一个方法,它返回所有我想搜索的字段,并以空格分隔:

public String getSearchString(){
      return StringUtils.join(
              Arrays.asList(
                      login, 
                      firstName, 
                      lastName, 
                      email, 
                      active ? "yes" : "no", 
                      profile.getDescription())
              , " ");
   }

在一个服务中,我从数据库加载所有用户,并通过以下搜索字符串进行过滤:

@Override
   public List<User> getUsers(final String searchText) {
      final List<User> users = getUsers();
      if(StringUtils.isBlank(searchText)){
         return users;
      }
      CollectionUtils.filter(users, new Predicate<User>() {
         @Override
         public boolean evaluate(User object) {
            return StringUtils.containsIgnoreCase(object.getSearchString(), searchText);
         }
      });
      return users;
   }

在JPQL的另一边,我最终得到了这样的查询,我不认为这是实现这个功能的最好和最简单的方法。另外,将布尔值转换为“yes”和“no”也有问题。

@Query("SELECT r FROM User r WHERE "
        + "r.firstname LIKE '%' || :searchString || '%' "
        + "OR r.lastname LIKE '%' || :searchString || '%' "
        + "OR r.login LIKE '%' || :searchString || '%' "
        + "OR r.profile.description LIKE '%' || :searchString || '%' "
        + "OR r.active LIKE '%' || :searchString || '%' "
        + "OR r.email LIKE '%' || :searchString || '%'")
List<User> selectUsers(@Param("searchString")String searchString, Pageable page);

是否有更好的解决方案?

2mbi3lxu

2mbi3lxu1#

通过在每次持久化和更新时将搜索字符串保存到数据库来解决这个问题。首先为searchString创建一个列:

@Column(name = "SEARCH_STRING", length = 1000)
   private String searchString;

存储很便宜,数据库的开销也不是很大。
然后保存更新和持久化:

@PreUpdate
   @PrePersist
   void updateSearchString() {
      final String fullSearchString = String.join(" ", List.of(
              login,
              firstName,
              lastName,
              email,
              Boolean.TRUE.equals(active) ? "tak" : "nie",
              profile.getDescription()));
      this.searchString = StringUtils.substring(fullSearchString, 0, 999);
   }

然后我可以用LIKE进行一个普通的JPQL查询:

SELECT u FROM User u WHERE u.searchString LIKE '%' || :text || '%'

或者使用Query By Example:

ExampleMatcher matcher = ExampleMatcher.matching().
          withMatcher("searchString", ExampleMatcher.GenericPropertyMatcher.of(ExampleMatcher.StringMatcher.CONTAINING).ignoreCase());
ssgvzors

ssgvzors2#

在大多数情况下,选项1并不是一个真正的替代方案。如果您的应用程序的寄存器增长,内存和性能问题将在一段时间后打击你。我保证
我看不出选项2有什么问题。性能不高,但很容易理解。
如果数据库的性能有问题,可以创建一个本机查询来调用数据库的本机函数来进行全文搜索。不会是JPQL查询,但是对于这类查询你正在尝试做的这个解决方案可以用。

相关问题