Elasticsearch

elasticsearch 是一款非常强大的开源搜索引擎,可以帮助我们从海量数据中快速找到需要的内容。是整个 ELK 技术栈的核心,负责存储、搜索、分析数据。

elasticsearch 结合 kibana、Logstash、Beats,也就是 elastic stack(ELK)。被广泛应用在日志数据分析、实时监控等。

一 、核心概念

1.倒排索引

对于传统的数据库是采用正向索引,一般以 id 创建索引,查询数据的效率比较差。而Elasticsearch 采用倒排索引。

倒排索引:对文档内容分词,对词条创建索引,并记录词条所在文档的信息。查询时先根据词条查询到文档 id,而后获取到文档。

文档(document):每条数据就是一个文档。

词条(term):文档按照语义分成的词语。

elasticsearch 是面向文档存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为 json 格式后存储在 elasticsearch 中。

索引(index):相同类型的文档的集合。

映射(mapping):索引中文档的字段约束信息,类似表的结构约束。

2.分词器

es 在创建倒排索引时需要对文档分词;在搜索时,需要对用户输入内容分词。但默认的分词规则对中文处理并不友好。

这里提供了 ik 分词器,ik 分词器支持中文的处理。

ik 分词器有两种模式:

ik_smart 最少切分粗粒度占用内存少 搜索到的概率比较小。

ik_max_word 最细切分细粒度 占用内存多 搜索到的概率大。

ik 分词器同时也提供了拓展词库的配置,只需要修改一个 ik 分词器目录中的 config 目录中的IkAnalyzer.cfg.xml 文件:

XML 复制代码
<?xml version="1.O" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
  
  <comment>IK Analyzer 扩展配置</comment>
  <!-- 用户可以在这里配置自己的扩展字典 *** 添加扩展词典 -->
    <entry key="ext_dbct">ext.dic</entry>
  <!-- 用户可以在这里配置自己的扩展停止词字典 *** 添加停用词词典 -->
<entry key="ext_stopwords">stopword.dic</entry>
  
</properties>

这里需要创建 ext.dic 文件 和 stopword.dic 文件;

保存完配置后重启 es 。

二、DSL 语句

Elasticsearch 通过 DSL 语句来进行一系列的操作。

常用的操作有对索引库和文档的操作以及对数据的相关操作。

1. 索引库操作

mapping 属性:mapping 是对索引库中文档的约束,常见的 mapping 属性包括:

(1) type:字段数据类型,常见的简单类型有:

字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)

数值:long、integer、short、byte、double、float

布尔:boolean

日期:date

对象:object

(2) index:是否创建索引,默认为 true

(3) analyzer:使用哪种分词器

(4) properties:该字段的子字段

创建索引库操作:

css 复制代码
PUT /索引库名称
{
  "mappings":{
    "properties":{
      "字段名1":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "字段名2":{
        "type": "keyword",
        "index": false
      },
      "字段名3":{
        "properties":{
          "子字段":{
            "type": "keyword"
          }
        }
      },
      // ... 略
    }}}

索引库其他的操作:

css 复制代码
# 查看索引库
GET /索引库名

# 删除索引库
DELETE /索引库名

# es默认不支持索引库的修改
# 索引库和mapping一旦创建无法修改,但是可以添加新的字段
PUT /索引库名/_mapping
{
"properties":{
  "新字段名":{
  "type": "integer"
}
}}

2.文档操作

新增文档:

css 复制代码
POST /索引库名/_doc/文档id
{
  "字段1": "值1",
  "字段2": "值2",
  "字段3": {
       "子属性1":"值3",
       "子属性2": "值4"
  },
  // ..
}

其他文档操作:

css 复制代码
# 查询文档
GET /"索引库名"/_doc/"文档id"

# 删除文档
DELETE /"索引库名"/_doc/"文档id"

修改文档:

css 复制代码
# 方式一:全量修改,会删除旧文档,添加新文档
# 用 PUT 请求方式
PUT /"索引库名"/_doc/"文档id"
{
"字段1": "值1",
"字段2": "值2",
//...略
}

# 方式二:增量修改,修改指定字段值
# 用 POST 请求方式
POST /"索引库名"/_update/"文档id"
{
"doc":{
"字段名": "新的值",
  // ...
  }
}

3.数据操作

DSL 查询方式:

Elasticsearch 提供基于 JSON 的 DSL(Domain Specific Language) 来定义查询。常见的查询类型包括:

**1. 查询所有:**查询出所有的数据,一般测试用。例如:match_all

**2. 全文检索(full text)查询:**利用分词器对用户输入的内容分词,然后去倒排索引库中匹配。例如:match_query、multi_match_query

**3. 精确查询:**根据精确词条值查找数据,一般是查找 keyword、数值、日期、boolean 等类型字段。例如:ids、range、term

**4.地理(geo)查询:**根据经纬度查询。例如:geo_distance、geo_bounding_box

**5.复合(compound)查询:**复合查询可以将上述各种条件组合起来,合并查询条件。例如:

bool、function_score

查询的基本语法:

css 复制代码
# 基本语法 
GET /indexName/_search
{
  "query": {
    "查询类型": {
      "查询条件": "条件值"
    }
  }
}

# 查询所有
GET /indexName/_search
{
  "query": {
    "match_all": {
    }
  }
}
全文检索查询

推荐使用 match 查询,利用 copy_to 将多个字段整合到一个字段中。

css 复制代码
# match查询:会对用户输入的内容进行分词,然后去倒排索引库检索
GET /indexName/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT"   # FIELD为查询字段 TEXT为查询内容
    }
  }
}
// 示例:查询 title 字段中包含 "Elasticsearch 入门" 的文档
GET /goods/_search
{
  "query": {
    "match": {
      "title": "Elasticsearch 入门"
    }
  }
}

# multi_match查询:允许同时查询多个字段
GET /indexName/_search
{
  "query": {
    "multi_match": {
      "query": "TEXT",
      "fields": ["FIELD1", "FIELD2"]
    }
  }
}
// 示例:在 title 和 content 字段中同时搜索 "Elasticsearch"
GET /goods/_search
{
  "query": {
    "multi_match": {
      "query": "Elasticsearch",
      "fields": ["title", "content"]
    }
  }
}
精确查询

一般是查找 keyword、数值、日期、bool,不会对搜索条件分词。

term:根据词条精确值查询。 range:根据值的范围查询。

css 复制代码
# term查询
GET /indexName/_search
{
  "query": {
    "term": {
      "FIELD": {    # 所要查询的字段
        "value": "VALUE"
      }
    }
  }
}
// 示例:查询 status 字段值为 "ON_SALE" 的商品
GET /goods/_search
{
  "query": {
    "term": {
      "status": {
        "value": "ON_SALE"
      }
    }
  }
}

# range 查询  gte是>=  lte是<=  gt是> lt是<
GET /indexName/_search
{
  "query": {
    "range": {
      "FIELD": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}
// 示例:查询价格在 10 到 20 之间(含两端)的商品
GET /goods/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 10,
        "lte": 20
      }
    }
  }
}
地理查询

**geo_bounding_box:**查询 geo_point 值落在某个矩形范围的所有文档

**geo_distance:**查询到指定中心点小于某个距离值的所有文档

css 复制代码
# geo_bounding_box查询
GET /indexName/_search
{
  "query": {
    "geo_bounding_box": {
      "FIELD": {        # 此查询的字段
        "top_left": {   # 矩形左上角的纬度经度
          "lat": 31.1,
          "lon": 121.5
        },
        "bottom_right": {  # 矩形右下角的纬度经度
          "lat": 30.9,
          "lon": 121.7
        }
      }
    }
  }
}

# 查询距离中心点(31.21,121.5)15公里内的店铺
GET /shop/_search
{
  "query": {
    "geo_distance": {
      "distance": "15km",
      "location": "31.21,121.5"  # 指定纬度,经度
    }
  }
}

基于算分的查询:

相关性算分

当我们利用 match 查询时,文档结果会根据与搜索词条的关联度打分(score),返回结果时按照分值降序排列。

TF-IDF:在elasticsearch5.0之前,会随着词频增加而越来越大

BM25:在elasticsearch5.0之后,会随着词频增加而增大,但增长曲线会趋于水平

FunctionScoreQuery(复合查询)

使用 FunctionScoreQuery,可以修改文档的相关性算分(query score),根据新得到的算分排序。

BooleanQuery(复合查询)

布尔查询是一个或多个查询子句得组合。子查询的组合方式有:

注意:参与算分的条件越多,查询的性能就会越差。

**must:**必须匹配每个子查询,类似"与"

**should:**选择性匹配子查询,类似"或"

**must_not:**必须不匹配,不参与算分,类似"非"

**filter:**必须匹配,不参与算分

css 复制代码
# 查询含义:查找上海地区、价格高于 500 元、评分≥45 分的酒店,
# 优先展示品牌为 "皇冠假日" 或 "华美达" 的结果(匹配这两个品牌的酒店得分更高,排在前面)
GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {"term": {"city": "上海" }}
      ],
      "should": [
        {"term": {"brand": "皇冠假日" }},
        {"term": {"brand": "华美达" }}
      ],
      "must_not": [
        { "range": { "price": { "lte": 500 } }}
      ],
      "filter": [
        { "range": {"score": { "gte": 45 } }}
      ]
    }
  }
}

搜索结果处理(排序、分页、高亮)

排序语法

elasticsearch 支持对搜索结果排序,默认是根据相关度算分(score)来排序。可以排序字段类型有:keyword 类型(字符串,字符顺序排序)、数值类型、地理坐标类型、日期类型等。

css 复制代码
# 数字、日期、keyword
GET /indexName/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [   # sort里可对多个字段排序
    {
      "score": "desc"  # 排序字段和排序方式ASC、DESC
    },
    {
      "price" : "asc"
    }
  ]
}

# 地理坐标
GET /indexName/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "_geo_distance": {
        "FIELD": "纬度, 经度",
        "order": "asc",
        "unit": "km"
      }
    }
  ]
}
分页语法

elasticsearch 默认情况下只返回 Top 10 的数据。而如果要查询更多数据就需要修改分页参数了。elasticsearch 中通过修改 from、size 参数来控制要返回的分页结果:

css 复制代码
GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "from": 990, // 分页开始的位置,默认为0
  "size": 10,  // 期望获取的文档总数
  "sort": [
    {
      "price": "asc"
    }
  ]
}
高亮显示

**高亮:**就是在搜索结果中把搜索关键字突出显示。

高亮原理:(ES 中实现的)

1)将搜索结果中的关键字用标签标记出来

2)在页面中给标签添加 css 样式

css 复制代码
# 高亮查询,默认情况下,ES搜素字段必须与高亮字段一致
GET /hotel/_search
{
  "query": {
    "match": {
      "FIELD": "TEXT"   # 需指定为关键字
    }
  },
  "highlight": {
    "fields": {
      "FIELD": {             # 指定高亮字段
        "pre_tags": "<em>",  # 用来标记高亮字段的前置标签
        "post_tags": "</em>", # 用来标记高亮字段的后置标签
        "require_field_match" : "false"  # 该字段用于 需不需要字段匹配(即高亮字段与搜索字段)
      }                                  # 比如搜索all 高亮字段为name 此时便可指定为false
    }
  }
}

Elasticsearch 的 DSL 语句其实就类比于 MySQL 的 SQL 语句嘛,也是易于理解的。而 SQL 语句可以写在 Java 中,那么 DSL 语句是否也可以写到 Java 中呢?

相关推荐
薪火铺子1 小时前
ElasticSearch 聚合查询与性能优化实战
大数据·elasticsearch·性能优化
CableTech_SQH1 小时前
F5G全光网络二层扁平架构技术拆解:OLT+ODN+ONU全链路原理详解
大数据·网络·5g·信息与通信
2601_949936961 小时前
2026年职业资格证书趋势分析:专业化与数字化融合视角
大数据
团象科技2 小时前
当出海合规压力持续上升时,多云服务容易忽略哪些细节
大数据·微服务·架构
AC赳赳老秦2 小时前
故障自愈实战:用 OpenClaw 实现服务器日志自动化分析、根因定位、解决方案自动生成
大数据·运维·服务器·自动化·github·deepseek·openclaw
大大大大晴天️2 小时前
深入理解Flink 算子链:原理机制与优化实践
大数据·flink
The Open Group3 小时前
AI智能体时代,如何构建数字化架构以实现持续成功
大数据·人工智能·架构
Elastic 中国社区官方博客3 小时前
将 Logstash Pipeline 从 Azure Event Hubs 迁移到 OTel Collector Kafka Receiver
大数据·数据库·人工智能·分布式·elasticsearch·搜索引擎·kafka
Elastic 中国社区官方博客3 小时前
使用 Elasticsearch 与 Kibana 中的 PromQL 调查 Kubernetes 基础设施问题
大数据·数据库·elasticsearch·搜索引擎·信息可视化·kubernetes·全文检索