ES 的增删改查

这里写目录标题

1 原生ES使用部分(在kibana上操作)

1.1 索引的建立、查看、删除

1 通过指定mapping 结构创建索引
PUT /product/
{
“mappings”: {
“properties”: {
“name”:{
“type”: “text”
},
“desc”:{
“type”: “text”
},
“price”:{
“type”: “float”
},
“date”:{
“type”: “date”
},
“tage”:{
“type”: “text”
}
}
}
}
2 通过插入数据动态创建索引
PUT /product/_doc/1
{
“name”:"xiaoMi has nfc ",
“price”: 39999,
“desc”:“xiao Mi Has Nfc”,
“data”:“2021-08-21”,
“tage”:[“huangmenji”,“chaohefen”,“koushuiji”]
}
3 索引的删除 :DELETE product
4 查询索引:GET _cat/indices?v 查看集群上所有索引情况。
5 查看某个索引内的所有数据 GET product/_search

1.1 文档的crud操作

1 修改文档数据:全量替换、指定字段更新
全量替换:PUT /product/_doc/1
自定字段替换:POST product/_update/1
{
“doc”: {
“price”: 89888
}
}

2 删除某文档数据 DELETE /product/_doc/1

DSL:构建复杂的查询条件

不要急着学用 Java Client 端去调用 Elasticsearch 接口。DSL 会了,Client 的也只是用法问题而已。

限制返回结果a、通过返回值进行限制,2、通过设置mapping限定返回的结果:
a :在GET product/_search下设置"_source":false,或设置"_source":”field”。;b:在inlucdes和excludes下设置返回的结果PUT /product2/
{
“mappings”: {
“_source”: {
“includes”:[
“name”,
“price”
],
“excludes”:[
“desc”
]
}
}
}

1.1.1 QuertString:查询索引某个字段含有keywords的文档、并可提供分页、排序功能(生产环境用的不多)

查询某个字段含有keywords的语句GET product/_search?q=name:xiaoMi&from=0&size=1&sort=price:asc

1.1.2 全文检索(用的多):

基本使用语法:GET product/_search
{
“query”: {
****
}
}
案例1:只当字段检索KeyWords
GET product/_search
{
“query”: {
“match”: {
“name”: “xiaoMi Nfc phone”
}
}
}
如果name包含 xiaoMi或者 Nfc 或者 phone就会被检索出来,并以评分高的排在前的顺序显示结果
案例2:返回所有结果
“match_all”: {}
把所有结果返回出来、
案例3:在多个字段内检索KeyWord
GET product/_search
{
“query”: {
“multi_match”: {
“query”: “xiaoMi huangmenji”,
“fields”: [“name”,“desc”]
}
}
}
相当于select * from table where a=xxx and b=xxx
案例4:短语搜索
GET product/_search
{
“query”: {
“match_phrase”: {
“name”: “xiaoMi has”
}
}
}
所查询的文档、被分词后必须包含 xiaoMi 和 has 关键字,且关键字的顺序必须连续。

1.1.3 精准查询 term (exact match)、范围查找range

Term:匹配和搜索词项完全相等的结果;简单说就是如果term=”xiaoMi phone” , xiaoMi phone不会被分词、当作一个item。在单排索引表中查找关键字xiaoMi phone包含的字段。文档在建立索引的时候关键字被拆分成了xiaoMi、phone等,不会存在xiaoMi phone,所以查询出来是空的。
GET product/_search
{
“query”: {
“term”: {
“name”: "xiaoMi has nfc”
}
}
}

Terms:相当于 sql中的in 例子如下
GET product/_search
{
“query”: {
“terms”: {
“tage”: [
“huangmenji”,
“chaohefen”
]
}
}
}
该例子相当于select * from table where tage in [“huagnmenji”,”chaohefen”].
查找price 大于等10000 小于等于20000的数据。
GET product/_search
{
“query”: {
“range”: {
“price”: {
“gte”: 10000,
“lte”: 20000
}
}
}
}

查找前一天的大数据。
GET product/_search
{
“query”: {
“range”: {
“date”: {
“gte”: “now -1d/d”,
“lte”: “now/d”
}
}
}
}
如果数据库中时间精度为yy-mm-dd,我们想加上具体时间。如下面的方法,数据库内的date属性默认在当天都是 yy-mm-dd 1:00。
GET product/_search
{
“query”: {
“range”: {
“date”: {
“time_zone”: “+01:00”,
“gte”: “now -1d/d”,
“lte”: “now/d”
}
}
}
}

1.1.4 过滤器filter

作用、用法、用途、和query的区别:

用法:GET product/_search
{
“query”: {
“constant_score”: {
“filter”: {
“term”: {
“name”: “xiaomi”
}
},
“boost”: 1.2
}
}
在 constant_score 内的filter中进行查询、查询值的权重不进行全职计算、默认为1.2。 也可以插入到bool查询中(后面会写)。
作用:如果要对全国14亿人口30-40岁的 女性 身高进行排序,如果全用match查询计算出值。那么会对14亿数据进行权重计算然后排序,使用filter后,先把30-40岁的女性过滤出来,结果筛选出来后在进行排序,减少计算。

1.1.5 组合查询 bool Query

Bool:可以组合多个查询条件,bool查询也是采用more_matches_is_beter 机制,因此满足must和should子句的文档将会合并起来计算分值。
Must: 必须满足子句(查询)、必须出现在匹配的文档中、并将有助于得分。
GET product/_search
{
“_source”: false,
“query”: {
“bool”: {
“must”: [
{
“match”: {
“name”: “xiaomi”
}
},
{
“match_phrase”: {
“tage”: “huangmenji”
}
}
]
}
}
}

Filter: 用法和Must很像结果也很像、唯一区别是计算值都为0。且filter存在缓存机制,下次再查询会直接返回结果。
GET product/_search
{
“_source”: false,
“query”: {
“bool”: {
“filter”: [
{
“match”: {
“name”: “xiaomi”
}
},
{
“match_phrase”: {
“tage”: “huangmenji”
}
}
]
}
}
}
Must_not: 把match匹配的字段全都排除、不匹配的字段筛选出来。GET product/_search
{
“_source”: false,
“query”: {
“bool”: {
“must_not”: [
{
“match”: {
“name”: “xiaomi”
}
}
]
}
}
}
Should:满足任意一个查询条件就能被筛选出来。类似于sql语句的or。
GET product/_search
{
“_source”: false,
“query”: {
“bool”: {
“should”: [
{
“match”: {
“name”: “xiaomi”
}
} ,
{
“term”: {
“tage”: “huangmenji”
}
}
]
}
}
}

组合查询:案例结合msut和filter,联合查询减少计算资源的消耗。如:计算中国14亿人口中女性、且年龄在20-30之间,并对所有相关度进行评分。
GET product/_search
{
“_source”: false,
“query”: {
“bool”: {
“filter”: [
{
“range”: {
“age”: {
“gte”: 20,
“lte”: 30
}
}
}
],
“must”: [
{
“term”: {
“sex”: “female”
}
}
]
}
}
}首先进行filter进行性别筛选、其次在对sex进行must匹配,经过must匹配后本身会进行相关度计算。
案例二:should和filter进行组合,因为已经存在filter、所以should
默认可以一个条件也不用满足,我们可以设置minimum_should_match指定should内必须满足的条件数量。
GET product/_search
{
“_source”: false,
“query”: {
“bool”: {
“filter”: [
{
“range”: {
“age”: {
“gte”: 20,
“lte”: 30
}
}
}
],
“should”: [
{
“term”: {
“sex”: “female”
}
}
],
“minimum_should_match”: 1
}
}
}
案例三:子句里面嵌入bool查询
{
“_source”: false,
“query”: {
“bool”: {
“filter”: [
{
“range”: {
“age”: {
“gte”: 20,
“lte”: 30
}
}
}
],
“should”: [
{
“match”: {
“sex”: “female”
}
},{
“match”: {
“local”: “beijing”
}
},
{
“bool”: {
“must”: [
{
“firstname”:“tang”
}
]
}
}
],
“minimum_should_match”: 1
}
}
}

增加文档数据

自动增加文数据:ES作为数据存储服务器, 应用程序中的数据直接对接到ES中, 这种场景适合自动生成id。在多节点并发生成大量数据的场景下, 自动生成id更具安全性.
POST /product/_doc/
{
“name”:“nfc”,
“price”: 40001,
“desc”:“xiao Mi Has Nfc”,
“data”:“2021-08-22”,
“tage”:[“huangmenji”,“chaohefen”,“koushuiji”]
}
手动增加文档数据 PUT /product/_doc/3。

删除某条文档数据

DELETE product/_doc/2

需要注意的点:

1、 索引名字必须大写;
2、 当使用query查询时,默认"hits" : [ ]只返回10条数据

我们需要设置size,突破10条限制;
3、term、terms、match、match_phrase 词的意义。

2 分词器

**本段落必读:分析器的配置中可以选用一个或多个字符过滤器(character filter),字符过滤器是对原始文本进行字符流级别的操作。它通常可以用于大小写转化,去除字母上标等等。在字符过滤器之后是分词器(Tokenizer),它是必须要配置的。分析器会使用分词器将字符流切分成词元(Token)系列,通常用在空格处切分这种简单的算法。后面的步骤是可选的,比如词元过滤器(Token Filter)(一般简称过滤器),会对词元(Token)进行许多种操作。最后产生的词元会被称为词(Term),即用于Lucene实际索引和查询的单位。注意有些词元过滤器如WordDelimeterFilterFactory也进行分词操作,但是它们是在词元上操作,而真正的分词器是操作一个字符流。

2.1 文档正常化:normalization

作用:提高单词的召回率。
过程:把分词后的英文单词大写转小写。语气词、符号不作为分分词后的关键词。把复数转为单数。把时态转为现代时。

2.2 字符过滤器:character_filter

过滤那些内容?如下2.2.1、2.2.2、2.2.3所示。
设置位置:setting:{analysis:{char_filter:{}}};
定义:字符过滤器在元素中定义,它是对字符流进行处理。字符过滤器种类不多

2.2.1过滤HTML标签html_strip、并可选择性保留” escaped_tags”哪些标签

PUT my_index
{
“settings”: {
“analysis”: {
“char_filter”: { //设置内置char_filte属性
“my_char_filter”:{ //自己取的名字
“type”:“html_strip”, //设置过滤html标签
“escaped_tags”:[“a”] // 保留a标签
}
} ,
“analyzer”:{
“my_analyzer”:{
“tokenizer”:“keyword”, // 设置分词类型,keyword分词器输出和它接收到的相同的字符串。即不分词,可以设置关键字的长度buffer_size默认256
“char_filter”:[“my_char_filter”],// 我们设置的过滤器
}
}

}

}
}
效果如下图所示

2.2.2 自定义哪些字符需要过滤mapping,而可用于敏感词替换。

敏感词替换

PUT my_index
{
“settings”: {
“analysis”: {
“char_filter”: {
“my_char_filter”:{
“type”:“mapping”,
“mappings”:[
“fuck => *”,
“sick => *”
]
}
} ,
“analyzer”:{
“my_analyzer”:{
“tokenizer”:“keyword”,
“char_filter”:[“my_char_filter”]
}
}
}

}
}
效果如图:

2.2.3 正则替换pattern_replace

案例匹配手机号的正则,把手机好的前三位、后四位保留,其他替换成*
PUT my_index
{
“settings”: {
“analysis”: {
“char_filter”: {
“my_char_filter”:{
“type”:“pattern_replace”,
“pattern”:"(\d{3})\d{4}(\d{4})",
“replacement”:"$1****$2"
}
} ,
“analyzer”:{
“my_analyzer”:{
“tokenizer”:“keyword”,
“char_filter”:[“my_char_filter”]
}
}

}

}
}
效果如图:

2.3 词元过滤器 token filter

使用场景:停用词、时态转换、大小写转换、同义词转换、语气词处理等,比如has=have him =>he apples=》apple the/oh/a=》消除等。
使用案例同义词替换:
PUT /test_index
{
“settings”: {
“analysis”: {
“filter”: {
“synoym”:{
“type”:“synoym”,
“synoym_path”:“analysis/synonym.txt” //指config包下的路径
}
},
“analyzer”: {
“synoym”:{
“tokenizer”:“whitespace”, // whitespace 类型的分词将文本通过空格进行分词
“filter”:[“synoym”]
}
}
}
}
}
具体使用去ES官网查询。
自定义同义词替换:
PUT /test_index
{
“settings”: {
“analysis”: {
“filter”: {
“my_synoym”:{
“type”:“synonym”,
“synonyms”:[“赵,钱,孙,李=>吴”]
}
},
“analyzer”: {
“my_analyzer”:{
“tokenizer”:“standard”,
“filter”:[“my_synoym”]
}
}
}
}
}
查看效果

2.4 分词器 tokenizer:切词

作用:把document进行切词、切成一个一个的词项iterm。
我们默认使用的分词器是standard,按照英文之间的空白符拆成一个个iterm,不满足中文的需求。中文分词器一般使用ik_max_word。官方一共给了15中分词器、具体就不一一列举。
常用分词器:
standard analyzer(前面已解释)、
pattern tokenizer:以正则匹配分隔符、把文本拆成若干词项)、
simple pattern tokenizer:以正则匹配分隔符、把文本拆成若干词项,速度比pattern tokenizer快 、
whitespace analyzer: 空白符分割。
这里介绍一下停用词:有些词在文本中出现的频率非常高,但是对文本所携带的信息基本不产生影响。文本经过分词之后,停用词通常被过滤掉,不会被进行索引。、在检索的时候,用户的查询中如果含有停用词,检索系统也会将其过滤掉(因为用户输入的查询字符串也要进行分词处理)。、排除停用词可以加快建立索引的速度,减小索引库文件的大小。
停用词的使用:“filter”: {
“my_stopwords”: {
“type”: “stop”,
“stopwords”: [“the”, “a”,“is”,“on”]
}
},

2.5 自定义分词器 custom_analysis

本小节使用一个自定义分词器的案例:本自定义分词器自定义了char_filter字符过滤器:该过滤器使用的是自定义的名为“&_to_and”字符过滤器,让每个&字符结果显示为and。
Tokenizer分词器使用的是自定义的名为“my_tokenizer”分词器,该分词器会将"the", “a”,“is”,“on” 设为判断分词条件。
Filter 过滤器使用的是自定义名为my_stopwords的过滤器吗,也成为停用词,被设为停用词的字符会被过滤掉,不会被进行索引。
PUT /my_index3
{
“settings”: {
“analysis”: {
“char_filter”: {
“&_to_and”: {
“type”: “mapping”,
“mappings”: ["&=> and"]
}
},
“filter”: {
“my_stopwords”: {
“type”: “stop”,
“stopwords”: [“the”, “a”,“is”,“on”]
}
},
“tokenizer”: {
“my_tokenizer”:{
“type”:“pattern”,
“pattern”:"[,!.]"
}
},
“analyzer”: {
“my_analyzer”: {
“type”: “custom”,
“char_filter”: [“html_strip”, “&_to_and”],
“tokenizer”: “my_tokenizer”,
“filter”: [“lowercase”, “my_stopwords”]
}
}
}
}
}
使用:

结果:

2.6 中文分词器

中文分词器ik,使用时需要去github上去下载,下载是注意下载自己ES对应的版本,否则服务启动失败
安装步骤:1、把下载好的文件解压重命名为ik、2、放到es的plugins文件夹下面3、重启es服务。
实际效果查看:这里面我们使用的是ik_max_word拆分的力度大,如果使用ik_smart拆分的力度小

2.6.1 ik内的文件解析

在ik包的config包下存在各种配置文件,针对这些文件我们来讲解
Extra_main文件中存的是汉字中经常使用的39.9w个常用词汇。
Stopword 文件中存放停用的引文单词、不会建立在倒排索引中。
特殊词库:quantifier:计量单位等;suffix:后缀名比如xxx乡,乡就是后缀名;surname:百家姓;preposition:语气词。

2.6.2 扩展分词词汇

如果我们一些网络词汇没有怎么办呢?如大g,这类词本身没有,我们就需要去扩展:1、先去创建自己的词典
2再去IKAnalyzer文件进行配置。
接下来我们看看扩展后的效果
效果为:

2.6.3 基于远程词库的热更新

在实际生产环境中,我们不能经常去改动扩展词库、每改一次就会重启一次服务,这种情况我们使用远程词库进行热更新,ES自动捕获远程状态改变情况。
现在我们就开始配置!
1、 配置我们的远程接口代码如下:
其中需要我们注意的是,我们根据url带的参数wordlib来判断读取的是扩展字典还是扩展停止字典

可copy直接用:
@RestController
@RequestMapping(value = “/api”)
public class EsController {
@RequestMapping(value = “hotword”)
public void apiWord(HttpServletResponse response, Integer wordlib) throws IOException {
File file = new File(wordlib==1?“D:\ES\elasticsearch-7.14.0\plugins\ik\config\custom\extend1.dic”:“D:\ES\elasticsearch-7.14.0\plugins\ik\config\custom\extend2.dic”);
FileInputStream fis = new FileInputStream(file);
byte[] buffer= new byte[(int)file.length()];
response.setContentType(“text/explain;charset=utf-8”);
response.setHeader(“Last-Modified”,String.valueOf(buffer.length));//查看我们的数据是否发生变化
response.setHeader(“ETag”,String.valueOf(buffer.length));//验证资源是否修改
int offset = 0;
while (fis.read(buffer,offset,buffer.length-offset)!=-1){

    }
    OutputStream out = response.getOutputStream();
    out.write(buffer);
    out.flush();
    fis.close();
}

}

前端访问:能读取字符串(这里显示的是停用词,扩展字典后面参数值改为1即可显示)。

2、 在ES服务器端的IKAnalyzer.cfg文件上配置远程接口地址。
看看结果

2.6.4 基于mysql的热更新

可根据:https://www.cnblogs.com/dalianpai/p/13892123.html完成配置。

3 聚合查询Aggregations

导读:ES学习最重要的部分是QueryDSL,其次就是Aggregation(从实用角度),ES三大功能为1、分布式的查询;2、分析;3、存储引擎;DSL完成的是分布式查询、那么Aggregation的作用就是分析.
本章节根据生活的例子,来讲解Aggregations能实现常用的功能。本章的聚合分析要讲三个:1、分桶聚合(Bucket agregations),指标聚合(Metrics agregations),3、管道聚合(Pipline agregations)。

3.1 聚合分析

我们结合以下四个案例来讲解聚合分析如何实现。案例的ES数据库构建代码在附录。
1、 某商城各个品牌手机的月销量是多少?
2、 中国不同地区的消费水平
3、 网站的的平均响应时长
4、 我们产品的消费群体在不同年龄段的分布式情况。
函数格式:GET product1/_search
{
“aggs”: {
“<aggs_name>”: { //自定义聚合名字
“AGG_TYPE”: {//聚合类型
“field”:<field_name>
}
}
}
}

3.1.1 分桶聚合Bucket aggregations+排序

解释就是把不同种类分到不同的桶 :如以下文章被分到该标签的”桶”内(当然正常情况还应该显示该标签所包含数据的数量),使用term操作,其实就类似于sql语句的group by。

Aggregation支持嵌套查询,比如java标签内部又可分为多线程、jvm等子标签。
具体案例: 对tags的标签,按包含该标签的文档数量进行排序:GET product1/_search
{
“size”: 0,
“aggs”: {
“aggs_tag”: {
“terms”: {
“field”: “tags.keyword”,
“size”: 3,
“order”: {
“_count”: “desc”
}
}
}
}
}
参数说明:该代码第一个红色框表示的是显示集中该tages分类;order下第二红色方框内参数表示,按照文档出现的次数降序出现。当然order内的_count参数可以更改为_term,结果就是对tag的名字进行文字的排序按ABC……,系版本改为了_key
doc_value和fielddata:
值得注意的是 聚合索引更适合作用到doc_value(正排索引)类型的字段上,如果该倒排索引的type不是:keyword。就需要用到该倒排索引的fielddata 上,如下:
我们原本tags的类型是text,要被分词,不属于doc_value,只有用到该字段的fielddata上,也就是tags.keyword 该字段子属性才能被统计。

还需要注意的是,针对需要分词的字段设置了fielddata,可以使用聚合,我们设置fielddata为true之后,在索引期间,就会以列式存储在内存中。Fielddata不是在索引创建期间被创建的,而是在执行查询期间动态创建,速度可能会比较慢且容易内存溢出。所以聚合分析时不建议用fileData。使用doc_value的字段在聚合分析前就创建好了存入磁盘,且如果确定不使用聚合分析时,把fielddata关掉。更深的原理可以看下面这篇https://blog.csdn.net/zhanglh046/article/details/78536143

3.1.2 指标聚合Metrics aggregations

包含了很多:

案例:搜索最贵、最便宜和平均价格三个指标。

结果为:

当然也可以使用stats一次性解决,结果为:

查找按照name去重后的数量,cardinality相当于distinct count()
GET product1/_search
{
“size”: 0,
“aggs”: {
“name_count”: {
“cardinality”: {
“field”: “name.keyword”
}
}
}
}

3.1.3 管道聚合Pipeline aggregations(二次聚合),一定要注意代码结构,与需要聚合的那一层的名字对齐,比如与”type_bucket”对齐。

概念:对聚合的结果二次聚合。
父类:父级 和兄弟级。
语法:buckets_path。
案例:统计平均价格最低的商品分类。
我们的思路:1、对所有商品进行分桶聚合,也就是商品分类。2、 对第一次聚合的商品类别,进行第二次聚合、也就是进行指标聚合,找出最低的商品类别。
实际案例,统计平均价格最低的商品分类:1、对每个商品进行分类并计算出每组分类的平均价,2、使用管道聚合,选取这么多上分类中价格最低的指标。
GET product1/_search
{
“size”: 0,
“aggs”: {
“type_bucket”: {
“terms”: {
“field”: “tags.keyword”,
“size”: 30
},
“aggs”: {
“price_bucket”: {
“avg”: {
“field”: “price”
}
}
}
},
“min_bucket”:{
“min_bucket”: {
“buckets_path”: “type_bucket>price_bucket”
}
}
}
}
结果为:

我们可以看到上图结果分为两个部分:
第一部分是”type_bucket”,其中terms中的field指出了桶分类的依据,在该terms后紧跟着嵌套了一个aggs,代表在每个桶内都会执行第二个指标聚合函数。

第二部分是操作是一个Pipeline 操作如下图所示:由此我们可以学习以下管道聚合的写法,第一个红方框是自定义返回结果的名字,红方框内的第一个名字是函数名,代表选取桶内最小值。第二个红方框使用了"buckets_path": "AAA>BBB”的写法,AAA代表的是按照什么分类的桶集合的名字,BBB是该AAA桶集合中,具体的桶内属性名。

3.1.4 嵌套聚合—基于聚合结果的聚合

这一段主要讲的是一些复杂的场景,比如统计不同类型、不同档次的 价格和不同类型、不同档次tag分类。。

我们可以查看对应的嵌套关系。
案例二:
统计每个商品类型中,不同档次分类商品中平均价格最低的档次,首先我们先写个错误的写法:这种选取价格最低的函数与type_aggs对齐,本身就是错的,因为我们的最低价格对应的是type桶内lv桶的价格。改正措施为:将最低函数与"lv_type"进行对齐。

改正后为: GET product1/_search
{
“size”: 0,
“aggs”: {
“type_aggs”: {
“terms”: {
“field”: “type”,
“size”: 20
},
“aggs”: {
“lv_type”: {
“terms”: {
“field”: “lv”,
“size”: 10
},
“aggs”: {
“price_avg”: {
“avg”: {
“field”: “price”
}
}
}
},
“min_bucket”: {
“min_bucket”: {
“buckets_path”: “lv_type>price_avg”
}
}
}
}
}
}
代码结构如图:

3.1.5 基于查询结果的聚合和基于聚合结果的查询

以上我们查询出来的聚合结果都,是对hit[]里面的数据之上进行agg的运算。当函数存在qery查询方法 和 agg函数时,我们agg的结果和query结果是什么样子的呢?
先说结果,我们agg是对query的结果进行操作的。 —基于查询结果聚合

GET product1/_search
{
“size”: 20,
“query”: {
“range”: {
“price”: {
“gte”: 3999,
“lte”: 9000
}
}
},
“aggs”: {
“tag_bucket”: {
“terms”: {
“field”: “tags.keyword”,
“size”: 10
}
}
}
}
我们分析下结果:

首先是hit部分:
我们的hits只有4条,这是query的结果。
我们的agg的结果也可以显示,这是从query之后的hits结果之上进行操作。当然我们可以用filter代替query。且query与agg的顺序对结果不影响。
以上是先查询再聚合,那么我们如何实现先聚合再查询呢?
我们需要把query变成post_filter:
首先我们分析下什么场景下需要用到先聚合后查询:
结果:

结果显示:hits中显示所有结果,agg显示分桶后的标签和数量,但是如何显示,比如这数量为3,含性价比的文档呢?
代码为:
GET product1/_search
{
“size”: 20,
“aggs”: {
“tag_bucket”: {
“terms”: {
“field”: “tags.keyword”,
“size”: 10
}
}
},
“post_filter”: {
“term”: {
“tags.keyword”: “性价比”
}
}
}
显示的结果为:

This里面就能显示聚合后,在进行查询的结果了–该3个包含性价比标签的文档的相信数据。
再介绍一个场景:再一个get操作中,如何显示可同时显示部分查询后的agg和全部文档的agg?需要加global{}
案例:显示价格为3000-8000商品的各个指标与显示所有商品的价格的各个指标
GET product1/_search
{
“size”: 0,
“query”: {
“range”: {
“price”: {
“gte”: 3000,
“lte”: 8000
}
}
},
“aggs”: {
“stas_price”: {
“stats”: {
“field”: “price”
}
},
“all_price”: {
“global”: {},
“aggs”: {
“allprice”: {
“stats”: {
“field”: “price”
}
}
}
}
}
}
代码结构为:

因为需要对全局数据进行取值global需要卸载aggs上面,代码结构可能一下子记不住,以后多练练就好了。
我们看看结果:

如果我们把global换成其他fiter,则下面的agg查询的结果是再此filter和上级别的query条件取交集。

3.1.6 聚合后指定数据进行排序:

回顾一下,物品,我们之前在操作函数里面在添加order函数进行排序:order里面的参数有_count 按照数量进行排序, _term按照key的名字abcd……的顺序进行排序.

现在有个多层聚合后排序的案例:对不同type,且tags包含耳机、手机的商品的各指标。对该指标进行排序。GET product1/_search
{
“size”: 0,
“aggs”: {
“type_buckets”: {
“terms”: {
“field”: “type”,
“order”: {
“agg_stats>stats.min”: “asc”
}
},
“aggs”: {
“agg_stats”: {
“filter”: {
“terms”: {
“type”: [
“手机”,
“耳机”
]
}
},
“aggs”: {
“stats”: {
“stats”: {
“field”: “price”
}
}
}
}
}

}

}
}

结构如图

结果为:

4 批量操作

4.1 基于_mget的批量查询

最简单批处理语句
GET product1/_mget
{
“ids”:[ 2,3,4 ]
}
结果为

当我们需要指定显示的字段时:
GET product1/_mget
{
“docs”: [
{
“_id”: 2,
“_source”: [
“name”,
“price”
]
},
{
“_id”: 3
}
]
}
结果为:

4.2 文档的操作类型
1、create:不存在则创建
2、delete:删除文档
3、 update: 全量替换或部分更新
4、index:索引(动词):对当前一条数据创建索引—索引一条数据。
1、create:不存在则创建,存在则报错:
PUT product1/_doc/1

现在用_creat这个API进行操作,可以显示更多详细信息。
创建后自动生成id:POST product/_doc
{
“name”:“macBook”
}

2、delete:删除文档
删除其实是软删除。
3、 update: 全量替换或部分更新
部分替换:
POST /product/_update /F0w1lnsBh5Z0pt7x-T9N
{
“doc”:{
“name”:“macbook pro”
}
}
全量替换:
PUT /product/_doc/F0w1lnsBh5Z0pt7x-T9N
{
“doc”:{
“name”:“macbook pro”
}
}
4、 index 可以是创建、也可以是全量替换:如果数据是存在则全量替换、如果不存在则创建
PUT /product/_doc/F0w1lnsBh5Z0pt7x-T9N 全量替换
PUT /product/_create/F0w1lnsBh5Z0pt7x-T9N 全量创建

4.3 基于_bulk的增删改

好处:批量操作不消耗额外内存
格式:
POST /bulk
POST //_bulk
{action:{metedata}}
{data}

创建的实例为:
POST _bulk
{“create”:{"_index":“product”,"_id":2}}
{“name”:"_bulk creat"}

删除的实例为:
POST _bulk
{“delete”:{"_index":“product”,"_id":2}}

修改的实例为: --部分修改
POST _bulk
{“update”:{"_index":“product”,"_id":2}}
{“name”:"_bulk creat2"}

怎么区分失败和成功的语句?
POST _bulk?filter_path=items.*.error
{“create”:{"_index":“product”,"_id":2}}
{“name”:"_bulk creat"}
{“create”:{"_index":“product”,"_id":12}}
{“name”:"_bulk creat"}
{“delete”:{"_index":“product”,"_id":3}}
{“updata “:{”_index”:“product”,"_id":5}}
{“name”:"_bulk creat"}
{“updata”:{"_index":“product”,"_id":1}}
{“name”:"_bulk creat"}
结果为:会显示是哪一条语句错了

4 ES客户端

数据库组成:客户端(kibana)+服务端+存储引擎
ES客户端是什么?
ES客户端是连接服务端的工具它包括:Rest API(本章节出现的代码绝大多数都是使用这个API),java API,JAVA REST API……
看看JAVA API 和 JAVA REST API的区别:
JAVA PIA:(7.x 暂时还在用,正在被遗弃,8.x完全弃用了)他的客户端是Transport,不同集群节点之间,节点和客户端Client之间沟通(也就是TransportClient)都使用的是Transport。

JAVA API

附录
1.1 返回结果值的说明
GET product/_search,不使用括号内的条件查询等于
GET product/_search
{
“query”: {
“match_all”: {}
}
}
查看结果返回的内容:

took: 查询消耗时间
timeout:是否超时
shards:分片情况
hits:返回结果
hits{ hits :_source:{}} 返回元数据
1.2 ES聚合分析数据库构建代码

聚合分析

PUT product1
{
“mappings”: {
“properties”: {
“name”:{
“type”: “text”,
“analyzer”: “ik_max_word”,
“fields”: {
“keyword”:{
“type”:“keyword”,
“ignore_above”:265
}
}
},
“desc”:{
“type”: “text” ,
“fields”: {
“keyword”:{
“type”:“keyword”,
“ignore_above”:265
}
}
},
“price”:{
“type”: “float”
},
“lv”:{
“type”: “keyword”
},
“type”:{
“type”: “keyword”
},
“createtime”:{
“type”: “date”
},
“tags”:{
“type”: “text” ,
“fields”: {
“keyword”:{
“type”:“keyword”,
“ignore_above”:265
}
}

  }
}

}
}

PUT /product1/_doc/1
{
“name”:“小米手机”,
“desc”:“手机中的战斗机”,
“price”:3999,
“lv”:“旗舰机”,
“type”:“手机”,
“createtime”:“2020-10-01”,
“tags”:[“性价比”,“发烧”,“不卡顿”]
}
PUT /product1/_doc/2
{
“name”:“小米NFC手机”,
“desc”:“支持全功能NFC,手机中的滑翔机”,
“price”:4999,
“lv”:“旗舰机”,
“type”:“手机”,
“createtime”:“2020-05-21”,
“tags”:[“性价比”,“发烧”,“公交卡”]
}
PUT /product1/_doc/3
{
“name”:“NFC手机”,
“desc”:“手机中的轰炸机”,
“price”:2999,
“lv”:“旗舰机”,
“type”:“手机”,
“createtime”:“2020-06-20”,
“tags”:[“性价比”,“快充”,“门禁卡”]
}
PUT /product1/_doc/4
{
“name”:“小米耳机”,
“desc”:“耳机中的黄焖鸡”,
“price”:999,
“lv”:“百元机”,
“type”:“耳机”,
“createtime”:“2020-06-23”,
“tags”:[“降噪”,“防水”,“蓝牙”]
}
PUT /product1/_doc/5
{
“name”:“红米耳机”,
“desc”:“耳机中的肯德基”,
“price”:333,
“lv”:“百元机”,
“type”:“耳机”,
“createtime”:“2020-07-20”,
“tags”:[“防火”,“低音炮”,“听声辩位”]
}
PUT /product1/_doc/6
{
“name”:“小米手机10”,
“desc”:“充电贼快掉电更快,超级无敌望远镜,高刷电竞屏”,
“price”:5999,
“lv”:“旗舰机”,
“type”:“手机”,
“createtime”:“2020-07-27”,
“tags”:[“120HZ刷新率”,“120W快充”,“120倍变焦”]
}
PUT /product1/_doc/7
{
“name”:“挨炮SE2”,
“desc”:“除了CPU一无是处”,
“price”:3299,
“lv”:“旗舰机”,
“type”:“手机”,
“createtime”:“2020-07-21”,
“tags”:[“割韭菜”,“割韭菜”,“割新韭菜”]
}
PUT /product1/_doc/8
{
“name”:“XS Max”,
“desc”:“听说要出12手机了,终于可以换掉手中的4s了”,
“price”:4399,
“lv”:“旗舰机”,
“type”:“手机”,
“createtime”:“2020-08-19”,
“tags”:[“SV1A”,“4G全网通”,“大”]
}
PUT /product1/_doc/9
{
“name”:“小米电视”,
“desc”:“70寸性价比之选,不要一万八,不要把钱吧,只要两千九百九十八”,
“price”:2988,
“lv”:“高端机”,
“type”:“电视”,
“createtime”:“2020-08-16”,
“tags”:[“巨屏”,“家庭影院”,“游戏”]
}
PUT /product1/_doc/10
{
“name”:“红米电视”,
“desc”:“我比上边更划算,我也2988,我也70寸,但我更好看”,
“price”:2988,
“lv”:“高端机”,
“type”:“电视”,
“createtime”:“2020-08-28”,
“tags”:[“大屏”,“蓝光8k”,“超薄”]
}
PUT /product1/_doc/11
{
“name”:“红米电视”,
“desc”:“我比上边更划算,我也2988,我也70寸,但我更好看”,
“price”:2988,
“lv”:“高端机”,
“type”:“电视”,
“createtime”:“2020-08-28”,
“tags”:[“大屏”,“蓝光8k”,“超薄”]
}

Guess you like

Origin blog.csdn.net/qq_36737214/article/details/120535141