商超场景 ES 查询关键字详解与会员标签存储方案
1. 查询关键字精准解析(含匹配逻辑与商超案例)
1.1. text 类型(完整查询方式)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
match(全文检索)
|
{"match": {"product_name": "有机牛奶"}}
|
- 关键字:match- 逻辑:分词后匹配任意分词(OR),计算相关性分数
|
搜索含 “有机” 或 “牛奶” 的商品
|
1. 配置 ik 分词器处理中文2. 对高频词设置stopwords(如 “特价”)
|
|
match_phrase(短语匹配)
|
{"match_phrase": {"product_name": "伊利纯牛奶"}}
|
- 关键字:match_phrase- 逻辑:分词按原顺序连续出现(中间无其他词)
|
搜索完整名称 “伊利纯牛奶” 的商品
|
1. 设置slop:1允许插入 1 个词2. 启用index_options:offsets提升精度
|
|
match_phrase_prefix(前缀短语)
|
{"match_phrase_prefix": {"product_name": "伊利纯牛"}}
|
- 关键字:match_phrase_prefix- 逻辑:前 N-1 词精确匹配,最后 1 词前缀匹配
|
搜索框输入提示(输入 “伊利纯牛” 提示完整商品名)
|
1. 设置max_expansions:10限制匹配数量2. 仅用于搜索提示场景
|
|
query_string(查询字符串)
|
{"query_string": {"query": "product_name:牛奶 AND category:乳制品"}}
|
- 关键字:query_string- 逻辑:支持 AND/OR 等逻辑运算符,多字段匹配
|
搜索名称含 “牛奶” 且品类为乳制品的商品
|
1. 限制default_field避免全字段扫描2. 禁用allow_leading_wildcard防止前缀*
|
|
simple_query_string(简化查询字符串)
|
{"simple_query_string": {"query": "牛奶 +乳制品", "fields": ["product_name", "category"]}}
|
- 关键字:simple_query_string- 逻辑:容错性更高,用+表示 AND,`
|
` 表示 OR
|
搜索含 “牛奶” 且含 “乳制品” 的商品
|
|
fuzzy(模糊匹配)
|
{"fuzzy": {"product_name": {"value": "牛乃", "fuzziness": 1}}}
|
- 关键字:fuzzy- 逻辑:允许 1-2 个字符编辑(替换 / 插入 / 删除)
|
处理 “牛乃” 匹配 “牛奶” 的输入错误
|
1. 设置prefix_length:2(前 2 字符精确匹配)2. 限制fuzziness:1减少计算量
|
|
multi_match(多字段匹配)
|
{"multi_match": {"query": "牛奶", "fields": ["product_name^3", "description"]}}
|
- 关键字:multi_match- 逻辑:多字段执行 match,支持字段权重(^3 为 3 倍权重)
|
同时搜索商品名和描述,名称权重更高
|
1. 限制匹配字段≤5 个2. 对重要字段设置更高权重
|
|
common(常用词查询)
|
{"common": {"product_name": {"query": "有机牛奶", "cutoff_frequency": 0.001}}}
|
- 关键字:common- 逻辑:低频词(如 “有机”)精确匹配,高频词(如 “牛奶”)模糊匹配
|
平衡 “牛奶”(高频)和 “有机”(低频)的匹配权重
|
1. cutoff_frequency设为 0.001(过滤 1% 以上高频词)2. 适合含高频 + 低频词的查询
|
1.2. keyword 类型(完整查询方式)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
term(精确匹配)
|
{"term": {"category.keyword": "乳制品"}}
|
- 关键字:term- 逻辑:字段值与查询值完全相等(不分词)
|
筛选品类为 “乳制品” 的商品
|
1. 用filter上下文缓存结果2. 必须使用keyword子字段
|
|
terms(多值匹配)
|
{"terms": {"shelf_code": ["A1", "B2"]}}
|
- 关键字:terms- 逻辑:字段值与数组中任意值相等(OR)
|
筛选 A1 或 B2 货架的商品
|
1. 数组长度≤10002. 启用execution:"bool"优化性能
|
|
range(范围匹配)
|
{"range": {"member_id.keyword": {"gte": "Vip2023001", "lte": "Vip2023100"}}}
|
- 关键字:range- 逻辑:按字典序匹配范围内的值
|
筛选 2023 年注册的前 100 名会员
|
1. 仅用于有序 keyword(如会员 ID、日期字符串)2. 避免对无序字符串使用(如商品名)
|
|
prefix(前缀匹配)
|
{"prefix": {"sku_id": "SKU202310"}}
|
- 关键字:prefix- 逻辑:字段值以前缀开头
|
筛选 2023 年 10 月生产的商品
|
1. 前缀长度≥3 字符2. 改用edge_ngram分词预处理提升性能
|
|
wildcard(通配符)
|
{"wildcard": {"phone.keyword": "138*5678"}}
|
- 关键字:wildcard- 逻辑:支持*(任意)和?(单个)字符匹配
|
按 “138****5678” 查询手机号
|
1. 禁止前缀*(如*5678)2. 通配符长度≤10 字符
|
|
regexp(正则匹配)
|
{"regexp": {"member_id.keyword": "Vip\\d{8}"}}
|
- 关键字:regexp- 逻辑:匹配正则表达式规则
|
筛选 “Vip+8 位数字” 格式的会员 ID
|
1. 正则尽量简单(避免复杂分组)2. 对固定格式字段优先使用
|
|
exists(字段存在)
|
{"exists": {"field": "brand.keyword"}}
|
- 关键字:exists- 逻辑:存在该字段且值非 null
|
筛选有品牌信息的商品
|
1. 结合must_not查询不存在的字段2. 对稀疏字段效率更高
|
1.3. 数值类型(integer/double/float/long)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
term(精确匹配)
|
{"term": {"stock": 0}}
|
- 关键字:term- 逻辑:字段值严格等于指定数值
|
筛选库存为 0 的商品(缺货提醒)
|
1. 用filter缓存结果2. 对高频值(如库存 0)优化统计
|
|
terms(多值匹配)
|
{"terms": {"price": [9.9, 19.9, 29.9]}}
|
- 关键字:terms- 逻辑:字段值与数组中任意值相等(OR)
|
筛选促销价为 9.9/19.9/29.9 的商品
|
1. 数组值按升序排列2. 数量多时拆分查询(>20 个值)
|
|
range(范围匹配)
|
{"range": {"points": {"gt": 1000, "lte": 5000}}}
|
- 关键字:range- 逻辑:字段值在指定范围内(支持gt/gte/lt/lte)
|
筛选积分 1000-5000 的会员
|
1. 范围尽量具体(避免gte:0)2. 用filter上下文缓存
|
|
function_score(函数评分)
|
{"function_score": {"query": {"match": {"name": "苹果"}},"functions": [{"field_value_factor": {"field": "sales","modifier": "log1p"}}]}}
|
- 关键字:function_score- 逻辑:基础查询结果按字段值加权评分
|
搜索 “苹果” 时销量高的商品排前
|
1. 用log1p平滑销量差距2. 对高频查询缓存结果
|
|
script(脚本计算)
|
{"script": {"script": "doc['price'].value * doc['discount'].value < 50"}}
|
- 关键字:script- 逻辑:执行自定义计算匹配条件
|
筛选折后价 < 50 元的商品
|
1. 脚本逻辑极简(避免循环)2. 用painless语言3. 冗余计算结果为字段(如discount_price)
|
|
exists(字段存在)
|
{"exists": {"field": "discount_rate"}}
|
- 关键字:exists- 逻辑:存在该字段且值非 null
|
筛选有折扣率的商品
|
1. 结合must_not查询无折扣商品2. 适合判断是否参与活动
|
1.4. 日期类型(date)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
term(精确匹配)
|
{"term": {"register_time": "2023-10-01T00:00:00Z"}}
|
- 关键字:term- 逻辑:字段值精确等于指定日期(毫秒级)
|
筛选 10 月 1 日 0 点注册的会员
|
1. 格式与映射一致(如yyyy-MM-dd HH:mm:ss)2. 仅用于精确时间点查询
|
|
range(范围匹配)
|
{"range": {"expire_date": {"gte": "now", "lte": "now+30d"}}}
|
- 关键字:range- 逻辑:日期在指定范围内,支持相对时间(now-30d)
|
筛选 30 天内过期的临期商品
|
1. 使用相对时间(now)避免硬编码2. 加format参数明确格式3. 大范围查询结合时间分片索引
|
|
script(日期计算)
|
{"script": {"script": "ChronoUnit.DAYS.between(doc['production_date'].value, doc['expire_date'].value) < 90"}}
|
- 关键字:script- 逻辑:计算日期差值(如保质期)匹配条件
|
筛选保质期 < 90 天的商品
|
1. 用ChronoUnit替代手动计算2. 冗余shelf_life字段存储保质期(避免查询计算)
|
|
exists(字段存在)
|
{"exists": {"field": "last_purchase_time"}}
|
- 关键字:exists- 逻辑:存在该字段且值非 null
|
筛选有购买记录的会员
|
1. 结合range筛选最近购买的会员2. 用于区分新老会员(无购买记录为新会员)
|
1.5. 布尔类型(boolean)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
term(精确匹配)
|
{"term": {"is_on_sale": true}}
|
- 关键字:term- 逻辑:字段值为true或false
|
筛选在售商品(is_on_sale: true)
|
1. 用filter上下文缓存结果2. 是最高效的查询类型之一
|
|
bool+must_not(非匹配)
|
{"bool": {"must_not": [{"term": {"is_promotion": true}}]}}
|
- 关键字:bool.must_not- 逻辑:字段值与查询值相反
|
筛选非促销商品(is_promotion: false)
|
1. 比term: {is_promotion: false}更直观2. 适合否定逻辑表达
|
|
exists(字段存在)
|
{"exists": {"field": "is_vip"}}
|
- 关键字:exists- 逻辑:存在该字段(无论true/false)
|
筛选有会员等级标记的用户
|
1. 用于判断是否完成会员等级初始化2. 结合term进一步筛选 VIP 会员
|
1.6. 数组类型(array)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
term(包含匹配)
|
{"term": {"preferences": "零食"}}
|
- 关键字:term- 逻辑:数组中至少包含一个指定值
|
筛选偏好含 “零食” 的会员
|
1. 数组元素用 keyword 类型2. 高频值放数组靠前位置
|
|
bool+must(全包含)
|
{"bool": {"must": [{"term": {"tags": "促销"}}, {"term": {"tags": "新品"}}]}}
|
- 关键字:bool.must- 逻辑:数组中同时包含所有指定值(AND)
|
筛选同时含 “促销” 和 “新品” 标签的商品
|
1. 条件数量≤5 个2. 先筛主文档(如在售商品)
|
|
terms_set(最小匹配)
|
{"terms_set": {"preferences": {"terms": ["乳制品", "肉类"], "minimum_should_match_script": {"source": "2"}}}}
|
- 关键字:terms_set- 逻辑:匹配值数量≥最小要求(此处需 2 个)
|
筛选同时偏好 “乳制品” 和 “肉类” 的会员
|
1. 用脚本动态设置最小匹配数2. 替代多个must子句更简洁
|
|
script(数组长度)
|
{"script": {"script": "doc['tags'].length > 3"}}
|
- 关键字:script- 逻辑:数组长度满足条件
|
筛选标签数 > 3 的商品
|
1. 冗余tags_count字段存储长度2. 避免查询时计算长度
|
|
script(包含逻辑)
|
{"script": {"script": "doc['preferences'].containsAll(['乳制品', '肉类'])"}}
|
- 关键字:script- 逻辑:数组包含所有指定值(全包含)
|
筛选同时偏好 “乳制品” 和 “肉类” 的会员
|
1. 比bool.must更直观(代码简洁)2. 性能略低于bool.must(适合条件多的场景)
|
1.7. 嵌套类型(nested)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
nested+term
|
{"nested": {"path": "tags", "query": {"term": {"tags.name": "高消费"}}}}
|
- 关键字:nested- 逻辑:匹配包含指定嵌套字段值的文档
|
筛选含 “高消费” 标签的会员
|
1. 嵌套前用filter缩小范围2. path必须精确
|
|
nested+bool+must
|
{"nested": {"path": "tags", "query": {"bool": {"must": [{"term": {"tags.name": "高消费"}}, {"range": {"tags.score": {"gte": 0.8}}}]}}}}
|
- 关键字:nested+bool.must- 逻辑:同一嵌套对象满足所有条件
|
筛选 “高消费标签且权重≥0.8” 的会员
|
1. 嵌套对象字段尽量少2. 高频条件冗余为根字段(如is_high_spender)
|
|
nested+bool+should
|
{"nested": {"path": "tags", "query": {"bool": {"should": [{"term": {"tags.name": "高消费"}}, {"term": {"tags.name": "高频次"}}]}}}}
|
- 关键字:nested+bool.should- 逻辑:同一嵌套对象满足任一条件
|
筛选含 “高消费” 或 “高频次” 标签的会员
|
1. 设置minimum_should_match:1确保至少匹配一个2. 配合boost调整标签权重
|
|
nested+exists
|
{"nested": {"path": "tags", "query": {"exists": {"field": "tags.update_time"}}}}
|
- 关键字:nested+exists- 逻辑:嵌套对象中存在指定字段
|
筛选有标签更新时间的会员
|
1. 结合range筛选最近更新的标签2. 用于排除旧标签数据
|
|
nested+script
|
{"nested": {"path": "tags", "query": {"script": {"script": "doc['tags.score'].value > 0.5 && doc['tags.update_time'].value.after(Instant.parse('2023-09-01T00:00:00Z'))"}}}}
|
- 关键字:nested+script- 逻辑:嵌套对象字段满足脚本条件
|
筛选标签权重 > 0.5 且 9 月后更新的会员
|
1. 脚本逻辑极简2. 避免在嵌套查询中使用复杂脚本
|
1.8. 地理坐标类型(geo_point)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
geo_distance
|
{"geo_distance": {"distance": "5km", "member_location": {"lat": 39.9, "lon": 116.4}}}
|
- 关键字:geo_distance- 逻辑:坐标与目标点距离≤指定值
|
筛选门店 5 公里内的会员
|
1. 用km单位(比m快)2. 创建 geo_hash 网格索引(precision:"1km")
|
|
geo_bounding_box
|
{"geo_bounding_box": {"member_location": {"top_left": {"lat": 40, "lon": 116}, "bottom_right": {"lat": 39.8, "lon": 116.5}}}}
|
- 关键字:geo_bounding_box- 逻辑:坐标在矩形区域内
|
筛选某商圈矩形范围内的会员
|
1. 矩形范围尽量小2. 比geo_polygon性能高
|
|
geo_polygon
|
{"geo_polygon": {"member_location": {"points": [{"lat": 39.9, "lon": 116.4}, {"lat": 40, "lon": 116.5}, {"lat": 39.8, "lon": 116.5}]}}}
|
- 关键字:geo_polygon- 逻辑:坐标在多边形区域内
|
筛选某行政区内的会员
|
1. 顶点数≤10 个2. 顶点按顺时针排列
|
|
geo_shape
|
{"geo_shape": {"member_location": {"shape": {"type": "circle", "coordinates": [116.4, 39.9], "radius": "5km"}}}}
|
- 关键字:geo_shape- 逻辑:坐标在几何图形内(圆 / 多边形等)
|
筛选圆形区域内的会员
|
1. 圆的性能优于复杂多边形2. 结合filter先筛城市
|
|
exists(字段存在)
|
{"exists": {"field": "member_location"}}
|
- 关键字:exists- 逻辑:存在地理位置信息
|
筛选有定位信息的会员
|
1. 用于区分是否授权定位2. 结合地理查询进一步筛选
|
1.9. IP 类型(ip)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
term(精确匹配)
|
{"term": {"login_ip": "192.168.1.100"}}
|
- 关键字:term- 逻辑:IP 地址完全匹配
|
筛选从特定 IP 登录的会员
|
1. 用filter缓存结果2. 适合异常登录检测
|
|
terms(多 IP 匹配)
|
{"terms": {"login_ip": ["192.168.1.100", "192.168.1.101"]}}
|
- 关键字:terms- 逻辑:IP 地址与数组中任意值匹配
|
筛选从多个 IP 登录的会员
|
1. 数组长度≤50(IP 字段匹配成本高)2. 用于批量 IP 查询
|
|
range(IP 段匹配)
|
{"range": {"login_ip": {"gte": "192.168.0.0", "lte": "192.168.255.255"}}}
|
- 关键字:range- 逻辑:IP 在指定网段内
|
筛选内网 IP 登录的会员
|
1. 比cidr更灵活(支持任意网段)2. 适合非标准网段查询
|
|
cidr(无类别域间路由)
|
{"term": {"login_ip": "192.168.0.0/16"}}
|
- 关键字:term(支持 CIDR 表示法)- 逻辑:IP 在 CIDR 网段内
|
筛选 192.168 网段登录的会员
|
1. 比range更简洁(标准网段)2. 性能优于range
|
|
exists(字段存在)
|
{"exists": {"field": "login_ip"}}
|
- 关键字:exists- 逻辑:存在登录 IP 记录
|
筛选有登录记录的会员
|
1. 用于区分是否登录过系统2. 结合 IP 查询分析地域分布
|
1.10. 范围类型(integer_range/double_range/date_range)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
term(包含值)
|
{"term": {"price_range": 50}}
|
- 关键字:term- 逻辑:范围字段包含指定值
|
筛选价格范围包含 50 元的商品(如 40-60 元)
|
1. 用于快速匹配值是否在范围内2. 比range更直观
|
|
range(范围重叠)
|
{"range": {"price_range": {"overlap": {"gte": 30, "lte": 70}}}}
|
- 关键字:range.overlap- 逻辑:两个范围存在重叠部分
|
筛选与 30-70 元有重叠的价格区间商品
|
1. 用于区间交叉分析2. 比手动计算重叠更高效
|
|
range(范围包含)
|
{"range": {"price_range": {"contains": {"gte": 40, "lte": 60}}}}
|
- 关键字:range.contains- 逻辑:范围字段完全包含目标范围
|
筛选完全包含 40-60 元的价格区间商品
|
1. 用于父区间查询2. 适合层级价格区间分析
|
|
exists(字段存在)
|
{"exists": {"field": "price_range"}}
|
- 关键字:exists- 逻辑:存在范围字段定义
|
筛选有价格区间的商品
|
1. 用于区分是否参与区间定价2. 结合范围查询分析定价策略
|
1.11. 混合类型(多类型组合查询)
|
查询类型
|
语法示例
|
关键字详细说明(匹配逻辑)
|
商超场景案例
|
优化点
|
|
text + 数值组合
|
{"bool": {"must": [{"match": {"product_name": "牛奶"}}, {"range": {"price": {"lte": 50}}}]}}
|
- 关键字:bool组合match和range- 逻辑:同时满足文本匹配和数值范围
|
搜索名称含 “牛奶” 且价格≤50 元的商品
|
1. 先执行range过滤(减少文本匹配数据量)2. 文本查询放must,数值放filter
|
|
嵌套 + 扁平化组合
|
{"bool": {"filter": [{"term": {"is_high_spender": true}}],"must": [{"nested": {"path": "tags", "query": {"term": {"tags.name": "高频次"}}}}]}}
|
- 关键字:bool组合扁平化字段和嵌套查询- 逻辑:先通过扁平化字段过滤,再执行嵌套查询
|
筛选 “高消费会员且含高频次标签”
|
1. 扁平化字段放filter(减少嵌套计算量)2. 高频条件用扁平化字段,低频用嵌套
|
|
地理 + 日期组合
|
{"bool": {"filter": [{"geo_distance": {"distance": "3km", "member_location": {"lat": 39.9, "lon": 116.4}}}, {"range": {"last_purchase_time": {"gte": "now-30d"}}}]}}
|
- 关键字:bool组合地理和日期查询- 逻辑:同时满足地理范围和日期范围
|
筛选 3 公里内且 30 天内有消费的会员
|
1. 先执行日期筛选(数据量更小)2. 地理查询放后面(计算成本高)
|
|
数组 + 布尔组合
|
{"bool": {"must": [{"term": {"tags": "促销"}}, {"term": {"is_on_sale": true}}]}}
|
- 关键字:bool组合数组和布尔查询- 逻辑:数组包含指定值且布尔字段为 true
|
筛选含 “促销” 标签且在售的商品
|
1. 布尔查询放filter(性能最优)2. 数组查询放must(需算分)
|
posted on
浙公网安备 33010602011771号