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接口 插入
查询名字为张三 并且年龄 13的
可以看到 刚刚插入的数据已经被搜索出来了
使用 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,等于还是查询了所有。
关于解决方案,在查询的时候,定义分页参数即可。如下:
/**
* 根据昵称 和 年龄查询
*
* @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/