场景
存在数据 :
| 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
}
}
}
]
}
}
}
以上即为当前场景的解决方案,希望对有相同场景的伙伴有帮助 , 加油 ~~~~