背景:需要根据一个实时计算处理的结果值进行排序,数据从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. NC23803 DongDong认亲戚

    题目链接 题目 题目描述 DongDong每年过春节都要回到老家探亲,然而DongDong记性并不好,没法想起谁是谁的亲戚(定义:若A和B是亲戚,B和C是亲戚,那么A和C也是亲戚),她只好求助于会编程 ...

  2. NC210981 mixup2混乱的奶牛

    题目链接 题目 题目描述 混乱的奶牛 [Don Piele, 2007] Farmer John的 N(4 <= N <= 16) 头奶牛中的每一头都有一个唯一的编号 \(S_i (1 & ...

  3. Java 封装、继承、多态的理解

    更好的阅读体验:Java 封装.继承.多态的理解 1.封装 封装:就是隐藏对象的属性和实现细节,仅对外提供公共访问方式.让使用者知道的才暴露出来,不需要让使用者知道的全部隐藏起来 封装的好处:避免使用 ...

  4. ABP的版本升级,从7.2.2升级到7.2.3

    1.升级ABP CLI 见前面的文章:ABP开发需要用到的命令 更新最新版本: ~~~ dotnet tool update -g Volo.Abp.Cli ~~~ 2.升级ABP Suite 见前面 ...

  5. 【Azure K8S】演示修复因AKS密钥过期而导致创建服务不成功的问题(The provided client secret keys for app ****** are expired)

    问题描述 在Azure Kubernetes 服务中,创建一个Internal Load Balancer服务,使用以下yaml内容: internallb.yaml apiVersion: v1 k ...

  6. 【Azure 环境】Update-MgEntitlementManagementAccessPackageAssignmentPolicy 命令执行时候遇见的 No HTTP Resource was found 问题分析

    Microsoft Graph PowerShell SDK: acts as an API wrapper for the Microsoft Graph APIs, exposing the en ...

  7. docker知识整理(备份)

    概念: 镜像:Docker 镜像类似于虚拟机镜像,可以将它理解为一个只读的模板 .镜像是创建 Docker 容器的基础.通过版本管理和增量的文件系统, Docker 提供了一套十分简单的机制来创建和更 ...

  8. 如何设计一个高性能的图 Schema

    本文整理自青藤云安全工程师--文洲在青藤云技术团队内部分享,分享视频参考:https://www.bilibili.com/video/BV1r64y1R72i 图数据库的性能和 schema 的设计 ...

  9. Codeforces Round 651 (Div. 2)C. Number Game(数学思维数论)

    C. Number Game 我们考虑那些状态是必胜态 我的回合时n为奇数(除1外),直接除以n则必胜 下面偶数的情况稍复杂 偶数我们能进行的操作只有除以一个奇数,需要考虑怎么把当前状态变为对手的必败 ...

  10. git 常见命令和资源

    git练习 常用git清单 强制切换分支所指位置 git branch -f main c3强制分支main指向c3 git branch -f main HEAD~3强制分支main指向head的父 ...