背景:需要根据一个实时计算处理的结果值进行排序,数据从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. Ubuntu下图形界面串口工具CuteCom的安装和升级

    串口的图形界面化工具在Windows下很多, 但是在Linux下可选择的就很少, CuteCom 是相对比较好用的一款了. Ubuntu20.04默认安装的是0.30.3, 这是一个比较早的版本, 最 ...

  2. 51单片机(STC89C52)在Ubuntu下的开发

    简介 都是8051衍生的8位单片机, STC单片机有89/90/10/11/12/15这几个大系列, 每个系列的特点如下 89系列是传统的8051单片机, 烧录方法有区别, 但是功能上可以和AT89系 ...

  3. 使用RegSetValueEx创建键值

    #include <iostream> #include <string> #include <sstream> #include <fstream> ...

  4. FART 脱壳机原理分析

    FART是一个基于Android 源码修改的脱壳机 可以脱整体壳和抽取壳 FART脱壳的步骤主要分为三步: 1.内存中DexFile结构体完整dex的dump 2.主动调用类中的每一个方法,并实现对应 ...

  5. 项目实战:Qt+FFmpeg录屏应用(支持帧率、清晰度设置)

    若该文为原创文章,转载请注明原文出处本文章博客地址:https://blog.csdn.net/qq21497936/article/details/109827936各位读者,知识无穷而人力有穷,要 ...

  6. 将模型对象转换为json字典:model_to_dict

    例子 from rest_framework.views import APIView class StudentAPIView(APIView): def get(self, request): p ...

  7. 【Azure Redis】中国区Redis在东三区的资源无法在通过门户上与北三区资源之间建立灾备链接

    问题描述 为应用启用灾备管理,在北三区建立了一个Azure Redis,同时,在东三区也建立了一个同样的Prem级Redis服务.但是在建立灾备(DR:Disease Recovery)时候,却无法选 ...

  8. 【Azure 事件中心】Spring Cloud Stream Event Hubs Binder 发送Event Hub消息遇见 Spec. Rule 1.3 - onSubscribe, onNext, onError and onComplete signaled to a Subscriber MUST be signaled serially 异常

    问题描述 开发Java Spring Cloud应用,需要发送消息到Azure Event Hub中.使用 Spring Cloud Stream Event Hubs Binder 依赖,应用执行一 ...

  9. 【应用服务 App Service】在Azure Web App的部署文件中,是否可以限制某些文件无法被访问?(如json)

    问题描述 当部署文件到Azure App Service上后,默认访问文件在wwwroot目录中,如appsettings.json文件,在通过URL+文件名的形式可以访问,这样敏感信息会被泄露出去, ...

  10. UG474

    为了对工程的资源利用率进行优化,我们首先需要知道当前工程对资源的利用率情况.在Vivado下,我们可以查看工程的资源利用率情况,在下面这张图中,其罗列出了整个工程所使用的资源情况.首先,下面我们需要一 ...