Elasticsearch的CRUD操作、多种复杂搜索查询方式和简单的集群管理

本文大纲

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"
              }
            }
          }
        }
      }
    }
  }
}

 

 

 

 

 

© 版权声明

☆ END ☆
喜欢就点个赞吧
点赞0 分享
图片正在生成中,请稍后...