背景:需要根据一个实时计算处理的结果值进行排序,数据从es中查询。(基于业务背景:佣金排序)

es版本:7.17.1;spring-data-elasticsearch版本:4.3.9

方式一:mysql新增字段:mysql根据业务操作,直接在在代码中刷取数据存储到mysql中(未采用)
优点:代码简单,后期查询时候排序简单

缺点:如果相关因素变化,需要扫描刷新大量相关数据

方式二:pipeline,数据往es同步的时候计算出来需排序的字段(暂未采用)

优点:实现简单,后期排序简单

缺点:当前业务背景下,也需要刷新大量数据,只是不用自己计算而已。相当于计算过程中部分字段在原始schema中也没有,复杂度和方式一差不多。

方式二.一:(暂未采用)
创建索引时指定mapping中设置runtime字段,支持数据插入的时候即生成runtime字段
优点:性能比查询时直接使用runtime mapping性能高
缺点:需要重新创建索引,reindex所有历史数据。

方式三:使用runtime mapping在查询过程中直接实现(采用)

优点:实现简单,直接用script脚本即可实现

缺点:数据量大的时候,会有性能问题,生产环境使用前,需要压测

下面重点讲解一下方式三实现:

版本一:

script是一个对象{},可以动态传递参数,更加灵活。

POST /t_spu/_search
{
"size": 1000,
"runtime_mappings": {
"commission": {
"type": "double",
"script": {
"source": """
String commissionStr = doc['commission_price.keyword'].value;
long price = doc['min_sku_sale_price'].value;
int indexNo = commissionStr.indexOf('~');
if (indexNo > 0) {
double allCommission = Double.parseDouble(commissionStr.substring(0, indexNo))*100;
emit(allCommission - price * params.platformCommissionRate);
} else {
emit(Double.parseDouble(commissionStr)*100 - price * params.platformCommissionRate);
}
""",
"lang": "painless",
"params": {
"platformCommissionRate": 0.03
}
}
}
},
"fields": [
"commission"
],
"query": {
"bool": {
"filter": [
{"term": {
"goods_source_type": {
"value": "1"
}
}}
]
}
},
"sort": [
{
"commission": {
"order": "desc"
}
}
]
}

java:(部分查询条件未具体实现)

1、这里用到的script是一个对象{},ElasticsearchRestTemplate不支持。es版本:7.17.1;spring-data-elasticsearch版本:4.3.9

只能用RestHighLevelClient客户端实现java代码。

@Autowired
private RestHighLevelClient restHighLevelClient;


String commissionScript = "String commissionStr = doc['commission_price.keyword'].value;" +
"long price = doc['min_sku_sale_price'].value;" +
"int indexNo = commissionStr.indexOf('~');" +
"if (indexNo > 0) {double allCommission = Double.parseDouble(commissionStr.substring(0, indexNo))*100;" +
"emit(allCommission - price * params.platform_commission_rate);}" +
"else {emit(Double.parseDouble(commissionStr)*100 - price * params.platform_commission_rate);}";
//runtime_mappings
final String COMMISSION = "commission";
Map<String, Object> params = new HashMap<>();
params.put(GoodsSearchEsConstant.PLATFORM_COMMISSION_RATE, realPlatformCommissionRate);
Script script = new Script(ScriptType.INLINE, "painless", commissionScript, params); SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// sourceBuilder.from(0);
// sourceBuilder.size(10);
// sourceBuilder.fetchSource(new String[]{"title"}, new String[]{});
// MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", "商品名称");
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("goods_source_type", 1);
// RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("payTime");
// rangeQueryBuilder.gte("2023-01-26T08:00:00Z");
// rangeQueryBuilder.lte("2023-01-26T20:00:00Z");
BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
// boolBuilder.must(matchQueryBuilder);
boolBuilder.must(termQueryBuilder);
// boolBuilder.must(rangeQueryBuilder);
sourceBuilder.query(boolBuilder); //runtime_mappings 部分
HashMap<String, Object> runtimeMappings = new HashMap<>();
HashMap<String, Object> commObj = new HashMap<>();
commObj.put("script", script);
commObj.put("type", FieldType.Double.getMappedName());
runtimeMappings.put(COMMISSION, commObj);
// sourceBuilder.fetchField("*");
sourceBuilder.fetchField("commission");
sourceBuilder.runtimeMappings(runtimeMappings); sourceBuilder.sort(COMMISSION, SortOrder.DESC);
SearchRequest searchRequest = new SearchRequest("index_t_spu");
searchRequest.source(sourceBuilder);
try {
SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}

版本二:

script直接传source,string类型脚本,这样的话动态参数params就无法添加了,只能自己提前拼接好完整的script。类型是String。

POST /tb-g-mysql.tb_retail_goods.t_spu/_search
{
"size": 1120,
"runtime_mappings": {
"commission": {
"type": "double",
"script": "String commissionStr = doc['commission_price.keyword'].value;long price = doc['min_sku_sale_price'].value / 100;int indexNo = commissionStr.indexOf('~');if (indexNo > 0) {double allCommission = Double.parseDouble(commissionStr.substring(0, indexNo))*100;emit(allCommission - price * 0.03);} else {emit(Double.parseDouble(commissionStr)*100 - price * 0.03);}"
}
},
"fields": [
"commission"
],
"query": {
"term": {
"goods_source_type": {
"value": "1"
}
}
},
"sort": [
{
"commission": {
"order": "desc"
}
}
]
}

java代码实现:(部分查询条件未具体实现)

spring-data-elasticsearch的客户端ElasticsearchRestTemplate 不支持:因为:RuntimeField不支持Script对象参数,因此直接string替换动态参数拼接script

es版本:7.17.1;spring-data-elasticsearch版本:4.3.9

@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;


NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder().filter(QueryBuilders.termQuery("xxx", 1));
NativeSearchQuery nativeSearchQuery = searchQueryBuilder.withTrackScores(true)
.withQuery(boolQueryBuilder)
.withPageable(PageRequest.of(dto.getPage() - 1, dto.getLimit()))
.withIndicesOptions(IndicesOptions.LENIENT_EXPAND_OPEN).build();
String realPlatformCommissionRate = BigDecimal.valueOf(platformCommissionRate).divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP).toString();
String commissionScript = ("String commissionRateStr = doc['commission_rate.keyword'].value;" +
"long price = doc['min_sku_sale_price'].value;" +
"int indexNo = commissionRateStr.indexOf('~');" +
"if (indexNo > 0) {double allCommission = Double.parseDouble(commissionRateStr.substring(0, indexNo))*100;" +
"emit(allCommission - price * realPlatformCommissionRate);}" +
"else {emit(Double.parseDouble(commissionRateStr)*100 - price * realPlatformCommissionRate);}")
.replace("realPlatformCommissionRate", realPlatformCommissionRate);
//runtime_mappings
final String COMMISSION = "commission";
//注意:RuntimeField不支持Script对象参数,因此直接string替换动态参数拼接script
RuntimeField runtimeField = new RuntimeField(COMMISSION, FieldType.Double.getMappedName(), commissionScript);
searchQuery.addRuntimeField(runtimeField);
searchQuery.addFields(COMMISSION);
if (StringUtils.isNotBlank(dto.getSort()) && SqlKeyword.DESC.name().equals(dto.getSort().toUpperCase())) {
searchQuery.addSort(Sort.by(Sort.Direction.DESC, COMMISSION));
} else {
searchQuery.addSort(Sort.by(Sort.Direction.ASC, COMMISSION));
}

elasticsearch中runtime_mapping实战的更多相关文章

  1. Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析

    Java生鲜电商平台-电商中海量搜索ElasticSearch架构设计实战与源码解析 生鲜电商搜索引擎的特点 众所周知,标准的搜索引擎主要分成三个大的部分,第一步是爬虫系统,第二步是数据分析,第三步才 ...

  2. Elasticsearch.net项目实战

    elasticsearch.net项目实战 目录 Elasticsearch+kibana 环境搭建 windows 10环境配置 安装Elasticsearch head安装(非必需) 安装kiba ...

  3. 一文带您了解 Elasticsearch 中,如何进行索引管理(图文教程)

    欢迎关注笔者的公众号: 小哈学Java, 每日推送 Java 领域干货文章,关注即免费无套路附送 100G 海量学习.面试资源哟!! 个人网站: https://www.exception.site/ ...

  4. Elasticsearch核心技术与实战,性能是真牛

    Elasticsearch 是一款非常强大的开源搜索及分析引擎.结合 Kibana.Logstash和Beats,Elasticsearch 还被广泛运用在大数据近实时分析,包括日志分析.指标监控.信 ...

  5. 如何在Elasticsearch中安装中文分词器(IK+pinyin)

    如果直接使用Elasticsearch的朋友在处理中文内容的搜索时,肯定会遇到很尴尬的问题--中文词语被分成了一个一个的汉字,当用Kibana作图的时候,按照term来分组,结果一个汉字被分成了一组. ...

  6. elasticsearch运维实战之2 - 系统性能调优

    elasticsearch性能调优 集群规划 独立的master节点,不存储数据, 数量不少于2 数据节点(Data Node) 查询节点(Query Node),起到负载均衡的作用 Linux系统参 ...

  7. elasticsearch中常用的API

    elasticsearch中常用的API分类如下: 文档API: 提供对文档的增删改查操作 搜索API: 提供对文档进行某个字段的查询 索引API: 提供对索引进行操作,查看索引信息等 查看API: ...

  8. 在Elasticsearch中查询Term Vectors词条向量信息

    这篇文章有点深度,可能需要一些Lucene或者全文检索的背景.由于我也很久没有看过Lucene了,有些地方理解的不对还请多多指正. 更多内容还请参考整理的ELK教程 关于Term Vectors 额, ...

  9. elasticsearch中的API

    elasticsearch中的API es中的API按照大类分为下面几种: 文档API: 提供对文档的增删改查操作 搜索API: 提供对文档进行某个字段的查询 索引API: 提供对索引进行操作 查看A ...

  10. 使用Hive或Impala执行SQL语句,对存储在Elasticsearch中的数据操作(二)

    CSSDesk body { background-color: #2574b0; } /*! zybuluo */ article,aside,details,figcaption,figure,f ...

随机推荐

  1. NVME CLI 命令使用

    1.下载地址https://github.com/linux-nvme/nvme-cli2.安装unzip nvme-cli-master.zipcd nvme-cli-master.zipmake ...

  2. 使用BP神经网络实现函数逼近

    1 一元函数逼近 1.1 待逼近函数 1.2 代码 clear,clc p=[-4:0.1:4]; %神经网络输入值 t=sin(0.5*pi*p)+sin(pi*p); %神经网络目标值 n=15; ...

  3. win10 wsl 运行后没有反应

    wsl 运行一段时间后执行没有反应, 需要重启LxssManager 管理员模式打开 powshell 找到pid, 结束pid >tasklist /svc /fi "service ...

  4. 【Android 逆向】【ARM汇编】 arm64部分知识

    arm64寄存器更多 X0-X30 SP CPSR PC 64位 W0-W30 32位 PC寄存器的值禁止修改 参数放在 X0-X7/W0-W7 结果放在 X0 函数返回 RET 相当于 bl lr ...

  5. 擅长使用iter

    def populate_ranks(votes, ranks): names = list(votes.keys()) names.sort(key=votes.get, reverse=True) ...

  6. React 组件通信方式

    人生的游戏不在于拿了一副好牌,而在于怎样去打好坏牌,世上没有常胜将军,勇于超越自我者才能得到最后的奖杯. 1. 父子组件通信方式 1.1 父组件传递到子组件 直接通过属性进行传递,数据的传递可以提高组 ...

  7. 【Azure 媒体服务】使用媒体服务 v3 对视频进行上载、编码和流式传输时遇见的AAD错误

    问题描述 使用媒体服务 v3 对视频进行上载.编码和流式传输示例时,遇见了AAD错误. TIP: Make sure that you have filled out the appsettings. ...

  8. 2023 年值得一读的技术文章 | NebulaGraph 技术社区

    在之前的产品篇,我们了解到了 NebulaGraph 内核及周边工具在 2023 年经历了什么样的变化.伴随着这些特性的变更和上线,在[文章]博客分类中,一篇篇的博文记录下了这些功能背后的设计思考和研 ...

  9. 利用微软官方API实现Office文档的在线预览功能

    随着互联网时代的飞速发展,越来越多的工作开始依赖于云端服务,我们的办公方式也逐渐发生了翻天覆地的变化.在这种背景下,急需一种无需本地安装Office软件,就能快速查看和共享Word.PowerPoin ...

  10. Python 中read()、 readline() 、readlines()三者之间的区别?

    read()方法用于一次性读取整个文件的内容,并将其作为一个字符串返回. readline()方法用于逐行读取文件的内容.每次调用readline()方法,它会读取文件的下一行,并将其作为一个字符串返 ...