添加文档
单个添加
带有文档id
POST /products/_doc/1
{
"name": "iPhone 13",
"price": 799.99,
"category": "smartphone",
"in_stock": true
}
不带文档id
POST /products/_doc/
{
"name": "iPad Air",
"price": 599.99,
"category": "tablet",
"in_stock": true
}
不带id添加同一个文档,es会随机一个文档id,导致es中会有重复的数据
建议带上文档id:1不会导致es有重复的数据;2.便于根据文档id删除/修改数据
批量添加
POST /_bulk
{ "index" : { "_index" : "<index>", "_id" : "<id>" } }
{ "field1": "value1", "field2": "value2" }
{ "create" : { "_index" : "<index>", "_id" : "<id>" } }
{ "field1": "value1", "field2": "value2" }
...
删除文档
根据文档id删除
DELETE /products/_doc/1
使用查询删除多个文档
POST /products/_delete_by_query
{
"query": {
"range": {
"price": {
"lt": 100
}
}
}
}
修改文档
完全替换文档
POST /products/_doc/101
{
"name": "Mac Studio Pro", // 修改名称
"price": 2499.99, // 更新价格
"category": "desktop",
"new_field": "added" // 添加新字段
}
当某个属性为空时,es中也会被设置为空
局部更新文档
POST /products/_update/101
{
"doc": {
"price": 2199.99, // 只更新价格字段
}
}
并发更新同一个文档会有数据不一致的问题(和mysql类似,不用乐观锁同时更新库存会出现问题)
使用 version 或者
_seq_no
+_primary_term
作为更新条件,实现乐观锁方案(业务要解决由于乐观锁导致的更新失败问题)建议
_seq_no
+_primary_term:version存在
版本号回滚的问题(分片故障后恢复,可能版本号会从v1重新开始)、_seq_no
是全局增长的
查询文档
基础查询
基本查询语句
GET /<index>/_search
{
"query": {
// 查询条件
},
"from": 0, // 分页起始位置
"size": 10, // 返回结果数量
"sort": [ // 排序规则
{"field1": "asc"},
{"field2": "desc"}
],
"_source": ["field1", "field2"] // 指定返回字段
}
1.全文检索查询
match查询
{
"query": {
"match": {
"title": "iPhone 13"
}
}
}
- 对查询文本进行分词处理
- 适用于文本字段(text类型)
- 支持模糊匹配
iPhone 13使用match被分词后为iPhone、13 ,类似mysql的 name = iPhone or name =13
如果限制同时出现,需要 operator 设置为 and
{
"from": 0,
"size": 5,
"query": {
"match": {
"name": {
"query": "iPhone 13",
"operator": "and"
}
}
}
}
指定最少匹配个数
{
"from": 0,
"size": 5,
"query": {
"match": {
"name": {
"query": "iPhone 14",
"minimum_should_match": 2 -- 最少匹配的个数
}
}
}
}
match_phrase 查询
{
"query": {
"match_phrase": {
"title": "iPhone 13"
}
}
}
特点:
- 要求词语按顺序完整匹配
- 适合精确短语匹配
如果限制iphone 13之间只能有2个字符,则使用slop (slop默认为0)
{
"query": {
"match_phrase": {
"title": {
"query": "iPhone 14",
"slop": 2
}
}
}
}
multi_match 查询
{
"query": {
"multi_match": {
"query": "iPhone",
"fields": ["title", "description"]
}
}
}
特点:
- 在多个字段中搜索相同内容
- 支持字段权重设置
2.精确值查询
term 查询
{
"query": {
"term": {
"status": {
"value": "active"
}
}
}
}
特点:
- 精确匹配,不进行分词
- 适用于keyword类型字段
terms 查询
{
"query": {
"terms": {
"status": ["active", "pending"]
}
}
}
特点:
- 匹配多个精确值
- 相当于SQL中的IN操作
range 查询
{
"query": {
"range": {
"price": {
"gte": 100,
"lte": 1000
}
}
}
}
3.复合查询
bool查询
{
"query": {
"bool": {
"must": [
{ "match": { "title": "iPhone" } }
],
"must_not": [
{ "range": { "price": { "gt": 1000 } } }
],
"should": [
{ "match": { "color": "silver" } },
{ "match": { "color": "gold" } }
],
"filter": [
{ "term": { "in_stock": true } }
],
"minimum_should_match": 1 // should条件最少满足1个
}
}
}
子句类型:
must
: 必须匹配(贡献得分)should
: 应该匹配(贡献得分)must_not
: 必须不匹配(不贡献得分)filter
: 必须匹配(不贡献得分,性能更好)
同⼀层级下的竞争字段,具有有相同的权重
通过嵌套 bool 查询,可以改变对算分的影响
{
"query": {
"bool": {
"must": [
{ "match": { "title": "iPhone" } }
],
"must_not": [
{ "range": { "price": { "gt": 1000 } } }
],
"filter": [
{ "term": { "in_stock": true } }
],
"should": [
{
"bool": { // 此处的bool权重低于上一层的bool(影响排序)
"should": [
{
"match": {
"color": {
"query":"silver",
"boost":4 // 该处的boost为4,相对于gold,优先级更高
} }
},
{ "match": { "color": "gold" } }
],
"minimum_should_match": 1
}
}
]
}
}
}
boosting 查询
(可以在bool种使用 boost参数替代)
{
"query": {
"boosting": {
"positive": {
"match": { "title": "iPhone" }
},
"negative": {
"match": { "color": "red" }
},
"negative_boost": 0.5
}
}
}
特点:
- 降低匹配negative条件的文档得分
negative_boost
指定降权系数(0-1) demo含义:降低red颜色的权重
评分权重的使用场景:同时搜索title 与 description 时,title的优先级高(bool中使用boost或者直接使用boosting)
should使用场景:1个搜索框,同时搜索用户名、用户昵称
should的算分过程
- 查询 should 语句中的两个查询
- 加和两个查询的评分
- 乘以匹配语句的总数
- 除以所有语句的总数
4.特殊查询
exists 查询
{
"query": {
"exists": {
"field": "tags"
}
}
}
prefix 查询
{
"query": {
"prefix": {
"sku": "IPH"
}
}
}
- 查找以指定前缀开头的字段值
wildcard 查询
{
"query": {
"wildcard": {
"sku": "IPH*13"
}
}
}
*
: 匹配任意数量字符?
: 匹配单个字符
query string查询
性能问题
{
"query": {
"query_string": {
"query": "(title:elasticsearch OR content:query) AND date:[2020-01-01 TO 2020-12-31]",
"default_field": "content",
"default_operator": "AND"
}
}
}
5.高级查询优化
dis_max查询
Disjunction Max Query 优化搜索,使用单个最高的查询结果作为最终评分
should的执行原理:会对每一个匹配结果算分,然后加权比较。因此会导致某些情况下不准
从给定的多个查询(这里是针对
title
字段和body
字段的match
查询,查找包含“Brown fox”的内容)中,选择得分最高的单个查询结果作为最终得分,通常用于在多个可选查询条件中,希望突出单个最佳匹配的场景 。
该查询文档1排在了前面,但是文档2有连着的brown fox
PUT /blogs/_doc/1
{
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}
PUT /blogs/_doc/2
{
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}
-- 查询
GET /blogs/_search
{
"explain": false,
"query": {
"dis_max": { // 取最高分的子查询
"queries": [
{
"match": {
"title": "Brown pets"
}
},
{
"match": {
"body": "Brown pets"
}
}
],
"tie_breaker": 0.1 // 其他匹配的子查询分数会按比例(tie_breaker值)
}
}
}
-
默认行为(无 tie_breaker):
- 执行所有子查询
- 只取最高分的子查询结果
- 其他子查询的匹配情况不影响最终分数
-
使用 tie_breaker 时:
- 仍然取最高分的子查询
- 但其他匹配的子查询分数会按比例(tie_breaker值)加入最终分数
- 公式:
最终分数 = 最高分 + (其他匹配查询分数 * tie_breaker)
- tie_breaker 取值范围: 0.0 到 1.0
0.0
(默认): 只使用最高分0.1-0.4
: 适度考虑其他匹配1.0
: 等同于 bool should 查询(所有子查询分数相加)
Multi Match查询
单字符串多字段查询(一个输入框,搜索多个字段)
Best Fields类型
作用:使用最高分的单个字段匹配计算文档的相关性得分
适用场景:优先考虑在单个字段中匹配的最好的排在前面
案例:优先搜索标题,然后是描述
GET /blogs/_search
{
"query": {
"multi_match": { // 多字段查询
"type": "best_fields", // 寻找最佳匹配字段,优先使用得分最高的字段得分
"query": "无线蓝牙耳机", // 查询的内容
"fields": ["title^2", "description"], // 查询的多个字段
"tie_breaker": 0.2, // 其他匹配字段的得分会按 0.2 的比例加入最终得分
"minimum_should_match": "20%" // 至少要有 20% 的 should 子句(在 best_fields 类型的 multi_match 中隐含的 should 逻辑)匹配才能认为文档匹配该查询
}
}
}
- 使用Disjunction Max Query(DisMax)
- 可通过
tie_breaker
(0-1)调整其他字段的贡献度 - 对每个词项在所有字段中搜索,但只保留最佳匹配字段的分数
Most Fields类型
// 伪代码表示
score = match(field1) + match(field2) + ... + match(fieldN)
作用:将所有匹配字段的分数合并计算文档的相关性的分
使用场景:多个字段包含相同内容的变体(如:title使用不同的分词器生成子字段,搜索时要匹配所有的子字段)
适用于需要在多个字段中搜索相同内容并希望匹配最多字段的文档获得更高分数的场景
问题:搜索 barking dog,结果文档1在前。因为 match会将分词,english分词器会提取词干(barking -> bark, barks -> bark)。文档1匹配上bark后,又因为title短,因此算分比较高。
解决方案:添加子字段,使用stansard分词器(不会提取词干),在使用most_fields类型
优化前的代码
PUT /titles
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "english"
}
}
}
}
POST titles/_bulk
{ "index": { "_id": 1 } }
{ "title": "My dog barks" }
{ "index": { "_id": 2 } }
{ "title": "I see a lot of barking dogs on the road " }
GET titles/_search
{
"query": {
"match": {
"title": "barking dogs"
}
}
}
优化后的查询
DELETE /titles
PUT /titles
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "english"
},
"title.std": { "type": "text", "analyzer": "standard" }
}
}
}
POST titles/_bulk
{ "index": { "_id": 1 } }
{ "title": "My dog barks" }
{ "index": { "_id": 2 } }
{ "title": "I see a lot of barking dogs on the road " }
GET /titles/_search
{
"query": {
"multi_match": { // 多字段查询
"query": "barking dogs",
"type": "most_fields", // 匹配最多字段的文档获得更高分数的场景
"fields": [ "title^3", "title.std" ] // 匹配的字段 ^3表示权重为
}
}
}
- 适合同文本不同分析的场景
most_fields
查询比best_fields
慢,因为需要计算多个字段的匹配分数
Cross Field类型
作用:跨字段搜索,将多个字段视为一个大字段处理,解决"字段孤岛"问题
场景:搜索姓名时,将first name,lastname作为一个整体匹配
// 人员搜索:姓名可能分布在不同字段
{
"query": {
"multi_match": {
"query": "张 小明",
"type": "cross_fields",
"fields": ["first_name", "last_name"],
"operator": "and" // 满足 张小名 在文档中必须全部出现,而不是只有一个 张 就可以
}
}
}
- 支持
and
/or
逻辑控制
优化:使用copy to 方式,将first name,list name整合到一个字段 full name中,只搜索full name(会占用磁盘空间)
copy to
{
"mappings": {
"properties": {
"first_name": { "type": "text", "copy_to": "full_name" },
"last_name": { "type": "text", "copy_to": "full_name" },
"full_name": { "type": "text" }
}
}
}
对比
查询类型 | 匹配逻辑 | 评分策略 | 适用场景 | 性能特点 |
---|---|---|---|---|
best_fields | 每个词项在最佳匹配字段上查找 | 取最高分字段的分数(DisMax) | 查询词集中在单个字段最有效 | 较高 |
most_fields | 每个字段独立查询后合并结果 | 累加所有匹配字段的分数 | 同内容多字段索引(不同分析器) | 中等 |
cross_fields | 所有字段视为一个大字段 | 类似"字段中心"的全局评分 | 查询词分散在多个字段 | 较低 |
6.分页
基础分页
{
"from": 0,
"size": 10
}
存在深度分页问题
search_after分页
{
"size": 10,
"sort": [
{"price": "asc"},
{"_id": "asc"}
],
"search_after": [799.99, "doc123"]
}
特点:
- 适合深度分页
- 需要稳定的排序条件
聚合统计
Metric Aggregations 指标聚合
计算数值统计,如sum、avg、max等
单值指标聚合
聚合类型 | 作用 | 示例场景 |
---|---|---|
avg | 计算平均值 | 如计算键盘按键的平均点击次数 |
sum | 计算总和 | 统计所有显示器总运行时长 |
min | 获取最小值 | 找出卡通人物中最矮的身高 |
max | 获取最大值 | 找出键盘中最高的单键压力值 |
value_count | 统计非空值数量 | 计算正在使用的显示器数量 |
多值指标聚合
聚合类型 | 作用 | 示例场景 |
---|---|---|
stats | 一次性返回count, min, max, avg, sum | 分析键盘压力数据的基本统计量 |
extended_stats | 增加方差、标准差、平方和 等 | 深度分析显示器亮度的分布情况 |
percentiles | 计算百分位数(如P50、P99) | 找出接口响应时间的99%分位值 |
percentile_ranks | 计算值所处的百分位等级 | 判断某键盘压力值在所有数据中的位置 |
cardinality | 计算唯一值数量(近似去重) | 统计不同卡通人物角色的出现次数,类似distinct |
top_hits | 返回每组的前N条详细数据 |
获取每个键盘型号的最新使用记录; 统计每个商品分类下 销量最高的3个商品详情 |
-- 统计电影的投票(平均值、最大值、最小值、条数、总数)
GET /movies/_search
{
"size": 0,
"aggs": {
"title_stats": {
"stats": {
"field": "voteCount"
}
}
}
}
-- 计算投票百分位(99位,95位,50位等)
GET /movies/_search
{
"size": 0,
"aggs": {
"title_stats": {
"percentiles": {
"field": "voteCount"
}
}
}
}
Bucket Aggregations(桶聚合)
将文档分组到桶中
terms词项分桶
{
"aggs": {
"type_bucket": { // 自定义聚合名称
"terms": { // 聚合类型:词项
"field": "type", // 聚合字段
"size": 10 , // 查询的结果数量
"order": { "_count": "desc" }
}
}
}
}
根据商品类型分桶,查询10个桶数据
range范围分桶
{
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 50 },
{ "from": 50, "to": 100 },
{ "from": 100 }
]
}
}
}
}
按照价格分桶
时间分桶
{
"aggs": {
"date_ranges": {
"date_range": {
"field": "date",
"format": "yyyy-MM-dd",
"ranges": [
{ "to": "now-10M/M" },
{ "from": "now-10M/M", "to": "now" },
{ "from": "now" }
]
}
}
}
}
直方图聚合
固定间隔的数值分桶:
{
"aggs": {
"prices": {
"histogram": {
"field": "price",
"interval": 50
}
}
}
}
日期直方图聚合
使用date属性,按照月聚合
{
"aggs": {
"sales_over_time": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
}
}
}
}
分桶优化技巧
- size参数:控制返回的桶数量,默认是10
- shard_size:提高terms聚合的精确度
- execution_hint:可以设置为"map"来提高性能
- missing值处理:使用
missing
参数指定如何处理缺失值 - min_doc_count:过滤掉文档数少于指定值的桶
嵌套聚合
{
"aggs": {
"type_bucket": {
"terms": {
"field": "type"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}
先按照type分桶,再按照price分桶
Pipeline Aggregations(管道聚合)
对其他聚合结果进行二次聚合
Derivative Aggregation(导数聚合)
计算相邻桶之间的变化率
示例:计算每月销售额变化率
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"total_sales": {
"sum": { "field": "amount" }
},
"sales_derivative": {
"derivative": {
"buckets_path": "total_sales"
}
}
}
}
}
}
Cumulative Sum Aggregation(累积和聚合)
计算运行总和
示例:计算销售额累积和
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"monthly_sales": {
"sum": { "field": "amount" }
},
"cumulative_sales": {
"cumulative_sum": {
"buckets_path": "monthly_sales"
}
}
}
}
}
}
Moving Function Aggregation(移动函数聚合)
实现各种窗口函数计算(替代已弃用的moving_avg)
示例:3个月移动平均
GET /sales/_search
{
"size": 0,
"aggs": {
"sales_per_month": {
"date_histogram": {
"field": "date",
"calendar_interval": "month"
},
"aggs": {
"monthly_sales": {
"sum": { "field": "amount" }
},
"three_month_moving_avg": {
"moving_fn": {
"buckets_path": "monthly_sales",
"window": 3,
"script": "MovingFunctions.unweightedAvg(values)"
}
}
}
}
}
}
Matrix Aggregations(矩阵聚合):多字段计算
Aggregation聚合
算分与排序
默认会以⽂档的相关度算分进⾏排序
字段排序
不能对text类型的排序,常见的排序字段keyword、number、date类型
基本排序语法
GET /events/_search
{
"sort": [
{
"event_date": {
"order": "desc" // 或 "asc"
}
}
]
}
多级排序示例
{
"sort": [
{ "event_date": "asc" },
{ "_score": "desc" }
]
}
缺失值处理
{
"sort": [
{
"event_date": {
"order": "asc",
"missing": "_last", // 可选 "_first" 或指定默认值
"time_zone": "+08:00" // 时区设置
}
}
]
}
score排序
score排序规则:将每一个匹配项,与文档都会计算一个分值,将所有的分值相加,加权后得到最终的评分。
boost改变排序优先级
POST movies/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"title.keyword": {
"value": "Logan",
"boost": 2 //使用boost变更分值权重
}
}
}
],
"should": [
{
"nested": { // should中使用nested
"path": "genres",
"query": {
"term": {
"genres.name": {
"value": "Action",
"boost": 1 //使用boost变更分值权重
}
}
}
}
}
]
}
}
}
negative改变排序优先级
negative为负分
案例:电商搜索,Positive Boost给畅销商品;Negative Boost给缺货商品
基础用法
{
"query": {
"boosting": {
"positive": {
"match": { "title": "matrix" }
},
"negative": {
"match": { "title": "horror" }
},
"negative_boost": 0.5 // 匹配negative条件的文档分数会乘以0.5
}
}
}
结合nested使用
POST movies/_search
{
"query": {
"boosting": {
"positive": {
"nested": {
"path": "genres",
"query": {
"term": {
"genres.name": "Action"
}
}
}
},
"negative": {
"nested": {
"path": "genres",
"query": {
"term": {
"genres.name": "Adventure"
}
}
}
},
"negative_boost": 0.9
}
}
}
多级boosting
{
"query": {
"bool": {
"must": { "match": { "content": "elasticsearch" } },
"should": [
{
"boosting": {
"positive": { "term": { "tags": "important" } },
"negative": { "term": { "tags": "deprecated" } },
"negative_boost": 0.2
}
},
{ "term": { "category": "tutorial" } } // 额外的positive boost
]
}
}
}
Function Score排序
参考使用其他字段的值排序(例如将 “热度”和“点赞数”作为算分的参考因素)
主要依据还是:评分的相关度,Function Score作为评分的惨老因素
使用场景
- 热门内容提升(基于点赞数、收藏数)
- 商品排序加权(基于销量、利润率)
- 文档优先级调整(基于权重字段)
搜索物品时,根据价格外,还根据卖家的发货率,提升权重。
参考字段值排序
PUT /blogs/_doc/1
{
"title": "About popularity",
"content": "In this post we will talk about...",
"votes": 0,
"comment_num":0
}
PUT /blogs/_doc/2
{
"title": "About popularity",
"content": "In this post we will talk about...",
"votes": 50,
"comment_num":50
}
PUT /blogs/_doc/3
{
"title": "About popularity",
"content": "In this post we will talk about...",
"votes": 100,
"comment_num":100
}
-- 普通的查询
POST /blogs/_search
{
"query": {
"multi_match": {
"query": "popularity",
"fields": ["title", "content"]
}
}
}
-- 含有function_score的查询
POST /blogs/_search
{
"query": {
"function_score": {
"query": {"match": {"title": "笔记本电脑"}},
"functions": [ //允许使用文档中的数值字段值来调整相关性评分
{
"field_value_factor": {
"field": "votes", // 要使用的数值字段名
"factor": 0.2, // 可选,字段值的缩放因子(默认1)
"modifier": "log1p", // 可选,对字段值的计算方式
"missing": 1 // 可选,字段缺失时的默认值
}
},
{
"field_value_factor": {
"field": "comment_num",
"factor": 1,
"modifier": "none",
"missing": 1
}
}
],
"score_mode": "sum", // votes与 comment_num 的结果相加
"boost_mode": "multiply" // 函数计算的结果如何与原始查询分数(_score)进行组合
}
}
}
算分逻辑:
新的分数=老分数*(
log(1 + factor*votes数值) + factor*comment_num 数值
)
参数 | 作用层级 | 控制目标 |
---|---|---|
modifier | 计算方式 | 控制使用哪个数学公式计算 |
score_mode | 多个函数间的结果组合方式 | 决定functions数组内各函数如何合并 |
boost_mode | 函数组与原始查询的组合方式 | 决定最终分数与原始查询的混合关系 |
modifier 计算方式参数详解
修饰器 | 公式 | 适用场景 |
---|---|---|
none | value | 直接使用原始值(大数值需谨慎) |
log | log(value) | 缓和极大值的影响 |
log1p | log(1 + value) | 处理含0的值,避免log(0)错误 |
log2p | log(2 + value) | 更平缓的对数处理 |
ln | ln(value) | 自然对数 |
ln1p | ln(1 + value) | 自然对数版log1p |
ln2p | ln(2 + value) | 自然对数版log2p |
square | value² | 强化高价值内容 |
sqrt | √value | 弱化高价值内容 |
reciprocal | 1/value | 实现反向排序(值越小得分越高) |
注意:计算公式的平滑度,会对新的分数有影响。如:使用none模式,
老分数比较低,但是*votes后会非常大,导致 相关度低的反而排在前面
boost_mode
参数详解
模式 | 计算公式 | 适用场景 |
---|---|---|
multiply | 原始分 × 函数结果 (默认) | 常规加权场景(如热门内容提升) |
replace | 完全使用函数结果 | 完全自定义评分 |
sum | 原始分 + 函数结果 | 叠加式增强 |
avg | (原始分 + 函数结果)/2 | 平衡式影响 |
max | max(原始分, 函数结果) | 取最大值优先 |
min | min(原始分, 函数结果) | 保障最低分数 |
商品搜索加权(乘法模式)
"modifier": "log1p"
"boost_mode": "multiply"
效果:销量越高的商品排名越靠前,同时保持关键词相关性
广告置顶(替换模式)
效果:广告商品固定高分,非广告商品得分为0
{
"query": {
"function_score": {
"query": {"match_all": {}},
"functions": [
{
"filter": {"term": {"is_promoted": true}},
"weight": 100
}
],
"boost_mode": "replace"
}
}
}
时效性内容(加法模式)
{
"query": {
"function_score": {
"query": {"match": {"content": "科技新闻"}},
"functions": [
{
"exp": {
"publish_date": {
"origin": "now",
"scale": "7d"
}
}
}
],
"boost_mode": "sum"
}
}
}
效果:新发布的内容获得时间衰减加分
随机值排序
random_score实现可重复的随机排序,即在保持随机性的同时,确保同一用户多次请求能得到相同顺序的结果
做:千人千面、推荐系统
{
"query": {
"function_score": {
"functions": [
{
"random_score": {
"seed": "用户ID或固定值", // 确保一致性关键
"field": "_seq_no" // 可选,基于文档字段值增强随机性
}
}
],
"boost_mode": "replace" // 完全用随机分替换原始分
}
}
}
1.个性化推荐(千人千面)
{
"query": {
"function_score": {
"query": {"term": {"category": "电子产品"}},
"functions": [
{
"random_score": {
"seed": "user_12345" // 每个用户固定种子
}
}
],
"boost_mode": "multiply" // 保持相关性基础上随机
}
}
}
2. A/B测试分组
{
"query": {
"function_score": {
"functions": [
{
"random_score": {
"seed": "session_id_67890" // 按会话固定分组
}
}
],
"boost_mode": "replace"
}
}
}
3. 分页随机但不重复
{
"query": {
"function_score": {
"functions": [
{
"random_score": {
"seed": "2023-08-page2", // 页码作为种子部分
"field": "_seq_no"
}
}
]
}
},
"from": 0,
"size": 10
}
查询性能优化
避免算分
-- 变更前 --
{
"query": {
"term": {
"name.keyword": "iPhone 14"
}
}
}
-- 使用 constant_score 避免算分 --
{
"query": {
"constant_score": {
"filter": {
"term": {
"name.keyword": "iPhone 14"
}
}
}
}
}
--使用 bool 查询filter 避免算分 --
{
"query": {
"bool": {
"filter": [
{
"term": {
"name.keyword": "iPhone 14"
}
}
]
}
}
}
只返回需要的字段
合理使用_source
- 避免脚本排序:尽量使用字段排序
- 使用index_prefixes:优化前缀查询性能
- 合理设置分片数:避免过多或过少
- 使用routing:定向查询到特定分片
- 预热查询缓存:对常用查询进行预热
其他
从多个索引查询
GET /product,product_1/_search
{
"query": {
"match_all": {}
}
}
GET /product*/_search
{
"query": {
"match_all": {}
}
}
解决结构化查询 – “包含⽽不是相等”的问题
优化用户使用
分析用户的行为(记录搜索之后的点击率、搜索结果为空),然后分析
高亮显示
POST movies/_search
{
"_source": ["title", "overview"],
"query": {
"multi_match": {
"query": "basketball with cartoon aliens",
"fields": ["title", "overview"]
}
},
"highlight": {
"fields": {
"overview": {
"pre_tags": ["<em>"],
"post_tags": ["</em>"]
},
"title": {
"pre_tags": ["<em>"],
"post_tags": ["</em>"]
}
}
}
}
需要前端渲染
解耦程序gong & 搜索 DSL
问题:java开发工程师、ES搜索工程师 是不同的人群时,要优化搜索,搜索工程师优化search后,开发人员需要修改代码,导致耦合。
解决方案:使用search模板,开发人员只需要调用模板即可,不需要了解具体的es查询逻辑。(即便java、es开发是同一个人,解耦后也能避免更改es搜索结构时,改动java代码,重新部署)
创建模板
(模板没有定义是那么索引的,因此在使用模板时,需要带上所有名)
POST _scripts/template_name // template_name需要自己定义
{
"script": {
"lang": "mustache", // 使用mustache模板语言来处理脚本模板,如占位符
"source": {
"query": {
"multi_match": {
"query": "{{query}}", // query参数
"fields": ["title", "overview"]
}
},
"size": "{{size}}", // size参数
"_source": ["title", "overview"]
}
}
}
使用模板
GET movies/_search/template // movies 是索引名
{
"id": "template_name", // template_name 是定义的模板名称
"params": {
"query": "basketball with cartoon aliens",
"size": 10
}
}
使用别名实现零停机维护索引
修改字段类型时,es不支持直接修改,需要重建索引,而重建索引就要换索引名字。如果不使用别名,就要停机维护
问题:如何解决索引变更时的数据变化