SpringBoot 集成 elasticsearch 搜索引擎

添加maven依赖

  <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-elasticsearch</artifactId>
      <version>3.2.6.RELEASE</version>
  </dependency>

在Yaml配置中添加节点

其实这里可以改用nginx做负载均衡 不用把所有节点添加到yaml配置文件里

project:
  esearchNode:
    - 10.211.55.6:9200

接收配置文件参数的实体类

@Data
@ConfigurationProperties("project")
@Configuration
public class ProjectConfig {
    private List<String> esearchNode;
}

注入es 的Client Bean对象

@Slf4j
@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class EsearchConfiguration extends AbstractElasticsearchConfiguration {
    private final ProjectConfig projectConfig;


    private InetSocketAddress createInetSocketAddress(String hostAndPorts) {
        Assert.isTrue(!StringUtils.isEmpty(hostAndPorts), "host and ports can not be null");
        String[] split = hostAndPorts.split(":");
        Assert.isTrue(split.length == 2, "host and ports is Unknown");
        return InetSocketAddress.createUnresolved(split[0], Integer.parseInt(split[1]));
    }

    /**
     * 配置要使用的客户端
     *
     * @return
     */
    @Override
    public RestHighLevelClient elasticsearchClient() {
        InetSocketAddress[] inetSocketAddresses = projectConfig.getEsearchNode().stream().map(this::createInetSocketAddress).toArray(InetSocketAddress[]::new);
        ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo(inetSocketAddresses)
                .build();
        return RestClients.create(clientConfiguration).rest();
    }

    /**
     * 使用元模型对象映射弹性搜索映射器。
     *
     * @return
     */
    @Bean
    @Override
    public EntityMapper entityMapper() {
        ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
                new DefaultConversionService());
        entityMapper.setConversions(elasticsearchCustomConversions());
        return entityMapper;
    }
}

添加Controller

@Slf4j
@RestController
@RequestMapping("/api/esearch")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Validated
public class EsearchController {
    private final ElasticsearchOperations elasticsearchOperations;


    @RequestMapping("insert")
    public UserModel insert() {
        AlternativeJdkIdGenerator alternativeJdkIdGenerator = new AlternativeJdkIdGenerator();
        UserModel userModel = new UserModel()
                .setAge(new Random().nextInt(29) + 1)
                .setNikeName("张三")
                .setUserId(alternativeJdkIdGenerator.generateId().toString());
        IndexQuery indexQuery = new IndexQueryBuilder()
                .withId(userModel.getUserId())
                .withObject(userModel)
                .build();

        String indexId = elasticsearchOperations.index(indexQuery);
        return userModel;
    }


    @RequestMapping("query")
    public List<UserModel> query(@NotBlank String nikeName, @NotNull Integer age) {
        CriteriaQuery criteriaQuery = new CriteriaQuery(
                Criteria.where("nikeName").is(nikeName).and("age").is(age)
        );
        return elasticsearchOperations.queryForList(criteriaQuery, UserModel.class);
    }
}

测试api接口 插入

springboot_elasticsearch_2

查询名字为张三 并且年龄 13的

可以看到 刚刚插入的数据已经被搜索出来了

springboot_elasticsearch_1

使用 ElasticsearchRepository

public interface UserRepository extends ElasticsearchRepository<UserModel, String> {
    /**
     * 根据昵称 和 年龄查询
     *
     * @param nikeName 名称
     * @param age      年龄
     * @return
     */
    List<UserModel> findByNikeNameAndAge(String nikeName, Integer age);
}

加上Repository包路径

@EnableElasticsearchRepositories(basePackages = "cn.pencilso.esearch.repository")
@Slf4j
@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class EsearchConfiguration extends AbstractElasticsearchConfiguration {
  ...

再次改动Controller

@Slf4j
@RestController
@RequestMapping("/api/esearch")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Validated
public class EsearchController {
    private final UserRepository userRepository;


    @RequestMapping("insert")
    public UserModel insert() {
        AlternativeJdkIdGenerator alternativeJdkIdGenerator = new AlternativeJdkIdGenerator();
        UserModel userModel = new UserModel()
                .setAge(new Random().nextInt(29) + 1)
                .setNikeName("张三")
                .setUserId(alternativeJdkIdGenerator.generateId().toString());
        return userRepository.save(userModel);
    }


    @RequestMapping("query")
    public List<UserModel> query(@NotBlank String nikeName, @NotNull Integer age) {
        return userRepository.findByNikeNameAndAge(nikeName, age);
    }
}

遇到的问题

userRepository.findByNikeNameAndAge 这种写法会调用两次Rest接口
最后跟踪RestCient 代码 查到,调用两次Rest接口是因为,先查询了一次count,然后会再次查询数据。
从源码层次来看Repository是为了强制分页,但是,查询到了itemCount后,创建了一个Page分页参数,页码是第一页,并且分页数量是itemCount,等于还是查询了所有。

springboot_elasticesearch_3

关于解决方案,在查询的时候,定义分页参数即可。如下:

    /**
     * 根据昵称 和 年龄查询
     *
     * @param nikeName 名称
     * @param age      年龄
     * @return
     */
    List<UserModel> findByNikeNameAndAge(String nikeName, Integer age, PageRequest pageRequest);

调用的地方

userRepository.findByNikeNameAndAge(nikeName, age, PageRequest.of(0,20))
文章内容参考自官方文档:https://docs.spring.io/spring-data/elasticsearch/docs/3.2.6.RELEASE/reference/html/