一、ElasticSearch 简介
1、简介
ElasticSearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多员工能力的全文搜索引擎,基于 RESTful web 接口。Elasticsearch 是用 Java 语言开发的,并作为 Apache 许可条款下的开放源码发布,是一种流行的企业级搜索引擎。
ElasticSearch 用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
2、特性
- 分布式的文档存储引擎
- 分布式的搜索引擎和分析引擎
- 分布式,支持PB级数据
3、使用场景
- 搜索领域:如百度、谷歌,全文检索等。
- 门户网站:访问统计、文章点赞、留言评论等。
- 广告推广:记录员工行为数据、消费趋势、员工群体进行定制推广等。
- 信息采集:记录应用的埋点数据、访问日志数据等,方便大数据进行分析。
二、ElasticSearch 基础概念
1、ElaticSearch 和 DB 的关系
在 Elasticsearch 中,文档归属于一种类型 type,而这些类型存在于索引 index 中,我们可以列一些简单的不同点,来类比传统关系型数据库:
- Relational DB -> Databases -> Tables -> Rows -> Columns
- Elasticsearch -> Indices -> Types -> Documents -> Fields
Elasticsearch 集群可以包含多个索引 indices,每一个索引可以包含多个类型 types,每一个类型包含多个文档 documents,然后每个文档包含多个字段 Fields。而在 DB 中可以有多个数据库 Databases,每个库中可以有多张表 Tables,没个表中又包含多行Rows,每行包含多列Columns。
2、索引
索引基本概念(indices):
索引是含义相同属性的文档集合,是 ElasticSearch 的一个逻辑存储,可以理解为关系型数据库中的数据库,ElasticSearch 可以把索引数据存放到一台服务器上,也可以 sharding 后存到多台服务器上,每个索引有一个或多个分片,每个分片可以有多个副本。
索引类型(index_type):
索引可以定义一个或多个类型,文档必须属于一个类型。在 ElasticSearch 中,一个索引对象可以存储多个不同用途的对象,通过索引类型可以区分单个索引中的不同对象,可以理解为关系型数据库中的表。每个索引类型可以有不同的结构,但是不同的索引类型不能为相同的属性设置不同的类型。
3、文档
文档(document):
文档是可以被索引的基本数据单位。存储在 ElasticSearch 中的主要实体叫文档 document,可以理解为关系型数据库中表的一行记录。每个文档由多个字段构成,ElasticSearch 是一个非结构化的数据库,每个文档可以有不同的字段,并且有一个唯一的标识符。
4、映射
映射(mapping):
ElasticSearch 的 Mapping 非常类似于静态语言中的数据类型:声明一个变量为 int 类型的变量,以后这个变量都只能存储 int 类型的数据。同样的,一个 number 类型的 mapping 字段只能存储 number 类型的数据。
同语言的数据类型相比,Mapping 还有一些其他的含义,Mapping 不仅告诉 ElasticSearch 一个 Field 中是什么类型的值, 它还告诉 ElasticSearch 如何索引数据以及数据是否能被搜索到。
ElaticSearch 默认是动态创建索引和索引类型的 Mapping 的。这就相当于无需定义 Solr 中的 Schema,无需指定各个字段的索引规则就可以索引文件,很方便。但有时方便就代表着不灵活。比如,ElasticSearch 默认一个字段是要做分词的,但我们有时要搜索匹配整个字段却不行。如有统计工作要记录每个城市出现的次数。对于 name 字段,若记录 new york 文本,ElasticSearch 可能会把它拆分成 new 和 york 这两个词,分别计算这个两个单词的次数,而不是我们期望的 new york。
三、SpringBoot 项目引入 ElasticSearch 依赖
下面介绍下 SpringBoot 如何通过 elasticsearch-rest-high-level-client 工具操作 ElasticSearch,这里需要说一下,为什么没有使用 Spring 家族封装的 spring-data-elasticsearch。
主要原因是灵活性和更新速度,Spring 将 ElasticSearch 过度封装,让开发者很难跟 ES 的 DSL 查询语句进行关联。再者就是更新速度,ES 的更新速度是非常快,但是 spring-data-elasticsearch 更新速度比较缓慢。
由于上面两点,所以选择了官方推出的 Java 客户端 elasticsearch-rest-high-level-client,它的代码写法跟 DSL 语句很相似,懂 ES 查询的使用其上手很快。
1、Maven 引入相关依赖
- lombok 工具依赖。
- fastjson:用于将 JSON 转换对象的依赖。
- spring-boot-starter-web的 Web 依赖。
- elasticsearch:ElasticSearch:依赖,需要和 ES 版本保持一致。
- elasticsearch-rest-high-level-client:用于操作 ES 的 Java 客户端。
<code>
<project> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0/<modelversion>
<parent>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-parent/<artifactid>
<version>2.2.4.RELEASE/<version>
<relativepath>
/<parent>
<groupid>club.mydlq/<groupid>
<artifactid>springboot-elasticsearch-example/<artifactid>
<version>0.0.1-SNAPSHOT/<version>
<name>springboot-elasticsearch-example/<name>
<description>Demo project for Spring Boot ElasticSearch/<description>
<properties>
<java.version>1.8/<java.version>
/<properties>
<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>com.alibaba/<groupid>
<artifactid>fastjson/<artifactid>
<version>1.2.61/<version>
/<dependency>
<dependency>
<groupid>org.elasticsearch.client/<groupid>
<artifactid>elasticsearch-rest-high-level-client/<artifactid>
<version>6.5.4/<version>
/<dependency>
<dependency>
<groupid>org.elasticsearch/<groupid>
<artifactid>elasticsearch/<artifactid>
<version>6.5.4/<version>
/<dependency>
/<dependencies>
<build>
<plugins>
<plugin>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-maven-plugin/<artifactid>
/<plugin>
/<plugins>
/<build>
/<project>
/<code>
2、ElasticSearch 连接配置
(1)、application.yml 配置文件
为了方便更改连接 ES 的连接配置,所以我们将配置信息放置于 application.yaml 中:
<code>#base
server:
port: 8080
#spring
spring:
application:
name: springboot-elasticsearch-example
#elasticsearch
elasticsearch:
schema: http
address: 127.0.0.1:9200
connectTimeout: 5000
socketTimeout: 5000
connectionRequestTimeout: 5000
maxConnectNum: 100
maxConnectPerRoute: 100
/<code>
(2)、java 连接配置类
这里需要写一个 Java 配置类读取 application 中的配置信息:
<code>
/**
* ElasticSearch 配置
*/
@Configuration
public class ElasticSearchConfig {
/** 协议 */
@Value("${elasticsearch.schema:http}")
private String schema;
/** 集群地址,如果有多个用“,”隔开 */
@Value("${elasticsearch.address}")
private String address;
/** 连接超时时间 */
@Value("${elasticsearch.connectTimeout:5000}")
private int connectTimeout;
/** Socket 连接超时时间 */
@Value("${elasticsearch.socketTimeout:10000}")
private int socketTimeout;
/** 获取连接的超时时间 */
@Value("${elasticsearch.connectionRequestTimeout:5000}")
private int connectionRequestTimeout;
/** 最大连接数 */
@Value("${elasticsearch.maxConnectNum:100}")
private int maxConnectNum;
/** 最大路由连接数 */
@Value("${elasticsearch.maxConnectPerRoute:100}")
private int maxConnectPerRoute;
@Bean
public RestHighLevelClient restHighLevelClient() {
// 拆分地址
List<httphost> hostLists = new ArrayList<>();
String[] hostList = address.split(",");
for (String addr : hostList) {
String host = addr.split(":")[0];
String port = addr.split(":")[1];
hostLists.add(new HttpHost(host, Integer.parseInt(port), schema));
}
// 转换成 HttpHost 数组
HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});
// 构建连接对象
RestClientBuilder builder = RestClient.builder(httpHost);
// 异步连接延时配置
builder.setRequestConfigCallback(requestConfigBuilder -> {
requestConfigBuilder.setConnectTimeout(connectTimeout);
requestConfigBuilder.setSocketTimeout(socketTimeout);
requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeout);
return requestConfigBuilder;
});
// 异步连接数配置
builder.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.setMaxConnTotal(maxConnectNum);
httpClientBuilder.setMaxConnPerRoute(maxConnectPerRoute);
return httpClientBuilder;
});
return new RestHighLevelClient(builder);
}
}
/<httphost>/<code>
四、索引操作示例
这里示例会指出通过 Kibana 工具操作与对应的 Java 代码操作的两个示例。
1、Restful 操作示例
创建索引
创建名为 mydlq-user 的索引与对应 Mapping。
<code>PUT /mydlq-user
{
"mappings": {
"doc": {
"dynamic": true,
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"address": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"remark": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"age": {
"type": "integer"
},
"salary": {
"type": "float"
},
"birthDate": {
"type": "date",
"format": "yyyy-MM-dd"
},
"createTime": {
"type": "date"
}
}
}
}
}
/<code>
删除索引
删除 mydlq-user 索引。
<code>DELETE /mydlq-user
/<code>
2、Java 代码示例
<code>
@Slf4j
@Service
public class IndexService2 {
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 创建索引
*/
public void createIndex() {
try {
// 创建 Mapping
XContentBuilder mapping = XContentFactory.jsonBuilder()
.startObject()
.field("dynamic", true)
.startObject("properties")
.startObject("name")
.field("type","text")
.startObject("fields")
.startObject("keyword")
.field("type","keyword")
.endObject()
.endObject()
.endObject()
.startObject("address")
.field("type","text")
.startObject("fields")
.startObject("keyword")
.field("type","keyword")
.endObject()
.endObject()
.endObject()
.startObject("remark")
.field("type","text")
.startObject("fields")
.startObject("keyword")
.field("type","keyword")
.endObject()
.endObject()
.endObject()
.startObject("age")
.field("type","integer")
.endObject()
.startObject("salary")
.field("type","float")
.endObject()
.startObject("birthDate")
.field("type","date")
.field("format", "yyyy-MM-dd")
.endObject()
.startObject("createTime")
.field("type","date")
.endObject()
.endObject()
.endObject();
// 创建索引配置信息,配置
Settings settings = Settings.builder()
.put("index.number_of_shards", 1)
.put("index.number_of_replicas", 0)
.build();
// 新建创建索引请求对象,然后设置索引类型(ES 7.0 将不存在索引类型)和 mapping 与 index 配置
CreateIndexRequest request = new CreateIndexRequest("mydlq-user", settings);
request.mapping("doc", mapping);
// RestHighLevelClient 执行创建索引
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
// 判断是否创建成功
boolean isCreated = createIndexResponse.isAcknowledged();
log.info("是否创建成功:{}", isCreated);
} catch (IOException e) {
log.error("", e);
}
}
/**
* 删除索引
*/
public void deleteIndex() {
try {
// 新建删除索引请求对象
DeleteIndexRequest request = new DeleteIndexRequest("mydlq-user");
// 执行删除索引
AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
// 判断是否删除成功
boolean siDeleted = acknowledgedResponse.isAcknowledged();
log.info("是否删除成功:{}", siDeleted);
} catch (IOException e) {
log.error("", e);
}
}
}
/<code>
五、文档操作示例
1、Restful 操作示例
增加文档信息
在索引 mydlq-user 中增加一条文档信息。
<code>POST /mydlq-user/doc
{
"address": "北京市",
"age": 29,
"birthDate": "1990-01-10",
"createTime": 1579530727699,
"name": "张三",
"remark": "来自北京市的张先生",
"salary": 100
}
/<code>
获取文档信息
获取 mydlq-user 的索引 id=1 的文档信息。
<code>GET /mydlq-user/doc/1
/<code>
更新文档信息
更新之前创建的 id=1 的文档信息。
<code>PUT /mydlq-user/doc/1
{
"address": "北京市海淀区",
"age": 29,
"birthDate": "1990-01-10",
"createTime": 1579530727699,
"name": "张三",
"remark": "来自北京市的张先生",
"salary": 100
}
/<code>
删除文档信息
删除之前创建的 id=1 的文档信息。
<code>DELETE /mydlq-user/doc/1
/<code>
2、Java 代码示例
<code>
@Slf4j
@Service
public class IndexService {
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 增加文档信息
*/
public void addDocument() {
try {
// 创建索引请求对象
IndexRequest indexRequest = new IndexRequest("mydlq-user", "doc", "1");
// 创建员工信息
UserInfo userInfo = new UserInfo();
userInfo.setName("张三");
userInfo.setAge(29);
userInfo.setSalary(100.00f);
userInfo.setAddress("北京市");
userInfo.setRemark("来自北京市的张先生");
userInfo.setCreateTime(new Date());
userInfo.setBirthDate("1990-01-10");
// 将对象转换为 byte 数组
byte[] json = JSON.toJSONBytes(userInfo);
// 设置文档内容
indexRequest.source(json, XContentType.JSON);
// 执行增加文档
IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
log.info("创建状态:{}", response.status());
} catch (Exception e) {
log.error("", e);
}
}
/**
* 获取文档信息
*/
public void getDocument() {
try {
// 获取请求对象
GetRequest getRequest = new GetRequest("mydlq-user", "doc", "1");
// 获取文档信息
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
// 将 JSON 转换成对象
if (getResponse.isExists()) {
UserInfo userInfo = JSON.parseObject(getResponse.getSourceAsBytes(), UserInfo.class);
log.info("员工信息:{}", userInfo);
}
} catch (IOException e) {
log.error("", e);
}
}
/**
* 更新文档信息
*/
public void updateDocument() {
try {
// 创建索引请求对象
UpdateRequest updateRequest = new UpdateRequest("mydlq-user", "doc", "1");
// 设置员工更新信息
UserInfo userInfo = new UserInfo();
userInfo.setSalary(200.00f);
userInfo.setAddress("北京市海淀区");
// 将对象转换为 byte 数组
byte[] json = JSON.toJSONBytes(userInfo);
// 设置更新文档内容
updateRequest.doc(json, XContentType.JSON);
// 执行更新文档
UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
log.info("创建状态:{}", response.status());
} catch (Exception e) {
log.error("", e);
}
}
/**
* 删除文档信息
*/
public void deleteDocument() {
try {
// 创建删除请求对象
DeleteRequest deleteRequest = new DeleteRequest("mydlq-user", "doc", "1");
// 执行删除文档
DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
log.info("删除状态:{}", response.status());
} catch (IOException e) {
log.error("", e);
}
}
}
/<code>
六、插入初始化数据
执行查询示例前,先往索引中插入一批数据:
1、单条插入
POST mydlq-user/_doc
<code>{"name":"零零","address":"北京市丰台区","remark":"低层员工","age":29,"salary":3000,"birthDate":"1990-11-11","createTime":"2019-11-11T08:18:00.000Z"}
/<code>
2、批量插入
POST _bulk
<code>{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"刘一","address":"北京市丰台区","remark":"低层员工","age":30,"salary":3000,"birthDate":"1989-11-11","createTime":"2019-03-15T08:18:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"陈二","address":"北京市昌平区","remark":"中层员工","age":27,"salary":7900,"birthDate":"1992-01-25","createTime":"2019-11-08T11:15:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"张三","address":"北京市房山区","remark":"中层员工","age":28,"salary":8800,"birthDate":"1991-10-05","createTime":"2019-07-22T13:22:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"李四","address":"北京市大兴区","remark":"高层员工","age":26,"salary":9000,"birthDate":"1993-08-18","createTime":"2019-10-17T15:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"王五","address":"北京市密云区","remark":"低层员工","age":31,"salary":4800,"birthDate":"1988-07-20","createTime":"2019-05-29T09:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"赵六","address":"北京市通州区","remark":"中层员工","age":32,"salary":6500,"birthDate":"1987-06-02","createTime":"2019-12-10T18:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"孙七","address":"北京市朝阳区","remark":"中层员工","age":33,"salary":7000,"birthDate":"1986-04-15","createTime":"2019-06-06T13:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"周八","address":"北京市西城区","remark":"低层员工","age":32,"salary":5000,"birthDate":"1987-09-26","createTime":"2019-01-26T14:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"吴九","address":"北京市海淀区","remark":"高层员工","age":30,"salary":11000,"birthDate":"1989-11-25","createTime":"2019-09-07T13:34:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"郑十","address":"北京市东城区","remark":"低层员工","age":29,"salary":5000,"birthDate":"1990-12-25","createTime":"2019-03-06T12:08:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"萧十一","address":"北京市平谷区","remark":"低层员工","age":29,"salary":3300,"birthDate":"1990-11-11","createTime":"2019-03-10T08:17:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"曹十二","address":"北京市怀柔区","remark":"中层员工","age":27,"salary":6800,"birthDate":"1992-01-25","createTime":"2019-12-03T11:09:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"吴十三","address":"北京市延庆区","remark":"中层员工","age":25,"salary":7000,"birthDate":"1994-10-05","createTime":"2019-07-27T14:22:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"冯十四","address":"北京市密云区","remark":"低层员工","age":25,"salary":3000,"birthDate":"1994-08-18","createTime":"2019-04-22T15:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"蒋十五","address":"北京市通州区","remark":"低层员工","age":31,"salary":2800,"birthDate":"1988-07-20","createTime":"2019-06-13T10:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"苗十六","address":"北京市门头沟区","remark":"高层员工","age":32,"salary":11500,"birthDate":"1987-06-02","createTime":"2019-11-11T18:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"鲁十七","address":"北京市石景山区","remark":"高员工","age":33,"salary":9500,"birthDate":"1986-04-15","createTime":"2019-06-06T14:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"沈十八","address":"北京市朝阳区","remark":"中层员工","age":31,"salary":8300,"birthDate":"1988-09-26","createTime":"2019-09-25T14:00:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"吕十九","address":"北京市西城区","remark":"低层员工","age":31,"salary":4500,"birthDate":"1988-11-25","createTime":"2019-09-22T13:34:00.000Z"}
{"index":{"_index":"mydlq-user","_type":"doc"}}
{"name":"丁二十","address":"北京市东城区","remark":"低层员工","age":33,"salary":2100,"birthDate":"1986-12-25","createTime":"2019-03-07T12:08:00.000Z"}
/<code>
3、查询数据
插入完成后再查询数据,查看之前插入的数据是否存在:
<code>GET mydlq-user/_search
/<code>
执行后得到下面记录:
<code>{
"took": 2,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 20,
"max_score": 1,
"hits": [
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "BeN0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "刘一",
"address": "北京市丰台区",
"remark": "低层员工",
"age": 30,
"salary": 3000,
"birthDate": "1989-11-11",
"createTime": "2019-03-15T08:18:00.000Z"
}
},
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "BuN0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "陈二",
"address": "北京市昌平区",
"remark": "中层员工",
"age": 27,
"salary": 7900,
"birthDate": "1992-01-25",
"createTime": "2019-11-08T11:15:00.000Z"
}
},
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "B-N0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "张三",
"address": "北京市房山区",
"remark": "中层员工",
"age": 28,
"salary": 8800,
"birthDate": "1991-10-05",
"createTime": "2019-07-22T13:22:00.000Z"
}
},
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "CON0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "李四",
"address": "北京市大兴区",
"remark": "高层员工",
"age": 26,
"salary": 9000,
"birthDate": "1993-08-18",
"createTime": "2019-10-17T15:00:00.000Z"
}
},
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "CeN0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "王五",
"address": "北京市密云区",
"remark": "低层员工",
"age": 31,
"salary": 4800,
"birthDate": "1988-07-20",
"createTime": "2019-05-29T09:00:00.000Z"
}
},
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "CuN0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "赵六",
"address": "北京市通州区",
"remark": "中层员工",
"age": 32,
"salary": 6500,
"birthDate": "1987-06-02",
"createTime": "2019-12-10T18:00:00.000Z"
}
},
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "C-N0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "孙七",
"address": "北京市朝阳区",
"remark": "中层员工",
"age": 33,
"salary": 7000,
"birthDate": "1986-04-15",
"createTime": "2019-06-06T13:00:00.000Z"
}
},
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "DON0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "周八",
"address": "北京市西城区",
"remark": "低层员工",
"age": 32,
"salary": 5000,
"birthDate": "1987-09-26",
"createTime": "2019-01-26T14:00:00.000Z"
}
},
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "DeN0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "吴九",
"address": "北京市海淀区",
"remark": "高层员工",
"age": 30,
"salary": 11000,
"birthDate": "1989-11-25",
"createTime": "2019-09-07T13:34:00.000Z"
}
},
{
"_index": "mydlq-user",
"_type": "_doc",
"_id": "DuN0BW8B7BNodGwRFTRj",
"_score": 1,
"_source": {
"name": "郑十",
"address": "北京市东城区",
"remark": "低层员工",
"age": 29,
"salary": 5000,
"birthDate": "1990-12-25",
"createTime": "2019-03-06T12:08:00.000Z"
}
}
]
}
}
/<code>
由于篇幅,请看下半部分
閱讀更多 開發者技術前線 的文章