【应用】SpringBoot 整合 Elasticsearch

准备工作

创建 maven 项目,引入下列依赖(ES 版本为 7.17.7)

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

创建配置文件 application.yml,在其中对 ES 地址进行配置

spring:
  elasticsearch:
    uris: <ES-server-ip>:9200

本文中所使用的实体类 User 代码如下

@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "user")
public class User {

    @Id
    private Long userId;

    @Field(type = FieldType.Text)
    private String username;

    @Field(type = FieldType.Text)
    private String password;

    @Field(type = FieldType.Text)
    private String country;

    @Field(type = FieldType.Integer)
    private int age;

    @Field(type = FieldType.Text)
    private String remark;

    @Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second_millis)
    private Date createTime;

    @Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second_millis)
    private Date updateTime;

    @Field(type = FieldType.Integer)
    private int status;

}

创建启动类

@SpringBootApplication
public class DemoStartApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoStartApplication.class);
    }

}

SpringBoot 整合 ES 使用

基本 CRUD 操作

spring-boot-starter-data-elasticsearch 包已经为我们封装好了相应的 CRUD 的操作,我们只需要创建接口并继承ElasticsearchRepository接口即可实现基本的 CRUD 操作

ElasticsearchRepository需要指定实体类以及 Id 的类型,如代码所示

public interface UserRepository extends ElasticsearchRepository<User, Long> {

}

创建业务层代码,注入UserRepository并调用相应的方法即可实现简单的 CRUD,所有方法的作用范围只针对 user 索引

注意:更新对象也使用save方法实现,ES 中的更新是对原数据的覆盖,如果传入的某项属性为 null,则会清除原数据的相应字段

方法概述
<S extends T> S save(S entity)新增对象
<S extends T> Iterable<S> saveAll(Iterable<S> entities)批量新增对象
Optional<T> findById(ID id)通过 id 查找对象
Iterable<T> findAll()查找全部对象
Iterable<T> findAllById(Iterable<ID> ids)通过 id 批量查找对象
void deleteById(ID id)通过 id 删除对象
void deleteAll(Iterable<? extends T> entities)删除全部对象
void deleteAllById(Iterable<? extends ID> ids)通过 id 批量删除对象

用于测试的业务层代码如下

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    /**
     * 新增 User
     * @return User
     */
    public User addUser(User user) {
        return userRepository.save(user);
    }

    /**
     * 批量新增 User
     * @return Object
     */
    public Object addUsers(List<User> users) {
        return userRepository.saveAll(users);
    }

    /**
     * 修改 User
     * @return User
     */
    public User updateUser(User user) {
        return userRepository.save(user);
    }

    /**
     * 通过 id 查找 User
     * @return User
     */
    public User findById(Long id) {
        Optional<User> userOptional = userRepository.findById(id);
        return userOptional.orElse(null);
    }

    /**
     * 通过 id 批量查找 User
     * @return Iterable<User>
     */
    public Iterable<User> findAllById(List<Long> ids) {
        return userRepository.findAllById(ids);
    }

    /**
     * 查找全部 User
     * @return Iterable<User>
     */
    public Iterable<User> findAll() {
        return userRepository.findAll();
    }

    /**
     * 通过 id 删除 User
     * @return String
     */
    public String deleteById(Long id) {
        userRepository.deleteById(id);
        return "success";
    }

    /**
     * 通过 id 批量删除 User
     * @return String
     */
    public String deleteAllById(List<Long> ids) {
        userRepository.deleteAllById(ids);
        return "success";
    }

    /**
     * 删除全部 User
     * @return String
     */
    public String deleteAll() {
        userRepository.deleteAll();
        return "success";
    }

}

测试代码如下

@SpringBootTest
class UserTest {

    @Autowired
    private UserService userService;

    /**
     * 新增 User
     */
    @Test
    public void testAddUser() {
        User user = User.builder()
                .userId(1L).username("曹操").password("123456").country("魏").remark("宁教我负天下人,休教天下人负我")
                .age(18).createTime(new Date()).updateTime(new Date()).status(0)
                .build();
        System.out.println(userService.addUser(user));
    }

    /**
     * 批量新增 User
     */
    @Test
    public void testAddUsers() {
        ArrayList<User> users = new ArrayList<>();
        User user = User.builder()
                .userId(1L).username("曹操").password("123456").country("魏").remark("宁教我负天下人,休教天下人负我")
                .age(18).createTime(new Date()).updateTime(new Date()).status(0)
                .build();
        users.add(user);
        User user1 = User.builder()
                .userId(2L).username("刘备").password("123456").country("蜀").remark("唯贤唯德,能服于人")
                .age(28).createTime(new Date()).updateTime(new Date()).status(0)
                .build();
        users.add(user1);
        User user2 = User.builder()
                .userId(3L).username("孙权").password("123456").country("吴").remark("合肥战神孙十万")
                .age(38).createTime(new Date()).updateTime(new Date()).status(0)
                .build();
        users.add(user2);
        User user3 = User.builder()
                .userId(4L).username("诸葛亮").password("123456").country("蜀").remark("我从未见过如此厚颜无耻之人")
                .age(16).createTime(new Date()).updateTime(new Date()).status(0)
                .build();
        users.add(user3);
        System.out.println(userService.addUsers(users));
    }

    /**
     * 修改 User
     */
    @Test
    public void testUpdateUser() {
        User user = User.builder()
                .userId(1L).username("曹操").password("654321").remark("宁教我负天下人,休教天下人负我")
                .age(20).createTime(new Date()).updateTime(new Date()).status(0)
                .build();
        System.out.println(userService.updateUser(user));
    }

    /**
     * 通过 id 查找 User
     */
    @Test
    public void testFindById() {
        System.out.println(userService.findById(1L));
    }

    /**
     * 通过 id 批量查找 User
     */
    @Test
    public void testFindAllById() {
        ArrayList<Long> ids = new ArrayList<>();
        ids.add(3L);
        ids.add(4L);
        Iterable<User> users = userService.findAllById(ids);
        for (User user: users) {
            System.out.println(user);
        }
    }

    /**
     * 查找全部 User
     */
    @Test
    public void testFindAll() {
        Iterable<User> users = userService.findAll();
        for (User user: users) {
            System.out.println(user);
        }
    }

    /**
     * 通过 id 删除 User
     */
    @Test
    public void testDeleteById() {
        System.out.println(userService.deleteById(1L));
    }

    /**
     * 通过 id 批量删除 User
     */
    @Test
    public void testDeleteAllById() {
        ArrayList<Long> ids = new ArrayList<>();
        ids.add(3L);
        ids.add(4L);
        System.out.println(userService.deleteAllById(ids));
    }

    /**
     * 删除全部 User
     */
    @Test
    public void testDeleteAll() {
        System.out.println(userService.deleteAll());
    }

}

自定义查询操作

我们可以在UserRepository接口中按照一定的命名规则自定义方法,spring-boot-starter-data-elasticsearch 将根据方法名自动生成实现类

自定义方法的命名规则如下所示,表格摘录自官方文档

KeywordSampleElasticsearch Query String
AndfindByNameAndPrice{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
OrfindByNameOrPrice{ "query" : { "bool" : { "should" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } }, { "query_string" : { "query" : "?", "fields" : [ "price" ] } } ] } }}
IsfindByName{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
NotfindByNameNot{ "query" : { "bool" : { "must_not" : [ { "query_string" : { "query" : "?", "fields" : [ "name" ] } } ] } }}
BetweenfindByPriceBetween{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
LessThanfindByPriceLessThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : false } } } ] } }}
LessThanEqualfindByPriceLessThanEqual{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
GreaterThanfindByPriceGreaterThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : false, "include_upper" : true } } } ] } }}
GreaterThanEqualfindByPriceGreaterThan{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
BeforefindByPriceBefore{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : null, "to" : ?, "include_lower" : true, "include_upper" : true } } } ] } }}
AfterfindByPriceAfter{ "query" : { "bool" : { "must" : [ {"range" : {"price" : {"from" : ?, "to" : null, "include_lower" : true, "include_upper" : true } } } ] } }}
LikefindByNameLike{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
StartingWithfindByNameStartingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
EndingWithfindByNameEndingWith{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
Contains/ContainingfindByNameContaining{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "*?*", "fields" : [ "name" ] }, "analyze_wildcard": true } ] } }}
In (when annotated as FieldType.Keyword)findByNameIn(Collection<String>names){ "query" : { "bool" : { "must" : [ {"bool" : {"must" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
InfindByNameIn(Collection<String>names){ "query": {"bool": {"must": [{"query_string":{"query": "\"?\" \"?\"", "fields": ["name"]}}]}}}
NotIn (when annotated as FieldType.Keyword)findByNameNotIn(Collection<String>names){ "query" : { "bool" : { "must" : [ {"bool" : {"must_not" : [ {"terms" : {"name" : ["?","?"]}} ] } } ] } }}
NotInfindByNameNotIn(Collection<String>names){"query": {"bool": {"must": [{"query_string": {"query": "NOT(\"?\" \"?\")", "fields": ["name"]}}]}}}
NearfindByStoreNearNot Supported Yet !
TruefindByAvailableTrue{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }}
FalsefindByAvailableFalse{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "false", "fields" : [ "available" ] } } ] } }}
OrderByfindByAvailableTrueOrderByNameDesc{ "query" : { "bool" : { "must" : [ { "query_string" : { "query" : "true", "fields" : [ "available" ] } } ] } }, "sort":[{"name":{"order":"desc"}}] }

接口编写的自定义查询方法如下

public interface UserRepository extends ElasticsearchRepository<User, Long> {

    /**
     * 通过 name 查找 User
     * @param name 姓名
     * @return List<User>
     */
    List<User> findByUsername(String name);

    /**
     * 通过 name 和 country 查找 User
     * @param name 姓名
     * @param country 国家
     * @return List<User>
     */
    List<User> findByUsernameAndCountry(String name, String country);

    /**
     * 通过 name 或 country 查找 User
     * @param name 姓名
     * @param country 国家
     * @return List<User>
     */
    List<User> findByUsernameOrCountry(String name, String country);

    /**
     * 通过 name not XXX 查找 User
     * @param name 姓名
     * @return List<User>
     */
    List<User> findByUsernameNot(String name);

    /**
     * 按照 age 范围查询 User
     * findByAgeLessThan\findByAgeLessThanEqual\findByAgeGreaterThan\findByAgeGreaterThan
     * findByAgeBefore\findByAgeAfter
     * @param start 范围左边界
     * @param end 范围右边界
     * @return List<User>
     */
    List<User> findByAgeBetween(int start, int end);

    /**
     * 通过 remark 分词查询 User
     * @param remark 备注
     * @return List<User>
     */
    List<User> findByRemarkLike(String remark);

    /**
     * 通过 remark ???查询 User
     * @param remark 备注
     * @return List<User>
     */
    List<User> findByRemarkContaining(String remark);

    /**
     * 通过 remark 以固定字符开始分词查询 User
     * findByRemarkEndingWith 同样使用
     * @param param 字符串
     * @return List<User>
     */
    List<User> findByRemarkStartingWith(String param);

    /**
     * 通过 name 批量分词查询 User
     * findByUsernameNotIn 同样使用
     * @param names 姓名集合
     * @return List<User>
     */
    List<User> findByUsernameIn(List<String> names);

    /**
     * 分页查询 -- 通过 name 批量分词查询 User
     * @param names 姓名集合
     * @param pageable 分页参数
     * @return List<User>
     */
    Page<User> findByUsernameIn(List<String> names, Pageable pageable);

}

用于测试的业务层代码如下

@Service
public class UserService01 {

    @Autowired
    UserRepository userRepository;

    public List<User> findByUsername(String name) {
        return userRepository.findByUsername(name);
    }

    public List<User> findByUsernameAndCountry(String name, String country) {
        return userRepository.findByUsernameAndCountry(name, country);
    }

    public List<User> findByUsernameOrCountry(String name, String country) {
        return userRepository.findByUsernameOrCountry(name, country);
    }

    public List<User> findByUsernameNot(String name) {
        return userRepository.findByUsernameNot(name);
    }

    public List<User> findByAgeBetween(int start, int end) {
        return userRepository.findByAgeBetween(start, end);
    }

    public List<User> findByRemarkLike(String remark) {
        return userRepository.findByRemarkLike(remark);
    }

    public List<User> findByRemarkContaining(String remark) {
        return userRepository.findByRemarkContaining(remark);
    }

    public List<User> findByRemarkStartingWith(String param) {
        return userRepository.findByRemarkStartingWith(param);
    }

    public List<User> findByUsernameIn(List<String> names) {
        return userRepository.findByUsernameIn(names);
    }

    public Page<User> findByUsernameIn(List<String> names, Pageable pageable) {
        return userRepository.findByUsernameIn(names, pageable);
    }

}

测试代码如下

@SpringBootTest
public class UserTest01 {

    @Autowired
    UserService01 userService;

    @Test
    public void testFindByUsername() {
        List<User> users = userService.findByUsername("诸葛");
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByUsernameAndCountry() {
        List<User> users = userService.findByUsernameAndCountry("曹操", "魏");
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByUsernameOrCountry() {
        List<User> users = userService.findByUsernameOrCountry("曹操", "蜀");
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByUsernameNot() {
        List<User> users = userService.findByUsernameNot("曹操");
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByAgeBetween() {
        List<User> users = userService.findByAgeBetween(10, 20);
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByRemarkLike() {
        List<User> users = userService.findByRemarkLike("天下人");
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByRemarkContaining() {
        List<User> users = userService.findByRemarkContaining("天下人");
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByRemarkStartingWith() {
        List<User> users = userService.findByRemarkStartingWith("唯有爱和真诚");
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByUsernameIn() {
        ArrayList<String> names = new ArrayList<>();
        names.add("刘备");
        names.add("诸葛");
        List<User> users = userService.findByUsernameIn(names);
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByUsernameInPage() {
        ArrayList<String> names = new ArrayList<>();
        names.add("刘备");
        names.add("诸葛");
        names.add("曹");
        PageRequest page = PageRequest.of(0, 2);
        Page<User> users = userService.findByUsernameIn(names, page);
        System.out.println(users.getContent());
    }

}

自定义 DSL 操作

我们可以在接口中自定义 DSL 查询方法,并使用@Query方法指定查询的语句,语句中可以使用问号+序号的形式指定查询参数

public interface UserRepository extends ElasticsearchRepository<User, Long> {

    /**
     * 自定义 DSL 查询 -- 通过 country 和 age 进行查询
     * @param country 国家
     * @param age 年龄
     * @return List<User>
     */
    @Query("{\"bool\":" +
                "{\"must\":" +
                    "[{\"match\":{\"country\":\"?0\"}}," +
                    "{\"match\":{\"age\":\"?1\"}}]}}")
    List<User> findByCountryAndAge(String country, int age);

    /**
     * 自定义 DSL 分页查询 -- 通过 country 和 age 进行查询
     * @param country 国家
     * @param age 年龄
     * @param pageable 分页查询参数
     * @return List<User>
     */
    @Query("{\"bool\":" +
            "{\"must\":" +
            "[{\"match\":{\"country\":\"?0\"}}," +
            "{\"match\":{\"age\":\"?1\"}}]}}")
    Page<User> findByCountryAndAge(String country, int age, Pageable pageable);

}

用于测试的业务层代码如下

@Service
public class UserService02 {

    @Autowired
    UserRepository userRepository;

    public List<User> findByCountryAndAge(String country, int age) {
        return userRepository.findByCountryAndAge(country, age);
    }

    public Page<User> findByCountryAndAge(String country, int age, Pageable pageable) {
        return userRepository.findByCountryAndAge(country, age, pageable);
    }

}

测试代码如下

@SpringBootTest
public class UserTest02 {

    @Autowired
    UserService02 userService;

    @Test
    public void testFindByCountryAndAge() {
        List<User> users = userService.findByCountryAndAge("蜀", 28);
        for (User user: users) {
            System.out.println(user);
        }
    }

    @Test
    public void testFindByCountryAndAgePage() {
        PageRequest pageRequest = PageRequest.of(0, 2);
        Page<User> users = userService.findByCountryAndAge("蜀", 28, pageRequest);
        System.out.println(users.getContent());
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值