Elastic Search

ELASTICSEARCH

esmysql2
索引数据库
类型数据表
文档数据
  • 倒排索引机制
    • 分词:将整句拆分为单词,保存每个单词都有哪些记录与之对应
    • 检索:将检索拆分为单词,并查询对应记录
    • 相关性得分:谁的正确率更高

docker上安装

  • 命令free -m可查看剩余内存

下载

  • es
docker pull elasticsearch:7.6.2
  • kibana(用于可视化,相当于sqlyog,可不安装,后面用postman等工具)
docker pull kibana:7.6.2

启动、设置开机自启

  • 新建目录
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo "http.host: 0.0.0.0" >>/mydata/elasticsearch/config/elasticsearch.yml
  • 权限
chmod -R 777 /mydata/elasticsearch/
  • chmod -R 777:任何用户、任何组可读、可写、可执行
  • 写入http.host: 0.0.0.0,允许所有ip访问
  • 启动Elastic search
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e  "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx128m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v  /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.6.2 
  • -Xms64m -Xmx128m:初始64M,es最多占用128M
  • -d:后台
  • 设置开机启动elasticsearch
docker update elasticsearch --restart=always
  • 启动kibana:(注意ip替换为虚拟机ip
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://ip:9200 -p 5601:5601 -d kibana:7.6.2

让kibana在http://ip:9200访问es

  • 设置开机启动kibana
docker update kibana  --restart=always

测试

  • 查看elasticsearch版本信息: http://ip:9200/
    返回json数据

  • 显示elasticsearch 节点信息http://ip:9200/_cat/nodes
    在这里插入图片描述

  • 访问Kibana: http://ip:5601/app/kibana

工具推荐

在线版postman(送给不想安装postman的)

注意:带请求体的get请求发不了!

  • 网址:https://www.apifox.cn/
  • 注册邮箱可随意,无需验证码
  • 然后就可以使用:
  • 使用web版
    在这里插入图片描述
  • 点示例项目
    在这里插入图片描述
  • 类似postman:
    在这里插入图片描述
    注意:get请求看似可以带body,实际应该并没有带!例如,可以测试查询部分字段,实际还是查了所有

idea插件(送给不想安装kibana的)

1、Cap-elasticsearch-client

注意:聚合函数aggs使用不了,无法返回聚合内容!

  • 在插件中搜索elasticsearch选第三个·
    在这里插入图片描述
  • 配置前两个就好了
    在这里插入图片描述
  • 选倒数第二个图标 -> Dev Tools -> console
    在这里插入图片描述
  • 简单使用:
    在这里插入图片描述

2、elasticsearch query-EDQL

  • 下图选第二个
  • 相对上一个插件(Cap-elasticsearch-client),它可以用聚合函数!
    在这里插入图片描述

初步检索

_CAT

  • GET/_cat/nodes:查看所有节点
    如:http://ip:9200/_cat/nodes:
    在这里插入图片描述

    注:*表示集群中的主节点

  • GET/_cat/health:查看es健康状况
    如: http://ip:9200/_cat/health
    在这里插入图片描述

    注:green表示健康值正常

  • GET/_cat/master:查看主节点
    如: http://ip:9200/_cat/master
    在这里插入图片描述

  • GET/_cat/indices:查看所有索引 ,等价于mysql数据库的show databases;
    如: http://ip:9200/_cat/indices

索引一个文档

  • 保存一个数据:保存在哪个索引的哪个类型下,指定用哪个唯一标识。

  • 例如:发PUT请求 http://ip:9200/customer/external/1携带如下json数据:

    {
     "name":"John Doe"
    }
    
  • 返回结果:
    在这里插入图片描述

  • 请求解释:在customer索引下的external类型下保存1号数据,数据内容为json内容

  • 返回结果解释:

    • 这些带有下划线开头的,称为元数据,反映了当前的基本信息。

    • "_index": "customer" 表明该数据在哪个数据库下;

    • "_type": "external" 表明该数据在哪个类型下;

    • "_id": "1" 表明被保存数据的id;

    • "_version": 1 被保存数据的版本

    • “result”: “created” 这里是创建了一条数据,如果重新put一条数据,则该状态会变为updated,并且版本号也会发生变化。

  • PUT和POST:

    • POST:新增。如果不指定id,会自动生成id。指定id就会修改这个数据,并新增版本号;
    • PUT:可以新增也可以修改。PUT必须指定id;由于PUT需要指定id,我们一般用来做修改操作,不指定id会报错。
  • 选用POST方式:

    • 添加数据的时候,不指定ID,会自动的生成id,并且类型是新增:
      在这里插入图片描述

查看文档

  • GET /customer/external/1
    http://ip:9200/customer/external/1
    在这里插入图片描述

  • _seq_no
    在请求后面加“?if_seq_no=1&if_primary_term=1 ”,当序列号匹配的时候,才进行修改,否则不修改。

  • 示例:

    • 第一次put请求:http://ip:9200/customer/external/1?if_seq_no=6&if_primary_term=1 参数:
      {
       "name":"1"
      }
      
    • 成功:
      在这里插入图片描述
    • 再发一次:409
      在这里插入图片描述

更新文档

1、 POST更新文档:

  • http://ip:9200/customer/external/1/

    {
    	"name":"update"
    }
    

    在这里插入图片描述
    再发一次:
    在这里插入图片描述
    两次都是更新

  • http://ip:9200/customer/external/1/_update ,数据:(相对上一次没变)

    {
        "doc":{
            "name":"update"
        }
    }
    

    结果:(result是noop,_sep_no等信息没变)在这里插入图片描述

2、put更新:
不可带_update,不会对比原来的数据,同不带_update的post。


带_update (post)不带_update (post、put)
数据封装在doc里直接写要更新的数据
会对比原来的数据不会对比原来的数据
如果要改数据与原数据相同,_seq_no等信息不变无论如何,_seq_no等信息都会改变

删除文档或索引

  • DELETE请求 http://ip:9200/customer/external/1

  • DELETE请求 http://ip:9200/customer
    注:elasticsearch并没有提供删除类型的操作,只提供了删除索引和文档的操作。

  • 实例:删除id=1的数据,删除后继续查询
    在这里插入图片描述
    在这里插入图片描述

  • 实例:删除整个costomer索引数据,再查,显示索引找不到
    在这里插入图片描述
    在这里插入图片描述

bulk批量api

  • 语法格式:(每两行是一部分)

    {action:{metadata}}
    {request body  }
    
    {action:{metadata}}
    {request body  }
    
    
  • 这里的批量操作,当发生某一条执行发生失败时,其他的数据仍然能够接着执行,也就是说彼此之间是独立的。

  • bulk api以此按顺序执行所有的action(动作)。如果一个单个的动作因任何原因失败,它将继续处理它后面剩余的动作。当bulk api返回时,它将提供每个动作的状态(与发送的顺序相同),所以您可以检查是否一个指定的动作是否失败了。


实例1: 执行多条数据(如果有错误,请在数据最后加一个空行)

POST customer/external/_bulk
{"index":{"_id":"1"}}
{"name":"John Doe"}
{"index":{"_id":"2"}}
{"name":"John Doe"}

或者发送post请求,http://ip:9200/customer/external/_bulk,将上面的数据删除第一行作为请求体

执行结果(部分)
在这里插入图片描述

如果有报错:
在这里插入图片描述
请在请求体最后加上一个空行,例如:
在这里插入图片描述


实例2:对于整个索引执行批量操作

POST /_bulk
{"delete":{"_index":"website","_type":"blog","_id":"123"}}
{"create":{"_index":"website","_type":"blog","_id":"123"}}
{"title":"my first blog post"}
{"index":{"_index":"website","_type":"blog"}}
{"title":"my second blog post"}
{"update":{"_index":"website","_type":"blog","_id":"123"}}
{"doc":{"title":"my updated blog post"}}

或者发送post请求,http://ip:9200/_bulk,将上面的数据删除第一行作为请求体

运行结果:(部分)
在这里插入图片描述

样本测试数据

网址:https://github.com/elastic/elasticsearch/blob/7.4/docs/src/test/resources/accounts.json

POST bank/account/_bulk
或:
post方式请求http://ip:9200/bank/account/_bulk
在这里插入图片描述
查看索引信息:
GET/_cat/indices
在这里插入图片描述

注意

前面主要使用apifox进行学习,由于其无法发送带请求体的get请求,后面学习将采用idea插件进行!

检索

search Api

ES支持两种基本方式检索;

  • 通过REST request uri 发送搜索参数 (uri +检索参数);
  • 通过REST request body 来发送它们(uri+请求体);

实例:

  • uri +检索参数
GET bank/_search?q=*&sort=account_number:asc
  • uri+请求体进行检索
GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" },
    {"balance":"desc"}
  ]
}

返回结果:
在这里插入图片描述
只返回10条数据
在这里插入图片描述

详细的字段信息,参照: https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started-search.html

The response also provides the following information about the search request:

  • took – how long it took Elasticsearch to run the query, in milliseconds
  • timed_out – whether or not the search request timed out
  • _shards – how many shards were searched and a breakdown of how many shards succeeded, failed, or were skipped.
  • max_score – the score of the most relevant document found
  • hits.total.value - how many matching documents were found
  • hits.sort - the document’s sort position (when not sorting by relevance score)
  • hits._score - the document’s relevance score (not applicable when using match_all)

Query DSL

基本语法格式

Elasticsearch提供了一个可以执行查询的Json风格的DSL。这个被称为Query DSL,该查询语言非常全面。

结构

一个查询语句的典型结构

QUERY_NAME:{
   ARGUMENT:VALUE,
   ARGUMENT:VALUE,...
}

如果针对于某个字段,那么它的结构如下:

{
  QUERY_NAME:{
     FIELD_NAME:{
       ARGUMENT:VALUE,
       ARGUMENT:VALUE,...
      }   
   }
}

实例:

GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0,
  "size": 5,
  "sort": [
    {
      "account_number": {
        "order": "desc"
      }
    }
  ]
}

结果:
在这里插入图片描述

返回结果解释

query定义如何查询;

  • match_all查询类型【代表查询所有的所有】,es中可以在query中组合非常多的查询类型完成复杂查询;
  • 除了query参数之外,我们可也传递其他的参数以改变查询结果,如sort,size;
  • from+size限定,完成分页功能;
  • sort排序,多字段排序,会在前序字段相等时后续字段内部排序,否则以前序为准;
_source(返回部分字段)
GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0,
  "size": 5,
  "sort": [
    {
      "account_number": {
        "order": "desc"
      }
    }
  ],
  "_source": ["balance","firstname"]
}

查询结果:

在这里插入图片描述

match(匹配查询)
  • 基本类型(非字符串),精确控制
GET bank/_search
{
  "query": {
    "match": {
      "account_number": "20"
    }
  }
}

match返回account_number=20的数据。

查询结果:
在这里插入图片描述

  • 字符串,全文检索
GET bank/_search
{
  "query": {
    "match": {
      "address": "Place"
    }
  }
}

在这里插入图片描述

全文检索,最终会按照评分进行排序,会对检索条件进行分词匹配。

分词实例:

GET bank/_search
{
  "query": {
    "match": {
      "address": "878 Street"
    }
  }
}

在这里插入图片描述

match_phrase (短句匹配,不分词)

将需要匹配的值当成一整个单词(不分词)进行检索

GET bank/_search
{
  "query": {
    "match_phrase": {
      "address": "mill road"
    }
  }
}

查处address中包含mill_road的所有记录,并给出相关性得分

查看结果:

在这里插入图片描述

如果用match,则结果为:
在这里插入图片描述

multi_math(多字段匹配)

在"address", “firstname"里面查"road lee”,会分词

GET bank/_search
{
  "query": {
    "multi_match": {
      "query": "road lee",
      "fields": ["address", "firstname"]
    }
  }
}

查询结果:
在这里插入图片描述

bool(条件查询,must、must_not、should)

复合语句可以合并,任何其他查询语句,包括符合语句。这也就意味着,复合语句之间
可以互相嵌套,可以表达非常复杂的逻辑。

  • must:必须达到must所列举的所有条件

  • must_not,必须不匹配must_not所列举的所有条件。

  • should,应该满足should所列举的条件。(可不满足)

    应该达到should列举的条件,如果到达会增加相关文档的评分,并不会改变查询的结果。如果query中只有should且只有一种匹配规则,那么should的条件就会被作为默认匹配条件二区改变查询结果。

实例1:

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "gender": "M"
          }
        },
        {
          "match": {
            "address": "road"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "age": "38"
          }
        }
      ],
      "should": [
        {
          "match": {
            "city": "alden"
          }
        }
      ]
    }
  }
}

查询结果:

在这里插入图片描述

能够看到相关度越高,得分也越高。

实例2:范围查询:age在20~30之间的

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "range": {
            "age": {
              "gte": 20,
              "lte": 30
            }
          }
        }
      ]
    }
  }
}

在这里插入图片描述

filter(结果过滤,不计算分数,效率比match高)

并不是所有的查询都需要产生分数,特别是哪些仅用于filtering过滤的文档。为了不计算分数,elasticsearch会自动检查场景并且优化查询的执行。

GET bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "address": "mill"
          }
        }
      ],
      "filter": {
        "range": {
          "balance": {
            "gte": "10000",
            "lte": "30000"
          }
        }
      }
    }
  }
}

先是查询所有匹配address=mill的文档,然后再根据10000<=balance<=20000进行过滤查询结果

查询结果:

在这里插入图片描述

在boolean查询中,must, shouldmust_not 元素都被称为查询子句 。 文档是否符合每个“must”或“should”子句中的标准,决定了文档的“相关性得分”。 得分越高,文档越符合您的搜索条件。 默认情况下,Elasticsearch返回根据这些相关性得分排序的文档。

“must_not”子句中的条件被视为“过滤器”。 它影响文档是否包含在结果中, 但不影响文档的评分方式。 还可以显式地指定任意过滤器来包含或排除基于结构化数据的文档。

filter在使用过程中,并不会计算相关性得分:

GET bank/_search
{
  "query": {
    "bool": {
      "filter": {
        "range": {
          "balance": {
            "gte": "10000",
            "lte": "30000"
          }
        }
      }
    }
  }
}

查询结果:(结果中,_score是0)
在这里插入图片描述

term(匹配非文本字段的属性值)

和match一样。匹配某个属性的值。全文检索字段用match,其他非text字段匹配用term

官网描述:https://www.elastic.co/guide/en/elasticsearch/reference/7.6/query-dsl-term-query.html

  • Avoid using the term query for text fields.
    避免对文本字段使用“term”查询
  • By default, Elasticsearch changes the values of text fields as part of analysis. This can make finding exact matches for text field values difficult.
    默认情况下,Elasticsearch作为analysis的一部分更改’ text '字段的值。这使得为“text”字段值寻找精确匹配变得困难。
  • To search text field values, use the match.
    要搜索“text”字段值,请使用匹配。

使用term匹配查询

GET bank/_search
{
  "query": {
    "term": {
      "age": 30
    }
  }
}

查询结果:

在这里插入图片描述
如果查文本,可能一条结果也没有!

Aggregation(执行聚合)

简介

聚合提供了从数据中分组和提取数据的能力。最简单的聚合方法大致等于SQL Group by和SQL聚合函数。在elasticsearch中,执行搜索返回this(命中结果),并且同时返回聚合结果,把以响应中的所有hits(命中结果)分隔开的能力。这是非常强大且有效的,你可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的API啦避免网络往返。

  • aggs:执行聚合。聚合语法如下:
"aggs":{
    "aggs_name":{
        "AGG_TYPE":{}
     }
}

terms、avg(桶聚合、度量聚合)

  • 搜索address中包含mill的所有人的年龄分布以及平均年龄,但不显示这些人的详情
GET /bank/_search
{
  "query": {
    "match": {
      "address":"mill"
    }
  },
  "aggs": {
    "myterms": {
      "terms": {
        "field": "age",
      },
    },
    "myavg": {
      "avg": {
        "field": "age"
      }
    }
  },
  "size": 0,
}

size:0 表示不显示搜索数据

查询结果:
在这里插入图片描述

  • 按照年龄聚合,并且求这些年龄段的这些人的平均薪资
GET /bank/_search
{
  "query": {
    "match_all": {}
  },

  "aggs": {
    "ageTerms": {
      "terms": {"field": "age"},

      "aggs": {
        "balanceAvg": {
          "avg": {
            "field": "balance"
          }
        }
      }

    },
  },

  "size": 0,
}

输出结果:
在这里插入图片描述

  • 查出所有年龄分布,并且这些年龄段中M的平均薪资和F的平均薪资以及这个年龄段的总体平均薪资
GET bank/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "ageAgg": {
      "terms": {
        "field": "age",
        "size": 100
      },
      "aggs": {
        "genderAgg": {
          "terms": {
            "field": "gender.keyword"
          },
          "aggs": {
            "balanceAvg": {
              "avg": {
                "field": "balance"
              }
            }
          }
        },
        "ageBalanceAvg": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  },
  "size": 0
}

输出结果:

在这里插入图片描述

Mapping(映射关系)

_mapping(查看映射关系)

  • 语法:
GET /索引库名/_mapping
  • 示例:
GET /bank/_mapping
  • 结果
    在这里插入图片描述

mappings(创建索引并指定类型)

https://www.elastic.co/guide/en/elasticsearch/reference/current/explicit-mapping.html

# 指定类型
PUT /my-index-000001
{
  "mappings": {
    "properties": {
      "age":    { "type": "integer" },
      "email":  { "type": "keyword"  },
      "name":   { "type": "text"  }
    }
  }
}
# 查看类型
GET /my-index-000001/_mapping
  • 结果:
    在这里插入图片描述
  • 添加字段映射:
PUT /my-index-000001/_mapping
{
  "properties": {
    "employee-id": {
      "type": "keyword",
      "index": false
    }
  }
}

在这里插入图片描述

  • index:是否被索引,默认true,如果为false,则可理解为冗余字段

_reindex(更新索引、数据迁移)

POST _reindex
{
  "source": {
    "index": "my-index-000001"
  },
  "dest": {
    "index": "my-new-index-000001"
  }
}
  • source:原数据位置
  • dest:迁移到的位置
  • 必须先建索引,并指定类型
  • 例如,把bank索引下account类型下的所有数据迁移到newbank索引下(新版es的类型被弃用了)
# 创建映射
PUT /newbank
{
  "mappings": {
    "properties": {
      "account_number": {
        "type": "long"
      },
      "address": {
        "type": "text",
      },
      "age": {
        "type": "integer"
      },
      "balance": {
        "type": "long"
      },
      "city": {
        "type": "keyword",
      },
      "email": {
        "type": "keyword",
      },
      "employer": {
        "type": "keyword",
      },
      "firstname": {
        "type": "text",
      },
      "gender": {
        "type": "keyword",
      },
      "lastname": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "state": {
        "type": "keyword",
      }
    }
  }
}
# 查看
GET /newbank/_mapping
# 迁移
POST _reindex
{
  "source": {
    "index": "bank",
    "type": "account",
  },
  "dest": {
    "index": "newbank"
  }
}
# 查询
GET /newbank/_search

  • 最终结果:
    在这里插入图片描述
    类型是_doc:
    在这里插入图片描述

数据类型

nested

https://www.elastic.co/guide/en/elasticsearch/reference/5.5/nested.html

  • 问题:

存储如下数据:

PUT my_index/my_type/1
{
  "group" : "fans",
  "user" : [ 
    {
      "first" : "John",
      "last" :  "Smith"
    },
    {
      "first" : "Alice",
      "last" :  "White"
    }
  ]
}

user是个数组对象,会被扁平化处理:在这里插入图片描述
此时检索在原记录中没有的Alice Smith,依然会有结果(结果略)

GET my_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "user.first": "Alice" }},
        { "match": { "user.last":  "Smith" }}
      ]
    }
  }
}
  • nested类型,可不进行扁平化处理,防止查到错误的数据
PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "user": {
          "type": "nested" 
        }
      }
    }
  }
}
  • 查询方法:
GET my_index/_search
{
  "query": {
    "nested": {
      "path": "数组名",
      "query": {
        "bool": {
          "must": [
            { "match": { "数组名.属性名1": "属性值1" }},
            { "match": { "数组名.属性名2":  "属性值2" }} 
          ]
        }
      }
    }
  }
}
  • 聚合方法:
{
   "query": {
    "match_all": {}
  },
 "aggs": {
    "聚合名称1": {
      "nested": {
        "path": "数组名"
      },
      "aggs": {
        "聚合名称2": {
          "terms": {
            "field": "数组名.属性名"
          },
        }
      }
    }
  }
}

分词器

https://www.elastic.co/guide/en/elasticsearch/reference/current/test-analyzer.html

标准分词器

  • 使用标准分词器测试分词:
POST _analyze
{
  "tokenizer": "standard",
  "filter":  [ "lowercase", "asciifolding" ],
  "text":      "Is this déja vu?"
}

结果:
在这里插入图片描述

  • 对中文不友好:
    在这里插入图片描述

ik分词器

下载:github地址:https://github.com/medcl/elasticsearch-analysis-ik/

解压:

  • 自定义解压到ik目录下:
    在这里插入图片描述

  • 解压后:
    在这里插入图片描述

传输到服务器上:

  • 利用xftp传输到虚拟机的es插件目录下:
    在这里插入图片描述

授权:

cd /mydata/elasticsearch/plugins
chmod -R 777 ik/

在这里插入图片描述

  • 或者直接在xftp中授权:
  • 右键文件点击更改权限:
    在这里插入图片描述
  • 改为777
    在这里插入图片描述

重启容器:

docker restart 容器id

在这里插入图片描述

测试一下:

POST _analyze
{
  "tokenizer": "ik_smart",
  "text":      "我是中国人"
}

在这里插入图片描述

  • ik_smart 和 ik_max_word:
    ik_smart 可以看成是把单词以空格分开了,单词个数没多
    ik_max_word:会得到更多的词

自定义词典:

在这里插入图片描述
在这里插入图片描述
输入网址即可

整合spring-boot

在7.15版本说推荐使用Java API:
在这里插入图片描述
但是,为了学习,依然选择Java High Level REST Clienthttps://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-getting-started.html

配置

  • maven
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.6.2</version>
</dependency>
  • yaml(将es的配置写在yaml文件中)
elasticsearch:
    localhost: ip #ip改为自己的
    port: 9200
    scheme: http
  • 配置类:
import lombok.Setter;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
@Setter
public class ESConfig {
    private String localhost;
    private int port;
    private String scheme;

    public ESConfig() {
    }

    public static RequestOptions REQUEST_OPTIONS;

    static {
        REQUEST_OPTIONS=RequestOptions.DEFAULT;
    }

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost(localhost, port, scheme)
                ));
    }
}
  • @ConfigurationProperties(prefix = “elasticsearch”):
    匹配配置文件中以elasticsearch为前缀的配置

测试存数据:

官网:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-document-index.html

import com.alibaba.fastjson.JSON;
import com.ljy.gulimallelasticsearch.config.ESConfig;
import lombok.Data;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;

@SpringBootTest
class GulimallElasticsearchApplicationTests {

    @Autowired
    RestHighLevelClient restHighLevelClient;
    
    @Test
    void 保存数据() throws IOException {
        //构造一个请求
        IndexRequest request = new IndexRequest("users");
        request.id("1");//数据id

        User user = new User();
        user.setId(1L);
        user.set姓名("张三");
        user.set性别("男");

        String jsonString = JSON.toJSONString(user);
        request.source(jsonString, XContentType.JSON);

        //发请求、获取响应
        IndexResponse indexResponse = restHighLevelClient.index(request, ESConfig.REQUEST_OPTIONS);
        System.out.println(indexResponse);
    }

    @Data
    class User {
        Long id;
        String 姓名;
        String 性别;
    }
}
  • GET /users/_search获取结果:
    在这里插入图片描述

Search API

官网:https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-search.html

  • 测试查询:
import com.ljy.gulimallelasticsearch.config.ESConfig;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.Avg;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

@SpringBootTest
public class SearchApiTest {
    @Autowired
    RestHighLevelClient client;

    @Test
    public void 测试查询() throws IOException {
        //https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-search.html

        //构造查询条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //query
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
        //terms
        searchSourceBuilder.aggregation(AggregationBuilders.terms("myterms").field("age"));
        //avg
        searchSourceBuilder.aggregation(AggregationBuilders.avg("balanceAvg").field("balance"));
        searchSourceBuilder.from(10).size(50).timeout(new TimeValue(60, TimeUnit.SECONDS));

        System.out.println("请求体:" + searchSourceBuilder);

        //构造请求
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("bank");//在/bank索引下查
        SearchRequest source = searchRequest.source(searchSourceBuilder);

        //发请求、得响应
        SearchResponse searchResponse = client.search(source, ESConfig.REQUEST_OPTIONS);

        //响应状态等信息
        RestStatus status = searchResponse.status();
        System.out.println("status:" + status);
//        TimeValue took = searchResponse.getTook();
//        Boolean terminatedEarly = searchResponse.isTerminatedEarly();
//        boolean timedOut = searchResponse.isTimedOut();
//        int totalShards = searchResponse.getTotalShards();
//        int successfulShards = searchResponse.getSuccessfulShards();
//        int failedShards = searchResponse.getFailedShards();

        //命中得记录:
        SearchHits hits = searchResponse.getHits();
//        TotalHits totalHits = hits.getTotalHits();
//        // the total number of hits, must be interpreted in the context of totalHits.relation
//        long numHits = totalHits.value;
//        // whether the number of hits is accurate (EQUAL_TO) or a lower bound of the total (GREATER_THAN_OR_EQUAL_TO)
//        TotalHits.Relation relation = totalHits.relation;
//        float maxScore = hits.getMaxScore();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            System.out.println("hit:" + hit);
            System.out.println("hit Score:" + hit.getScore() );
            System.out.println("Source:" + hit.getSourceAsMap());
        }

        //聚合函数:
        Aggregations aggregations = searchResponse.getAggregations();
        Terms terms = aggregations.get("myterms");
        for (Terms.Bucket bucket : terms.getBuckets()) {
            System.out.println("key:"+bucket.getKeyAsString());
            System.out.println("doc count:"+bucket.getDocCount());
        }

        Avg averageAge = searchResponse.getAggregations().get("balanceAvg");
        double avg = averageAge.getValue();
        System.out.println("balance avg:" + avg);
    }
}
  • 结果:(部分)
    在这里插入图片描述
    在这里插入图片描述

批量存数据 – 模板

  • service:
import com.alibaba.fastjson.JSON;
import com.ljy.common.to.es.SkuEsModel;
import com.ljy.gulimallelasticsearch.config.ESConfig;
import com.ljy.gulimallelasticsearch.constant.ProductConstant;
import com.ljy.gulimallelasticsearch.service.EsSaveService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @program: gulimall
 * @description:
 * @author: liangjiayy
 * @create: 2022-04-27 18:57
 **/
@Service
@Slf4j
public class EsSaveServiceImpl implements EsSaveService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    
    /**
     * 批量保存上架商品数据
     * @param skuEsModelList:要保存的数据
     */
    public boolean productStatusUp(List<SkuEsModel> skuEsModelList) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        for (SkuEsModel skuEsModel : skuEsModelList) {
            //索引
            IndexRequest request = new IndexRequest(ProductConstant.PRODUCT_INDEX);
            //id
            request.id(skuEsModel.getSkuId().toString());
            //请求体
            request.source(JSON.toJSONString(skuEsModel), XContentType.JSON);
            bulkRequest.add(request);
        }

        //批量保存
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, ESConfig.REQUEST_OPTIONS);

        List<String> ids = Arrays.stream(bulk.getItems())
                .filter(b -> b.getFailure() != null).map(BulkItemResponse::getId)
                .collect(Collectors.toList());

        if (bulk.hasFailures()) {
            log.info("商品上架部分信息保存失败...失败的集合:{}", ids);
            return false;
        }

        return true;
    }
}

  • 要用到的常量:
public class ProductConstant {
    //sku在es中保存的索引
    public static final String PRODUCT_INDEX="gulimall_product";
}

例子

有如下索引:
在这里插入图片描述
查询:

# 条件查询
GET gulimall_product/_search
{
  "query": {
    "bool": {
      #标题搜索手机
      "must": [
        {
          "match": {
            "skuTitle":"手机",
          },
        }],
      "filter": [
      	#catalogId必须为指定值
        {
          "term": {
            "catalogId":"225"
          },
        },
        #brandId必须为指定得几个值
        {
          "terms": {
            "brandId":[
              "2",
              "3"],
          },
        },
        # 数组的查询
        {
          "nested": {
            "path": "attrs",
            "query": {
              "bool": {
                "must": [
                  {
                    "term":
                    {
                      "attrs.attrId":"10"
                    }
                  },
                  {
                    "terms":
                    {
                      "attrs.attrValue":  [
                        "16G",
                        "32G"]
                    }
                  }
                ]
              }
            }
          }
        },
        #hasStock必须为指定值
        {
          "term": {
            "hasStock": "true"
          }
        },
        #确定价格范围;gte(great-than-equal):大于等于;gt:大于;
        {
          "range": {
            "skuPrice": {
              "gte": 0,
              "lte": 123,
            }
          }
        }
      ],
    },
  },
  #排序,asc升序,desc降序
  "sort": [
    {
      "skuPrice": {
        "order": "desc"
      }
    }
  ],
  #从0开始,返回五条
  "from": 0, "size": 5,
  #高亮显示
  "highlight":{
    "fields": {
      "skuTitle":{
      }
    },
    "pre_tags":"<b style='color:red'>",
    "post_tags":"</b>"
  }
}

聚合:

# 聚合查询
GET gulimall_product/_search
{
  "query": {
    "match_all": {
    }
  },
  "aggs": {
    #根据brandId聚合
    "brandId_aggs": {
      "terms": {
        "field": "brandId",
        "size": 10,
      },
      #上一个结果,再根据brandName、brandImg聚合
      "aggs": {
        "brandName_aggs": {
          "terms": {
            "field": "brandName",
          },
        },
        "brandImg_aggs":{
          "terms": {
            "field": "brandImg",
          },
        }
      }
    },
    #根据catalogId聚合
    "catalogId_aggs":{
      "terms": {
        "field": "catalogId",
      },
      #上一个聚合结果再根据catalogName聚合
      "aggs": {
        "catalogName_aggs": {
          "terms": {
            "field": "catalogName",
          },
        }
      }
    },
    #数组的聚合
    "attrs_aggs":{
      "nested": {
        "path": "attrs"
      },
      #根据attrs.attrId聚合,聚合后,再根据attrName、attrValue聚合
      "aggs": {
        "attrs_id_aggs": {
          "terms": {
            "field": "attrs.attrId",
          },
          "aggs": {
            "attrs_name_aggs": {
              "terms": {
                "field": "attrs.attrName",
              },
            },
            "attrs_value_aggs": {
              "terms": {
                "field": "attrs.attrValue",
              },
            }
          }
        }
      }
    }
  },
  #不显示查询的结果,显示有聚合的结果
  "size": 0,
}

想去哪里?

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值