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 } } } }
- 分页查询
-
可以使用
from
和size
参数进行分页查询 -
可以指定要跳过的文档数量(
from
)和需要返回的文档数量(size
)
-
GET /<index_name>/_search { "size": 10, "from": 0, "query": { "match_all": {} } }
- 查询结果排序
-
-
sort
字段可以进行排序desc
和asc
-
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
过滤器查询category
为books
的产品:
-
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); }