init
This commit is contained in:
3
sl-express-ms-search-service/.gitignore
vendored
Normal file
3
sl-express-ms-search-service/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.idea
|
||||
target/
|
||||
*.iml
|
13
sl-express-ms-search-service/Dockerfile
Normal file
13
sl-express-ms-search-service/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM openjdk:11-jdk
|
||||
LABEL maintainer="研究院研发组 <research@itcast.cn>"
|
||||
|
||||
# 时区修改为东八区
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
WORKDIR /app
|
||||
ARG JAR_FILE=target/*.jar
|
||||
ADD ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["sh","-c","java -Djava.security.egd=file:/dev/./urandom -jar $JAVA_OPTS app.jar"]
|
120
sl-express-ms-search-service/pom.xml
Normal file
120
sl-express-ms-search-service/pom.xml
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.sl-express</groupId>
|
||||
<artifactId>sl-express-parent</artifactId>
|
||||
<version>1.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.sl-express.ms.search</groupId>
|
||||
<artifactId>sl-express-ms-search-service</artifactId>
|
||||
<version>1.1-SNAPSHOT</version>
|
||||
<description>搜索微服务</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<maven.compiler.target>11</maven.compiler.target>
|
||||
<sl-express-common.version>1.2-SNAPSHOT</sl-express-common.version>
|
||||
<sl-express-mq.version>1.1-SNAPSHOT</sl-express-mq.version>
|
||||
<sl-express-ms-search-domain.version>1.1-SNAPSHOT</sl-express-ms-search-domain.version>
|
||||
<sl-express-ms-work-api.version>1.1-SNAPSHOT</sl-express-ms-work-api.version>
|
||||
<sl-express-ms-oms-api.version>1.1-SNAPSHOT</sl-express-ms-oms-api.version>
|
||||
<elasticsearch.version>7.17.5</elasticsearch.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.sl-express.ms.search</groupId>
|
||||
<artifactId>sl-express-ms-search-domain</artifactId>
|
||||
<version>${sl-express-ms-search-domain.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sl-express.common</groupId>
|
||||
<artifactId>sl-express-common</artifactId>
|
||||
<version>${sl-express-common.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sl-express.mq</groupId>
|
||||
<artifactId>sl-express-mq</artifactId>
|
||||
<version>${sl-express-mq.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sl-express.ms.work</groupId>
|
||||
<artifactId>sl-express-ms-work-api</artifactId>
|
||||
<version>${sl-express-ms-work-api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sl-express.ms.oms</groupId>
|
||||
<artifactId>sl-express-ms-oms-api</artifactId>
|
||||
<version>${sl-express-ms-oms-api.version}</version>
|
||||
</dependency>
|
||||
<!--elasticsearch-->
|
||||
<dependency>
|
||||
<groupId>co.elastic.clients</groupId>
|
||||
<artifactId>elasticsearch-java</artifactId>
|
||||
<version>${elasticsearch.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
<version>2.13.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.json</groupId>
|
||||
<artifactId>jakarta.json-api</artifactId>
|
||||
<version>2.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
<goal>build-info</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<!--指定主类-->
|
||||
<mainClass>com.sl.SearchApplication</mainClass>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
@@ -0,0 +1,14 @@
|
||||
package com.sl;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
|
||||
@EnableFeignClients
|
||||
@SpringBootApplication
|
||||
public class SearchApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SearchApplication.class, args);
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package com.sl.ms.search.config;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
|
||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||
import co.elastic.clients.transport.rest_client.RestClientTransport;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* ES配置类
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "elasticsearch")
|
||||
public class ElasticSearchConfig {
|
||||
|
||||
private String host;
|
||||
private Integer port;
|
||||
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Bean
|
||||
public ElasticsearchClient client() {
|
||||
log.info("es连接ip:{},端口:{}", host, port);
|
||||
// 基本的连接
|
||||
RestClient restClient = RestClient.builder(new HttpHost(host, port)).build();
|
||||
|
||||
// 使用自定义json序列化
|
||||
JacksonJsonpMapper jacksonJsonpMapper = new JacksonJsonpMapper(objectMapper);
|
||||
|
||||
// Create the transport with a Jackson mapper
|
||||
ElasticsearchTransport transport = new RestClientTransport(restClient, jacksonJsonpMapper);
|
||||
|
||||
// And create the API client
|
||||
return new ElasticsearchClient(transport);
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package com.sl.ms.search.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
|
||||
|
||||
/**
|
||||
* LocalDateTime序列化配置
|
||||
**/
|
||||
@Configuration
|
||||
public class LocalDateTimeSerializingConfig {
|
||||
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
// 序列化
|
||||
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(NORM_DATETIME_FORMATTER));
|
||||
// 反序列化
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(NORM_DATETIME_FORMATTER));
|
||||
|
||||
om.registerModule(javaTimeModule);
|
||||
return om;
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package com.sl.ms.search.controller;
|
||||
|
||||
import com.sl.ms.search.domain.dto.CourierTaskDTO;
|
||||
import com.sl.ms.search.domain.dto.CourierTaskPageQueryDTO;
|
||||
import com.sl.ms.search.service.CourierTaskService;
|
||||
import com.sl.transport.common.util.PageResponse;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiImplicitParams;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 快递员任务Controller
|
||||
**/
|
||||
@RestController
|
||||
@Api(tags = "快递员任务")
|
||||
@RequestMapping("courierSearch")
|
||||
public class CourierTaskController {
|
||||
|
||||
@Resource
|
||||
private CourierTaskService courierTaskService;
|
||||
|
||||
@PostMapping("pageQuery")
|
||||
@ApiOperation(value = "分页查询")
|
||||
public PageResponse<CourierTaskDTO> pageQuery(@RequestBody CourierTaskPageQueryDTO pageQueryDTO) {
|
||||
return courierTaskService.pageQuery(pageQueryDTO);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@ApiOperation(value = "新增/全量修改快递员任务")
|
||||
public void saveOrUpdate(@RequestBody CourierTaskDTO courierTaskDTO) {
|
||||
courierTaskService.saveOrUpdate(courierTaskDTO);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@ApiOperation(value = "根据取派件id查询快递员任务")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "id", value = "取派件id", required = true, dataTypeClass = Long.class)
|
||||
})
|
||||
public CourierTaskDTO findById(@PathVariable("id") Long id) {
|
||||
return courierTaskService.findById(id);
|
||||
}
|
||||
}
|
@@ -0,0 +1,112 @@
|
||||
package com.sl.ms.search.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 快递员任务实体类
|
||||
**/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class CourierTaskEntity {
|
||||
|
||||
/**
|
||||
* 取派件任务id
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 订单id
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 运单id
|
||||
*/
|
||||
private String transportOrderId;
|
||||
|
||||
/**
|
||||
* 电话
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 任务类型
|
||||
*/
|
||||
private Integer taskType;
|
||||
|
||||
/**
|
||||
* 任务状态
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 快递员id
|
||||
*/
|
||||
private Long courierId;
|
||||
|
||||
/**
|
||||
* 机构id
|
||||
*/
|
||||
private Long agencyId;
|
||||
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* 预计开始时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime estimatedStartTime;
|
||||
|
||||
/**
|
||||
* 实际开始时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime actualStartTime;
|
||||
|
||||
/**
|
||||
* 预计结束时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime estimatedEndTime;
|
||||
|
||||
/**
|
||||
* 实际结束时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime actualEndTime;
|
||||
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
private Integer isDeleted;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime created;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private LocalDateTime updated;
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package com.sl.ms.search.enums;
|
||||
|
||||
import cn.hutool.core.util.EnumUtil;
|
||||
import com.sl.transport.common.enums.BaseExceptionEnum;
|
||||
|
||||
/**
|
||||
* 搜索微服务异常枚举
|
||||
**/
|
||||
public enum ExceptionEnum implements BaseExceptionEnum {
|
||||
ES_ACCESS_ERROR(9901, "访问es出现未知异常!"),
|
||||
TASK_NOT_FOUND(9902, "任务不存在!");
|
||||
|
||||
private final Integer code;
|
||||
private final Integer status;
|
||||
private final String value;
|
||||
|
||||
ExceptionEnum(Integer code, String value) {
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
this.status = 500;
|
||||
}
|
||||
|
||||
ExceptionEnum(Integer code, Integer status, String value) {
|
||||
this.code = code;
|
||||
this.value = value;
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public static ExceptionEnum codeOf(Integer code) {
|
||||
return EnumUtil.getBy(ExceptionEnum::getCode, code);
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package com.sl.ms.search.mq;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import com.sl.ms.search.service.CourierTaskService;
|
||||
import com.sl.transport.common.constant.Constants;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.core.ExchangeTypes;
|
||||
import org.springframework.amqp.rabbit.annotation.Exchange;
|
||||
import org.springframework.amqp.rabbit.annotation.Queue;
|
||||
import org.springframework.amqp.rabbit.annotation.QueueBinding;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 快递员任务消息处理
|
||||
**/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CourierTaskMQListener {
|
||||
|
||||
@Resource
|
||||
private CourierTaskService courierTaskService;
|
||||
@Resource
|
||||
private ElasticsearchClient client;
|
||||
@Value("${sl.es.index_name}")
|
||||
private String indexName;
|
||||
|
||||
/**
|
||||
* 新增/更新快递员任务
|
||||
*
|
||||
* @param msg 消息
|
||||
*/
|
||||
@RabbitListener(bindings = @QueueBinding(
|
||||
value = @Queue(name = Constants.MQ.Queues.COURIER_TASK_SAVE_OR_UPDATE),
|
||||
exchange = @Exchange(name = Constants.MQ.Exchanges.COURIER_TASK, type = ExchangeTypes.TOPIC),
|
||||
key = Constants.MQ.RoutingKeys.COURIER_TASK_SAVE_OR_UPDATE
|
||||
))
|
||||
public void listenCourierTaskCreateMsg(String msg) {
|
||||
log.info("接收到新增/更新快递员任务的消息 ({})-> {}", Constants.MQ.Queues.COURIER_TASK_SAVE_OR_UPDATE, msg);
|
||||
// TODO 具体业务逻辑的处理
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建运单后同步数据到es
|
||||
*
|
||||
* @param msg 消息
|
||||
*/
|
||||
@RabbitListener(bindings = @QueueBinding(
|
||||
value = @Queue(name = Constants.MQ.Queues.SEARCH_TRANSPORT_ORDER_CREATED),
|
||||
exchange = @Exchange(name = Constants.MQ.Exchanges.TRANSPORT_ORDER_DELAYED, type = ExchangeTypes.TOPIC, delayed = Constants.MQ.DELAYED),
|
||||
key = Constants.MQ.RoutingKeys.TRANSPORT_ORDER_CREATE
|
||||
))
|
||||
public void listenTransportOrderCreatedMsg(String msg) {
|
||||
log.info("接收到新增运单的消息 ({})-> {}", Constants.MQ.Queues.TRACK_TRANSPORT_ORDER_CREATED, msg);
|
||||
// TODO 具体业务逻辑的处理
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package com.sl.ms.search.service;
|
||||
|
||||
import com.sl.ms.search.domain.dto.CourierTaskDTO;
|
||||
import com.sl.ms.search.domain.dto.CourierTaskPageQueryDTO;
|
||||
import com.sl.ms.search.entity.CourierTaskEntity;
|
||||
import com.sl.transport.common.util.PageResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 快递员任务服务接口
|
||||
**/
|
||||
public interface CourierTaskService {
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param pageQueryDTO 分页查询条件
|
||||
* @return 分页查询结果
|
||||
*/
|
||||
PageResponse<CourierTaskDTO> pageQuery(CourierTaskPageQueryDTO pageQueryDTO);
|
||||
|
||||
/**
|
||||
* 新增快递员任务
|
||||
*
|
||||
* @param courierTaskDTO 快递员任务
|
||||
*/
|
||||
void saveOrUpdate(CourierTaskDTO courierTaskDTO);
|
||||
|
||||
/**
|
||||
* 根据取派件id查询快递员任务
|
||||
*
|
||||
* @param id 取派件id
|
||||
* @return 快递员任务
|
||||
*/
|
||||
CourierTaskDTO findById(Long id);
|
||||
|
||||
/**
|
||||
* 根据订单id查询快递员任务
|
||||
*
|
||||
* @param orderId 订单id
|
||||
* @return 快递员任务列表
|
||||
*/
|
||||
List<CourierTaskEntity> findByOrderId(Long orderId);
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
|
||||
_ ${spring.application.name} ${application.version}
|
||||
___ | | ___ __ __ _ __ _ __ ___ ___ ___ Port: ${server.port}
|
||||
/ __|| | _____ / _ \\ \/ /| '_ \ | '__|/ _ \/ __|/ __| Pid: ${pid} Profile(s): ${AnsiColor.GREEN}${spring.profiles.active}${AnsiColor.DEFAULT}
|
||||
\__ \| ||_____|| __/ > < | |_) || | | __/\__ \\__ \
|
||||
|___/|_| \___|/_/\_\| .__/ |_| \___||___/|___/ https://sl-express.itheima.net/
|
||||
|_|
|
@@ -0,0 +1,22 @@
|
||||
server:
|
||||
port: 18099
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
threads:
|
||||
max: 1000
|
||||
min-spare: 30
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: nacos
|
||||
server-addr: 192.168.150.101:8848
|
||||
discovery:
|
||||
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
|
||||
ip: 192.168.150.1
|
||||
config:
|
||||
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
|
||||
shared-configs: #共享配置
|
||||
- data-id: shared-spring-rabbitmq.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
@@ -0,0 +1,21 @@
|
||||
server:
|
||||
port: 18099
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
threads:
|
||||
max: 1000
|
||||
min-spare: 30
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: vO5/dZ9,iL
|
||||
server-addr: nacos-service.yjy-public-slwl-java-prod.svc.cluster.local:8848
|
||||
discovery:
|
||||
namespace: 92312ba8-1119-440f-81af-c29618df303b
|
||||
config:
|
||||
namespace: 92312ba8-1119-440f-81af-c29618df303b
|
||||
shared-configs: #共享配置
|
||||
- data-id: shared-spring-rabbitmq.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
@@ -0,0 +1,21 @@
|
||||
server:
|
||||
port: 18099
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
threads:
|
||||
max: 1000
|
||||
min-spare: 30
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: nacos
|
||||
server-addr: 192.168.150.101:8848
|
||||
discovery:
|
||||
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
|
||||
config:
|
||||
namespace: ecae68ba-7b43-4473-a980-4ddeb6157bdc
|
||||
shared-configs: #共享配置
|
||||
- data-id: shared-spring-rabbitmq.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
@@ -0,0 +1,21 @@
|
||||
server:
|
||||
port: 18099
|
||||
tomcat:
|
||||
uri-encoding: UTF-8
|
||||
threads:
|
||||
max: 1000
|
||||
min-spare: 30
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
username: nacos
|
||||
password: nacos
|
||||
server-addr: nacos-service.yjy-public-slwl-java.svc.cluster.local:8848
|
||||
discovery:
|
||||
namespace: 92312ba8-1119-440f-81af-c29618df303b
|
||||
config:
|
||||
namespace: 92312ba8-1119-440f-81af-c29618df303b
|
||||
shared-configs: #共享配置
|
||||
- data-id: shared-spring-rabbitmq.yml
|
||||
group: SHARED_GROUP
|
||||
refresh: false
|
@@ -0,0 +1,23 @@
|
||||
application:
|
||||
version: v1.0
|
||||
logging:
|
||||
config: classpath:logback-spring.xml
|
||||
spring:
|
||||
application:
|
||||
name: sl-express-ms-search
|
||||
profiles:
|
||||
active: local
|
||||
mvc:
|
||||
pathmatch:
|
||||
#解决异常:swagger Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
|
||||
#因为Springfox使用的路径匹配是基于AntPathMatcher的,而Spring Boot 2.6.X使用的是PathPatternMatcher
|
||||
matching-strategy: ant_path_matcher
|
||||
sl:
|
||||
swagger:
|
||||
package-path: com.sl.ms.search.controller
|
||||
title: 神领物流 - 搜索微服务接口文档
|
||||
description: 该微服务完成搜索相关业务。
|
||||
contact-name: 传智教育·研究院
|
||||
contact-url: http://www.itcast.cn/
|
||||
contact-email: yjy@itcast.cn
|
||||
version: ${application.version}
|
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。-->
|
||||
<!--scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。-->
|
||||
<!--debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。-->
|
||||
<configuration debug="false" scan="false" scanPeriod="60 seconds">
|
||||
<springProperty scope="context" name="appName" source="spring.application.name"/>
|
||||
<!--文件名-->
|
||||
<property name="logback.appname" value="${appName}"/>
|
||||
<!--文件位置-->
|
||||
<property name="logback.logdir" value="/data/logs"/>
|
||||
|
||||
<!-- 定义控制台输出 -->
|
||||
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} - [%thread] - %-5level - %logger{50} - %msg%n</pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>DEBUG</level>
|
||||
</filter>
|
||||
<File>${logback.logdir}/${logback.appname}/${logback.appname}.log</File>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<FileNamePattern>${logback.logdir}/${logback.appname}/${logback.appname}.%d{yyyy-MM-dd}.log.zip</FileNamePattern>
|
||||
<maxHistory>90</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<charset>UTF-8</charset>
|
||||
<pattern>%d [%thread] %-5level %logger{36} %line - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
<!--evel:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,-->
|
||||
<!--不能设置为INHERITED或者同义词NULL。默认是DEBUG。-->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="stdout"/>
|
||||
</root>
|
||||
</configuration>
|
@@ -0,0 +1,718 @@
|
||||
package com.sl.ms.search;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch._types.FieldValue;
|
||||
import co.elastic.clients.elasticsearch._types.SuggestMode;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQueryField;
|
||||
import co.elastic.clients.elasticsearch.core.SearchRequest;
|
||||
import co.elastic.clients.elasticsearch.core.SearchResponse;
|
||||
import co.elastic.clients.elasticsearch.sql.QueryResponse;
|
||||
import co.elastic.clients.json.JsonData;
|
||||
import com.sl.ms.search.entity.Person;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 复杂查询相关
|
||||
**/
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class ComplexSearchTest {
|
||||
@Resource
|
||||
private ElasticsearchClient client;
|
||||
|
||||
private static final String INDEX_NAME = "person";
|
||||
|
||||
/**
|
||||
* term查询(传参为函数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchTerm01() throws IOException {
|
||||
// 1.构造查询条件,并进行查询(此处search方法传参为lambda函数)
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.term(t -> t
|
||||
.field("name")
|
||||
.value("张三")))
|
||||
, Person.class);
|
||||
// 2.打印查询结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* term查询(传参为对象)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchTerm02() throws IOException {
|
||||
// 1.构造查询条件,并进行查询
|
||||
Query query = new Query.Builder().term(t -> t
|
||||
.field("name")
|
||||
.value("张三")).build();
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest.Builder()
|
||||
.index(INDEX_NAME)
|
||||
.query(query).build();
|
||||
|
||||
// 此处search方法传参为SearchRequest对象
|
||||
SearchResponse<Person> response = client.search(searchRequest, Person.class);
|
||||
|
||||
// 2.打印查询结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* terms查询(传参为函数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchTerms01() throws IOException {
|
||||
// 1.构造查询条件,并进行查询
|
||||
List<FieldValue> values = new ArrayList<>();
|
||||
values.add(new FieldValue.Builder().stringValue("20").build());
|
||||
values.add(new FieldValue.Builder().stringValue("21").build());
|
||||
|
||||
// 此处search方法传参为lambda函数
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.terms(t -> t
|
||||
.field("age")
|
||||
.terms(f -> f.value(values))))
|
||||
, Person.class);
|
||||
|
||||
// 2.打印查询结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* terms查询(传参为对象)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchTerms02() throws IOException {
|
||||
// 1.构造查询条件,并进行查询
|
||||
List<FieldValue> values = new ArrayList<>();
|
||||
values.add(new FieldValue.Builder().stringValue("20").build());
|
||||
values.add(new FieldValue.Builder().stringValue("21").build());
|
||||
TermsQueryField termsQueryField = new TermsQueryField.Builder().value(values).build();
|
||||
|
||||
Query query = new Query.Builder().terms(t -> t
|
||||
.field("age")
|
||||
.terms(termsQueryField)).build();
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest.Builder()
|
||||
.index(INDEX_NAME)
|
||||
.query(query).build();
|
||||
|
||||
// 此处search方法传参为SearchRequest对象
|
||||
SearchResponse<Person> response = client.search(searchRequest, Person.class);
|
||||
|
||||
// 2.打印查询结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* range查询(传参为函数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchRange01() throws IOException {
|
||||
// 此处search方法传参为lambda函数
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.range(r -> r
|
||||
.field("age")
|
||||
.gte(JsonData.of("20"))
|
||||
.lt(JsonData.of("40"))))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* range查询(传参为对象)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchRange02() throws IOException {
|
||||
// 1.构造查询条件,并进行查询
|
||||
Query query = new Query.Builder().range(r -> r
|
||||
.field("age")
|
||||
.gte(JsonData.of("20"))
|
||||
.lt(JsonData.of("40"))).build();
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest.Builder()
|
||||
.index(INDEX_NAME)
|
||||
.query(query).build();
|
||||
|
||||
// 此处search方法传参为SearchRequest对象
|
||||
SearchResponse<Person> response = client.search(searchRequest, Person.class);
|
||||
|
||||
// 2.打印查询结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* match查询(传参为函数)
|
||||
* match 查询,对输入内容先分词再查询
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchMatch01() throws IOException {
|
||||
// 此处search方法传参为lambda函数
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.match(m -> m
|
||||
.field("address")
|
||||
.query("昌平区天通苑")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* match查询(传参为对象)
|
||||
* match 查询,对输入内容先分词再查询
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchMatch02() throws IOException {
|
||||
// 1.构造查询条件,并进行查询
|
||||
Query query = new Query.Builder().match(m -> m
|
||||
.field("address")
|
||||
.query("昌平区天通苑")).build();
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest.Builder()
|
||||
.index(INDEX_NAME)
|
||||
.query(query).build();
|
||||
|
||||
// 此处search方法传参为SearchRequest对象
|
||||
SearchResponse<Person> response = client.search(searchRequest, Person.class);
|
||||
|
||||
// 2.打印查询结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* multi_match查询(传参为函数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchMultiMatch01() throws IOException {
|
||||
// 此处search方法传参为lambda函数
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.multiMatch(m -> m
|
||||
.fields("name", "address")
|
||||
.query("顺义")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* multi_match查询(传参为对象)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchMultiMatch02() throws IOException {
|
||||
// 1.构造查询条件,并进行查询
|
||||
Query query = new Query.Builder().multiMatch(m -> m
|
||||
.fields("name", "address")
|
||||
.query("顺义")).build();
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest.Builder()
|
||||
.index(INDEX_NAME)
|
||||
.query(query).build();
|
||||
|
||||
// 此处search方法传参为SearchRequest对象
|
||||
SearchResponse<Person> response = client.search(searchRequest, Person.class);
|
||||
|
||||
// 2.打印查询结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* matchPhrase查询(传参为函数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchMatchPhrase01() throws IOException {
|
||||
// 此处search方法传参为lambda函数
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.matchPhrase(m -> m
|
||||
.field("address")
|
||||
.query("金燕龙")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* matchPhrase查询(传参为对象)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchMatchPhrase02() throws IOException {
|
||||
// 1.构造查询条件,并进行查询
|
||||
Query query = new Query.Builder().matchPhrase(m -> m
|
||||
.field("address")
|
||||
.query("金燕龙")).build();
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest.Builder()
|
||||
.index(INDEX_NAME)
|
||||
.query(query).build();
|
||||
|
||||
// 此处search方法传参为SearchRequest对象
|
||||
SearchResponse<Person> response = client.search(searchRequest, Person.class);
|
||||
|
||||
// 2.打印查询结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* match_all 查询(传参为函数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchMatchAll01() throws IOException {
|
||||
// 此处search方法传参为lambda函数
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.matchAll(m -> m))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* match_all 查询(传参为对象)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchMatchAll02() throws IOException {
|
||||
// 1.构造查询条件,并进行查询
|
||||
Query query = new Query.Builder().matchAll(m -> m).build();
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest.Builder()
|
||||
.index(INDEX_NAME)
|
||||
.query(query).build();
|
||||
|
||||
// 此处search方法传参为SearchRequest对象
|
||||
SearchResponse<Person> response = client.search(searchRequest, Person.class);
|
||||
|
||||
// 2.打印查询结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* query_string查询(等同于match)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchQueryString01() throws IOException {
|
||||
// 类似 match
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.queryString(qs -> qs
|
||||
.defaultField("address")
|
||||
.query("昌平区天通苑")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* query_string查询(等同于match_phrase)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchQueryString02() throws IOException {
|
||||
// 类似 match_phrase
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.queryString(qs -> qs
|
||||
.defaultField("address")
|
||||
.query("\"昌平区天通苑\"")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* query_string查询(运算符查询)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchQueryString03() throws IOException {
|
||||
// 带运算符查询,运算符两边的词不再分词
|
||||
// 查询同时包含 ”昌平区“ 和 ”金燕龙“ 的文档
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.queryString(qs -> qs
|
||||
.fields("address")
|
||||
// 如果不写 AND ,之间是空格,默认是OR
|
||||
.query("昌平区 AND 天通苑")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* query_string查询(同上一个)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchQueryString04() throws IOException {
|
||||
// 查询同时包含 ”昌平区“ 和 ”金燕龙“ 的文档
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.queryString(qs -> qs
|
||||
.fields("address")
|
||||
.query("昌平区 金燕龙")
|
||||
.defaultOperator(Operator.And)))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* query_string查询(复合运用)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchQueryString05() throws IOException {
|
||||
// 查询 name 或 address 字段包含"昌平区"和"金燕龙"这两个单词,或者包含"李四"这个单词的文档。
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.queryString(qs -> qs
|
||||
.fields("name", "address")
|
||||
.query("(昌平区 AND 金燕龙) OR 李四")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* simple_query_string 查询
|
||||
* <p>
|
||||
* 类似 query_string,主要区别如下:<p>
|
||||
* 1、不支持AND OR NOT ,会当做字符处理<p>
|
||||
* 2、使用 + 代替 AND,| 代替OR,- 代替 NOT<p>
|
||||
* 3、term之间默认关系为OR<p>
|
||||
* 4、会忽略错误的语法
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchSimpleQueryString() throws IOException {
|
||||
// 查询同时包含 ”昌平区“ 和 ”金燕龙“ 的文档
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.simpleQueryString(sqs -> sqs
|
||||
.fields("address")
|
||||
.query("昌平区 AND 天通苑")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊查询(使用match)
|
||||
*
|
||||
* @throws IOException IO查询
|
||||
*/
|
||||
@Test
|
||||
void searchFuzzy01() throws IOException {
|
||||
// 全文查询时使用模糊参数,先分词再计算模糊选项。
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.match(m -> m
|
||||
.field("address")
|
||||
// 中英文的分词不一样,尽量用英文测试
|
||||
.query("Pennsylvania Avenue NN")
|
||||
.fuzziness("1")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊查询(使用fuzzy)
|
||||
*
|
||||
* @throws IOException IO查询
|
||||
*/
|
||||
@Test
|
||||
void searchFuzzy02() throws IOException {
|
||||
// 使用 fuzzy query,对输入不分词,直接计算模糊选项。
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.fuzzy(f -> f
|
||||
.field("name")
|
||||
.value("Joa Biden")
|
||||
.fuzziness("1")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 组合查询(must)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchBool() throws IOException {
|
||||
// 查询 name 包含 “张三” 且 age 在 [16-40] 之间的文档
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.bool(b -> b
|
||||
.must(q1 -> q1
|
||||
.match(m -> m
|
||||
.field("name")
|
||||
.query("张三"))
|
||||
)
|
||||
.must(q2 -> q2
|
||||
.range(r -> r
|
||||
.field("age")
|
||||
.gte(JsonData.of("16"))
|
||||
.lte(JsonData.of("40")))
|
||||
)
|
||||
)
|
||||
)
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 组合查询(filter)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchFilter() throws IOException {
|
||||
// 过滤出 name 包含 “张三” 且 age 在 [16-40] 之间的文档,不计算得分
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.bool(b -> b
|
||||
.filter(q1 -> q1
|
||||
.match(m -> m
|
||||
.field("name")
|
||||
.query("张三"))
|
||||
)
|
||||
.filter(q2 -> q2
|
||||
.range(r -> r
|
||||
.field("age")
|
||||
.gte(JsonData.of("16"))
|
||||
.lte(JsonData.of("40")))
|
||||
)
|
||||
)
|
||||
)
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合查询(求和)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchAggs01() throws IOException {
|
||||
// 求和
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.aggregations("age_sum", a -> a
|
||||
.sum(sa -> sa
|
||||
.field("age")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合查询(distinct计数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchAggs02() throws IOException {
|
||||
// 类似 select count distinct(age) from person
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.aggregations("age_count", a -> a
|
||||
.cardinality(ca -> ca.field("age")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合查询(统计)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchAggs03() throws IOException {
|
||||
// 数量、最大、最小、平均、求和
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.aggregations("age_stats", a -> a
|
||||
.stats(sa -> sa
|
||||
.field("age")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合查询(group统计)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchAggs04() throws IOException {
|
||||
// select name,count(*) from person group by name
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.aggregations("name_terms", a -> a
|
||||
.terms(ta -> ta
|
||||
.field("name")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 聚合查询(嵌套计数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchAggs05() throws IOException {
|
||||
// select name,age,count(*) from person group by name,age
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.aggregations("name_terms", a -> a
|
||||
.terms(ta -> ta
|
||||
.field("name")
|
||||
)
|
||||
.aggregations("age_terms", a1 -> a1
|
||||
.terms(ta1 -> ta1
|
||||
.field("age")
|
||||
))
|
||||
)
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 复杂聚合查询
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchAggs06() throws IOException {
|
||||
// 类似 select avg(age) from person where name='李四'
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.bool(b -> b
|
||||
.filter(q1 -> q1
|
||||
.term(t -> t
|
||||
.field("name")
|
||||
.value("李四")))))
|
||||
.aggregations("ave_age", a -> a
|
||||
.avg(aa -> aa.field("age")))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 推荐搜索
|
||||
* 注意:该测试会报 JsonpMappingException 错误,该bug官方会在8.4.1版本修复
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchSuggest() throws IOException {
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.suggest(suggest -> suggest
|
||||
.suggesters("address_suggest", fs -> fs
|
||||
.text("金燕龙")
|
||||
.term(ts -> ts
|
||||
.field("address")
|
||||
.suggestMode(SuggestMode.Always)
|
||||
.minWordLength(2)
|
||||
)
|
||||
)
|
||||
)
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 高亮显示
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchHighlight() throws IOException {
|
||||
SearchResponse<Person> response = client.search(s -> s
|
||||
.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.match(m -> m
|
||||
.field("address")
|
||||
.query("昌平区")))
|
||||
.highlight(h -> h
|
||||
.preTags("<span color='red'>")
|
||||
.postTags("</span>")
|
||||
.fields("address", hf -> hf))
|
||||
, Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL 查询
|
||||
* 注意:该测试会报 JsonpMappingException 错误,该bug官方会在8.4.1版本修复
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void searchSql() throws IOException {
|
||||
QueryResponse response = client.sql().query(q -> q
|
||||
.format("json")
|
||||
.query("SELECT * FROM \"" + INDEX_NAME + "\" limit 3"));
|
||||
log.info(response.toString());
|
||||
}
|
||||
}
|
@@ -0,0 +1,398 @@
|
||||
package com.sl.ms.search;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
|
||||
import co.elastic.clients.elasticsearch.core.*;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;
|
||||
import co.elastic.clients.elasticsearch.core.search.Hit;
|
||||
import co.elastic.clients.elasticsearch.core.search.TotalHits;
|
||||
import co.elastic.clients.elasticsearch.core.search.TotalHitsRelation;
|
||||
import com.sl.SearchApplication;
|
||||
import com.sl.ms.search.entity.Person;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* ES文档相关测试
|
||||
**/
|
||||
@Slf4j
|
||||
@SpringBootTest(classes = SearchApplication.class)
|
||||
class DocumentTest {
|
||||
@Resource
|
||||
private ElasticsearchClient client;
|
||||
|
||||
private static final String INDEX_NAME = "person";
|
||||
|
||||
/**
|
||||
* 新增或更新文档(传参为函数)
|
||||
* 注意:这里的更新是全量更新,原来没有的字段会增加,没有数据的字段会删掉
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void createOrUpdateDoc01() throws IOException {
|
||||
// 1.构造对象
|
||||
Person person = new Person(18, "张三", "18912345678", "北京市昌平区西三旗街道金燕龙办公楼");
|
||||
|
||||
// 2.新增或更新文档数据(构造函数,使用lambda表达式形式)
|
||||
IndexResponse response = client.index(i -> i
|
||||
.index(INDEX_NAME)
|
||||
.id("1")
|
||||
// 此处传参也可以是map
|
||||
.document(person));
|
||||
|
||||
// 3.version为1表示新增,大于1表示更新
|
||||
log.info("Indexed with version " + response.version());
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增或更新文档(传参为对象)
|
||||
* 注意:这里的更新是全量更新,原来没有的字段会增加,没有数据的字段会删掉
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void createOrUpdateDoc02() throws IOException {
|
||||
// 1.构造对象
|
||||
Person person = new Person(18, "张三", "18912345678", "北京市昌平区西三旗街道金燕龙办公楼");
|
||||
|
||||
// 2.使用DSL语法创建对象,进行新增或更新
|
||||
IndexRequest<Person> request = IndexRequest.of(i -> i
|
||||
.index(INDEX_NAME)
|
||||
.id("2")
|
||||
// 此处传参也可以是map
|
||||
.document(person));
|
||||
IndexResponse response = client.index(request);
|
||||
|
||||
// 3.version为1表示新增,大于1表示更新
|
||||
log.info("Indexed with version " + response.version());
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增文档(使用对象)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void createDoc01() throws IOException {
|
||||
// 1.构造对象
|
||||
Person person = new Person(18, "张三", "18912345678", "北京市昌平区西三旗街道金燕龙办公楼");
|
||||
|
||||
// 2.新增文档数据(若重复新增则会报错,错误码409)
|
||||
CreateResponse response = client.create(c -> c
|
||||
.index(INDEX_NAME)
|
||||
.id("3")
|
||||
.document(person));
|
||||
|
||||
// 3.响应结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增文档(使用map)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void createDoc02() throws IOException {
|
||||
// 1.数据封装到map
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put("age", 78);
|
||||
doc.put("name", "Joe Biden");
|
||||
doc.put("phone", "1-202-456-1414");
|
||||
doc.put("address", "1600 Pennsylvania Avenue NW, Washington, DC 20500");
|
||||
|
||||
// 2.新增文档数据(若重复新增则会报错,错误码409)
|
||||
CreateResponse response = client.create(c -> c
|
||||
.index(INDEX_NAME)
|
||||
.id("4")
|
||||
.document(doc));
|
||||
|
||||
// 3.响应结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量新增
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void bulkCreateDoc() throws IOException {
|
||||
// 1.构造数据
|
||||
List<Person> personList = new ArrayList<>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Person person = new Person(18 + i, "李四", "18912345678", "北京市顺义区京顺路99号");
|
||||
personList.add(person);
|
||||
}
|
||||
|
||||
// 2.循环添加新增操作
|
||||
int i = 5;
|
||||
BulkRequest.Builder br = new BulkRequest.Builder();
|
||||
for (Person person : personList) {
|
||||
String id = String.valueOf(i++);
|
||||
br.operations(op -> op.index(idx -> idx
|
||||
.index(INDEX_NAME)
|
||||
.id(id)
|
||||
.document(person)));
|
||||
}
|
||||
|
||||
// 3.批量新增
|
||||
BulkResponse result = client.bulk(br.build());
|
||||
|
||||
// 4.如果出现错误,输出所有错误信息
|
||||
if (result.errors()) {
|
||||
log.error("Bulk had errors");
|
||||
for (BulkResponseItem item : result.items()) {
|
||||
if (item.error() != null) {
|
||||
log.error(item.error().reason());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文档(使用对象,增量更新)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void updateDoc01() throws IOException {
|
||||
// 1.构造数据(传入为null的字段不会更新)
|
||||
Person person = new Person(20, null, null, null);
|
||||
|
||||
// 2.数据更新
|
||||
UpdateResponse<Person> response = client.update(u -> u
|
||||
.index(INDEX_NAME)
|
||||
.id("1")
|
||||
.doc(person)
|
||||
, Person.class);
|
||||
|
||||
// 3.响应结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新文档(使用map,增量更新)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void updateDoc02() throws IOException {
|
||||
// 1.构造数据(sex是原来不存在的字段,该方法会新增这个字段)
|
||||
Map<String, Object> doc = new HashMap<>();
|
||||
doc.put("age", 33);
|
||||
doc.put("sex", "男");
|
||||
|
||||
// 2.数据更新
|
||||
UpdateResponse response = client.update(builder -> builder
|
||||
.index(INDEX_NAME)
|
||||
.id("1")
|
||||
.doc(doc)
|
||||
, Map.class);
|
||||
|
||||
// 3.响应结果
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文档
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void deleteDoc() throws IOException {
|
||||
DeleteResponse response = client.delete(d -> d
|
||||
.index(INDEX_NAME)
|
||||
.id("1"));
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件删除文档(传参为函数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void deleteByQueryDoc01() throws IOException {
|
||||
// deleteByQuery方法传参为lambda函数
|
||||
DeleteByQueryResponse response = client.deleteByQuery(d -> d.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.match(m -> m
|
||||
.field("age")
|
||||
.query("20"))));
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件删除文档(传参为对象)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void deleteByQueryDoc02() throws IOException {
|
||||
// 构造删除条件
|
||||
Query query = new Query.Builder().match(m -> m
|
||||
.field("age")
|
||||
.query("20")).build();
|
||||
|
||||
DeleteByQueryRequest searchRequest = new DeleteByQueryRequest.Builder()
|
||||
.index(INDEX_NAME)
|
||||
.query(query).build();
|
||||
|
||||
// 此处search方法传参为DeleteByQueryRequest对象
|
||||
DeleteByQueryResponse response = client.deleteByQuery(searchRequest);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有文档(用对象接收)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void getDocAll01() throws IOException {
|
||||
SearchResponse<Person> response = client.search(s -> s.index(INDEX_NAME), Person.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有文档(用map接收)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void getDocAll02() throws IOException {
|
||||
SearchResponse<Map> response = client.search(s -> s.index(INDEX_NAME), Map.class);
|
||||
log.info(response.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单个文档(使用对象接收)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void getDoc01() throws IOException {
|
||||
// 1.查询id为2的文档
|
||||
GetResponse<Person> response = client.get(g -> g.index(INDEX_NAME).id("2"), Person.class);
|
||||
|
||||
// 2.判断响应数据是否为空
|
||||
if (response.found()) {
|
||||
Person person = response.source();
|
||||
assert person != null;
|
||||
log.info("Person name " + person.getName());
|
||||
} else {
|
||||
log.info("Person not found");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询单个文档(使用map接收)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void getDoc02() throws IOException {
|
||||
// 1.查询id为2的文档
|
||||
GetResponse<Map> response = client.get(g -> g.index(INDEX_NAME).id("2"), Map.class);
|
||||
|
||||
// 2.判断响应数据是否为空
|
||||
if (response.found()) {
|
||||
Map person = response.source();
|
||||
assert person != null;
|
||||
log.info("Person name " + person.get("name"));
|
||||
} else {
|
||||
log.info("Person not found");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单查询(传入函数)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void simpleSearch01() throws IOException {
|
||||
// 1.构造查询条件,并进行查询(此处search方法传参为lambda函数)
|
||||
SearchResponse<Person> response = client.search(s -> s.index(INDEX_NAME)
|
||||
.query(q -> q
|
||||
.match(m -> m
|
||||
.field("name")
|
||||
.query("张三")))
|
||||
, Person.class);
|
||||
|
||||
// 2.查询结果数量
|
||||
TotalHits total = response.hits().total();
|
||||
assert total != null;
|
||||
// 默认情况下,hits.total.value是不确切的命中计数,
|
||||
// 在这种情况下,当hits.total.relation的值是eq时,hits.total.value的值是准确计数。
|
||||
// 当hits.total.relation的值是gte时,hits.total.value的值是不准确的。
|
||||
boolean isExactResult = total.relation() == TotalHitsRelation.Eq;
|
||||
|
||||
if (isExactResult) {
|
||||
log.info("There are " + total.value() + " results");
|
||||
} else {
|
||||
log.info("There are more than " + total.value() + " results");
|
||||
}
|
||||
|
||||
// 3.解析hit结构,输出全部结果
|
||||
List<Hit<Person>> hits = response.hits().hits();
|
||||
for (Hit<Person> hit : hits) {
|
||||
Person person = hit.source();
|
||||
assert person != null;
|
||||
log.info("Found person " + person.getName() + ", score " + hit.score());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单查询(传入对象)
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void simpleSearch02() throws IOException {
|
||||
// 1.构造查询条件,并进行查询
|
||||
Query query = new Query.Builder().match(m -> m
|
||||
.field("name")
|
||||
.query("张三")).build();
|
||||
|
||||
SearchRequest searchRequest = new SearchRequest.Builder()
|
||||
.index(INDEX_NAME)
|
||||
.query(query).build();
|
||||
|
||||
// 此处search方法传参为SearchRequest对象
|
||||
SearchResponse<Person> response = client.search(searchRequest, Person.class);
|
||||
|
||||
// 2.查询结果数量
|
||||
TotalHits total = response.hits().total();
|
||||
assert total != null;
|
||||
// 默认情况下,hits.total.value是不确切的命中计数,
|
||||
// 在这种情况下,当hits.total.relation的值是eq时,hits.total.value的值是准确计数。
|
||||
// 当hits.total.relation的值是gte时,hits.total.value的值是不准确的。
|
||||
boolean isExactResult = total.relation() == TotalHitsRelation.Eq;
|
||||
|
||||
if (isExactResult) {
|
||||
log.info("There are " + total.value() + " results");
|
||||
} else {
|
||||
log.info("There are more than " + total.value() + " results");
|
||||
}
|
||||
|
||||
// 3.解析hit结构,输出全部结果
|
||||
List<Hit<Person>> hits = response.hits().hits();
|
||||
for (Hit<Person> hit : hits) {
|
||||
Person person = hit.source();
|
||||
assert person != null;
|
||||
log.info("Found person " + person.getName() + ", score " + hit.score());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,114 @@
|
||||
package com.sl.ms.search;
|
||||
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.DeleteIndexResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.GetIndexResponse;
|
||||
import co.elastic.clients.elasticsearch.indices.PutMappingResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* ES索引相关测试
|
||||
**/
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class IndexTest {
|
||||
@Resource
|
||||
private ElasticsearchClient client;
|
||||
|
||||
private static final String INDEX_NAME = "person";
|
||||
|
||||
/**
|
||||
* 创建索引
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void createIndex() throws IOException {
|
||||
CreateIndexResponse response = client.indices().create(builder -> builder
|
||||
// 设置索引分片:number_of_shards:主分片数,默认为1;number_of_replicas:副本分片数,默认为1
|
||||
// 合理设置索引分片可以提高ES的查询性能
|
||||
.settings(indexSettingsBuilder -> indexSettingsBuilder.numberOfReplicas("1").numberOfShards("2"))
|
||||
.mappings(typeMappingBuilder -> typeMappingBuilder
|
||||
.properties("age", propertyBuilder -> propertyBuilder.integer(integerNumberPropertyBuilder -> integerNumberPropertyBuilder))
|
||||
.properties("name", propertyBuilder -> propertyBuilder.keyword(keywordPropertyBuilder -> keywordPropertyBuilder))
|
||||
.properties("phone", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("whitespace").searchAnalyzer("whitespace")))
|
||||
.properties("address", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_max_word")))
|
||||
)
|
||||
.index(INDEX_NAME));
|
||||
log.info("索引创建是否成功:{}", response.acknowledged());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改 _mapping 信息
|
||||
* 字段可以新增,已有的字段只能修改字段的 search_analyzer 属性。
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void modifyIndex() throws IOException {
|
||||
PutMappingResponse response = client.indices().putMapping(typeMappingBuilder -> typeMappingBuilder
|
||||
.index(INDEX_NAME)
|
||||
// 已有字段和之前一样,则不变
|
||||
.properties("age", propertyBuilder -> propertyBuilder.integer(integerNumberPropertyBuilder -> integerNumberPropertyBuilder))
|
||||
// 已有字段修改search_analyzer属性
|
||||
.properties("address", propertyBuilder -> propertyBuilder.text(textPropertyBuilder -> textPropertyBuilder.analyzer("ik_max_word").searchAnalyzer("ik_smart")))
|
||||
// 新增字段
|
||||
.properties("sex", propertyBuilder -> propertyBuilder.integer(integerNumberPropertyBuilder -> integerNumberPropertyBuilder))
|
||||
);
|
||||
log.info("修改索引是否成功:{}", response.acknowledged());
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除索引
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void deleteIndex() throws IOException {
|
||||
DeleteIndexResponse response = client.indices().delete(builder -> builder.index(INDEX_NAME));
|
||||
log.info("删除索引是否成功:{}", response.acknowledged());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询索引列表
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void getIndex() throws IOException {
|
||||
// 此处使用 * 也可以
|
||||
// 同时需要注意,在反序列化时某些索引的属性无法识别,该方法可能会执行失败
|
||||
GetIndexResponse response = client.indices().get(builder -> builder.index("_all"));
|
||||
log.info(response.result().toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询索引是否存在
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void existsIndex() throws IOException {
|
||||
boolean exists = client.indices().exists(e -> e.index(INDEX_NAME)).value();
|
||||
log.info("person索引是否存在:{}", exists);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询索引详情
|
||||
*
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
@Test
|
||||
void getIndexDetail() throws IOException {
|
||||
// 该方法需要查询ES中存在的索引,否则会报错
|
||||
GetIndexResponse response = client.indices().get(builder -> builder.index(INDEX_NAME));
|
||||
log.info(response.result().toString());
|
||||
}
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
package com.sl.ms.search;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.elasticsearch.core.BulkRequest;
|
||||
import com.sl.ms.oms.api.OrderFeign;
|
||||
import com.sl.ms.oms.dto.OrderDTO;
|
||||
import com.sl.ms.search.entity.CourierTaskEntity;
|
||||
import com.sl.ms.work.api.PickupDispatchTaskFeign;
|
||||
import com.sl.ms.work.api.TransportOrderFeign;
|
||||
import com.sl.ms.work.domain.dto.PickupDispatchTaskDTO;
|
||||
import com.sl.ms.work.domain.dto.TransportOrderDTO;
|
||||
import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskStatus;
|
||||
import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskType;
|
||||
import com.sl.transport.common.exception.SLException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* ES初始化数据
|
||||
**/
|
||||
@Slf4j
|
||||
@SpringBootTest
|
||||
class InitTest {
|
||||
@Resource
|
||||
private ElasticsearchClient client;
|
||||
@Resource
|
||||
private OrderFeign orderFeign;
|
||||
@Resource
|
||||
private PickupDispatchTaskFeign pickupDispatchTaskFeign;
|
||||
@Resource
|
||||
private TransportOrderFeign transportOrderFeign;
|
||||
@Value("${sl.es.index_name}")
|
||||
private String indexName;
|
||||
|
||||
/**
|
||||
* 初始化同步数据到es
|
||||
*/
|
||||
@Test
|
||||
void init() throws IOException {
|
||||
// 1.删除所有文档(不需要删除的话,注释这部分代码)
|
||||
client.deleteByQuery(d -> d
|
||||
.index(indexName)
|
||||
.query(q -> q
|
||||
.matchAll(m -> m)));
|
||||
|
||||
// 2.查询全部取派件任务
|
||||
List<PickupDispatchTaskDTO> items = pickupDispatchTaskFeign.findAll(null, null, null, null);
|
||||
|
||||
// 3.根据订单id列表批量查询订单
|
||||
List<String> orderIds = items.stream().map(item -> item.getOrderId().toString()).collect(Collectors.toList());
|
||||
List<OrderDTO> orderDTOS = orderFeign.findByIds(orderIds);
|
||||
Map<Long, OrderDTO> map = orderDTOS.stream().collect(Collectors.toMap(OrderDTO::getId, dto -> dto));
|
||||
|
||||
// 4.数据封装为entity
|
||||
List<CourierTaskEntity> entities = new ArrayList<>();
|
||||
for (PickupDispatchTaskDTO item : items) {
|
||||
// 封装快递员任务对象
|
||||
CourierTaskEntity courierTaskEntity = this.getCourierTaskEntity(item, map);
|
||||
|
||||
// 封装好的对象,添加到快递员任务列表
|
||||
entities.add(courierTaskEntity);
|
||||
}
|
||||
|
||||
// 5.循环添加新增操作
|
||||
BulkRequest.Builder br = new BulkRequest.Builder();
|
||||
for (CourierTaskEntity entity : entities) {
|
||||
br.operations(op -> op.index(idx -> idx
|
||||
.index(indexName)
|
||||
.id(String.valueOf(entity.getId()))
|
||||
.document(entity)));
|
||||
}
|
||||
|
||||
// 6.批量新增
|
||||
client.bulk(br.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装快递员任务对象
|
||||
*
|
||||
* @param pickupDispatchTaskDTO 取派件任务
|
||||
* @return 快递员任务
|
||||
*/
|
||||
private CourierTaskEntity getCourierTaskEntity(PickupDispatchTaskDTO pickupDispatchTaskDTO, Map<Long, OrderDTO> map) {
|
||||
// 1.取派件任务字段复制到快递员任务
|
||||
CourierTaskEntity courierTaskEntity = BeanUtil.toBean(pickupDispatchTaskDTO, CourierTaskEntity.class);
|
||||
courierTaskEntity.setTaskType(pickupDispatchTaskDTO.getTaskType().getCode());
|
||||
courierTaskEntity.setStatus(pickupDispatchTaskDTO.getStatus().getCode());
|
||||
courierTaskEntity.setIsDeleted(pickupDispatchTaskDTO.getIsDeleted().getCode());
|
||||
|
||||
|
||||
// 2.根据id查询订单
|
||||
OrderDTO orderDTO = map.get(pickupDispatchTaskDTO.getOrderId());
|
||||
if (ObjectUtil.isEmpty(orderDTO)) {
|
||||
String errorMsg = CharSequenceUtil.format("id为{}的取派件任务的订单不存在!", pickupDispatchTaskDTO.getId());
|
||||
throw new SLException(errorMsg);
|
||||
}
|
||||
// 3.设置快递员任务的姓名、电话、地址字段
|
||||
this.setNameAndPhoneAndAddress(pickupDispatchTaskDTO.getTaskType(), orderDTO, courierTaskEntity);
|
||||
|
||||
// 为快递员任务set运单号:取件任务新任务状态和已取消状态不查运单
|
||||
if (ObjectUtil.equal(PickupDispatchTaskType.DISPATCH, pickupDispatchTaskDTO.getTaskType()) || ObjectUtil.equal(PickupDispatchTaskStatus.COMPLETED, pickupDispatchTaskDTO.getStatus())) {
|
||||
TransportOrderDTO transportOrderDTO = transportOrderFeign.findByOrderId(pickupDispatchTaskDTO.getOrderId());
|
||||
courierTaskEntity.setTransportOrderId(transportOrderDTO.getId());// 运单id
|
||||
}
|
||||
return courierTaskEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置快递员任务的姓名、电话、地址字段
|
||||
*
|
||||
* @param taskType 任务类型
|
||||
* @param orderDTO 订单
|
||||
* @param courierTaskEntity 快递员任务
|
||||
*/
|
||||
private void setNameAndPhoneAndAddress(PickupDispatchTaskType taskType, OrderDTO orderDTO, CourierTaskEntity courierTaskEntity) {
|
||||
String name;
|
||||
String phone;
|
||||
String address;
|
||||
if (taskType.equals(PickupDispatchTaskType.PICKUP)) {
|
||||
name = orderDTO.getSenderName();
|
||||
phone = orderDTO.getSenderPhone();
|
||||
address = orderDTO.getSenderAddress();
|
||||
} else {
|
||||
name = orderDTO.getReceiverName();
|
||||
phone = orderDTO.getReceiverPhone();
|
||||
address = orderDTO.getReceiverAddress();
|
||||
}
|
||||
courierTaskEntity.setAddress(address);
|
||||
courierTaskEntity.setName(name);
|
||||
courierTaskEntity.setPhone(phone);
|
||||
}
|
||||
|
||||
@Test
|
||||
void setOrderFeign() throws IOException {
|
||||
List<PickupDispatchTaskDTO> items = pickupDispatchTaskFeign.findAll(null, null, null, null);
|
||||
System.out.println(items);
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,36 @@
|
||||
package com.sl.ms.search.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Person对象
|
||||
**/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Person {
|
||||
|
||||
/**
|
||||
* 年龄
|
||||
*/
|
||||
private Integer age;
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 电话
|
||||
*/
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
private String address;
|
||||
}
|
Reference in New Issue
Block a user