Elasticsearch入门使用

#ES的部分原理

默认打分规则

默认相关性评分 (Relevance Score)
默认使用 BM25 算法(可以看作是 TF-IDF 的升级版)。它会综合考虑:

  • 词频 (Term Frequency):关键词在文档中出现的次数。
  • 逆文档频率 (Inverse Document Frequency):关键词在所有文档中的稀有程度。
  • 字段长度 (Field Length):字段越短,匹配到的权重越高。

#ES的使用
##索引库操作
索引库就类似数据库表,mapping映射就类似表的结构。
我们要向es中存储数据,必须先创建“库”和“表”。

1. Mapping映射属性

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

  • type:字段数据类型,常见的简单类型有:
    • 字符串:text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址)

      keyword类型只能整体搜索,不支持搜索部分内容

    • 数值:long、integer、short、byte、double、float、
    • 布尔:boolean
    • 日期:date
    • 对象:object
  • index:是否创建索引,默认为true
  • analyzer:使用哪种分词器
  • properties:该字段的子字段

例如下面的json文档:

1
2
3
4
5
6
7
8
9
10
11
12
{
    "age": 21,
    "weight": 52.1,
    "isMarried": false,
    "info": "真相只有一个!",
"email": "zy@itcast.cn",
"score": [99.1, 99.5, 98.9],
    "name": {
        "firstName": "柯",
        "lastName": "南"
    }
}

对应的每个字段映射(mapping):

  • age:类型为 integer;参与搜索,因此需要index为true;无需分词器
  • weight:类型为float;参与搜索,因此需要index为true;无需分词器
  • isMarried:类型为boolean;参与搜索,因此需要index为true;无需分词器
  • info:类型为字符串,需要分词,因此是text;参与搜索,因此需要index为true;分词器可以用ik_smart
  • email:类型为字符串,但是不需要分词,因此是keyword;不参与搜索,因此需要index为false;无需分词器
  • score:虽然是数组,但是我们只看元素的类型,类型为float;参与搜索,因此需要index为true;无需分词器
  • name:类型为object,需要定义多个子属性- name.firstName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器
    • name.lastName;类型为字符串,但是不需要分词,因此是keyword;参与搜索,因此需要index为true;无需分词器

2. 索引库的CRUD

CRUD简单描述:

  • 创建索引库:PUT /索引库名
  • 查询索引库:GET /索引库名
  • 删除索引库:DELETE /索引库名
  • 修改索引库(添加字段):PUT /索引库名/_mapping

这里统一使用Kibana编写DSL的方式来演示。

2.1 创建索引库和映射

基本语法:

  • 请求方式:PUT
  • 请求路径:/索引库名,可以自定义
  • 请求参数:mapping映射

格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PUT /索引库名称
{
  "mappings": {
    "properties": {
      "字段名":{
        "type""text",
        "analyzer""ik_smart"
      },
      "字段名2":{
        "type""keyword",
        "index""false"
      },
      "字段名3":{
        "properties": {
          "子字段": {
            "type""keyword"
          }
        }
      },
// ...略
    }
  }
}

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
PUT /conan
{
"mappings": {
"properties": {
"column1":{
"type": "text",
"analyzer": "ik_smart"
},
"column2":{
"type": "keyword",
"index": "false"
},
"column3":{
"properties": {
"子字段1": {
"type": "keyword"
},
"子字段2": {
"type": "keyword"
}
}
},
// ...
}
}
}

2.2 查询索引库

基本语法

  • 请求方式:GET
  • 请求路径:/索引库名
  • 请求参数:无

格式

1
GET /索引库名

2.3 修改索引库

这里的修改是只能增加新的字段到mapping中

倒排索引结构虽然不复杂,但是一旦数据结构改变(比如改变了分词器),就需要重新创建倒排索引,这简直是灾难。因此索引库一旦创建,无法修改mapping
虽然无法修改mapping中已有的字段,但是却允许添加新的字段到mapping中,因为不会对倒排索引产生影响。
语法说明

1
2
3
4
5
6
7
8
PUT /索引库名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}

2.4 删除索引库

语法:

  • 请求方式:DELETE
  • 请求路径:/索引库名
  • 请求参数:无

格式:

1
DELETE /索引库名

文档操作

文档操作有哪些?

  • 创建文档:POST /{索引库名}/_doc/文档id
  • 查询文档:GET /{索引库名}/_doc/文档id
  • 删除文档:DELETE /{索引库名}/_doc/文档id
  • 修改文档:- 全量修改:PUT /{索引库名}/_doc/文档id
    • 增量修改:POST /{索引库名}/_update/文档id { “doc”: {字段}}

1. 文档的CRUD

1.1 新增文档

语法:

1
2
3
4
5
6
7
8
9
10
POST /索引库名/_doc/文档id
{
    "字段1""值1",
    "字段2""值2",
    "字段3": {
        "子属性1""值3",
        "子属性2""值4"
    },
// ...
}

示例:

1
2
3
4
5
6
7
8
9
POST /heima/_doc/1
{
    "info""真相只有一个!",
    "email""zy@itcast.cn",
    "name": {
        "firstName""柯",
        "lastName""南"
    }
}

1.2 查询文档

根据rest风格,新增是post,查询应该是get,不过查询一般都需要条件,这里我们把文档id带上。
语法:

1
2
3
GET /{索引库名称}/_doc/{id}
//批量查询:查询该索引库下的全部文档
GET /{索引库名称}/_search

通过kibana查看数据:

1
GET /heima/_doc/1

1.3 删除文档

删除使用DELETE请求,同样,需要根据id进行删除:
语法:

1
DELETE /{索引库名}/_doc/id值

示例:

1
2
# 根据id删除数据
DELETE /heima/_doc/1

1.4 修改文档

修改有两种方式:

  • 全量修改:直接覆盖原来的文档
  • 增量修改:修改文档中的部分字段
1.4.1 全量修改

全量修改是覆盖原来的文档,其本质是:

  • 根据指定的id删除文档
  • 新增一个相同id的文档

注意:如果根据id删除时,id不存在,第二步的新增也会执行,也就从修改变成了新增操作了。
语法:

1
2
3
4
5
6
PUT /{索引库名}/_doc/文档id
{
    "字段1""值1",
    "字段2""值2",
// ...
}

示例:

1
2
3
4
5
6
7
8
9
PUT /heima/_doc/1
{
    "info""黑马程序员高级Java讲师",
    "email""zy@itcast.cn",
    "name": {
        "firstName""云",
        "lastName""赵"
    }
}
1.4.2 增量修改

增量修改是只修改指定id匹配的文档中的部分字段。
语法:

1
2
3
4
5
6
POST /{索引库名}/_update/文档id
{
    "doc": {
"字段名""新的值",
}
}

示例:

1
2
3
4
5
6
POST /heima/_update/1
{
  "doc": {
    "email""ZhaoYun@itcast.cn"
  }
}

DSL基本使用

第二部分:在 Kibana 中编写 DSL 的基本语法

Kibana 的 Dev Tools (开发工具) 提供了一个控制台,可以让你直接编写和发送 DSL 查询给 Elasticsearch。

1. 基本查询结构

一个典型的搜索请求结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
GET /your_index_name/_search
{
"query": {
// 查询子句放在这里
},
// 其他选项,如排序、分页、聚合等
"from": 0,
"size": 10,
"sort": [
{ "timestamp": "desc" }
]
}
  • GET /your_index_name/_search: 请求方法是 GET,路径是你的索引名加上 _search 端点。
  • query: 最核心的部分,定义了你的搜索条件。
  • from: 从第几条结果开始返回,用于分页(从0开始)。
  • size: 返回多少条结果,用于分页。
  • sort: 定义结果的排序规则。

2. 常用查询子句 (Query Clauses)

a. match_all: 匹配所有文档

最简单的查询,返回索引中的所有文档。

1
2
3
4
5
6
GET /logs/_search
{
"query": {
"match_all": {}
}
}
b. match: 全文搜索

用于标准的全文搜索。它会先对查询字符串进行分词 (Analyze),然后去匹配分词后的词条。

  • 场景: 搜索日志消息或文章内容。
1
2
3
4
5
6
7
8
GET /logs/_search
{
"query": {
"match": {
"message": "connect to database"
}
}
}

这个查询会找到 message 字段中包含 “connect”、”to” 或 “database” 的文档,并根据相关性评分排序。

c. multi_match: 多字段全文搜索

match 查询的升级版,允许你用一个关键词同时搜索多个字段

  • 场景: 在电商网站搜索“笔记本电脑”,希望同时匹配商品 titledescription 字段。
1
2
3
4
5
6
7
8
9
GET /products/_search
{
"query": {
"multi_match": {
"query": "笔记本电脑",
"fields": ["title", "description"]
}
}
}
d. match_phrase: 短语匹配

match 不同,它要求所有词条必须以相同的顺序紧邻出现。

  • 场景: 搜索一个完整的句子或特定短语,例如 “真相只有一个”。
1
2
3
4
5
6
7
8
GET /conan/_search
{
"query": {
"match_phrase": {
"info": "真相只有一个"
}
}
}
e. term: 精确匹配

用于匹配未经分词的精确值。通常用于 keywordintegerboolean 等类型的字段。

  • 场景: 查找特定状态码、标签或ID。
1
2
3
4
5
6
7
8
GET /logs/_search
{
"query": {
"term": {
"level.keyword": "ERROR"
}
}
}

注意: 如果字段类型是 text,它会被分词。例如 “user-service” 可能会被分成 “user” 和 “service”。如果你想对整个 “user-service” 进行精确匹配,需要查询它的 .keyword 子字段(如果 mapping 中定义了的话)。

f. terms: 多个精确匹配 (OR)

term 的复数形式,匹配字段值在给定数组中的任意一个。

1
2
3
4
5
6
7
8
GET /logs/_search
{
"query": {
"terms": {
"service.keyword": ["user-service", "order-service"]
}
}
}
g. range: 范围查询

用于数字、日期或字符串范围的查询。

  • gt: 大于 (greater than)
  • gte: 大于等于 (greater than or equal to)
  • lt: 小于 (less than)
  • lte: 小于等于 (less than or equal to)
1
2
3
4
5
6
7
8
9
10
11
json复制GET /logs/_search
{
"query": {
"range": {
"timestamp": {
"gte": "now-1h", // 大于等于1小时前
"lt": "now" // 小于现在
}
}
}
}
h. bool: 组合查询 (布尔查询)

这是最强大、最常用的查询。它允许你将多个查询子句组合起来,实现复杂的逻辑。

  • must: AND 逻辑。所有子句都必须匹配,并且会计算相关性得分。
  • should: OR 逻辑。至少有一个子句需要匹配。
  • must_not: NOT 逻辑。所有子句都不能匹配。
  • filter: AND 逻辑,但它在“过滤上下文”中执行。它不计算相关性得分,并且结果可以被缓存,因此性能更高。非常适合用于精确匹配的过滤条件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /logs/_search
{
"query": {
"bool": {
"must": [
{ "match": { "message": "database" } }
],
"filter": [
{ "term": { "level.keyword": "ERROR" } },
{ "range": { "timestamp": { "gte": "now-24h" } } }
]
}
}
}

3. 进阶查询与相关性控制

以下查询子句提供了更精细的控制能力,用于优化搜索结果的相关性或处理特殊场景。

a. function_score: 自定义相关性得分

这是 Elasticsearch 的一个核心高级功能,它允许你修改一个查询产生的原始 _score。你可以引入业务相关的因子来影响最终排序。

  • 场景:1. 新发布的文章应该有更高的排名。
    1. 点赞数、评论数越多的商品,排名越靠前。
    2. 距离用户当前位置越近的店铺,排名越靠前。

下面的例子中,我们在 match 查询的基础上,将文档的 likes (点赞数) 作为一个因子来提升最终得分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /articles/_search
{
"query": {
"function_score": {
"query": { "match": { "content": "elasticsearch" } },
"field_value_factor": {
"field": "likes",
"modifier": "log1p",
"factor": 0.1
},
"boost_mode": "sum"
}
}
}
b. boosting: 降权查询

当你需要匹配一些内容,但希望其中某些特定条件会降低其相关性得分时使用。

  • 场景: 搜索“苹果”,但希望包含“手机壳”的文档排名靠后一些。
1
2
3
4
5
6
7
8
9
10
GET /products/_search
{
"query": {
"boosting": {
"positive": { "match": { "description": "苹果" } },
"negative": { "match": { "description": "手机壳" } },
"negative_boost": 0.2
}
}
}
c. prefixwildcard: 前缀与通配符查询
  • prefix: 查找以特定前缀开头的词条。
  • wildcard: 使用 *? 进行通配符匹配。
  • 场景: 用于搜索建议、自动补全或不确定的模式匹配。
1
2
3
4
5
6
7
8
GET /users/_search
{
"query": {
"prefix": {
"username.keyword": "admin"
}
}
}

性能警告: 这两种查询(尤其是 wildcard)会消耗大量资源,因为它们需要扫描索引中的大量词条。除非必要,否则应避免在用户输入的查询中


Elasticsearch入门使用
https://whosefrienda.github.io/2025/12/15/Elasticsearch入门使用/
作者
WhosefriendA
发布于
2025年12月15日
许可协议