es增删改查常用语法
我们日常开发中,操作数据库写sql倒是不可能忘记,但是操作es的dsl语句有时候很容易忘记,特地记录一下方便查找。
DSL语句
1、创建索引
-- 创建索引
PUT /my_index
{"mappings": {"properties": {"title": {"type": "text"},"description": {"type": "text"},"timestamp": {"type": "date"}}}
}
2、插入文档
-- 插入文档
POST /my_index/_doc/1
{"title": "Sample Document","description": "This is a sample document for Elasticsearch","timestamp": "2022-01-01"
}
3、更新文档
-- 更新文档
POST /my_index/_update/主键值
{"doc": {"description": "Updated description"}
}
4、删除文档(单独、多个、全部)
-- 删除单条文档
DELETE /my_index/_doc/或者
-- 删除单条文档
POST 索引名/_delete_by_query
{"query":{"term":{"_id":4043}}
}-- 删除索引中的所有数据
POST my_index/_delete_by_query
{"query": { "match_all": {}}
}
5、删除索引
-- 删除索引
DELETE /my_index
6、设置索引别名
-- 设置索引别名
POST /_aliases{"actions": [{"add": {"index": "my_index2", "alias": "my_index"}}]}
7、设置切片和副本数量
-- 设置切片和副本数量
PUT your_index
{"mappings" : {"properties" : {#索引字段(略)}}"settings" : {"number_of_shards" : 3,"number_of_replicas" : 1}
}
8、查询
-- 查询单个条件
POST /my_index/_search
{"query": {"bool": {"must": {"term": {"messageId": "CS202303160008-2"}}}}
}-- 分页排序查询 不带其他条件POST /my_index/_search
{"query": {"match_all": {}},"from": 0,"size": 20,"sort": [{"createdAt": {"order": "desc"}}]
}
-- 分页排序查询 带其他条件
{"query": {"bool": {"must": [{"prefix": {"action": "aa开头"}},{"wildcard": {"param": "*左右匹配内容*"}}],"must_not": [],"should": []}},"from": 0,"size": 10,"sort": [{"createdAt": {"order": "desc"}}]
}
9、统计
POST /my_index/_count
{"query": {"bool": {"must": {"term": {"messageId": "CS202303160008-2"}}}}
}
代码
pom依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.demo.sdk</groupId>
<artifactId>elasticsearch-util</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>elasticsearch-util</name>
<description>Spring Boot Support for elasticsearch-util</description><properties><java.version>11</java.version><elasticsearch.version>7.10.0</elasticsearch.version><spring-boot.version>2.7.0</spring-boot.version><hutool.version>5.8.15</hutool.version><pagehelper.version>1.4.2</pagehelper.version>
</properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>${spring-boot.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><version>${spring-boot.version}</version><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId><version>${spring-boot.version}</version></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>${elasticsearch.version}</version></dependency><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>${pagehelper.version}</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>${hutool.version}</version></dependency>
</dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>11</source><target>11</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-source-plugin</artifactId><configuration><attach>true</attach></configuration><executions><execution><phase>compile</phase><goals><goal>jar</goal></goals></execution></executions></plugin></plugins>
</build>
1、ES配置类
import cn.hutool.core.text.CharSequenceUtil;import org.apache.http.auth.AuthScope;import org.apache.http.auth.UsernamePasswordCredentials;import org.apache.http.client.CredentialsProvider;import org.apache.http.impl.client.BasicCredentialsProvider;import org.springframework.boot.context.properties.ConfigurationProperties;/*** ES配置类* @author ppp* @date 2023/3/21*/@ConfigurationProperties(prefix = "elasticsearch.config")public class ElasticsearchProperties {/*** 域名*/private String host;/*** 端口*/private String port;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 连接超时时间*/private int connectTimeOut;/*** 连接超时时间*/private int socketTimeOut;/*** 获取连接的超时时间*/private int connectionRequestTimeOut;/*** 获取搜索的超时时间*/private long searchRequestTimeOut = 10000L;/*** 最大连接数*/private int maxConnectNum;/*** 最大路由连接数*/private int maxConnectPerRoute;public String getHost() {return host;}public void setHost(String host) {this.host = host;}public String getPort() {return port;}public void setPort(String port) {this.port = port;}public int getConnectTimeOut() {return connectTimeOut;}public void setConnectTimeOut(int connectTimeOut) {this.connectTimeOut = connectTimeOut;}public int getSocketTimeOut() {return socketTimeOut;}public void setSocketTimeOut(int socketTimeOut) {this.socketTimeOut = socketTimeOut;}public int getConnectionRequestTimeOut() {return connectionRequestTimeOut;}public void setConnectionRequestTimeOut(int connectionRequestTimeOut) {this.connectionRequestTimeOut = connectionRequestTimeOut;}public long getSearchRequestTimeOut() {return searchRequestTimeOut;}public void setSearchRequestTimeOut(long searchRequestTimeOut) {this.searchRequestTimeOut = searchRequestTimeOut;}public int getMaxConnectNum() {return maxConnectNum;}public void setMaxConnectNum(int maxConnectNum) {this.maxConnectNum = maxConnectNum;}public int getMaxConnectPerRoute() {return maxConnectPerRoute;}public void setMaxConnectPerRoute(int maxConnectPerRoute) {this.maxConnectPerRoute = maxConnectPerRoute;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public CredentialsProvider getCredentialsProvider() {if (CharSequenceUtil.isNotBlank(username) && CharSequenceUtil.isNotBlank(password)) {CredentialsProvider credentialsProvider = new BasicCredentialsProvider();credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));return credentialsProvider;}return null;}}
2、ES工具自动配置类
import cn.hutool.core.text.CharSequenceUtil;
import com.demo.sdk.elasticsearch.template.ElasticsearchUtilTemplate;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/**
* ES工具自动配置类
* @author ppp
* @date 2023/3/21
*/
@Configuration
@ConditionalOnClass(ElasticsearchUtilTemplate.class)
@EnableConfigurationProperties(ElasticsearchProperties.class)
public class ElasticsearchUtilAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic RestHighLevelClient esRestClient(ElasticsearchProperties esearchProperties) {if (esearchProperties == null) {throw new NullPointerException("Es Configuration Properties Is Null");}String host = esearchProperties.getHost();String port = esearchProperties.getPort();RestClientBuilder builder;if (CharSequenceUtil.isNotBlank(host) && CharSequenceUtil.isBlank(port)) {builder = RestClient.builder(HttpHost.create(host));}else {builder = RestClient.builder(new HttpHost(host, Integer.parseInt(port)));}// 异步httpclient连接延时配置builder.setRequestConfigCallback(requestConfigBuilder -> {requestConfigBuilder.setConnectTimeout(esearchProperties.getConnectTimeOut());requestConfigBuilder.setSocketTimeout(esearchProperties.getSocketTimeOut());requestConfigBuilder.setConnectionRequestTimeout(esearchProperties.getConnectionRequestTimeOut());return requestConfigBuilder;});// 异步httpclient连接数配置builder.setHttpClientConfigCallback(httpClientBuilder -> {httpClientBuilder.setMaxConnTotal(esearchProperties.getMaxConnectNum());httpClientBuilder.setMaxConnPerRoute(esearchProperties.getMaxConnectPerRoute());httpClientBuilder.setDefaultCredentialsProvider(esearchProperties.getCredentialsProvider());return httpClientBuilder;});return new RestHighLevelClient(builder);}@Bean@ConditionalOnMissingBeanpublic ElasticsearchUtilTemplate elasticsearchUtilTemplate(RestHighLevelClient esRestClient, ElasticsearchProperties elasticsearchProperties) {if (esRestClient == null) {throw new NullPointerException("RestHighLevelClient init Error");}return new ElasticsearchUtilTemplate(esRestClient,elasticsearchProperties.getSearchRequestTimeOut());}}
3、自动装配配置
装配要生效需要ElasticsearchUtilAutoConfiguration加入springboot的自动装配文件spring.factories
这是spring配置的特定目录文件,自己新建一个,名字和目录要一致
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.demo.sdk.elasticsearch.config.ElasticsearchUtilAutoConfiguration
4、定义一个模板工具类
import cn.hutool.json.JSONUtil;import com.github.pagehelper.Page;import com.github.pagehelper.page.PageMethod;import com.demo.sdk.elasticsearch.exception.ElasticsearchErrorException;import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;import org.elasticsearch.action.bulk.BulkRequest;import org.elasticsearch.action.bulk.BulkResponse;import org.elasticsearch.action.delete.DeleteRequest;import org.elasticsearch.action.delete.DeleteResponse;import org.elasticsearch.action.get.GetRequest;import org.elasticsearch.action.get.GetResponse;import org.elasticsearch.action.index.IndexRequest;import org.elasticsearch.action.index.IndexResponse;import org.elasticsearch.action.search.SearchRequest;import org.elasticsearch.action.search.SearchResponse;import org.elasticsearch.action.support.master.AcknowledgedResponse;import org.elasticsearch.action.update.UpdateRequest;import org.elasticsearch.action.update.UpdateResponse;import org.elasticsearch.client.RequestOptions;import org.elasticsearch.client.RestHighLevelClient;import org.elasticsearch.client.core.CountRequest;import org.elasticsearch.client.core.CountResponse;import org.elasticsearch.client.indices.CreateIndexRequest;import org.elasticsearch.client.indices.CreateIndexResponse;import org.elasticsearch.common.unit.TimeValue;import org.elasticsearch.common.xcontent.XContentType;import org.elasticsearch.search.SearchHit;import org.elasticsearch.search.builder.SearchSourceBuilder;import java.util.ArrayList;import java.util.List;import java.util.Map;/*** ES搜索引擎模板* @author ppp* @date 2023/3/21*/public class ElasticsearchUtilTemplate {private final RestHighLevelClient esRestClient;private final long searchRequestTimeOut;public ElasticsearchUtilTemplate(RestHighLevelClient esRestClient, long searchRequestTimeOut) {this.searchRequestTimeOut = searchRequestTimeOut;this.esRestClient = esRestClient;}/*** 列表查询** @param searchSourceBuilder SearchSourceBuilder* @param clazz 返回结果class对象* @param indices ES索引* @return java.util.List 对象列表*/public <T> List<T> listSearch(SearchSourceBuilder searchSourceBuilder, Class<T> clazz, String... indices) {Page<T> resultPage = PageMethod.getLocalPage();boolean isResultPage = resultPage != null;if (isResultPage) {PageMethod.clearPage();searchSourceBuilder.from((int) resultPage.getStartRow());searchSourceBuilder.size(resultPage.getPageSize());}if (isResultPage && resultPage.isCount()) {resultPage.setTotal(count(searchSourceBuilder, indices));}SearchResponse searchResponse = search(searchSourceBuilder, indices);List<T> resultList = formatSearchResult(searchResponse, clazz);if (isResultPage && resultPage.isCount()) {resultPage.addAll(resultList);return resultPage;}return resultList;}public SearchResponse search(SearchSourceBuilder searchSourceBuilder, String... indices) {SearchRequest searchRequest = new SearchRequest(indices);searchSourceBuilder.timeout(TimeValue.timeValueMillis(searchRequestTimeOut));searchRequest.source(searchSourceBuilder);return search(searchRequest);}public SearchResponse search(SearchRequest searchRequest) {try {return esRestClient.search(searchRequest, RequestOptions.DEFAULT);} catch (Exception e) {throw new ElasticsearchErrorException(e.getMessage(), e.getCause());}}/*** 统计数量** @param searchSourceBuilder SearchSourceBuilder* @param indices ES索引* @return CountResponse*/public long count(SearchSourceBuilder searchSourceBuilder, String... indices) {CountRequest countRequest = new CountRequest(indices);searchSourceBuilder.timeout(TimeValue.timeValueMillis(searchRequestTimeOut));countRequest.query(searchSourceBuilder.query());return count(countRequest, RequestOptions.DEFAULT).getCount();}public CountResponse count(CountRequest countRequest, RequestOptions options) {try {return esRestClient.count(countRequest, options);} catch (Exception e) {throw new ElasticsearchErrorException(e.getMessage(), e.getCause());}}/*** 创建索引** @param createIndexRequest CreateIndexRequest* @return CreateIndexResponse*/public CreateIndexResponse createIndices(CreateIndexRequest createIndexRequest) {try {return esRestClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);} catch (Exception e) {throw new ElasticsearchErrorException(e.getMessage(), e.getCause());}}/*** 删除索引(谨慎操作,索引下所有数据都将清空)** @param index 索引名称* @return AcknowledgedResponse*/public AcknowledgedResponse deleteIndex(String index) {DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest();deleteIndexRequest.indices(index);return deleteIndices(deleteIndexRequest);}/*** 删除索引(谨慎操作,索引下所有数据都将清空)** @param deleteIndexRequest DeleteIndexRequest* @return AcknowledgedResponse*/public AcknowledgedResponse deleteIndices(DeleteIndexRequest deleteIndexRequest) {try {return esRestClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);} catch (Exception e) {throw new ElasticsearchErrorException(e.getMessage(), e.getCause());}}/*** 插入数据** @param index 索引* @param id 唯一id* @param source 插入对象* @return IndexResponse*/public IndexResponse add(String index, Object id, Object source) {IndexRequest indexRequest = new IndexRequest(index).id(String.valueOf(id));indexRequest.source(JSONUtil.toJsonStr(source), XContentType.JSON);try {return esRestClient.index(indexRequest, RequestOptions.DEFAULT);} catch (Exception e) {throw new ElasticsearchErrorException(e.getMessage(), e.getCause());}}/*** 批量插入数据* 建议:数量控制在5000以内** @param index 索引* @param sourceMap 数据<唯一id,对象>* @return BulkResponse*/public BulkResponse addBulk(String index, Map<String, Object> sourceMap) {BulkRequest request = new BulkRequest();sourceMap.forEach((id, source) -> {request.add(new IndexRequest(index).id(id).source(JSONUtil.toJsonStr(source), XContentType.JSON));});try {return esRestClient.bulk(request, RequestOptions.DEFAULT);} catch (Exception e) {throw new ElasticsearchErrorException(e.getMessage(), e.getCause());}}/*** 获取数据** @param getRequest GetRequest* @return GetResponse*/public GetResponse get(GetRequest getRequest) {try {return esRestClient.get(getRequest, RequestOptions.DEFAULT);} catch (Exception e) {throw new ElasticsearchErrorException(e.getMessage(), e.getCause());}}/*** 更新数据** @param updateRequest UpdateRequest* @return UpdateResponse*/public UpdateResponse update(UpdateRequest updateRequest) {try {return esRestClient.update(updateRequest, RequestOptions.DEFAULT);} catch (Exception e) {throw new ElasticsearchErrorException(e.getMessage(), e.getCause());}}/*** 删除数据** @param deleteRequest DeleteRequest* @return DeleteResponse*/public DeleteResponse delete(DeleteRequest deleteRequest) {try {return esRestClient.delete(deleteRequest, RequestOptions.DEFAULT);} catch (Exception e) {throw new ElasticsearchErrorException(e.getMessage(), e.getCause());}}/*** 格式化搜索结果** @param searchResponse 搜索结果* @param clazz 返回对象* @return java.util.List*/public <T> List<T> formatSearchResult(SearchResponse searchResponse, Class<T> clazz) {SearchHit[] searchHits = searchResponse.getHits().getHits();List<T> resultList = new ArrayList<T>();for (SearchHit searchHit : searchHits) {resultList.add(JSONUtil.toBean(searchHit.getSourceAsString(), clazz));}return resultList;}}
5、定义一个异常
/**
* 错误异常
* @author ppp
* @date 2023/3/21
*/
public class ElasticsearchErrorException extends RuntimeException {public ElasticsearchErrorException(String message) {super(message);}public ElasticsearchErrorException(String message, Throwable cause) {super(message, cause);}
}
6、application.yml配置
# elasticsearch
elasticsearch.config:host: 127.0.0.1port: 9200username: adminpassword: admin123connect-time-out: 1000socket-time-out: 30000connection-request-time-out: 500search-request-time-out: 5000max-connect-num: 100max-connect-per-route: 100
7、测试
class DemoApplicationTests {/*** 获取ElasticsearchUtilTemplate模板*/@Autowiredprivate ElasticsearchUtilTemplate elasticsearchTemplate;/*** 分页查询*/
@Test
void getListPage() {// 分页PageHelper.startPage(1, 10);SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();// 筛选boolQueryBuilder.filter(QueryBuilders.termQuery("ip", "192.168.0.1"));// 筛选多个值 类似mysq termsQuery和termQuery区别boolQueryBuilder.filter(QueryBuilders.termsQuery("name", Arrays.asList("张三","李四"));// 模糊boolQueryBuilder.must(QueryBuilders.wildcardQuery("ext1", "*测试*"));searchSourceBuilder.query(boolQueryBuilder);// 排序searchSourceBuilder.sort("createTime", SortOrder.DESC);List<StudyLogIndex> logIndexList = this.elasticsearchTemplate.listSearch(searchSourceBuilder, StudyLogIndex.class, index);PageInfo<StudyLogIndex> studyLogIndexPageInfo = new PageInfo<>(logIndexList);System.out.println(JSONUtil.toJsonStr(studyLogIndexPageInfo));
}/*** 统计*/
@Test
void countTest() {// 分页PageHelper.startPage(1, 10);SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();// 筛选boolQueryBuilder.filter(QueryBuilders.termQuery("ip", "127.0.0.1"));// 模糊boolQueryBuilder.must(QueryBuilders.wildcardQuery("ext2", "*用*"));searchSourceBuilder.query(boolQueryBuilder);CountResponse count = elasticsearchTemplate.count(searchSourceBuilder, index);System.out.println("统计总数:"+ count.getCount());
}/*** 删除索引*/
@Test
void deleteIndicesTest() {elasticsearchTemplate.deleteIndex(index);
}/*** 创建es索引*/
@Test
void createIndicesTest() {CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);createIndexRequest.mapping("{\n" +" \"properties\": {\n" +" \"globalId\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"site\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"tag\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"uid\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"classId\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"courseId\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"videoId\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"startTime\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"time\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"ip\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"start\": {\n" +" \"type\": \"integer\"\n" +" },\n" +" \"end\": {\n" +" \"type\": \"integer\"\n" +" },\n" +" \"createTime\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"ext1\": {\n" +" \"type\": \"keyword\"\n" +" },\n" +" \"ext2\": {\n" +" \"type\": \"keyword\"\n" +" }\n" +" }\n" +"}", XContentType.JSON);elasticsearchTemplate.createIndices(createIndexRequest);
}/*** 插入es数据*/
@Test
void addDataTest() {for (int i = 0; i < 10; i++) {StudyLogIndex studyLogIndex = new StudyLogIndex();studyLogIndex.setGlobalId("CX"+i);studyLogIndex.setSite("CX");studyLogIndex.setTag("SUCCESS");studyLogIndex.setUid(12000000L+i);studyLogIndex.setClassId(123456L);studyLogIndex.setCourseId(123456L);studyLogIndex.setVideoId(123456L);studyLogIndex.setStartTime(123456L);studyLogIndex.setTime(123456L);studyLogIndex.setIp("127.0.0.1");studyLogIndex.setStart(0);studyLogIndex.setEnd(0);studyLogIndex.setCreateTime(0L);studyLogIndex.setExt1("测试es工具");studyLogIndex.setExt2("备用");elasticsearchTemplate.add(index, studyLogIndex.getGlobalId(), studyLogIndex);}
}/*** 批量插入es*/
@Test
void bulkAddDataTest() {Map<String, Object> map = new HashMap<>(16);for (int i = 100; i < 1000; i++) {StudyLogIndex studyLogIndex = new StudyLogIndex();studyLogIndex.setGlobalId(IdUtil.getSnowflakeNextIdStr());studyLogIndex.setSite("ZJ");studyLogIndex.setTag("SUCCESS");studyLogIndex.setUid(12000000L+i);studyLogIndex.setClassId(123456L);studyLogIndex.setCourseId(123456L);studyLogIndex.setVideoId(123456L);studyLogIndex.setStartTime(123456L);studyLogIndex.setTime(123456L);studyLogIndex.setIp("192.168.0.3");studyLogIndex.setStart(0);studyLogIndex.setEnd(0);studyLogIndex.setCreateTime(0L);studyLogIndex.setExt1("批量测试es工具");studyLogIndex.setExt2("备用");map.put(studyLogIndex.getGlobalId(), studyLogIndex);}elasticsearchTemplate.addBulk(index, map);
}/*** 聚合检索*/
@Test
void aggregateTest() {SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 聚合检索不设置大小会默认只返回10个统计结果TermsAggregationBuilder field = AggregationBuilders.terms("group_by_ip").field("ip").size(1000);searchSourceBuilder.aggregation(field).size(10);SearchResponse search = elasticsearchTemplate.search(searchSourceBuilder, index);System.out.println(JSONUtil.toJsonStr(search));Map<String, Long> countMap = AggregationsUtil.getCountMap(search.getAggregations());System.out.println(JSONUtil.toJsonStr(countMap));
}
}