本文大纲
1、document 数据格式
2、简单的集群管理
(1)快速检查集群的健康状况
(2)快速查看集群中有哪些索引
(3)简单的索引操作
(4)商品的 CRUD 操作
新增商品:建立索引,新建文档
查询商品
修改商品:替换文档
修改商品:更新文档
删除商品:删除文档
3、多种搜索方式
(1)查询字符串搜索:query string search
(2)query DSL
查询所有的商品
查询名称包含水杯的商品,同时按照价格降序排序
分页查询商品
(3)query filter
搜索商品名称包含水杯,而且售价大于 100 元的商品
(4)full-text search(全文检索)
(5)phrase search(短语搜索)
(6)highlight search(高亮搜索结果)
(7)分组后进行排序取第一条
(8)script 语法的用法
(9)聚合分析
分组后求总数:计算每个 producer 下的商品数量
第二个聚合分析的需求:对名称中包含水杯的商品,计算每个 producer 下的商品数量
第三个聚合分析的需求:先分组,再算每组的平均值,计算每个 producer 下的商品的平均价格
第四个数据分析需求:计算每个 producer 下的商品的平均价格,并且按照平均价格降序排序
第五个数据分析需求:按照指定的价格范围区间进行分组,然后在每组内再按照 tag 进行分组,最后再计算每组的平均价格
1、document 数据格式
elasticsearch 是面向文档的搜索分析引擎,它和数据库相比,主要的区别如下:
(1)应用系统的数据结构都是面向对象的,复杂的
(2)对象数据存储到数据库中,只能拆解开来,变为扁平的多张表,每次查询的时候还得还原回对象格式,相当麻烦
(3)ES 是面向文档的,文档中存储的数据结构,与面向对象的数据结构是一样的,基于这种文档数据结构,es 可以提供复杂的索引,全文检索,分析聚合等功能
(4)es 的 document 用 json 数据格式来表达
2、简单的集群管理
(1)快速检查集群的健康状况
es 提供了一套 api,叫做 cat api,可以查看 es 中各种各样的数据
GET /_cat/health?v
如何快速了解集群的健康状况?green、yellow、red?
green:每个索引的 primary shard 和 replica shard 都是 active 状态的
yellow:每个索引的 primary shard 都是 active 状态的,但是部分 replica shard 不是 active 状态,处于不可用的状态
red:不是所有索引的 primary shard 都是 active 状态的,部分索引有数据丢失了
(2)快速查看集群中有哪些索引
GET /_cat/indices?v
(3)简单的索引操作
创建索引:
PUT /test_index?pretty
删除索引:
DELETE /test_index?pretty
(4)商品的 CRUD 操作
基于 restful 风格的基本 ES 索引增删改查操作。一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更筒洁,更有层次,更易于实现缓存等机制。
(1)新增商品:建立索引,新建文档
PUT /product/_doc/2
{
“name” : “阿道夫护发素”,
“desc” : “阿道夫护发素的正确使用方式,许多人用洗发水洗完头发后,因为怕麻烦就不再愿意使用护发素了(尤其是男士),其实这个想法是错误的,洗发水和护发素的功能是不一样的”,
“price” : 40,
“producer” : “阿道夫”,
“tags”: [ “护发素”, “女士” ]
}
es 会自动建立 index 和 type,不需要提前创建,而且 es 默认会对 document 每个 field 都建立倒排索引,让其可以被搜索
(2)查询商品
GET /product/_search
{
“query”: {
“match_all”: {}
}
}
(3)修改商品:替换文档
PUT /product/_doc/2
{
“name” : “阿道夫护发素”,
“desc” : “阿道夫护发素的正确使用方式,许多人用洗发水洗完头发后,因为怕麻烦就不再愿意使用护发素了(尤其是男士),其实这个想法是错误的,洗发水和护发素的功能是不一样的”,
“price” : 40,
“producer” : “阿道夫”,
“tags”: [ “护发素”, “女士”,”养发” ]
}
替换方式有一个不好,即必须带上所有的 field,不然更新之后其他的 fileid 会被丢弃掉。
(4)修改商品:更新文档
POST product/_update/2
{
“doc”:{
“name” : “阿道夫护发素 3”
}
}
查询 es 之后,其 fileid 还在,只更新了 name
(5)删除商品:删除文档
DELETE /product/_doc/1
3、多种搜索方式
took:耗费了几毫秒
timed_out:是否超时,这里是没有
_shards:数据拆成了 5 个分片,所以对于搜索请求,会打到所有的 primary shard(或者是它的某个 replica shard 也可以)
hits.total:查询结果的数量,3 个 document
hits.max_score:score:document 对于一个 search 的相关度的匹配分数,越相关,就越匹配,分数也高
hits.hits:包含了匹配搜索的 document 的详细数据
1、查询字符串搜索:query string search
搜索全部商品:GET /product/_search
GET /product/_search
query string search 的由来,因为 search 参数都是以 http 请求的 query string 来附带的,比如搜索商品名称中包含水杯的商品,而且按照售价降序排序:
GET /product/_search?q=name:水杯&sort=price:desc
query string search 适用于临时的在命令行使用一些工具,比如 curl,快速的发出请求,来检索想要的信息;但是如果查询请求很复杂,是很难去构建的
在生产环境中,几乎很少使用 query string search
2、query DSL
DSL:Domain Specified Language,特定领域的语言
http request body:请求体,可以用 json 的格式来构建查询语法,比较方便,可以构建各种复杂的语法,比 query string search 肯定强大多了
2.1 查询所有的商品
GET /product/_search
{
“query”: {
“match_all”: {}
}
}
2.2 查询名称包含水杯的商品,同时按照价格降序排序
GET /product/_search
{
“query”: {
“match”: {
“name”: “水杯”
}
},
“sort”: [
{
“price”: “desc”
}
]
}
2.3 分页查询商品
总共 3 条商品,假设每页就显示 1 条商品,现在显示第 2 页,所以就查出来第 2 个商品
GET /product/_search
{
“query”: { “match_all”: {} },
“from”: 1,
“size”: 1
}
指定要查询出来商品的名称和价格就可以
GET /ecommerce/product/_search
{
“query”: { “match_all”: {} },
“_source”: [“name”, “price”]
}
query DSL 更加适合生产环境的使用,可以构建复杂的查询
3、query filter
3.1 搜索商品名称包含水杯,而且售价大于 100 元的商品
GET /product/_search
{
"query": {
"bool": {
"must": {
"match": {
"name": "水杯"
}
},
"filter": {
"range": {
"price": {
"gt": 25
}
}
}
}
}
}
4、full-text search(全文检索)
GET /product/_search
{
"query": {
"match": {
"name": "水杯"
}
}
}
5、phrase search(短语搜索)
跟全文检索相对应,相反,全文检索会将输入的搜索串拆解开来,去倒排索引里面去一一匹配,只要能匹配上任意一个拆解后的单词,就可以作为结果返回。
phrase search,要求输入的搜索串,必须在指定的字段文本中,完全包含一模一样的,才可以算匹配,才能作为结果返回,在使用时需要根据具体的业务来配合使用。
GET /product/_search
{
"query": {
"bool": {
"must": [
{
"match_phrase": {
"name": "水杯"
}
}
]
}
}
}
6、highlight search(高亮搜索结果)
GET /product/_search
{
"query": {
"bool": {
"must": [
{
"match_phrase": {
"name": "水杯"
}
}
]
}
},
"highlight": {
"fields": {
"name": {}
}
}
}
7、分组后进行排序取第一条
根据 producer 进行分组,然后根据 price 进行倒序排序并取每个分组的第一条数据:
GET /product/_search
{
"query": {
"bool": {
"must": [
{
"match_all": {}
}
]
}
},
"size": 0,
"aggs": {
"nameAgg": {
"terms": {
"field": "producer.keyword",
"size": 10,
"min_doc_count": 1
},
"aggs": {
"top1": {
"top_hits": {
"size": 1,
"sort": [
{
"price": {
"order": "desc"
}
}
]
}
}
}
}
}
}
8、通过比较或者计算两个字段值,来过滤符合条件的数据时,那么我们就需要用到 script
(1) script 排序写法
"sort": [
{
"_script": {
"script": {
"inline": "doc['cas_end_time'].value - doc['createtime'].value",
"lang": "painless"
},
"type": "number",
"order": "asc"
}
}
]
对应的 java 写法:
Script script = new Script("doc['cas_end_time'].value - doc['createtime'].value");
must.add(QueryBuilders.scriptQuery(script));
(2)script 平常写法
{
"script": {
"script": {
"inline": "doc['cas_end_time'].value - doc['createtime'].value",
"lang": "painless"
},
"boost": 1
}
}
java 写法:
Script script = new Script("doc['cas_end_time'].value - doc['createtime'].value");
ScriptSortBuilder sortBuilder = SortBuilders.scriptSort(script, ScriptSortBuilder.ScriptSortType.NUMBER).order(SortOrder.DESC);
builder.addSort(sortBuilder);
9、聚合分析
分组后求总数:计算每个 producer 下的商品数量
#计算每个 producer 下的商品数量
GET /product/_search
{
"size": 0,
"aggs": {
"producerAggs": {
"terms": {
"field": "producer",
"size": 10
}
}
}
}
执行以上语句会报错,报错信息如下,主要原因是默认情况下,文本字段的 Fielddata 是禁用的。需要在[查询的字段]上设置 fielddata=true 或者使用关键字字段代替。
方式一:使用关键字的方式进行查询
GET /product/_search
{
"size": 0,
"aggs": {
"producerAggs": {
"terms": {
"field": "producer.keyword",
"size": 10
}
}
}
}
方式二:将文本 field 的 fielddata 属性设置为 true,重新执行该语句
#设置 producer 字段的字段类型
PUT /product/_mapping
{
"properties": {
"producer": {
"type": "text",
"fielddata": true
}
}
}
#重新执行语句
GET /product/_search
{
"size": 0,
"aggs": {
"producerAggs": {
"terms": {
"field": "producer",
"size": 10
}
}
}
}
在平常使用时,按照具体的需求决定使用哪种方式。
第二个聚合分析的需求:对名称中包含水杯的商品,计算每个 producer 下的商品数量
#对名称中包含水杯的商品,计算每个 producer 下的商品数量
GET /product/_search
{
"size": 0,
"query": {
"bool": {
"must": [
{
"match_phrase": {
"name": "水杯"
}
}
]
}
},
"aggs": {
"producerAggs": {
"terms": {
"field": "producer.keyword",
"size": 10
}
}
}
}
第三个聚合分析的需求:先分组,再算每组的平均值,计算每个 producer 下的商品的平均价格
#先分组,再算每组的平均值,计算每个 producer 下的商品的平均价格
GET /product/_search
{
"size": 0,
"aggs": {
"producerAggs": {
"terms": {
"field": "producer.keyword",
"size": 10
},
"aggs": {
"avgPrice": {
"avg": {
"field": "price"
}
}
}
}
}
}
第四个数据分析需求:计算每个 producer 下的商品的平均价格,并且按照平均价格降序排序
#计算每个 producer 下的商品的平均价格,并且按照平均价格降序排序
GET /product/_search
{
"size": 0,
"aggs": {
"producerAggs": {
"terms": {
"field": "producer.keyword",
"order": {
"_key": "desc"
}
},
"aggs": {
"avgPrice": {
"avg": {
"field": "price"
}
}
}
}
}
}
第五个数据分析需求:按照指定的价格范围区间进行分组,然后在每组内再按照 tag 进行分组,最后再计算每组的平均价格
#按照指定的价格范围区间进行分组,然后在每组内再按照 producer 进行分组,最后再计算每组的平均价格
GET /product/_search
{
"size": 0,
"aggs": {
"priceInterval": {
"range": {
"field": "price",
"ranges": [
{
"from": 0,
"to": 50
},
{
"from": 50,
"to": 100
},
{
"from": 100,
"to": 150
}
]
},
"aggs": {
"producerAggs": {
"terms": {
"field": "producer.keyword",
"size": 10
},
"aggs": {
"avgPrice": {
"avg": {
"field": "price"
}
}
}
}
}
}
}
}