ES 常用语法

基础命令

查看索引列表

GET /_cat/indices?v=true&pretty

查看分片情况

GET /_cat/shards?v=true&pretty

创建索引(Create Index)

PUT /<index_name>
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  }
}

查看索引是否存在( 结果是200 和 404)

HEAD /<index_name>

获取索引(Get Index)

GET /<index_name>

更新索引设置(Update Index Settings)

PUT /<index_name>/_settings
{
  "settings": {
    "number_of_replicas": 2
  }
}

删除索引(Delete Index)

DELETE /<index_name>

Document核心操作

查询文档

GET /<index_name>/_doc/1

新增文档(需要指定ID)

PUT /<index_name>/_doc/1
{
  "id":5555,
  "title":"ES索引",
  "pv":144
}

新增文档(不指定ID或者指定ID都可以),会自动生成id

POST /<index_name>/_doc
{
  "id":123,
  "title":"ES索引",
  "pv":244
}

修改(put和post都行,需要指定id)

PUT /<index_name>/_doc/1
{
  "id":999,
  "title":"ES索引v2",
  "pv":999,
  "uv":55
}


POST /<index_name>/_doc/1
{
  "id":999,
  "title":"ES索引v3",
  "pv":999,
  "uv":559
}

搜索

GET /<index_name>/_search

字段解释
  took字段表示该操作的耗时(单位为毫秒)。
  timed_out字段表示是否超时。
  hits字段表示搜到的记录,数组形式。
  total:返回记录数,本例是1条。
  max_score:最高的匹配程度,本例是1.0

删除数据

DELETE /<index_name>/_doc/1

Mapping和常见字段类型

  • Dynamic Mapping(动态映射)

    • 用于在索引文档时自动检测和定义字段的数据类型

    • 当我们向索引中添加新文档时,Elasticsearch会自动检测文档中的各个字段,并根据它们的值来尝试推断字段类型

    • 常见的字段类型包括文本(text)、关键词(keyword)、日期(date)、数值(numeric)等

    • 动态映射具备自动解析和创建字段的便利性,但在某些情况下,由于字段类型的不确定性,动态映射可能会带来一些问题

    • 例如字段解析错误、字段类型不一致等,如果对字段类型有明确的要求,最好在索引创建前通过显式映射定义来指定字段类型

  • ElasticSearch常见的数据类型

    • 在 ES 7.X后有两种字符串类型:Text 和 Keyword

      • Text类型:用于全文搜索的字符串类型,支持分词和索引建立

      • Keyword类型:用于精确匹配的字符串类型,不进行分词,适合用作过滤和聚合操作。

    • Numeric类型:包括整数类型(long、integer、short、byte)和浮点数类型(double、float)。

    • Date类型:用于存储日期和时间的类型。

    • Boolean类型:用于存储布尔值(true或false)的类型。

    • Binary类型:用于存储二进制数据的类型。

    • Array类型:用于存储数组或列表数据的类型。

    • Object类型:用于存储复杂结构数据的类型

  • 最高频使用的数据类型
    • text字段类型

      • text类型主要用于全文本搜索,适合存储需要进行全文本分词的文本内容,如文章、新闻等。  

      • text字段会对文本内容进行分词处理,将文本拆分成独立的词项(tokens)进行索引  

      • 分词的结果会建立倒排索引,使搜索更加灵活和高效。  

      • text字段在搜索时会根据分词结果进行匹配,并计算相关性得分,以便返回最佳匹配的结果。  

    • keyword字段类型

      • keyword类型主要用于精确匹配和聚合操作,适合存储不需要分词的精确值,如ID、标签、关键字等。  

      • keyword字段不会进行分词处理,而是将整个字段作为一个整体进行索引和搜索  

      • 这使得搜索只能从精确的值进行匹配,而不能根据词项对内容进行模糊检索。  

      • keyword字段适合用于过滤和精确匹配,同时可以进行快速的基于精确值的聚合操作。  

    • 总结

      • 在选择text字段类型和keyword字段类型时,需要根据具体的需求进行权衡和选择:  

      • 如果需要进行全文本检索,并且希望根据分词结果计算相关性得分,以获得最佳的匹配结果,则选择text字段类型。  

      • 如果需要进行精确匹配、排序或聚合操作,并且不需要对内容进行分词,则选择keyword字段类型  

查看索引库的字段类型

GET /<index_name>/_mapping

指定索引库字段类型mapping

PUT /<index_name>
{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "title": {
        "type": "text"
      },
      "price": {
        "type": "float"
      }
    }
  }
}

查询匹配关键词的文档一

GET /<index_name>/_search
{
  "query": {
    "match": {
      "title": "Elasticsearch"
    }
  }
}

查询匹配关键词的文档二

GET /<index_name>/_search
{
  "query": {
    "match": {
      "tags": "data"
    }
  }
}

分词器相关

查看分词效果

  • 使用analyze API 来对文本进行分词处理并查看分词结果,基本语法如下
GET /_analyze
{
  "analyzer": "分词器名称",
  "text": "待分析的文本"
}

案例

#字段是text类型
POST /<index_name>/_analyze
{
  "field": "title",
  "text": "This is some text to analyze"
}

#字段是text类型
POST /<index_name>/_analyze
{
  "field": "title",
  "text": ""This is some text to analyze"
}
  • 每个分词结果对象包含

    • 分词后的单词(token)

    • 开始位置(start_offset)

    • 结束位置(end_offset)

    • 类型(type)

      • ALPHANUM是一种数据类型,表示一个字符串字段只包含字母和数字,并且不会进行任何其他的分词或处理

      • 它会忽略字段中的任何非字母数字字符(如标点符号、空格等),只保留字母和数字字符

    • 单词在原始文本中的位置(position)

查询DSL语法

基本语法

GET /索引库名/_search 
{ 
  "query":{ 
    "查询类型":{
    
    }
}

常见Query DSL查询语句和功能

  • match 查询:用于执行全文搜索,它会将搜索查询与指定字段中的文本进行匹配

    • match,对查询内容进行分词, 然后进行查询,多个词条之间是 or的关系

    • 然后在与文档里面的分词进行匹配,匹配度越高分数越高越前面

{
  "query": {
    "match": {
      "title": "elasticsearch"
    }
  }
}
  • match高级用法之多字段匹配和短语搜索实战
    • 业务查询,需要在多个字段上进行文本搜索,用 multi_match

    • 在 match的基础上支持对多个字段进行文本查询匹配

    • 语法格式

GET /<index_name>/_search
{
  "query": {
    "multi_match": {
      "query": "要搜索的文本",
      "fields": ["字段1", "字段2", ...]
    }
  }
}

# query:需要匹配的查询文本。
# fields:一个包含需要进行匹配的字段列表的数组。
    • 短语搜索匹配
    • 是Elasticsearch中提供的一种高级匹配查询类型,用于执行精确的短语搜索

    • 相比于match查询,match_phrase会在匹配时考虑到单词之间的顺序和位置

    • 语法格式

GET /<index_name>/_search
{
  "query": {
    "match_phrase": {
      "field_name": {
        "query": "要搜索的短语"
      }
    }
  }
}

# field_name:要进行匹配的字段名。
# query:要搜索的短语。
  • term 查询:用于精确匹配一个指定字段的关键词,不进行分词处理。

    • term查询,不会将查询条件分词,直接与文档里面的分词进行匹配

    • 虽然match也可以完成,但是match查询会多一步进行分词,浪费资源

{
  "query": {
    "term": {
      "category": "books"
    }
  }
}
  • 查询全部数据(match_all):是一种简单的查询,匹配索引中的所有文档
GET /<index_name>/_search
{
  "query": {
    "match_all": {}
  }
}
  •  获取指定字段
    • 某些情况场景下,不需要返回全部字段,太废资源,可以指定source返回对应的字段

GET /<index_name>/_search
{
"_source":["price","title"],
  "query": {
    "term": {
      "title": {
        "value": "Spring Boot"
      }
    }
  }
}
  • 总结

    • match在匹配时会对所查找的关键词进行分词,然后分词匹配查找;term会直接对关键词进行查找

    • 一般业务里面需要模糊查找的时候,更多选择match,而精确查找时选择term查询

  • 范围查询
    • range 查询
    • 用于根据范围条件进行查询,例如指定价格在一定区间内的商品  

    • 范围符号

      • gte:大于等于  

      • gt:大于  

      • lte:小于等于  

      • lt:小于  

GET /<index_name>/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 5,
        "lte": 100
      }
    }
  }
}
  • 分页查询
    • 可以使用 fromsize 参数进行分页查询

    • 可以指定要跳过的文档数量(from)和需要返回的文档数量(size

 

GET /<index_name>/_search
{
  "size": 10,
  "from": 0,
  "query": {
    "match_all": {}
  }
}
  • 查询结果排序
    • sort字段可以进行排序 descasc

GET /<index_name>/_search
{
  "size": 10,
  "from": 0,
  "sort": [
    {
      "price": "asc"
    }
  ],
  "query": {
    "match_all": {}
  }
}
  • bool 查询
    • 通过组合多个查询条件,使用布尔逻辑(与、或、非)进行复杂的查询操作  

    • 语法格式

      • "must"关键字用于指定必须匹配的条件,即所有条件都必须满足  

      • "must_not"关键字指定必须不匹配的条件,即所有条件都不能满足  

      • "should"关键字指定可选的匹配条件,即至少满足一个条件  

{
  "query": {
    "bool": {
      "must": [
        // 必须匹配的条件
      ],
      "must_not": [
        // 必须不匹配的条件
      ],
      "should": [
        // 可选匹配的条件
      ],
      "filter": [
        // 过滤条件
      ]
    }
  }
}
    • 案例
GET /<index_name>/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "summary": "Cloud" }},
        { "range": { "price": { "gte": 5 }}}
      ]
    }
  }
}
  • filter查询
    • 来对搜索结果进行筛选和过滤,仅返回符合特定条件的文档,而不改变搜索评分

    • Filter查询对结果进行缓存,提高查询性能,用于数字范围、日期范围、布尔逻辑、存在性检查等各种过滤操作。

    • 语法格式

      • "filter"关键字用于指定一个过滤条件,可以是一个具体的过滤器,如term、range等,也可以是一个嵌套的bool过滤器

{
  "query": {
    "bool": {
      "filter": {
        // 过滤条件
      }
    }
  }
}
    • 案例一 :使用 term 过滤器查询 categorybooks 的产品:

GET /<index_name>/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "category": "books"
        }
      }
    }
  }
}
    • 案例二:使用 range 过滤器查询价格 price 在 30 到 50 之间的产品:

GET /<index_name>/_search
{
  "query": {
    "bool": {
      "filter": {
        "range": {
          "price": {
            "gte": 30,
            "lte": 50
          }
        }
      }
    }
  }
}
  • 总结

    • 过滤条件通常用于对结果进行筛选,并且比查询条件更高效

    • 而bool查询可以根据具体需求组合多个条件、过滤器和查询子句

 

  • 日常单词拼写错误-fuzzy模糊查询案例实战
    • fuzzy查询是Elasticsearch中提供的一种模糊匹配查询类型,用在搜索时容忍一些拼写错误或近似匹配

    • 使用fuzzy查询,可以根据指定的编辑距离(即词之间不同字符的数量)来模糊匹配查询词

    • 拓展:编辑距离

      • 是将一个术语转换为另一个术语所需的一个字符更改的次数。

      • 比如

        • 更改字符(box→fox)

        • 删除字符(black→lack)

        • 插入字符(sic→sick)

        • 转置两个相邻字符(dgo→dog)

    • fuzzy模糊查询是拼写错误的简单解决方案,但具有很高的 CPU 开销和非常低的精准度

    • 用法和match基本一致,Fuzzy query的查询不分词

    • 基本语法格式

GET /<index_name>/_search
{
  "query": {
    "fuzzy": {
      "field_name": {
        "value": "要搜索的词",
        "fuzziness": "模糊度"
      }
    }
  }
}
    • 解析

      • field_name:要进行模糊匹配的字段名。

      • value:要搜索的词

      • fuzziness参数指定了模糊度,常见值如下

        • 0,1,2

          • 指定数字,表示允许的最大编辑距离,较低的数字表示更严格的匹配,较高的数字表示更松散的匹配

          • fuziness的值,表示是针对每个词语而言的,而不是总的错误的数值

        • AUTO:Elasticsearch根据词的长度自动选择模糊度

          • 如果字符串的长度大于5,那 funziness 的值自动设置为2

          • 如果字符串的长度小于2,那么 fuziness 的值自动设置为 0

  • 高亮显示多案例

    • 在 ES 中,高亮语法用于在搜索结果中突出显示与查询匹配的关键词

    • 高亮显示是通过标签包裹匹配的文本来实现的,通常是 <em> 或其他 HTML 标签

    • 基本用法:在 highlight 里面填写要高亮显示的字段,可以填写多个

    • 单条件查询高亮显示

GET /<index_name>/_search 
{
  "query": {
    "match": {
      "content": "电影"
    }
  },
  "highlight": {
    "fields": {
      "content": {}
    }
  }
}
    • 组合多条件查询,highlight里面填写需要高亮的字段

GET /<index_name>/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "课堂"
          }
        },
        {
          "match": {
            "content": "老王"
          }
        }
      ]
    }
  },
  "highlight": {
    "fields": {
      "title": {},
      "content": {}
    }
  }
}
    • match查询,使用highlight属性,可以增加属性,修改高亮样式
      • pre_tags:前置标签

      • post_tags:后置标签

      • fields:需要高亮的字段

GET /<index_name>/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "张三"
          }
        },
        {
          "match": {
            "content": "老王"
          }
        }
      ]
    }
  },
  "highlight": {
    "pre_tags": "<font color='yellow'>",
    "post_tags": "</font>",
    "fields": [{"title":{}},{"content":{}}]
  } 
}

搜索聚合查询(报表类搜索

什么是聚合查询

  • 对大量数据聚合统计处理,类似Mysql数据库操作里面的group by 分组、sum、avg、max等函数处理

  • 是 Elasticsearch 中强大的功能之一,根据数据进行分组、过滤、计算和统计,提取有关数据集信息,进行数据分析

  • 数据可视化大屏里面的饼状图、柱状图、折线图、仪表盘数据等都是聚合查询的关键应用

  •  术语一:对数据集求最大、最小、和、平均值等指标的聚合,称为 指标聚合 metric
    • 基本语法格式如下

GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "aggregation_name": {
      "aggregation_type": {
        "aggregation_field": "field_name"
        // 可选参数
      }
    }
    // 可以添加更多的聚合
  }
}

# 解析
index:要执行聚合查询的索引名称。
size: 设置为 0 来仅返回聚合结果,而不返回实际的搜索结果,这里将hits改为0表示返回的原始数据变为0
aggs:指定聚合操作的容器。

aggregation_name:聚合名称,可以自定义。
aggregation_type:聚合操作的类型,例如 terms、avg、sum 等。
aggregation_field:聚合操作的目标字段,对哪些字段进行聚合
  • 术语二:对数据集进行分组group by,然后在组上进行指标聚合,在 ES 中称为分桶,桶聚合bucketing
    • 基本语法格式如下
GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "aggregation_name": {
      "bucket_type": {
        "bucket_options": {
          "bucket_option_name": "bucket_option_value",
          ...
        },
        "aggs": {
          "sub_aggregation_name": {
            "sub_aggregation_type": {
              "sub_aggregation_options": {
                "sub_aggregation_option_name": "sub_aggregation_option_value",
                ...
              }
            }
          }
        }
      }
    }
  }
}
#解析
index: 替换为要执行聚合查询的索引名称。
aggregation_name: 替换为自定义的聚合名称。
bucket_type: 替换为特定的桶聚合类型(如 terms、date_histogram、range 等)。
bucket_option_name 和 bucket_option_value: 替换为特定桶聚合选项的名称和值。

sub_aggregation_name: 替换为子聚合的名称。
sub_aggregation_type: 替换为特定的子聚合类型(如 sum、avg、max、min 等)。
sub_aggregation_option_name 和 sub_aggregation_option_value: 替换为特定子聚合选项的名称和值

常见聚合用途和应用场景案例

  • 聚合指标(Aggregation Metrics):

    • Avg Aggregation:计算文档字段的平均值。

    • Sum Aggregation:计算文档字段的总和。

    • Min Aggregation:找到文档字段的最小值。

    • Max Aggregation:找到文档字段的最大值。

  • 聚合桶(Aggregation Buckets):

    • Terms Aggregation:基于字段值将文档分组到不同的桶中。

    • Date Histogram Aggregation:按日期/时间字段创建时间间隔的桶。

    • Range Aggregation:根据字段值的范围创建桶。

  • 嵌套聚合(Nested Aggregations)、聚合过滤(Aggregation Filtering)

  • 案例

-- 创建索引
PUT /sales
{
  "mappings": {
    "properties": {
      "product": {
        "type": "keyword"
      },
      "sales": {
        "type": "integer"
      }
    }
  }
}

-- 批量插入数据
POST /sales/_bulk
{"index": {}}
{"product": "iPhone", "sales": 4}
{"index": {}}
{"product": "Samsung", "sales": 60}
{"index": {}}
{"product": "iPhone", "sales": 100}
{"index": {}}
{"product": "Samsung", "sales": 80}
{"index": {}}

-- 执行聚合查询:分别按照商品名称(product)进行分组
GET /sales/_search
{
  "aggs":{//聚合操作
    "product_group":{//名称,随意起名
      "terms":{//分组
        "field":"product"//分组字段
      }
    }
  }
}
    • 计算每组的销售总量,使用了 terms 聚合和 sum 聚合来实现

    • 查询结果将返回每个产品的名称和销售总量

GET /sales/_search
{
  "size": 0,
  "aggs": {
    "product_sales": {
      "terms": {
        "field": "product"
      },
      "aggs": {
        "total_sales": {
          "sum": {
            "field": "sales"
          }
        }
      }
    }
  }
}

 

什么是指标聚合

 

  • 对数据集求最大、最小、和、平均值等指标的聚合,称为 指标聚合 metric

  • 比如 max、min、avg、sum等函数使用

  • 聚合查询 max 应用案例:
    • 案例说明:使用 max 聚合查询来获取产品价格的最高值
GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "max_price": {
      "max": {
        "field": "price"
      }
    }
  }
}
  • 聚合查询 - min 应用案例:
    • 案例说明:使用 min 聚合查询来获取学生的最低考试分数

GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "min_score": {
      "min": {
        "field": "score"
      }
    }
  }
}
  • 聚合查询 - avg 应用案例:
    • 案例说明:使用 sum 聚合查询来计算销售记录的总销售数量。
GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "total_sales": {
      "sum": {
        "field": "sales_count"
      }
    }
  }
}

桶聚合语法和Terms案例实战

什么桶bucket聚合

  • 对数据集进行分组group by,然后在组上进行指标聚合,在 ES 中称为分桶,桶聚合bucketing

  • 基本语法格式如下

GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "aggregation_name": {
      "bucket_type": {
        "bucket_options": {
          "bucket_option_name": "bucket_option_value",
          ...
        },
        "aggs": {
          "sub_aggregation_name": {
            "sub_aggregation_type": {
              "sub_aggregation_options": {
                "sub_aggregation_option_name": "sub_aggregation_option_value",
                ...
              }
            }
          }
        }
      }
    }
  }
}
#解析
index: 替换为要执行聚合查询的索引名称。
aggregation_name: 替换为自定义的聚合名称。
bucket_type: 替换为特定的桶聚合类型(如 terms、date_histogram、range 等)。
bucket_option_name 和 bucket_option_value: 替换为特定桶聚合选项的名称和值。

sub_aggregation_name: 替换为子聚合的名称。
sub_aggregation_type: 替换为特定的子聚合类型(如 sum、avg、max、min 等)。
sub_aggregation_option_name 和 sub_aggregation_option_value: 替换为特定子聚合选项的名称和值
  • 案例说明:使用 terms 聚合查询将图书按销售数量进行分桶,并获取每个分桶内的销售数量总和。

GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "book_buckets": {
      "terms": {
        "field": "book_title",
        "size": 10
      },
      "aggs": {
        "total_sales": {
          "sum": {
            "field": "sales_count"
          }
        }
      }
    }
  }
}

桶聚合Date Histogram介绍和案例实战

分桶聚合查询 - Date Histogram

  • 将日期类型的字段按照固定的时间间隔进行分桶,并对每个时间间隔内的文档进行进一步的操作和计算

  • 基本语法如下

GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "date_histogram_name": {
      "date_histogram": {
        "field": "date_field_name",
        "interval": "interval_expression"
      },
      "aggs": {
        "sub_aggregation": {
          "sub_aggregation_type": {}
        }
      }
    }
  }
}

#解析
index:替换为要执行聚合查询的索引名称。
date_histogram_name:替换为自定义的 date_histogram 聚合名称。
date_field_name:替换为要聚合的日期类型字段名。
interval_expression:指定用于分桶的时间间隔。时间间隔可以是一个有效的日期格式(如 1d、1w、1M),也可以是一个数字加上一个时间单位的组合(如 7d 表示 7 天,1h 表示 1 小时)。
sub_aggregation:指定在每个日期桶内进行的子聚合操作。
sub_aggregation_type:替换单独子聚合操作的类型,可以是任何有效的子聚合类型。
  • 案例说明:使用 date_histogram 聚合查询将订单按日期进行分桶,并计算每个分桶内的订单金额总和。

GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "sales_per_month": {
      "date_histogram": {
        "field": "order_date",
        "calendar_interval": "month",
        "format": "yyyy-MM"
      },
      "aggs": {
        "total_sales": {
          "sum": {
            "field": "amount"
          }
        }
      }
    }
  }
}

桶聚合Range介绍和案例实战

分桶聚合查询 - Range

  • 将字段的值划分为不同的范围,并将每个范围内的文档分配给相应的桶,对这些范围进行各种操作和计算。

  • 语法介绍

GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "range_name": {
      "range": {
        "field": "field_name",
        "ranges": [
          { "key": "range_key_1", "from": from_value_1, "to": to_value_1 },
          { "key": "range_key_2", "from": from_value_2, "to": to_value_2 },
          ...
        ]
      },
      "aggs": {
        "sub_aggregation": {
          "sub_aggregation_type": {}
        }
      }
    }
  }
}

#解析
index:替换为要执行聚合查询的索引名称。
range_name:替换为自定义的 range 聚合名称。
field_name:替换为要聚合的字段名。
ranges:指定范围数组,每个范围使用 key、from 和 to 参数进行定义。
key:范围的唯一标识符。
from:范围的起始值(包含)。
to:范围的结束值(不包含)。
sub_aggregation:指定在每个范围内进行的子聚合操作。
sub_aggregation_type:替换单独子聚合操作的类型,可以是任何有效的子聚合类型。
  • 案例说明:使用 range 聚合查询将商品按价格范围进行分桶,并计算每个分桶内的商品数量。

    • 如果没写key,则会默认生成

 
GET /<index_name>/_search
{
  "size": 0,
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 100 },
          { "from": 100, "to": 200 },
          { "from": 200 }
        ]
      }
    }
  }
}

SpringBoot整合ES

添加依赖

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
 </dependency>

配置文件

# 添加配置
spring.elasticsearch.uris=http://112.74.167.42:9200

什么是ElasticsearchTemplate

  • 是 Spring Data Elasticsearch 提供的一个核心类,是 ElasticsearchClient 的一个具体实现  

  • 用于在 Spring Boot 中操作 Elasticsearch 进行数据的存取和查询  

  • 提供了一组方法来执行各种操作,如保存、更新、删除和查询文档,执行聚合操作等  

ElasticsearchTemplate 的一些常用方法

  • save(Object): 保存一个对象到 Elasticsearch 中。  

  • index(IndexQuery): 使用 IndexQuery 对象执行索引操作。  

  • delete(String, String): 删除指定索引和类型的文档。  

  • get(String, String): 获取指定索引和类型的文档。  

  • update(UpdateQuery): 使用 UpdateQuery 对象执行更新操作。  

  • search(SearchQuery, Class): 执行搜索查询,并将结果映射为指定类型的对象。  

  • count(SearchQuery, Class): 执行搜索查询,并返回结果的计数  

 ElasticsearchTemplate 常见注解配置(都是属于spring data elasticsearch)

  • @Id 指定主键

  • @Document指定实体类和索引对应关系

indexName:索引名称
  • @Field指定普通属性
type 对应Elasticsearch中属性类型,使用FiledType枚举快速获取。

text 类型能被分词

keywords 不能被分词

index  是否创建索引,作为搜索条件时index必须为true

analyzer 指定分词器类型。

案例实战

创建DTO

@Document(indexName = "video")
public class VideoDTO {

  
    @Id
    @Field(type = FieldType.Text, index = false)
    private Long id;

    @Field(type = FieldType.Text)
    private String title;

    @Field(type = FieldType.Text)
    private String description;

    @Field(type = FieldType.Keyword)
    private String category;

    @Field(type = FieldType.Integer)
    private Integer duration;

    @Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
    private LocalDateTime createTime;

    public VideoDTO(){}

    public VideoDTO(Long id, String title, String description, Integer duration,String category) {
        this.id = id;
        this.title = title;
        this.description = description;
        this.duration = duration;
        this.createTime = LocalDateTime.now();
        this.category = category;
    }

}

单元测试

  @Autowired
  private ElasticsearchTemplate restTemplate;
  /**
   * 判断索引是否存在索引
   */
  @Test
  void existsIndex() {
    IndexOperations indexOperations = restTemplate.indexOps(VideoDTO.class);
    boolean exists = indexOperations.exists();
    System.out.println(exists);
  }

  /**
   * 创建索引
   */
  @Test
  void createIndex() {
    // spring data es所有索引操作都在这个接口
    IndexOperations indexOperations = restTemplate.indexOps(VideoDTO.class);
    // 是否存在,存在则删除
    if(indexOperations.exists()){
      indexOperations.delete();
    }

    // 创建索引
    indexOperations.create();

    //设置映射: 在正式开发中,几乎不会使用框架创建索引或设置映射,这是架构或者管理员的工作,不适合使用代码实现
    restTemplate.indexOps(VideoDTO.class).putMapping();
  }

  /**
   * 删除索引
   */
  @Test
  void deleteIndex() {
    IndexOperations indexOperations = restTemplate.indexOps(VideoDTO.class);
    boolean delete = indexOperations.delete();
    System.out.println(delete);
  }

CRUD操作

@Test
  void insert(){
    VideoDTO videoDTO = new VideoDTO();
    videoDTO.setId(1L);
    VideoDTO saved = restTemplate.save(videoDTO);
    System.out.println(saved);
  }
@Test
  void update(){
    VideoDTO videoDTO = new VideoDTO();
    videoDTO.setId(1L);
    VideoDTO saved = restTemplate.save(videoDTO);
    System.out.println(saved);
  }
    @Test
    void batchInsert() {
        List<VideoDTO> list = new ArrayList<>();
        Iterable<VideoDTO> result = restTemplate.save(list);
        System.out.println(result);
    }
@Test
  void  searchById(){
    VideoDTO videoDTO = restTemplate.get("3", VideoDTO.class);
        assert videoDTO != null;
        System.out.println(videoDTO);
  }
@Test
  void  deleteById() {
    String delete = restTemplate.delete("2", VideoDTO.class);
    System.out.println(delete);
  }

其他多案例搜索

Query是Spring Data Elasticsearch的接口,有多种具体实现

  • CriteriaQuery

    • 创建Criteria来搜索数据,而无需了解 Elasticsearch 查询的语法或基础知识

    • 允许用户通过简单地连接和组合,指定搜索文档必须满足的对象来构建查询

  • StringQuery

    • 将Elasticsearch查询作为JSON字符串,更适合对Elasticsearch查询的语法比较了解的人

    • 也更方便使用kibana或postman等客户端工具行进调试

  • NativeQuery

    • 复杂查询或无法使用CriteriaAPI 表达的查询时使用的类,例如在构建查询和使用聚合的场景

  • 案例一:搜索全部

  /**
   * 查询所有
   */
  @Test
  void searchAll(){

    SearchHits<VideoDTO> search = restTemplate.search(Query.findAll(), VideoDTO.class);
    List<SearchHit<VideoDTO>> searchHits = search.getSearchHits();
    // 获得searchHits,进行遍历得到content
    List<VideoDTO> videoDTOS = new ArrayList<>();
    searchHits.forEach(hit -> {
      videoDTOS.add(hit.getContent());
    });
    System.out.println(videoDTOS);
  }
  • 案例二:匹配搜索

/**
   * match查询
   */
  @Test
  void matchQuery(){

    Query query = NativeQuery.builder().withQuery(q -> q
        .match(m -> m
            .field("description") //字段
            .query("spring") //
        )).build();
    SearchHits<VideoDTO> searchHits = restTemplate.search(query, VideoDTO.class);

    // 获得searchHits,进行遍历得到content
    List<VideoDTO> videoDTOS = new ArrayList<>();
    searchHits.forEach(hit -> {
      videoDTOS.add(hit.getContent());
    });
    System.out.println(videoDTOS);
  }
  • 案例三:分页搜索

  /**
     * 分页查询
     */
    @Test
    void pageSearch() {
        Query query = NativeQuery.builder().withQuery(Query.findAll())
                .withPageable(Pageable.ofSize(3).withPage(0)).build();

        SearchHits<VideoDTO> searchHits = restTemplate.search(query, VideoDTO.class);
        // 获得searchHits,进行遍历得到content
        List<VideoDTO> videoDTOS = new ArrayList<>();
        searchHits.forEach(hit -> {
            videoDTOS.add(hit.getContent());
        });
        System.out.println(videoDTOS);
    }
  • 案例四:搜索排序,withSort() 需要传入 Sort 对象,.by代表根据一个字段进行排序

    • .ascending() 方法:默认的,正序排序

    • .descending()方法:倒叙排序

   /**
     * 排序查询,根据时长降序排列
     */
    @Test
    void sortSearch() {
        Query query = NativeQuery.builder().withQuery(Query.findAll())
                .withPageable(Pageable.ofSize(10).withPage(0))
                .withSort(Sort.by("duration").descending()).build();

        SearchHits<VideoDTO> searchHits = restTemplate.search(query, VideoDTO.class);
        // 获得searchHits,进行遍历得到content
        List<VideoDTO> videoDTOS = new ArrayList<>();
        searchHits.forEach(hit -> {
            videoDTOS.add(hit.getContent());
        });
        System.out.println(videoDTOS);
    }

什么是StringQuery

  • 将Elasticsearch查询作为JSON字符串,更适合对Elasticsearch查询的语法比较了解的人

  • 也更方便使用kibana或postman等客户端工具行进调试

  • 案例一:布尔must查询,搜索标题有 架构 关键词,描述有 spring关键字,时长范围是 10~6000之间的
  @Test
    void stringQuery() {

        //搜索标题有 架构 关键词,描述有 spring关键字,时长范围是 10~6000之间的
        String dsl = """
                   {"bool":{"must":[{"match":{"title":"架构"}},{"match":{"description":"spring"}},{"range":{"duration":{"gte":10,"lte":6000}}}]}}
                """;
        Query query = new StringQuery(dsl);

        List<SearchHit<VideoDTO>> searchHitList = restTemplate.search(query, VideoDTO.class).getSearchHits();

        // 获得searchHits,进行遍历得到content
        List<VideoDTO> videoDTOS = new ArrayList<>();
        searchHitList.forEach(hit -> {
            videoDTOS.add(hit.getContent());
        });
        System.out.println(videoDTOS);
    }

原始DSL查询

GET /video/_search
{
  "query": {
    "bool": {
      "must": [{
        "match": {
          "title": "架构"
        }
      }, {
        "match": {
          "description": "spring"
        }
      }, {
        "range": {
          "duration": {
            "gte": 10,
            "lte": 6000
          }
        }
      }]
    }
  }
}

聚合搜索案例

  • 方案一:可以使用原始DSL进行处理

  • 方案二:使用NativeQuery完成聚合搜索

  /**
     * 聚合查询
     */
    @Test
    void aggQuery() {
        Query query = NativeQuery.builder()
                .withAggregation("category_group", Aggregation.of(a -> a
                        .terms(ta -> ta.field("category").size(2))))
                .build();

        SearchHits<VideoDTO> searchHits = restTemplate.search(query, VideoDTO.class);

        //获取聚合数据
        ElasticsearchAggregations aggregationsContainer = (ElasticsearchAggregations) searchHits.getAggregations();
        Map<String, ElasticsearchAggregation> aggregations = Objects.requireNonNull(aggregationsContainer).aggregationsAsMap();

        //获取对应名称的聚合
        ElasticsearchAggregation aggregation = aggregations.get("category_group");
        Buckets<StringTermsBucket> buckets = aggregation.aggregation().getAggregate().sterms().buckets();

        //打印聚合信息
        buckets.array().forEach(bucket -> {
            System.out.println("组名:"+bucket.key().stringValue() + ", 值" + bucket.docCount());
        });

        // 获得searchHits,进行遍历得到content
        List<VideoDTO> videoDTOS = new ArrayList<>();
        searchHits.forEach(hit -> {
            videoDTOS.add(hit.getContent());
        });
        System.out.println(videoDTOS);
    }

 

posted @ 2024-09-08 23:59  陈彦斌  阅读(753)  评论(0编辑  收藏  举报