ES支持哪些数据类型??
① 基本字段类型
字符串:text(分词),keyword(不分词) StringField(不分词文本),TextFiled(要分词文本)
text默认为全文文本,keyword默认为非全文文本
数字:long,integer,short,double,float
日期:date
逻辑:boolean
② 复杂数据类型
对象类型:object
数组类型:array
地理位置:geo_point,geo_shape
注意:如果已经有数据,不能直接做映射,先删除掉,在添加映射,再添加数据
***简单映射***
POST {indexName}/{typeName}/_mapping
{
"{typeName}": {
"properties": {
"id": {
"type": "long"
},
"content": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer": "ik_smart"
}
}
}
注意:你可以在第一次创建索引的时候指定映射的类型。此外,你也可以晚些时候为新类型添加映射(或者为已有的类型更新映射)。
你可以向已有映射中增加字段,但你不能修改它。如果一个字段在映射中已经存在,这可能意味着那个字段的数据已经被索引。如果你改变了字段映射,那已经被索引的数据将错误并且不能被正确的搜索到。
我们可以更新一个映射来增加一个新字段,但是不能把已有字段的类型那个从 analyzed 改到 not_analyzed。
**同时对多个类型的映射配置方式(推荐)**
PUT {indexName}
{
"mappings": {
"user": {
"properties": {
"id": {
"type": "integer"
},
"info": {
"type": "text",
"analyzer": "ik_smart",
"search_analyzer"
}
}
},
"dept": {
"properties": {
"id": {
"type": "integer"
},
...更多字段映射配置
}
}
}
}
对象及数组类型映射
数组与对象数组 --> 注意:数组中元素的类型必须一致。
)对象数组 - 里面所有对象字段的类型是一样的–>只需映射一个就好
注意1:同内联对象一样,对象数组也会被扁平化索引
注意2:扁平化后,对象属性的相关性已经丢失,因为每个多值字段只是一个数值集,不是排序的数组。
比如查询:哪个女朋友的年龄是18岁? 这个是无法查询到答案,如果需要保留关系,需要使用嵌套对象nested objects。
全局映射
-
【 全局映射可以通过动态模板和默认设置两种方式实现 】
ex:员工类型的id要改为integer,而且部门类型的也要改为integer,甚至所有的都要改.这时每个都去修改非常麻烦.可以设置全局映射
默认方式:default
[ 一般用来设置_all不要了 ]
索引下所有的类型映射配置会继承_default_的配置,user类型的文档中将不会合并所有字段到_all,而dept会 如:
动态模板:dynamic_templates
注意:ES会默认把string类型的字段映射为text类型(默认使用标准分词器)和对应的keyword类型,如:
在实际应用场景中,一个对象的属性中,需要全文检索的字段较少,大部分字符串不需要分词,因此,需要利用全局模板覆盖自带的默认模板:
说明:上例中定义了两种动态映射模板string_as_text和string_as_keyword
在实际的类型字段映射时,会依次匹配:
①字段自定义配置
②全局dynamic_templates[string_as_text、string_as_keyword]
③索引dynamic_templates[...]
④ES自带的string类型映射,以最先匹配上的为准。
注意:索引库在创建的时候会继承当前最新的dynamic_templates,索引库创建后,修改动态模板,无法应用到已存在的索引库。
实践:映射的配置会影响到后续数据的索引过程,因此,在实际项目中应遵循如下顺序规则: 【有数据不做映射+根据优先级倒序来】
① 配置全局动态模板映射(覆盖默认的string映射)
② 配置字段映射(由于基本类型主要用于过滤和普通查询,因此,字段映射主要对需要全文检索的字段进行配置)
③ 创建、更新和删除文档
④ 搜索
优化后的代码
@Data
public class InvokeRequest {
/**
* 请求地址
*/
private String url;
/**
* 请求接口名称
*/
private String interfaceName;
/**
* 请求参数
*/
private Object request;
/**
* 请求方法
*/
private String method;
/**
* 编码方式
*/
private String codeScheme;
/**
* 接口路径
*/
private String interfacePath;
}
import **************************;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Slf4j
@Service
@AllArgsConstructor
@NoArgsConstructor
public class InvokeService {
@Value("${method.address}")
private String address;
@Resource
private RequestContext requestContext;
@Autowired
private TokenService tokenService;
@Autowired
private LogService logService;
public <E extends BaseResponse> E invoke(InvokeRequest invokeRequest, Class<E> responseClazz) {
log.info("调用 {} 接口请求信息: {}", invokeRequest.getInterfacePath(), invokeRequest);
String token = tokenService.getToken();
String result = "";
TimeInterval timeInterval = new TimeInterval();
ICommonParam commonParam = requestContext.getCommonParam() == null ? new BaseCommonParam() : requestContext.getCommonParam();
// 定义需要返回的对象
E response = null;
String url = address + "/openapi/httpService/" + invokeRequest.getInterfacePath() + "?access_token=" + token + "&method=" + invokeRequest.getMethod() + "&format=json";
String requestParm = CustomJsonUtil.objectToJson(invokeRequest.getRequest());
try {
response = responseClazz.newInstance();
if (StrUtil.isEmpty(token)) {
response.setResCode("-1");
response.setResDesc("未获取到token");
}
result = HttpRequestUtil.sendPost(url, requestParm, invokeRequest.getCodeScheme());
log.info("调用 {} 接口返回信息: {} ", invokeRequest.getInterfaceName(), result);
response = (E) CustomJsonUtil.jsonToObject(result, responseClazz);
} catch (Exception e) {
log.error(" error", e);
response.fail(e.getMessage());
} finally {
log.info("调用 {} 接口用时: {} 毫秒", invokeRequest.getInterfaceName(), timeInterval.interval());
logService.addLog(invokeRequest, url, commonParam, requestParm, result, response.getResCode(), timeInterval.interval());
}
return response;
}
}
addLog() ,日志入es
import **************;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.frameworkset.elasticsearch.ElasticSearchHelper;
import org.frameworkset.elasticsearch.client.ClientInterface;
import org.frameworkset.spi.async.annotation.Async;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Date;
/**
* 记录接口日志
*/
@Slf4j
@Service
public class LogService {
private static final String BASE_INDEX_NAME_LOG_CMOP = "ubp-log-cmop";
public static final String CREATE_CMOP_LOG = "createCmopLog";
private static String INDEX_NAME;
private String mappath = "esmapper/LogCmop.xml";
@Autowired
private ProjectProperties projectProperties;
@PostConstruct
public void LogApiEsService() {
// ubp-log-cmop-stage-ubp
INDEX_NAME = BASE_INDEX_NAME_LOG_CMOP.concat(StrUtil.DASHED).concat(ProjectProperties.env).concat(StrUtil.DASHED).concat(projectProperties.getGroup());
}
@Async
public void addLog(InvokeRequest invokeRequest, String url, ICommonParam commonParam, String requestParm, String result, String returnCode, long interval) {
LogCmop logCmop = new LogCmop();
logCmop.setRequestUrl(url);
logCmop.setRequestParam(StrUtil.subPre(requestParm, 2000));
logCmop.setMethodName(invokeRequest.getMethod());
logCmop.setRemark(invokeRequest.getInterfaceName());
logCmop.setDuration(interval);
logCmop.setReturnCode(returnCode);
logCmop.setReturnMessage(StrUtil.subPre(result, 5000));
logCmop.setChainId(commonParam.getChainId());
logCmop.preInsert();
ClientInterface client = ElasticSearchHelper.getConfigRestClientUtil(mappath);
String dynamicIndexName = this.createDynamicIndexIfNotExist(client);
LogCmopES logCmopES = new LogCmopES();
BeanUtils.copyProperties(logCmop, logCmopES);
logCmopES.setUserId(logCmop.getCreateBy());
logCmopES.setTimestamp(DateUtil.format(new Date(), DatePattern.UTC_MS_WITH_ZONE_OFFSET_PATTERN));
client.addDocument(dynamicIndexName, logCmopES);
}
private String createDynamicIndexIfNotExist(ClientInterface client) {
String dynamicIndexName = client.getDynamicIndexName(INDEX_NAME);
boolean existence = client.existIndice(dynamicIndexName);
if (!existence) {
client.createIndiceMapping(dynamicIndexName, CREATE_CMOP_LOG);
}
return dynamicIndexName;
}
}
日志的实体类
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("tb_log_cmop")
public class LogCmop extends BaseEntity {
/**
* 请求URL
*/
private String requestUrl;
/**
* 请求参数
*/
private String requestParam;
/**
* 方法名称
*/
private String methodName;
/**
* 请求耗时
*/
private Long duration;
/**
* 返回码
*/
private String returnCode;
/**
* 返回信息
*/
private String returnMessage;
/**
* 链路ID
*/
private String chainId;
}
ES日志实体
@Data
public class LogCmopES {
/**
* 请求URL
*/
private String requestUrl;
/**
* 请求参数
*/
private String requestParam;
/**
* 方法名称
*/
private String methodName;
/**
* 请求耗时
*/
private Long duration;
/**
* 返回码
*/
private String returnCode;
/**
* 返回信息
*/
private String returnMessage;
/**
* 接口名称(中文)
*/
private String interfaceName;
/**
* 链路ID
*/
private String chainId;
/**
* 格林尼治时间
*/
private String timestamp;
/**
* 用户id
*/
private Long userId;
}
esmapper.xml
<properties>
<property name="createCmopLog">
<![CDATA[{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"requestUrl": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"requestParam": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"methodName": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"duration": {
"type": "integer"
},
"returnCode": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"returnMessage": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"interfaceName": {
"type": "keyword"
},
"chainId": {
"type": "keyword"
},
"userId": {
"type": "keyword"
},
"timestamp": {
"type": "date"
}
}
}
}]]>
</property>
</properties>
未优化的代码
import *********************;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.frameworkset.spi.async.annotation.Async;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
/**
* 记录接口日志
*/
@Slf4j
@Service
@AllArgsConstructor
@NoArgsConstructor
public class LogServiceImpl implements LogService {
@Autowired
private RequestContext requestContext;
@Autowired
private LogCmopService logCmopService;
@Value("${method.address}")
private String address;
@Async
@Override
public <T, E extends BaseResponse> E invoke(LogRecordRequest recordRequest, Class<E> responseClazz) {
log.info("调用 {} 接口", recordRequest.getInterfacePath());
String result = "";
TimeInterval timeInterval = new TimeInterval();
ICommonParam commonParam = requestContext.getCommonParam() == null ? new BaseCommonParam() : requestContext.getCommonParam();
//定义需要返回的对象
E response = null;
String token = recordRequest.getToken();
if (StrUtil.isEmpty(token)) {
response.setResCode("-1");
response.setResDesc("未获取到token");
} else {
String url = address + recordRequest.getInterfacePath() + "?access_token=" + token + "&method=" + recordRequest.getMethod() + "&format=json";
try {
response = responseClazz.newInstance();
result = HttpRequestUtil.sendPost(url, recordRequest.getRequest(), recordRequest.getCodeScheme());
log.info("调用 {} 接口返回信息: {} ",recordRequest.getInterfaceName(), result);
response = (E) CustomJsonUtil.jsonToObject(result, recordRequest.getT().getClass());
} catch (Exception e) {
log.error(" error", e);
response.setResCode(AdapterEnum.CODE_1004.getCode());
response.setResDesc(AdapterEnum.CODE_1004.getValue());
return response;
} finally {
log.info("调用 {} 接口用时: {} 毫秒",recordRequest.getInterfaceName(), timeInterval.interval());
LogCmopAddRequest logCmopAddRequest = new LogCmopAddRequest();
logCmopAddRequest.setRequestUrl(recordRequest.getUrl());
logCmopAddRequest.setRequestParam(StrUtil.subPre(recordRequest.getRequest(), 2000));
logCmopAddRequest.setMethodName(recordRequest.getMethod());
logCmopAddRequest.setDuration(timeInterval.interval());
logCmopAddRequest.setReturnCode(response.getResCode());
logCmopAddRequest.setReturnMessage(StrUtil.subPre(result, 5000));
logCmopAddRequest.setChainId(commonParam.getChainId());
logCmopService.add(logCmopAddRequest);
}
}
return response;
}
}