场景
某个优惠券批次,设置了以下领取条件:
- 仅部分省份或部分城市可用
- 仅部分领取渠道可用
数据库中存储的结构如下(简化版):
表:券批次表 列: 券批次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
全文检索。
但是这种方案,需要在数据写入到索引时,对数据做一些处理。交接时文档要写清楚。