简介
ES核心概念
关系型数据库 | Elasticsearch |
---|
数据库database | 索引 index(数据库) |
表tables | types |
行rows | documents (文档) |
字段columns | fields |
- 物理设计:在后台把每个索引划分为多个分片,每片可以再集群中的不同服务器间迁移
- 逻辑设计:
- 文档:索引和搜索数据的最小单位是文档
- 自我包含:key:value
- 层次型:一个文档中包含文档(json对象)
- 类型:文档的逻辑容器
- 索引:数据库
- 倒排索引:es使用倒排索引的结构,采用Lucene倒排索引作为底层。用于快速全文检索
主要组件
- 索引
- ES将数据存储于一个或多个索引中,索引是具有类似特性的文档的集合。类比传统的关系型数据库领域来说,索引相当于SQL中的一个数据库,或者一个数据存储方案(schema)。索引由其名称(必须为全小写字符)进行标识,并通过引用此名称完成文档的创建、搜索、更新及删除操作。一个ES集群中可以按需创建任意数目的索引。
- 类型
- 类型是索引内部的逻辑分区(category/partition),然而其意义完全取决于用户需求。因此,一个索引内部可定义一个或多个类型(type)。一般来说,类型就是为那些拥有相同的域的文档做的预定义。例如,在索引中,可以定义一个用于存储用户数据的类型,一个存储日志数据的类型,以及一个存储评论数据的类型。类比传统的关系型数据库领域来说,类型相当于表。
- 映射
- Mapping,就是对索引库中索引的字段名称及其数据类型进行定义,类似于mysql中的表结构信息。不过es的mapping比数据库灵活很多,它可以动态识别字段。一般不需要指定mapping都可以,因为es会自动根据数据格式识别它的类型,如果你需要对某些字段添加特殊属性(如:定义使用其它分词器、是否分词、是否存储等),就必须手动添加mapping。
- 需要注意的是映射是不可修改的,一旦确定就不允许改动,在使用自动识别功能时,会以第一个存入的文档为参考来建立映射,后面存入的文档也必须符合该映射才能存入
- 文档
- 文档是Lucene索引和搜索的原子单位,它是包含了一个或多个域的容器,基于JSON格式进行表示。文档由一个或多个域组成,每个域拥有一个名字及一个或多个值,有多个值的域通常称为多值域。每个文档可以存储不同的域集,但同一类型下的文档至应该有某种程度上的相似之处。
分片和副本
- ES的分片(shard)机制可将一个索引内部的数据分布地存储于多个节点,它通过将一个索引切分为多个底层物理的Lucene索引完成索引数据的分割存储功能,这每一个物理的Lucene索引称为一个分片(shard)。每个分片其内部都是一个全功能且独立的索引,因此可由集群中的任何主机存储。创建索引时,用户可指定其分片的数量,默认数量为5个。
- Shard有两种类型:primary和replica,即主shard及副本shard。Primary shard用于文档存储,每个新的索引会自动创建5个Primary shard,当然此数量可在索引创建之前通过配置自行定义,不过,一旦创建完成,其Primaryshard的数量将不可更改。Replica shard是Primary Shard的副本,用于冗余数据及提高搜索性能。每个Primaryshard默认配置了一个Replica shard,但也可以配置多个,且其数量可动态更改。ES会根据需要自动增加或减少这些Replica shard的数量。
Rest风格
- RESTful是一种架构的规范与约束、原则,符合这种规范的架构就是RESTful架构。
method | url地址 | 描述 |
---|
PUT | localhost:9100/索引名称/类型名称/文档id | 创建文档(指定id) |
POST | localhost:9100/索引名称/类型名称 | 创建文档(随机id) |
POST | localhost:9100/索引名称/文档类型/文档id/_update | 修改文档 |
DELETE | localhost:9100/索引名称/文档类型/文档id | 删除文档 |
GET | localhost:9100/索引名称/文档类型/文档id | 查询文档通过文档id |
POST | localhost:9100/索引名称/文档类型/_search | 查询所有文档 |
- 数据类型
- 字符串 text, keyword
- 数据类型 long, integer,short,byte,double,float,half_float,scaled_float
- 日期 date
- 布尔 boolean
- 二进制 binary
elasticsearch.yml配置文件详解
配置Elasticsearch的集群名称,默认是elasticsearch,Elasticsearch会自动发现在同一网段下的Elasticsearch 节点,如果在同一网段下有多个集群,就可以用这个属性来区分不同的集群。
cluster.name: elasticsearch
节点名,默认随机指定一个name列表中名字,不能重复。
node.name: "node1"
指定该节点是否有资格被选举成为node,默认是true,es是默认集群中的第一台机器为master,如果这台机挂了就会重新选举master。
node.master: true
指定该节点是否存储索引数据,默认为true。
node.data: true
设置默认索引分片个数,默认为5片。
index.number_of_shards: 5
设置默认索引副本个数,默认为1个副本。
index.number_of_replicas: 1
设置配置文件的存储路径,默认是es根目录下的config文件夹。
path.conf: /path/to/conf
设置索引数据的存储路径,默认是es根目录下的data文件夹
path.data: /path/to/data
可以设置多个存储路径,用逗号(半角)隔开,如下面这种配置方式:
path.data: /path/to/data1,/path/to/data2
设置临时文件的存储路径,默认是es根目录下的work文件夹。
path.work: /path/to/work
设置日志文件的存储路径,默认是es根目录下的logs文件夹
path.logs: /path/to/logs
设置插件的存放路径,默认是es根目录下的plugins文件夹
path.plugins: /path/to/plugins
设置为true来锁住内存。因为当jvm开始swapping时es的效率会降低,所以要保证它不swap,可以把ES_MIN_MEM和ES_MAX_MEM两个环境变量设置成同一个值,并且保证机器有足够的内存分配给es。同时也要允许elasticsearch的进程可以锁住内存,linux下可以通过`ulimit -l unlimited`命令。
bootstrap.mlockall: true
设置绑定的ip地址,可以是ipv4或ipv6的,默认为0.0.0.0。
network.bind_host: 192.168.0.1
设置其它节点和该节点交互的ip地址,如果不设置它会自动判断,值必须是个真实的ip地址。
network.publish_host: 192.168.0.1
这个参数是用来同时设置bind_host和publish_host上面两个参数。
network.host: 192.168.0.1
设置节点间交互的tcp端口,默认是9300,(集群的时候,注意端口区分)。
transport.tcp.port: 9300
设置是否压缩tcp传输时的数据,默认为false,不压缩。
transport.tcp.compress: true
设置对外服务的http端口,默认为9200(集群的时候,同台机器,注意端口区分)。
http.port: 9200
设置内容的最大容量,默认100mb
http.max_content_length: 100mb
是否使用http协议对外提供服务,默认为true,开启。
http.enabled: false
gateway的类型,默认为local即为本地文件系统,可以设置为本地文件系统,分布式文件系统,hadoop的HDFS,和amazon的s3服务器。
gateway.type: local
设置集群中N个节点启动时进行数据恢复,默认为1。
gateway.recover_after_nodes: 1
设置初始化数据恢复进程的超时时间,默认是5分钟。
gateway.recover_after_time: 5m
设置这个集群中节点的数量,默认为2,一旦这N个节点启动,就会立即进行数据恢复。
gateway.expected_nodes: 2
初始化数据恢复时,并发恢复线程的个数,默认为4。
cluster.routing.allocation.node_initial_primaries_recoveries: 4
添加删除节点或负载均衡时并发恢复线程的个数,默认为4。
cluster.routing.allocation.node_concurrent_recoveries: 2
设置数据恢复时限制的带宽,如入100mb,默认为0,即无限制。
indices.recovery.max_size_per_sec: 0
设置这个参数来限制从其它分片恢复数据时最大同时打开并发流的个数,默认为5。
indices.recovery.concurrent_streams: 5
设置这个参数来保证集群中的节点可以知道其它N个有master资格的节点。默认为1,对于大的集群来说,可以设置大一点的值(2-4)
discovery.zen.minimum_master_nodes: 1
设置集群中自动发现其它节点时ping连接超时时间,默认为3秒,对于比较差的网络环境可以高点的值来防止自动发现时出错。
discovery.zen.ping.timeout: 3s
设置是否打开多播发现节点,默认是true。
discovery.zen.ping.multicast.enabled: false
设置集群中master节点的初始列表,可以通过这些节点来自动发现新加入集群的节点。
discovery.zen.ping.unicast.hosts: ["host1", "host2:port", "host3[portX-portY]"]
基本操作
建立索引
语法:PUT /索引名
在没有特殊设置的情况下,默认有5个分片,1个备份,也可以通过请求参数的方式来指定
参数格式:
{
"settings": {
"number_of_shards": 5, //设置5个片区
"number_of_replicas": 1 //设置1个备份
}
}
删除索引
语法:DELETE /索引名
查看索引
// 查看集群健康信息
语法:GET /_cat/health?v
// 查看节点信息
语法:GET /_cat/nodes?v
// 查看es集群所有索引及数据大小
语法:GET /_cat/indices?v
// 查看单个索引库
语法:GET /索引名?pretty=true
建立索引和映射
语法:PUT /索引名
{
"mappings": { //描述索引
类型名: {
"properties": { //描述字段
字段名: {
"type": 字段类型,
"analyzer": 分词器类型,
"search_analyzer": 分词器类型,
"index": "not_analyzed",
"ignore_above": 20,// 忽略所有长度超过 20 的字符串,对超过的 analyzer 不会进行处理,对 not_analyzed 字段有用
"fields"{
字段名: { //附属多字段:用于通过不同的方法索引相同的字段。例如,一个字符串字段可以映射为text字段(被拆分)用于全文本搜索,也可以映射为keyword字段(不被拆分)用于排序或聚合。
"type": 字段类型
...
}
...
}
}
}
}
}
}
// 注意:text和keyword都是字符串类型,但是只有text类型的数据才能分词,字段的配置一旦确定就不能更改映射的配置项有很多,我们可以根据需要只配置用得上的属性
查询映射
GET /索引名/_mapping
新增和修改文档
POST/PUT /索引名/类型名/文档ID
{
field1: value1, //字段列
field2: value2,
...
}
// 注意:当索引/类型/映射不存在时,会使用默认设置自动添加
// ES中的数据一般是从别的数据库导入的,所以文档的ID会沿用原数据库中的ID
// 索引库中没有该ID对应的文档时则新增,拥有该ID对应的文档时则替换
// 更新数据,推荐使用POST _update,例POST psz/user/1/_update
- 文档内置字段
- _index:所属索引
- _type:所属类型
- _id:文档ID
- _version:乐观锁版本号
- _source:数据内容
查询文档
// 根据id查询单个文档
GET /索引名/类型名/文档ID
// 查询所有文档
GET /索引名/类型名/_search
- 查询所有结果中包含以下字段
- took:耗时
- _shards.total:分片总数
- hits.total:查询到的数量
- hits.max_score:最大匹配度
- hits.hits:查询到的结果
- hits.hits._score:匹配度
删除文档
// 删除单个
DELETE /索引名/类型名/文档ID
//删除多个
DELETE /索引名/类型名/_delete_by_query
// 这里的删除并且不是真正意义上的删除,仅仅是清空文档内容而已,并且标记该文档的状态为删除
高级查询
- Elasticsearch基于JSON提供完整的查询DSL(Domain Specific Language:领域特定语言)来定义查询
基本查询
GET /索引名/类型名/_search
- 一般都是需要配合查询参数来使用的,配合不同的参数有不同的查询效果
- 参数配置项可以参考博客:https://www.jianshu.com/p/6333940621ec
精确检索查询
- value值不会被分词器拆分,按照倒排索引匹配,精确一个条件
GET /索引名/类型名/_search
{
"query": {
"term": {
"字段名": "字段值" //或"字段":{"value": "字段值"}
}
}
}
GET /索引名/类型名/_search
{
"query": {
"terms": {
"字段名":["字段值1","字段值2"...]
}
}
}
全文检索查询
- value值会被分词器拆分,然后去倒排索引中匹配,即先分词后查询
GET /索引名/类型名/_search
{
"query": {
"match": {
"字段名": "字段值"
}
}
}
范围检索查询
- value值是一个对象,如{ “range”: {field: {比较规则: value, …}} } 比较规则有gt / gte / lt / lte 等
- 注意:term和match都能用在数值和字符上,range用在数值上
GET /索引名/类型名/_search
{
"query": {
"range": {
"字段名": {
"比较转义符":"值",
...
}
}
}
}
关键字查询
- 在多个字段间做检索,只要其中一个字段满足条件就能查询出来,多用在字段上
GET /索引名/类型名/_search
{
"query": {
"multi_match": {
"query": "字段值",
"fields": [字段名1, 字段名2, ...]
}
}
}
逻辑查询
- 逻辑规则:must / should / must_not,相当于and / or / not
GET /索引名/类型名/_search
{
"query": {
"bool": {
"逻辑规则": [
{
"检索方式": {
"字段名": "字段值"
}
},
...
],
...
}
}
}
过滤查询
- 和检索查询能做一样的效果,区别在于过滤查询不评分,结果能缓存,检索查询要评分,结果不缓存
- 一般是不会直接使用过滤查询,都是在检索了一定数据的基础上再使用
- 更多请参考:https://blog.csdn.net/laoyang360/article/details/80468757
GET /索引名/类型名/_search
{
"query": {
"bool": {
"filter": [
{
"检索方式": {
"字段名": "字段值"
}
},
...
]
}
}
}
高亮显示
- 需要在fields中配置哪些字段中检索到该内容需要高亮显示,必须配合检索(term / match)一起使用
GET /索引名/类型名/_search
{
"query": { ... },
"highlight": {
"fields": {
"字段名": {},
...
},
"pre_tags": 开始标签,
"post_tags" 结束标签
}
}
排序
GET /索引名/类型名/_search
{
"sort": [
{
"字段名":{ //或"字段名":"排序规则"
"order": "desc" //排序规则
}
},
...
]
}
// 排序规则:asc表示升序,desc:表示降序,没有配置排序的情况下,默认按照评分降序排列
分页
GET /索引名/类型名/_search
{
"from": start, //起始索引
"size": pageSize //长度
}
分组
- 分组运算:avg / sum / min / max / value_count / stats(执行以上所有功能的)
- 显示结果中buckets即桶(Buckets):相当于 group by 把若干个数据放入到不同的桶中
- 指标(Metrics):相当于mysql中的组函数 avg,sum ,max,min等
GET /索引名/类型名/_search
{
"size": 0,
"aggs": {
自定义分组字段: {
"terms": { //定义多个桶
"field": 分组字段,
"order": {自定义统计字段:排序规则},
"size": 10 //默认显示10组
},
"aggs": { //分组后的统计查询,相当于MySQL分组函数查询
自定义统计字段: {
分组运算: {
"field": 统计字段
}
}
}
}
}
}
// 注意:这里是size=0其目的是为了不要显示hit内容,专注点放在观察分组上
批处理
- 当需要集中的批量处理文档时,如果依然使用传统的操作单个API的方式,将会浪费大量网络资源,Elasticsearch
为了提高操作的性能,专门提供了批处理的API - mget批量查询
GET /索引名/类型/_mget
{
"docs": [
{"_id": 文档ID},
...
]
}
POST /索引名/类型/_bulk
{
{动作:{"_id": 文档ID}}
{...}
{动作:{"_id": 文档ID}}
{...}
}
// 动作:create / update / delete,其中delete只有1行JSON,其他操作都是有2行JSON,并且JSON不能格式化,如果是update动作,它的数据需要加个key为doc
// 例:
// {"update": {"_id": xx}}
// {"doc": {"xx":xx, "xx":xx}}
Spring Data Elasticsearch
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
// @Document:配置操作哪个索引下的哪个类型
// @Id:标记文档ID字段
// @Field:配置映射信息,如:分词器
@Getter@Setter@ToString
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "test1",type = "user")
public class User implements Serializable {
@Id
private String id;
@Field(name = "uid",type = FieldType.Integer)
private Integer uid;
@Field(name = "uname",type = FieldType.Text,analyzer = "ik_smart")
private String uname;
@Field(name = "tel",type = FieldType.Keyword)
private String tel;
@Field(name = "city",type = FieldType.Text)
private String city;
@Field(name = "age",type = FieldType.Long)
private Long age;
@Field(name = "address",type = FieldType.Text)
private String address;
}
- application.properties配置文件
# 配置集群名称,名称写错会连不上服务器,默认elasticsearch
spring.data.elasticsearch.cluster-name=elasticsearch
# 配置集群节点
spring.data.elasticsearch.cluster-nodes=localhost:9300
组件介绍
- ElasticsearchRepository类
- 该接口是框架封装的用于操作Elastsearch的高级接口,只要我们自己的写个接口去继承该接口就能直接对Elasticsearch进行CRUD操作
// 泛型1:domain的类型
// 泛型2:文档主键类型
// 该接口直接该给Spring,底层会使用JDK代理的方式创建对象,交给容器管理
@Repository
public interface ProductESRepository extends ElasticsearchRepository<Product, String> {
// 符合Spring Data规范的高级查询方法
}
- ElasticsearchTemplate类:框架封装的用于便捷操作Elasticsearch的模板类
- 该模板类,封装了便捷操作Elasticsearch的模板方法,包括 索引 / 映射 / CRUD 等底层操作和高级操作,该对象用起来会略微复杂些,尤其是对于查询,还需要把查询到的结果自己封装对象
- ElasticsearchTemplate和ElasticsearchRepository是分工合作的,ElasticsearchRepository已经能完成绝大部分的功能,如果遇到复杂的查询则要使用ElasticsearchTemplate,如多字段分组、高亮显示等
//该对象已经由SpringBoot完成自动配置,直接注入即可
@Autowired
private ElasticsearchTemplate template;
- NativeSearchQueryBuilder类:用于生成查询条件的构建器,需要去封装各种查询条件
- QueryBuilder:该接口表示一个查询条件,其对象可以通过QueryBuilders工具类中的方法快速生成各种条件
- boolQuery():生成bool条件,相当于 “bool”: { }
- matchQuery():生成match条件,相当于 “match”: { }
- rangeQuery():生成range条件,相当于 “range”: { }
- AbstractAggregationBuilder:用于生成分组查询的构建器,其对象通过AggregationBuilders工具类生成
- Pageable:表示分页参数,对象通过PageRequest.of(页数, 容量)获取
- SortBuilder:排序构建器,对象通过SortBuilders.fieldSort(字段).order(规则)获取
实例
@SpringBootTest
class ApplicationTests {
@Autowired
private UserRepository userRepository;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Test
void test1() {
User s = new User();
s.setUid(100);
s.setUname("mao");
s.setAge(18L);
s.setTel("123");
s.setCity("沈阳");
s.setAddress("皇姑");
User u = (User) userRepository.save(s);
System.out.println(u);
}
@Test
void test2() {
User s=new User();
s.setId(1000+"");
s.setUid(200);
s.setUname("maomaocong2");
s.setAge(19L);
s.setTel("123");
s.setCity("沈阳");
s.setAddress("上海黄浦区");
User u = userRepository.save(s);
System.out.println(u);
}
@Test
void test3() {
User s=new User();
s.setId(1000+"");
userRepository.deleteById("2000");
System.out.println("删除成功!");
}
@Test
void test3_1() {
userRepository.deleteAll();
System.out.println("删除成功!");
}
@Test
void test4() {
Optional<User> byId = userRepository.findById("5");
User user = byId.get();
System.out.println(user);
}
@Test
void test4_1() {
Iterable<User> all = userRepository.findAll();
for (User user : all) {
System.out.println(user);
}
}
}
复杂查询
@SpringBootTest
class ApplicationTests {
@Autowired
private UserRepository userRepository;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Test
void test4_2() {
Iterable<User> all = userRepository.findAll(PageRequest.of(1,4, Sort.Direction.DESC,"age"));
for (User user : all) {
System.out.println(user);
}
}
@Test
void test4_3() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withPageable(PageRequest.of(0,3));
Page<User> users = userRepository.search(builder.build());
System.out.println("当前页="+users.getNumber());
System.out.println("每页显示多少条="+users.getSize());
System.out.println("总行数="+users.getTotalElements());
System.out.println("总页数="+users.getTotalPages());
System.out.println("list="+users.getContent());
System.out.println("users="+users);
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_4() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withSort(
SortBuilders.fieldSort("age").order(SortOrder.DESC)
);
builder.withSort(
SortBuilders.fieldSort("uid").order(SortOrder.ASC)
);
Page<User> users = userRepository.search(builder.build());
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_5() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.termQuery("city.keyword","沈阳")
);
Page<User> users = userRepository.search(builder.build());
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_7() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.matchQuery("city","辽阳")
);
Page<User> users = userRepository.search(builder.build());
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_8() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.multiMatchQuery("辽阳","city","address")
);
Page<User> users = userRepository.search(builder.build());
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_9() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.rangeQuery("age").gte(15).lte(20)
);
Page<User> users = userRepository.search(builder.build());
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_10() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.boolQuery()
.must(
QueryBuilders.matchQuery("city","辽阳")
)
.must(
QueryBuilders.rangeQuery("age").lte(20).gte(15)
)
);
Page<User> users = userRepository.search(builder.build());
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_11() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.boolQuery()
.should(
QueryBuilders.matchQuery("city","辽阳")
)
.should(
QueryBuilders.rangeQuery("age").lte(20).gte(15)
)
);
Page<User> users = userRepository.search(builder.build());
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_11() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.boolQuery()
.mustNot(
QueryBuilders.rangeQuery("age").lte(20).gte(15)
)
);
Page<User> users = userRepository.search(builder.build());
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_12() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withQuery(
QueryBuilders.boolQuery()
.filter(
QueryBuilders.rangeQuery("age").lte(20).gte(15)
)
);
Page<User> users = userRepository.search(builder.build());
for (User user : users) {
System.out.println(user);
}
}
@Test
void test4_13() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.addAggregation(AggregationBuilders.terms("mycity").field("city.keyword"));
AggregatedPage<User> page = (AggregatedPage<User>) userRepository.search(builder.build());
StringTerms mycity = (StringTerms) page.getAggregation("mycity");
List<StringTerms.Bucket> buckets = mycity.getBuckets();
for (StringTerms.Bucket bucket : buckets) {
System.out.println("桶="+bucket.getKey());
System.out.println("每桶数量="+bucket.getDocCount());
}
}
@Test
void test4_14() {
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.addAggregation(
AggregationBuilders.terms("mycity").field("city.keyword")
.subAggregation(
AggregationBuilders.stats("ageItem").field("age")
)
);
AggregatedPage<User> page = (AggregatedPage<User>) userRepository.search(builder.build());
StringTerms mycity = (StringTerms) page.getAggregation("mycity");
List<StringTerms.Bucket> buckets = mycity.getBuckets();
for (StringTerms.Bucket bucket : buckets) {
System.out.println("桶="+bucket.getKey()+"===="+"每桶数量="+bucket.getDocCount());
InternalStats statsAge = bucket.getAggregations().get("ageItem");
System.out.println(statsAge.getSumAsString());
System.out.println(statsAge.getAvgAsString());
System.out.println(statsAge.getMaxAsString());
System.out.println(statsAge.getMinAsString());
System.out.println(statsAge.getCount());
}
}
@Test
void test4_15() {
ObjectMapper mapper = new ObjectMapper();
NativeSearchQueryBuilder builder=new NativeSearchQueryBuilder();
builder.withIndices("test1").withTypes("user");
builder.withQuery(QueryBuilders.multiMatchQuery("辽阳","city","address"));
builder.withHighlightFields(
new HighlightBuilder.Field("city")
.preTags("<span style='color:red'>").postTags("</span>"),
new HighlightBuilder.Field("address")
.preTags("<span style='color:red'>").postTags("</span>")
);
AggregatedPage<User> users = elasticsearchTemplate.queryForPage(builder.build(), User.class, new SearchResultMapper() {
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
List<T> list = new ArrayList<>();
for (SearchHit hit : searchResponse.getHits().getHits()) {
try{
T t = mapper.readValue(hit.getSourceAsString(), aClass);
for (HighlightField field : hit.getHighlightFields().values()) {
BeanUtils.setProperty(t, field.getName(), field.getFragments()[0].string());
}
list.add(t);
}catch(Exception e){
e.printStackTrace();
}
}
long total = searchResponse.getHits().totalHits;
AggregatedPageImpl ret=new AggregatedPageImpl<>(list, pageable, total);
return ret;
}
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
return null;
}
});
for (User user : users) {
System.out.println(user);
}
}
@Test
void test5() {
int counter = 0;
try {
if(!elasticsearchTemplate.indexExists("test1")){
elasticsearchTemplate.createIndex("test1");
}
ObjectMapper mapper = new ObjectMapper();
List<User> users = new ArrayList<>();
User u1=new User();
u1.setId("4001");
u1.setUid(301);
u1.setUname("mao1");
u1.setCity("大连");
users.add(u1);
User u2=new User();
u2.setId("4002");
u2.setUid(302);
u2.setUname("mao2");
u2.setCity("大连");
users.add(u2);
List<IndexQuery> queries = new ArrayList<>();
for (User u : users) {
IndexQuery indexQuery = new IndexQuery();
indexQuery.setIndexName("test1");
indexQuery.setType("user");
indexQuery.setId(u.getId().toString());
indexQuery.setSource(mapper.writeValueAsString(u));
queries.add(indexQuery);
counter++;
if (counter % 500 == 0) {
elasticsearchTemplate.bulkIndex(queries);
queries.clear();
System.out.println("bulkIndex counter : " + counter);
}
}
if (queries.size() > 0) {
elasticsearchTemplate.bulkIndex(queries);
}
elasticsearchTemplate.refresh("test1");
System.out.println("bulkIndex completed.");
} catch (Exception e) {
System.out.println("IndexerService.bulkIndex e;" + e.getMessage());
e.printStackTrace();
}
}
}