首页 > 解决方案 > Springboot:使用多个特征搜索实体列表的最佳方法是什么

问题描述

我正在 spring-boot 中开发一个 Web 应用程序,用户可以在其中使用搜索字段搜索用户。将根据用户名、名字和姓氏来查询正在搜索的用户(取决于输入字段中输入的值)。这是我的用户模型:

    @Component
@Entity
@Table(name = "Users")
public class User extends DefaultEntity {


    @Column(name = "FirstName")
    @NotNull(message = "Enter a FirstName")
    private String firstName;


    @Column(name = "LastName")
    @NotBlank(message = "Enter a LastName")
    private String lastName;


    @Column(unique = true,name = "UserName")
    @NotBlank(message = "Enter a UserName")
    private String userName;


    @Column(unique = true, name = "Email")
    @NotBlank(message = "Please enter an Email address")
    @Email(message = "Enter a valid Email")
    private String email;


    @Column(name = "Password")
    @NotBlank(message = "Enter a Password")
    private String password;


    @Enumerated(EnumType.STRING)
    @Column(name = "Gender")
    private Gender gender;


    @Column(name = "Address")
    @NotBlank(message = "Please enter your Home Address")
    private String address;


    @Column(name = "Country")
    @NotBlank(message = "Please enter your Country")
    private String country;


    @Column(name = "Picture")
    private String picture;


    @Column(unique = true, name = "PhoneNumber") //make this accept only numbers
    private String phoneNumber;


    @Column(name = "Bio")
    private String bio;


    @Enumerated(EnumType.STRING)
    @Column(name = "OnlineStatus")
    private OnlineStatus onlineStatus;

    @Enumerated(EnumType.STRING)
    @Column(name = "UserType")
    private UserType userType;


    @Column(name = "Money")
    private double money;


    //@MapsId()
    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "playerstats")
    private PlayerStats playerStats;

    //columnDefinition = "tinyint default false"
    @Column(name = "locked",columnDefinition = "BOOL default false")
    private Boolean locked;



    @Transient
    private MultipartFile file;


    public String getFirstName() {
        return firstName;
    }

    public User setFirstName(String firstName) {
        this.firstName = firstName;
        return this;
    }

    public String getLastName() {
        return lastName;
    }

    public User setLastName(String lastName) {
        this.lastName = lastName;
        return this;
    }

    public String getUserName() {
        return userName;
    }

    public User setUserName(String userName) {
        this.userName = userName;
        return this;
    }

    public String getEmail() {
        return email;
    }

    public User setEmail(String email) {
        this.email = email;
        return this;
    }

    public String getPassword() {
        return password;
    }

    public User setPassword(String password) {
        this.password = password;
        return this;
    }

    public Enum.Gender getGender() {
        return gender;
    }

    public User setGender(Enum.Gender gender) {
        this.gender = gender;
        return this;
    }

    public String getAddress() {
       return address;
    }

    public User setAddress(String address) {
       this.address = address;
       return this;
    }

    public String getCountry() {
        return country;
    }

    public User setCountry(String country) {
        this.country = country;
        return this;
    }

    public String getPicture() {
        return picture;
    }

    public User setPicture(String picture) {
        this.picture = picture;
        return this;
    }

    public String getBio() {
        return bio;
    }

    public User setBio(String bio) {
        this.bio = bio;
        return this;
    }

    public Enum.OnlineStatus getOnlineStatus() {
        return onlineStatus;
    }

    public User setOnlineStatus(Enum.OnlineStatus onlineStatus) {
        this.onlineStatus = onlineStatus;
        return this;
    }

    public Enum.UserType getUserType() {
        return userType;
    }

    public User setUserType(Enum.UserType userType) {
        this.userType = userType;
        return this;
    }

    public double getMoney() {
        return money;
    }

    public User setMoney(double money) {
        this.money = money;
        return this;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public User setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
        return this;
    }

    public MultipartFile getFile() {
        return file;
    }

    public User setFile(MultipartFile file) {
        this.file = file;
        return this;
    }

    public PlayerStats getPlayerStats() {
        return playerStats;
    }

    public User setPlayerStats(PlayerStats playerStats) {
        this.playerStats = playerStats;
        return this;
    }

    public Boolean getLocked() {
        return locked;
    }

    public void setLocked(Boolean locked) {
        this.locked = locked;
    }

}

这是我在 UserRepository 中查询用户模型的方法:

    @Repository
public interface UserRepository extends JpaRepository<User,Long> {


    Page<User> findUsersByUserNameContainingOrFirstNameContainingOrLastNameContaining(String UserName, String FirstName, String LastName, Pageable pageable);

}

我的问题:有没有更好的方法或更有效的方法来实现查询用户实体?

标签: spring-bootsearchspring-data-jpa

解决方案


如评论中所述,您正在寻找的是模糊搜索。这不是您可以在数据库中轻松完成的事情,但是您可以使用单独的搜索引擎:

  • Apache Solr(基于 Apache Lucene 的平台)
  • 弹性搜索
  • Hibernate Search(Hibernate 与 Apache Lucene 集成)
  • ...

使用此类解决方案时,您还必须将实体索引到搜索引擎中。Spring Data 可以为您提供帮助,因为还有一个 Solr 库

首先,您需要一个新类来表示您的实体在 Solr 中的外观。请注意,如果您有嵌套关系,您想要“展平”所有内容:

@Document
public class UserDocument {
    @Id
    @Indexed("id")
    private String id;
    @Indexed("firstName")
    private String firstName;
    @Indexed("lastName")
    private String lastName;
    @Indexed("userName")
    private String userName;

    // ...
}

之后,您可以像使用 Spring Data 一样编写存储库:

public interface UserDocumentRepository extends SolrCrudRepository<UserDocument, String> {
    @Query("userName:?0 OR firstName:?0 OR lastName:?0")
    List<UserDocument> findAll(String searchTerm);
}

之后,您可以执行以下操作:

public User create(User input) {
    // Create user in database
    documentRepository.save(new UserDocument(input.getFirstName(), input.getLastName(), input.getUserName());
}

您还可以使用存储库查询模糊搜索:

documentRepository.findAll("vickz~3");

这将使用我刚刚编写的查询,并将查找包含 vickz 的名字、姓氏或用户名。~3末尾是一种特殊语法,表示名称可以与我刚刚使用的名称不同 3 个字符(= 编辑距离)。

但是,这将返回UserDocumentSolr 实体。如果要获取实体,还必须查找它们,这可以通过它们的用户名来完成:

List<String> usernames = documentRepository
    .findAll("vickz~3")
    .stream()
    .map(UserDocument::getUserName)
    .collect(Collectors.toList());
repository.findByUsername(usernames); // Look in database for users matching those usernames

推荐阅读