Elasticsearch DSL 的结果处理

一、sort 排序

Elasticsearch 的 sort 参数用于控制搜索结果的排序方式,支持按字段值、相关性评分(_score)或自定义脚本进行排序。

1.1 基础排序语法
  1. 按单个字段排序

    GET /index/_search
    {
      "sort": [
        { "price": "desc" }  // 按 price 字段降序排列
      ]
    }
    
  2. 按相关性评分排序
    默认情况下,结果按 _score(相关性评分)降序排列。若需显式指定:

    {
      "sort": [
        { "_score": "desc" }
      ]
    }
    
  3. 指定排序模式(多值字段)
    当字段为数组(多值字段)时,需通过 mode 指定排序依据的取值方式:

    • min:取最小值
    • max:取最大值
    • avg:取平均值
    • sum:取总和
    • median:取中位数
    {
      "sort": [
        {
          "prices": {
            "order": "asc",
            "mode": "avg"  // 按多值字段的平均值升序排序
          }
        }
      ]
    }
    
1.2 排序类型
  1. 字段值排序
    • 最常见的排序方式,基于字段值排序。
      {
        "sort": [
          { "price": { "order": "asc" } }
        ]
      }
      
  2. 特殊排序
    • _score:按相关性评分排序
      {
        "query": { "match": { "title": "elasticsearch" } },
        "sort": [
          "_score",
          { "date": "desc" }
        ]
      }
      
    • _doc:按索引顺序排序(性能最高)
      {
        "sort": "_doc"
      }
      
    • _shard_doc:按分片文档顺序排序
      {
        "sort": "_shard_doc"
      }
      
  3. 多值字段排序
    • 当字段有多个值时,可指定多个排序条件,按优先级依次排序:
    {
      "sort": [
        { "date": "desc" },   // 第一优先级:日期降序
        { "_score": "desc" }, // 第二优先级:相关性评分降序
        { "price": "asc" }    // 第三优先级:价格升序
      ]
    }
    
1.3 特殊字段的排序处理
  1. 文本字段(text)
    默认情况下,text 类型字段无法直接排序(因其被分词为多个词项)。需使用 keyword 子字段:

    {
      "sort": [
        { "title.keyword": "asc" }  // 按未分词的原始文本排序
      ]
    }
    
  2. 日期字段
    Elasticsearch 自动识别日期格式,支持按时间排序:

    {
      "sort": [
        { "create_time": "desc" }
      ]
    }
    
  3. 地理坐标字段
    按距离某个坐标的远近排序:

    {
      "sort": [
        {
          "_geo_distance": {
            "location": { "lat": 40.73, "lon": -74.1 }, // 目标坐标
            "order": "asc",                             // 按距离升序
            "unit": "km"                                // 距离单位(km/mi)
          }
        }
      ]
    }
    
1.4 处理缺失值(missing参数)

当字段缺失时,可指定默认值或排序规则:

  • missing: “_last”:缺失值的文档排在最后。
  • missing: “_first”:缺失值的文档排在最前。
  • missing: 100:为缺失字段赋予默认值(数值或日期)。
{
  "sort": [
    {
      "price": {
        "order": "asc",
        "missing": "_last"  // 缺失 price 的文档排在最后
      }
    }
  ]
}
1.5 脚本排序

使用 Painless 脚本动态计算排序值:

{
  "sort": [
    {
      "_script": {
        "type": "number",
        "script": {
          "source": "doc['price'].value * params.factor", // 按价格乘以系数排序
          "params": { "factor": 0.8 }
        },
        "order": "desc"
      }
    }
  ]
}
1.6 嵌套对象排序
  1. 基本嵌套排序

    {
      "sort": [
        {
          "comments.date": {
            "order": "desc",
            "nested": {
              "path": "comments"
            }
          }
        }
      ]
    }
    
  2. 带过滤的嵌套排序

    {
      "sort": [
        {
          "comments.date": {
            "order": "asc",
            "nested": {
              "path": "comments",
              "filter": {
                "term": { "comments.verified": true }
              }
            }
          }
        }
      ]
    }
    
1.7 性能优化建议
  1. 避免对 text 字段排序
    • 使用 keyword 子字段或开启 fielddata(需谨慎,可能占用内存)。
  2. 使用 doc_values 优化数值/日期排序
    • Elasticsearch 默认对非文本字段启用 doc_values,加速排序和聚合。
  3. 限制排序字段数量
    • 多字段排序会增加计算开销。

二、_source 返回字段控制

在 Elasticsearch 的 DSL 中,_source 参数用于控制返回的原始文档内容,类似于 SQL 中的 SELECT 字段列表。合理使用 _source 可以显著减少网络传输开销和提高查询性能。

2.1 _source 的核心功能

_source 是 Elasticsearch 存储的原始 JSON 文档。默认情况下,当你执行搜索时,Elasticsearch 会返回完整的 _source 文档。

  1. 返回完整文档(默认行为):

    • 返回所有字段(_source 包含完整 JSON 文档)。
    GET /products/_search
    {
      "query": { "match_all": {} }
    }
    
  2. 过滤返回字段:

    {
      "_source": ["title", "price"],  // 只返回 title 和 price 字段
      "query": { ... }
    }
    
  3. 排除特定字段:

    {
      "_source": {
        "excludes": ["description", "tags"]  // 排除 description 和 tags
      },
      "query": { ... }
    }
    
2.2 为什么需要控制 _source
  1. 减少网络传输:只返回需要的字段。
  2. 提高查询性能:减少数据序列化/反序列化开销。
  3. 安全性:隐藏敏感字段。
  4. 灵活性:可以重命名字段或添加脚本字段。
2.3 _source 的高级用法
  1. 动态字段过滤(Wildcards)

    • 使用通配符匹配字段名:
    {
      "_source": ["user.*", "*.timestamp"],  // 返回所有 user 子字段和以 .timestamp 结尾的字段
      "query": { ... }
    }
    
  2. 嵌套字段控制

    • 指定嵌套对象中的子字段:
    {
      "_source": ["author.name", "comments.content"],  // 返回嵌套字段
      "query": { ... }
    }
    
  3. 字段重命名(需结合 Script Fields)

    • 通过脚本修改返回字段名(非原生支持,需额外处理):
    {
      "_source": ["original_field"],
      "script_fields": {
        "new_name": { 
          "script": { "source": "doc['original_field'].value" }
        }
      }
    }
    
2.4 性能优化建议
  1. 限制返回字段

    • 适用场景:只需部分字段时(如前端列表页)。
    • 示例:
      {
        "_source": ["id", "name", "price"],
        "query": { ... }
      }
      
  2. 禁用 _source(谨慎使用)

    • 适用场景:仅需聚合结果或文档 ID 时。
    • 注意:禁用后无法获取原始文档内容!
      {
        "_source": false,  // 不返回 _source
        "query": { ... }
      }
      
  3. 结合 docvalue_fields 获取存储列

    • 适用场景:对非文本字段(如数值、日期)高效读取。
    • 示例:
      {
        "docvalue_fields": ["price", "create_time"],  // 从列存中获取
        "query": { ... }
      }
      

三、script_fields 脚本字段

在 Elasticsearch 的 DSL 中,script_fields 允许你通过脚本动态计算并返回新字段,这些字段不会存储在索引中,而是在查询时实时生成。

3.1 script_fields 基础
  1. 基本语法结构

    GET /index/_search
    {
      "script_fields": {
        "新字段名": {                  // 自定义字段名称
          "script": {
            "source": "脚本逻辑",      // Painless 脚本
            "params": { ... }         // 可选参数
          }
        }
      }
    }
    
  2. script_fields 的核心功能

    • 动态计算字段:基于已有字段值,通过脚本生成新字段。
    • 不修改原始文档:仅在查询结果中临时添加字段。
    • 支持复杂逻辑:使用 Painless 脚本语言实现条件判断、数学运算等。
3.2 基本用法
  1. 数值计算

    {
      "script_fields": {
        "total_price": {
          "script": {
            "source": "doc['price'].value * doc['quantity'].value"
          }
        }
      }
    }
    
  2. 条件判断

    {
      "script_fields": {
        "discount_label": {
          "script": {
            "source": """
              if (doc['price'].value > 1000) {
                return 'premium';
              } else {
                return 'standard';
              }
            """
          }
        }
      }
    }
    
  3. 字符串处理

    {
      "script_fields": {
        "short_title": {
          "script": {
            "source": "doc['title'].value.substring(0, 10) + '...'"  // 截取前10个字符
          }
        }
      }
    }
    
  4. 日期格式化

    {
      "script_fields": {
        "date_formatted": {
          "script": {
            "source": """
              LocalDateTime.ofInstant(
                Instant.ofEpochMilli(doc['timestamp'].value.millis), 
                ZoneId.of('UTC')
              ).format(DateTimeFormatter.ISO_DATE)
            """
          }
        }
      }
    }
    
3.3 高级用法
  1. 使用参数(params)

    {
      "script_fields": {
        "price_with_tax": {
          "script": {
            "source": "doc['price'].value * params.tax_rate",
            "params": {
              "tax_rate": 1.2
            }
          }
        }
      }
    }
    
  2. 处理空值

    {
      "script_fields": {
        "safe_division": {
          "script": {
            "source": """
              if (doc['divisor'].size() == 0 || doc['divisor'].value == 0) {
                return 0;
              }
              return doc['dividend'].value / doc['divisor'].value;
            """
          }
        }
      }
    }
    
  3. 访问嵌套字段

    {
      "script_fields": {
        "author_first_name": {
          "script": {
            "source": "doc['author.name'].value.splitOnToken(' ')[0]"
          }
        }
      }
    }
    
3.4 性能优化建议
  1. 避免高频使用:脚本计算会显著增加查询延迟。
  2. 优先使用 Doc Values:对非文本字段使用 doc[‘field’].value 比 _source.field 更高效。
  3. 参数化脚本:通过 params 传递变量,避免硬编码。
  4. 缓存脚本:对重复使用的脚本启用 “lang”: “painless” 并利用缓存。

四、highlight 高亮显示

在 Elasticsearch 的 DSL 中,highlight 功能用于标记搜索结果中与查询条件匹配的文本片段,通常以高亮、加粗或颜色变化的形式展示,帮助用户快速定位关键内容。

4.1 highlight 的核心功能
  1. 标记匹配文本:在返回的字段值中突出显示匹配的关键词。
  2. 支持多字段:可同时对多个字段进行高亮。
  3. 自定义样式:通过 HTML 标签或自定义标记包裹匹配内容。
4.2 基础用法
  1. 基本语法结构

    GET /index/_search
    {
      "query": { ... },  // 查询条件
      "highlight": {
        "fields": {
          "字段名": { ... }  // 高亮配置
        }
      }
    }
    
  2. 简单示例

    {
      "query": {
        "match": { "content": "Elasticsearch" }
      },
      "highlight": {
        "fields": {
          "content": {}
        }
      }
    }
    
4.3 常见配置参数
参数作用示例值
pre_tags匹配文本的前置标签(如 <em>["<strong>"]
post_tags匹配文本的后置标签(如 </em>["</strong>"]
number_of_fragments返回的文本片段数量(默认 5)3
fragment_size每个片段的最大字符数(默认 100)150
no_match_size无匹配时返回的字段前缀字符数(默认 0)50
fragment_size是否仅高亮查询中明确指定的字段(默认 true)false
4.4 典型使用场景
  1. 基础高亮(HTML 标签)

    {
      "query": { "match": { "content": "elasticsearch" } },
      "highlight": {
        "fields": {
          "content": {}  // 使用默认 <em> 标签
        }
      }
    }
    
  2. 自定义高亮标签

    {
      "highlight": {
        "pre_tags": ["<mark>"],
        "post_tags": ["</mark>"],
        "fields": {
          "title": {},
          "description": {}
        }
      }
    }
    
  3. 控制片段数量和长度

    {
      "highlight": {
        "fields": {
          "content": {
            "number_of_fragments": 3,  // 返回3个片段
            "fragment_size": 200       // 每个片段200字符
          }
        }
      }
    }
    
  4. 高亮所有字段(忽略查询字段限制)

    {
      "highlight": {
        "require_field_match": false,  // 高亮所有匹配字段
        "fields": { "*": {} }          // 通配符匹配所有字段
      }
    }
    
4.5 高级功能
  1. 多字段差异化配置

    {
      "highlight": {
        "fields": {
          "title": { "pre_tags": ["<b>"], "post_tags": ["</b>"] },
          "content": { "number_of_fragments": 0 }  // 返回完整字段(不分片)
        }
      }
    }
    
  2. 边界字符高亮(Boundary Scanners)

    • 控制分片边界(按句子或单词划分):
    {
      "highlight": {
        "fields": {
          "content": {
            "boundary_scanner": "sentence",  // 按句子分片
            "boundary_scanner_locale": "zh-CN"  // 中文分句
          }
        }
      }
    }
    
  3. 命中词排序

    • 按匹配词出现顺序排序片段:
    {
      "highlight": {
        "order": "score",  // 按相关性排序
        "fields": { "content": {} }
      }
    }
    
4.6 性能优化建议
  1. 限制高亮字段:避免对大型文本字段(如日志内容)高亮。
  2. 合理设置 fragment_size:根据前端展示需求调整片段长度。
  3. 谨慎使用通配符:“*” 可能意外高亮不必要字段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值