使用elasticsearch解决复杂查询场景:查询可领取的优惠券

场景

某个优惠券批次,设置了以下领取条件:

  • 仅部分省份或部分城市可用
  • 仅部分领取渠道可用

数据库中存储的结构如下(简化版):
表:券批次表 列: 券批次id,可用地区,可用渠道
可用地区是一个json,示例

[
    {
        "province": "浙江省"
    },
    {
        "province": "安徽省",
        "city": "黄山市"
    }
]

可用渠道也是个json,存的枚举值,示例

[1,2,3,4]

前端app上,会传参数用户所在城市参数+渠道参数来查询可领取的优惠券
后端就要返回,(用户所在城市可领取 或 所在城市对应的省份可领取)且 领取渠道 包含 渠道参数的优惠券批次

如果用SQL写,可以说很难写,写出来的效率也不高
如果是SQL + 程序处理,难度和复杂度也很高

那借助es怎么实现呢

方案一

直接把这条数据写到es,es文档如下

{
    "id": 1,
    "allowed_areas": [
        {
            "province": "浙江省"
        },
        {
            "province": "安徽省",
            "city": "黄山市"
        }
    ],
    "allowed_channels": [
        1,
        2,
        3,
        4
    ]
}

前端查询参数:

{
    "province": "安徽省",
    "city": "黄山市",
    "channel": 1
}

后端根据参数去查询es,query为

{
  "query": {
    "bool": {
      "must": [
        {
          "bool": {
            "should": [
              {
                "term": {
                  "allowed_areas.city": {
                    "value": "黄山市"
                  }
                }
              },
              {
                "term": {
                  "allowed_areas.province": {
                    "value": "安徽省"
                  }
                }
              }
            ]
          }
        },
        {
          "terms": {
            "allowed_channels": [
              3,
              2
            ]
          }
        }
      ]
    }
  }
}

洋洋洒洒一大堆

有没有更简洁的方法呢

方案二

利用分词完成,思路是这样的:把allowed_areas拼成一个字符串,得到浙江省,安徽省黄山市,然后存到es索引中,设置类型为text,自定义一个按照逗号分词的分词器。

自定义分词器
"self_define_comma_analyzer": {
              "pattern": ",",
              "type": "pattern"
            }

测试分词器

POST coupon_batch_test/_analyze
{
  "analyzer": "self_define_comma_analyzer",
  "text": ["浙江省,安徽省黄山市"]
}

结果

{
  "tokens": [
    {
      "token": "浙江省",
      "start_offset": 0,
      "end_offset": 3,
      "type": "word",
      "position": 0
    },
    {
      "token": "安徽省黄山市",
      "start_offset": 4,
      "end_offset": 10,
      "type": "word",
      "position": 1
    }
  ]
}
借助自定义分词器

收到前端的查询参数后,把allowed_areas处理为省份,省份城市安徽省,安徽省黄山市,然后用match完成条件匹配。query,如下

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "allowed_areas": "安徽省,安徽省黄山市"
          }
        },
        {
          "term": {
            "allowed_channels": 1
          }
        }
      ]
    }
  }
}
如果allowed_channels参数也是多个呢?

前端参数如果是

{
    "province": "安徽省",
    "city": "黄山市",
    "channels": [1, 4, 3]
}

我们怎么实现呢?
参考方案二,把allowed_channels也做成一个拼接的字符串,然后使用分词器完成查询,如下

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "allowed_areas": "安徽省,安徽省黄山市"
          }
        },
        {
          "match": {
            "allowed_channels": {
              "query":  "2",
              "operator": "and",
              "fuzziness": 0
            }
          }
        }
      ]
    }
  }
}
方案二总结

不论是单个查询还是多个条件查询,方案二都很简洁。利用了es的match全文检索。
但是这种方案,需要在数据写入到索引时,对数据做一些处理。交接时文档要写清楚。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lanicc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值