【数据开发离谱场景记录】Hive + ES 复杂查询场景处理

场景

存在数据 :

id value
1 1001_1,1002_3,1004_5
2 1002_1, 1003_1, 1005_3
3 1001_2, 1003_3, 1004_1

查询要求:

场景1: 查询 value 前半部分 in (1001,1002 ) 且 后半部为 = 2 的数据(即 1001_2, 1002_2)

场景2:查询 value 前半部分 in (1001,1002) 且 所有的后半部分之和 > 8 (即拿id2举例 1001, 1003,1005 且 1+1+3 > 8)

相当于 在标签1的情况下 (1001_1 = 子标签(1001) + 标签值(1))

ps : 先不管为很么是这个数据结构,问就是前人留坑,到你这里已经没法改变了,但是你得把现在的需求实现,呼呼~~~~

熟话说,没有挑战就没有进步 干他

实现方式

当前的数据是存在 hive中的,value 字段为逗号分割的字符串, 查询引擎使用ES

第一步:

es 索引设计

思路:要实现同一个字段 1001_1 前半部分和后半部分分开查询,肯定要将字段拆开存为 1001 和 1 两部分,

官网寻找后 发现, es 数据类型 nested (类似map结构 {part1:'1001', part2: 1})可以支持, 定义方式:

sql 复制代码
{
  "mappings": {
    "properties": {
      "cus_field_name": { // 字段名称
        "type": "nested", // 字段类型
        "properties": {
          "value": {       // 第一部分
            "type": "keyword" 
          },
          "int_value": { // 第二部分
            "type": "integer"  
          }
        }
      }
    }
  }
}

hive 数据构建即将 逗号分割的字符串转成 map 数组的格式:

sql 复制代码
select 
COLLECT_LIST(
    NAMED_STRUCT(
      'value', split(single_str, '_')[0], -- 获取下划线前的部分,例如 '1001'
      'int_value', split(single_str, '_')[1]  -- 获取下划线后的部分并转为整数,例如 1
    )
  ) AS f_000073
  from tb
  LATERAL VIEW EXPLODE(split(f_field,',')) exploded_table AS single_str -- 先将字符串数组"炸开"成多行
  group by 
  user_id 

最终效果

sql 复制代码
[
{"value":"10006","int_value":"8"},
{"value":"10007","int_value":"1"}
]

写入es数据效果:

sql 复制代码
"cus_field_name" : 
[
 {
   "value" : "10006",
   "int_value" : "8"
},
{
   "value" : "10007",
   "int_value" : "1"
}
]

第二步 查询

场景1: 查询 value 前半部分 in (1001,1002 ) 且 后半部为 = 2 的数据(即 1001_2, 1002_2)

sql 复制代码
{ 
  "size":10, 
  "query": {
    "nested": { // 查询类型
            "path": "cus_field_name", // nested 类型的字段名称
            "query": {
              "bool": {
                "must": [
                  {
                    "terms": {
                      "cus_field_name.value": [ // 表示第一部分在 1007 和 1008 中
                        "10007",
                        "10008"
                      ]
                    }
                  },
                  {
                    "range": {
                      "f_000073.int_value": { // 并且 后面的值 = 2
                        "eq": 2
                      }
                    }
                  }
                ]
              }
            }
          }
        }
  }

场景2: 查询 value 前半部分 in (1001,1002) 且 所有的后半部分之和 > 8 (即拿id2举例 1001, 1003,1005 且 1+1+3 > 8)

需要求和 要使用到 ES 的 Agg

sql 复制代码
"aggs": {
    "docs_aggs": { // docs_aggs 自定义 聚合的名称 
      "terms": { // 表示 根据哪个字段分组 相当于 group by 的字段
        "field": "user_id.keyword", 
        "size": 10000
      },
      "aggs": {  
        "netsed_field": { // netsed_field 自定义聚合名称 
          "nested": { // 指定为 nested 类型 
            "path": "f_000073" // 指定 nested 类型字段
          },
          "aggs": { 
            "sum_int_value": { // sum_int_value 自定义聚合名称 
              "sum": { // 定义聚合方式
                "field": "f_000073.int_value" // 指定需要聚合的字段
              }
            }
          }
        }
        ,
        "sum_filter": { // 定义过滤名称 sum_filter 和 aggs 同一个层级
          "bucket_selector": { // bucket_selector 过滤方式, 该方式可以使用 aggs 上下文中自定义的字段信息,这里是 sum_int_value 
            "buckets_path": {
              "sum_value":"netsed_field.sum_int_value" // sum_value 定义参数名: 
              // netsed_field.sum_int_value 为参数的实际层级
              // sum_filter 
            },
            "script": "params.sum_value>19" // 固定写法
          }
        },
        "res":{
          "top_hits": { // 表示返回前10个
            "size": 10 
          }
        
        }
      }
     
    }
  }

查询结果展示:

需要注意的是 在 hits 中返回的数据不会根据聚合中的 params.sum_value>19 进行过滤,所以我们一般将 size 设置为0,表示 hits 中不返回数据,使用 aggregations 中返回的数据作为最终结果

sql 复制代码
{
  "took" : 4,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 4,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "docs_aggs" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "183624",
          "doc_count" : 1,
          "res" : {
            "hits" : {
              "total" : {
                "value" : 1,
                "relation" : "eq"
              },
              "max_score" : 3.6376085,
              "hits" : [
                {
                  "_index" : "te_emp_tags_v4",
                  "_type" : "_doc",
                  "_id" : "183624",
                  "_score" : 3.6376085,
                  "_source" : {
                    "user_id" : "183624",
                    "f_000077" : "100119",
                    "f_000075" : "100083",
                    "f_000081" : "100090",
                    "f_000074" : "100078",
                    "f_000008" : "100026",
                    "f_000073" : [
                      {
                        "value" : "100073",
                        "int_value" : "10"
                      },
                      {
                        "value" : "100075",
                        "int_value" : "10"
                      }
                    ]
                  }
                }
              ]
            }
          },
          "netsed_field" : {
            "doc_count" : 2,
            "sum_int_value" : {
              "value" : 20.0
            }
          }
        }
      ]
    }
  }
}

以上即为当前场景的解决方案,希望对有相同场景的伙伴有帮助 , 加油 ~~~~

相关推荐
-拟墨画扇-2 小时前
Git | 简介与安装
大数据·git·elasticsearch
Elastic 中国社区官方博客3 小时前
Elasticsearch:圣诞晚餐 BBQ
大数据·人工智能·elk·elasticsearch·搜索引擎·ai·全文检索
无泪无花月隐星沉3 小时前
uos server 1070e部署Hadoop
大数据·运维·服务器·hadoop·分布式·uos·国产化os
豆豆3 小时前
哪些cms网站内容管理系统支持lucene或Elasticsearch的全站全文检索功能
elasticsearch·全文检索·cms·lucene·低代码平台·单点登录·工单系统
悟能不能悟14 小时前
springboot全局异常
大数据·hive·spring boot
Elasticsearch19 小时前
Elasticsearch:圣诞晚餐 BBQ - 图像识别
elasticsearch
是Judy咋!19 小时前
Elasticsearch---单机部署
大数据·elasticsearch·搜索引擎
是阿威啊20 小时前
【第一站】本地虚拟机部署Hadoop分布式集群
大数据·linux·hadoop·分布式
java坤坤20 小时前
Elasticsearch Java实战手册:搭建、条件构建与分页优化
java·elasticsearch