ElasticSearch 应用实践 笔记

news2024/10/6 2:30:04

概述

介绍

ES 是一个开源的高扩展的分布式全文搜索引擎,是整个Elastic Stack技术栈的核心。它可以近乎实时的存储,检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。
ElasticSearch的底层是开源库Lucene,但是你没办法直接用Lucene,必须自己写代码去调用它的接口,Elastic是Lucene的封装,提供了REST API的操作接口,开箱即用。天然的跨平台。
ElasticSearch是目前全文检索引擎的首选,它可以快速的存储,搜索和分析海量的数据,维基百科,GitHub,Stack Overflow都采用了ElasticSearch。

用途

- 搜索的数据对象是大量的非结构化的文本数据。
- 文件记录达到数十万或数百万个甚至更多。
- 支持大量基于交互式文本的查询。
- 需求非常灵活的全文搜索查询。
- 对高度相关的搜索结果的有特殊需求,但是没有可用的关系数据库可以满足。
- 对不同记录类型,非文本数据操作或安全事务处理的需求相对较少的情况。

基本概念

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Mysql:擅长事务类型操作,可以确保数据的安全和一致性

Elasticsearch:擅长海量数据的搜索、分析、计算
MySQLElasticsearch说明
TableIndex索引(index),就是文档的集合,类似数据库的表(table)
RowDocument文档(Document),就是一条条的数据,类似数据库中的行(Row),文档都是JSON格式
ColumnField字段(Field),就是JSON文档中的字段,类似数据库中的列(Column)
SchemaMappingMapping(映射)是索引中文档的约束,例如字段类型约束。类似数据库的表结构(Schema)
SQLDSLDSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD

imagepng

索引

索引(indices)在这儿很容易和MySQL数据库中的索引产生混淆,其实是和MySQL数据库中的Databases数据库的概念是一致的。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

类型

类型(Type),对应的其实就是数据库中的 Table(数据表),类型是模拟mysql中的table概念,一个索引库下可以有不同类型的索引,比如商品索引,订单索引,其数据格式不同。

文档

文档(Document),对应的就是具体数据行(Row)。

字段

字段(field)相对于数据表中的列,也就是文档中的属性。

倒排索引

Elasticsearch是通过Lucene的倒排索引技术实现比关系型数据库更快的过滤。特别是它对多条件的过滤支持非常好。
imagepng
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 优点:
    • 根据词条搜索、模糊搜索时,速度非常快
  • 缺点:
    • 只能给词条创建索引,而不是字段
    • 无法根据字段做排序

相关安装

Docker es安装

docker pull elasticsearch:7.4.2

mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo "http.host : 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 -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.4.2
# ======================== Elasticsearch Configuration =========================
#
# NOTE: Elasticsearch comes with reasonable defaults for most settings.
#       Before you set out to tweak and tune the configuration, make sure you
#       understand what are you trying to accomplish and the consequences.
#
# The primary way of configuring a node is via this file. This template lists
# the most important settings you may want to configure for a production cluster.
#
# Please consult the documentation for further information on configuration options:
# https://www.elastic.co/guide/en/elasticsearch/reference/index.html
#
# ---------------------------------- Cluster -----------------------------------
#
# Use a descriptive name for your cluster:
#
cluster.name: my-application
#
# ------------------------------------ Node ------------------------------------
#
# Use a descriptive name for the node:
#
#node.name: node-1
#
# Add custom attributes to the node:
#
#node.attr.rack: r1
#
# ----------------------------------- Paths ------------------------------------
#
# Path to directory where to store the data (separate multiple locations by comma):
#
#path.data: /path/to/data
#
# Path to log files:
#
#path.logs: /path/to/logs
#
# ----------------------------------- Memory -----------------------------------
#
# Lock the memory on startup:
#
#bootstrap.memory_lock: true
#
# Make sure that the heap size is set to about half the memory available
# on the system and that the owner of the process is allowed to use this
# limit.
#
# Elasticsearch performs poorly when the system is swapping the memory.
#
# ---------------------------------- Network -----------------------------------
#
# By default Elasticsearch is only accessible on localhost. Set a different
# address here to expose this node on the network:
#
network.host: 0.0.0.0
#
# By default Elasticsearch listens for HTTP traffic on the first free port it
# finds starting at 9200. Set a specific HTTP port here:
#
http.port: 9200
#
# For more information, consult the network module documentation.
#
# --------------------------------- Discovery ----------------------------------
#
# Pass an initial list of hosts to perform discovery when this node is started:
# The default list of hosts is ["127.0.0.1", "[::1]"]
#
#discovery.seed_hosts: ["host1", "host2"]
#
# Bootstrap the cluster using an initial set of master-eligible nodes:
#
#cluster.initial_master_nodes: ["node-1"]
#
# For more information, consult the discovery and cluster formation module documentation.
#
# ---------------------------------- Various -----------------------------------
#
# Allow wildcard deletion of indices:
#
#action.destructive_requires_name: false

#true将启用X-Pack安全功能,并默认禁止root用户访问
#xpack.security.enabled: false
#
xpack.security.enabled: false

discovery.type: single-node

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

chmod -R 777 /mydata/elasticsearch/

默认情况下,不能root启动es

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

docker kibana安装

docker pull kibana:7.4.2

docker run --name kibana -e -p 5601:5601 -d kibana:7.4.2
server.name: kibana
server.host: "0.0.0.0"
elasticsearch.hosts: [ "http://192.168.56.10:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true

ES入门

_cat

_cat接口说明
GET /_cat/nodes查看所有节点
GET /_cat/health查看ES健康状况
GET /_cat/master查看主节点
GET /_cat/indices查看所有索引信息

imagepng

字段名含义说明
healthgreen(集群完整) yellow(单点正常、集群不完整) red(单点不正常)
status是否能使用
index索引名
uuid索引统一编号
pri主节点几个
rep从节点几个
docs.count文档数
docs.deleted文档被删了多少
store.size整体占空间大小
pri.store.size主节点占

索引操作

创建索引

PUT http://192.168.56.10:9200/bobo(索引名)
imagepng

  1. 分片:在ES中,一个索引通常会被分解成多个部分,这些部分就是分片。每个分片都是一个完整的Lucene索引,可以独立地进行搜索和写入操作。通过将数据分布在多个分片上,ES可以实现数据的并行处理,提高系统的吞吐量和性能。每个分片可以独立地存储和处理一部分数据,这样就能够有效地利用集群中的多个节点,实现数据的分布式存储和处理。
  2. 副本:副本是分片的复制品,它的存在主要是为了提高系统的容错性和可用性。每个分片可以有多个副本,副本和原始分片之间保持数据的一致性。当某个节点故障或者网络发生故障时,系统可以自动将副本升级为主分片,确保数据的可用性和一致性。同时,副本也可以处理查询请求,分担主分片的查询压力。

imagepng

查询索引

GET http://192.168.56.10:9200/bobo(索引名)
imagepng

{
  "bobo": {
    "aliases": {},
    "mappings": {},
    "settings": {
      "index": {
        "creation_date": "1702564134116",
        "number_of_shards": "3",
        "number_of_replicas": "2",
        "uuid": "VpSOVO9aR5OBPYtdvqgT8Q",
        "version": {
          "created": "7040299"
        },
        "provided_name": "bobo"
      }
    }
  }
}

查询所有索引
http://192.168.56.10:9200/*

删除索引

DELETE http://192.168.56.10:9200/bobo(索引名)

文档操作

创建文档

POST http://192.168.56.10:9200/bobo(索引)/typess(类型)/22(id) POST方式,如果id不写会自动生成
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
id存在的情况下是更新,POST和PUT方法
imagepng
PUT http://192.168.56.10:9200/bobo(索引)/typess(类型)/22(id) PUT方式,id必须填写

提交方式描述
PUT提交的id如果不存在就是新增操作,如果存在就是更新操作,id不能为空
POST如果不提供id会自动生成一个id,如果id存在就更新,如果id不存在就新增

查看文档

GET http://192.168.56.10:9200/bobo(索引)/typess(类型)/mAzcaIwBAOzWskCU9S7N(id)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

字段含义
_index索引名称
_type类型名称
_id记录id
_version版本号
_seq_no并发控制字段,每次更新都会+1,用来实现乐观锁
_primary_term同上,主分片重新分配,如重启,就会发生变化
found找到结果
_source真正的数据内容

乐观锁
imagepng
imagepng

更新文档

除了有无id来更新数据,也可以使用另一种方式更新数据
POST http://192.168.56.10:9200/bobo(索引)/typess(类型)/mAzcaIwBAOzWskCU9S7N(id)/_update
如果更新的数据和文档中的数据是一样的,那么POST方式提交是不会有任何操作的
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

{
    "doc": {
        "name": "王五",
        "sex": 13,
        "adds": "中国东部"
    }
}

删除文档

DELETE http://192.168.56.10:9200/bobo(索引)/typess(类型)/1aad11(id)
DELETE http://192.168.56.10:9200/bobo(索引) 删除整个索引
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

_bulk批量操作

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

POST /_bulk
{"delete":{"_index":"website","_type":"blog","_id":"123"}}

{"create":{"_index":"website","_type":"blog","_id":"123"}}
{"title":"My first bolg post ..."}

{"index":{"_index":"website","_type":"blog"}}
{"title":"My second blog post ..."}

{"update":{"_index":"website","_type":"blog","_id":"123"}}
{"doc":{"title":"My updated blog post ..."}}
#! Deprecation: [types removal] Specifying types in bulk requests is deprecated.
{
  "took" : 242,
  "errors" : false,
  "items" : [
    {
      "delete" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 1,
        "result" : "not_found",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 0,
        "_primary_term" : 1,
        "status" : 404
      }
    },
    {
      "create" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 2,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 1,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "index" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "mQwDaYwBAOzWskCUVy4x",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 2,
        "_primary_term" : 1,
        "status" : 201
      }
    },
    {
      "update" : {
        "_index" : "website",
        "_type" : "blog",
        "_id" : "123",
        "_version" : 3,
        "result" : "updated",
        "_shards" : {
          "total" : 2,
          "successful" : 1,
          "failed" : 0
        },
        "_seq_no" : 3,
        "_primary_term" : 1,
        "status" : 200
      }
    }
  ]
}

进阶

es的检索方式

在ElasticSearch中支持两种检索方式

  1. 通过使用REST request URL 发送检索参数(uri+检索参数)
  2. 通过使用 REST request body 来发送检索参数 (uri+请求体)

第一种方式

POST /bank/account/_search?/q=*&sort=account_number:desc
GEt /bank/account/_search?/q=*&sort=account_number:desc

//不写类型会查询索引
POST /bank/_search?sort=account_number:desc
GET /bank/_search?sort=account_number:desc

![image.png](https://img-blog.csdnimg.cn/img_convert/3a0c4f2e36cacb64e0277d06a68d0bff.png)

| 信息 | 描述 |
| --- | --- |
| took | ElasticSearch执行搜索的时间(毫秒) |
| time_out | 搜索是否超时 |
| _shards | 有多少个分片被搜索了,统计成功/失败的搜索分片 |
| hits | 搜索结果 |
| hits.total | 搜索结果统计 |
| hits.hits | 实际的搜索结果数组(默认为前10条文档) |
| sort | 结果的排序key,没有就按照score排序 |
| score和max_score | 相关性得分和最高分(全文检索使用) |

<a name="uELY3"></a>
### 第二种方式
**match_all**:获取所有数据
```json
GET /bank/account/_search
{
  "query":{
    "match_all":{}
  },
  "sort":[
      {
        "account_number":"asc"
      }
    ]
}

Query DSL

match

GET /bank/account/_search
{
  "query":{
    "match":{
      "account_number":20
    }
  }
}

imagepng

//模糊查询,有分词功能,分为Kings词和Place词,查询出address包含这两个词的文档
//_score为相关计算分数,也就是匹配度
GET /bank/account/_search
{
  "query":{
    "match":{
      "address":"Kings Place"
    }
  }
}

imagepng

match_phrase

不进行分词的检索,短语匹配

GET /bank/account/_search
{
  "query":{
    "match_phrase":{
      "address":"Kings Place"
    }
  }
}

imagepng

multi_match

多字段匹配

//查询出state或者address中包含 NH Kings的记录
//有做分词
GET /bank/account/_search
{
  "query":{
    "multi_match":{
      "query":"NH Kings",
      "fields":["address","state"]
    }
  }
}

imagepng

bool(复合查询)

组合查询
bool把各种其它查询通过 must(与)、must_not(非)、should`(或)的方式进行组合

//must必须是,must_not必须不是
//必须age=40且必须不是state=UT
GET /bank/account/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "age": 40
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "state": "UT"
          }
        }
      ]
    }
  }
}                                


//address="659 Highland Boulevard"或state = "UT"
GET /bank/account/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match_phrase": {
            "address" : "659 Highland Boulevard"
          }
        },
        {
          "match": {
            "state" : "UT"
          }
        }
      ]
    }
  }
}

imagepng

filter[结果过滤]

//查address=含有Pierrepont或Place会Pierrepont Place,且state不为UT,结果取age为20<=x<=35
GET /bank/account/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "address": "Pierrepont Place"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "state": "UT"
          }
        }
      ],
      "filter":{
        "range":{
          "age":{
            "gte": 20,
            "lte": 35
          }
        }
      }
    }
  }
}

term

非text字段的精确匹配

GET /bank/account/_search
{
  "query":{
    "term":{
      "age" : 28
    }
  }
}

imagepng

检索关键字描述
term非text使用
match在text中我们实现全文检索-分词
match keyword在属性字段后加.keyword 实现精确查询-不分词
match_phrase短语查询,不分词,模糊查询
GET /bank/account/_search
{
  "query":{
    "match":{
      "city.keyword" : "Bellfountain"
    }
  }
}

聚合(aggregations)

聚合可以让我们极其方便的实现对数据的统计、分析。例如:

  • 什么品牌的手机最受欢迎?
  • 这些手机的平均价格、最高价格、最低价格?
  • 这些手机每月的销售情况如何?

概念

Elasticsearch中的聚合,包含多种类型,最常用的两种,一个叫 桶,一个叫 度量。

桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个 桶,例如我们根据国籍对人划分,可以得到 中国桶、英国桶,日本桶……或者我们按照年龄段对人进行划分:0-10,10-20等。
Elasticsearch中提供的划分桶的方式有很多:

  • Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
  • Histogram Aggregation:根据数值阶梯分组,与日期类似
  • Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组
  • Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组
  • ……

bucket aggregations 只负责对数据进行分组,并不进行计算,因此往往bucket中往往会嵌套另一种聚合:metrics aggregations即度量。

度量

度量,分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为 度量。
比较常用的一些度量聚合方式:

  • Avg Aggregation:求平均值
  • Max Aggregation:求最大值
  • Min Aggregation:求最小值
  • Percentiles Aggregation:求百分比
  • Stats Aggregation:同时返回avg、max、min、sum、count等
  • Sum Aggregation:求和
  • Top hits Aggregation:求前几
  • Value Count Aggregation:求总数
  • ……

实例

  • 搜索address中包含mill的所有人的年龄分布以及平均年龄

    //
    GET /bank/account/_search
    {
    "query": {
      "match": {
        "address": "mill"
      }
    },
    "aggs": {
      //ageAgg为定义的变量名
      "ageAggs": {
        //terms为内容分组
        "terms": {
          //对那个字段进行分组(age)
          //分几组
          "field": "age",
          "size": 10
        }
      },
      "ageAvgs": {
        //对哪个字段进行平均值计算
        "avg": {
          "field": "age"
        }
      }
    },
    //查询的结果不显示
    "size": 0
    }
    

    imagepng

  • 请求这些年龄段的这些人的平均薪资

    GET /bank/account/_search
    {
    "query": {
      "match_all":{}
    },
    "aggs": {
    
      "balanceAvgs": {
        "avg": {
          "field": "balance"
        }
      }
    },
    "size": 0
    }
    

    imagepng

  • 查出所有年龄分布,并且这些年龄段中M的平均薪资和F的平均薪资以及这个年龄段的总体平均薪资

    GET /bank/account/_search
    {
    "query": {
      "match_all": {}
    },
    "aggs": {
      "ageAgg": {
        "terms": {
          "field": "age",
          "size": 50
        },
        "aggs": {
          "genderAgg": {
            "terms": {
              "field": "gender.keyword",
              "size": 10
            },
            "aggs": {
              "balanceAvg": {
                "avg": {
                  "field": "balance"
                }
              }
            }
          },
          "ageBalanceAvg": {
            "avg": {
              "field": "balance"
            }
          }
        }
      }
    },
    "size": 0
    }
    

    imagepng

映射配置(_mapping)

imagepng

ElasticSearch7-去掉type概念

关系型数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用,但ES中不是这样的。elasticsearch是基于Lucene开发的搜索引擎,而ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的。
两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type中定义相同的filed映射。否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降。
去掉type就是为了提高ES处理数据的效率。
Elasticsearch 7.x
URL中的type参数为可选。比如,索引一个文档不再要求提供文档类型。
Elasticsearch 8.x
不再支持URL中的type参数。
解决:将索引从多类型迁移到单类型,每种类型文档一个独立索引

创建映射字段

PUT /bank1/_mapping
{
  "properties": {
    "pid": {
      "type": "long",
      "index": true,
      "store": true,
      "analyzer": "ik_smart"
    }
  }
}

字段名:类似于列名,properties下可以指定许多字段。
每个字段可以有很多属性。例如:

  • type:类型,可以是text、long、short、date、integer、object等

  • index:是否索引,默认为true

  • store:是否存储,默认为false

  • analyzer:分词器,这里使用ik分词器:ik_max_word或者ik_smart

    新增映射字段

    如果我们创建完成索引的映射关系后,又要添加新的字段的映射,这时怎么办?第一个就是先删除索引,然后调整后再新建索引映射,还有一个方式就在已有的基础上新增

更新映射

对于存在的映射字段,我们不能更新,更新必须创建新的索引进行数据迁移。

数据迁移

先创建出正确的索引,然后使用如下的方式来进行数据的迁移

POST_reindex [固定写法]
{
  "source":{
    "index":"twitter"
  },
  "dest":{
    "index":"new_twitter"
  }
}

老的数据有type的情况

POST _reindex 
{
  "source": {
    "index": "product"
    //"type": ""
  },
  "dest": {
    "index": "mall-product"
  }
}

案例:新创建了索引,并指定了映射属性

分词

安装ik分词器

https://github.com/medcl/elasticsearch-analysis-ik 下载对应的版本,然后解压缩到plugins目录中

然后检查是否安装成功:进入容器 通过如下命令来检测

检查下载的文件是否完整,如果不完整就重新下载。

插件安装OK后我们重新启动ElasticSearch服务

ik分词

ik_smart分词

# 通过ik分词器来分词
POST /_analyze
{
  "analyzer": "ik_smart"
  ,"text": "我是中国人,我热爱我的祖国"
}


ik_max_word

POST /_analyze
{
  "analyzer": "ik_max_word"
  ,"text": "我是中国人,我热爱我的祖国"
}

java ES整合

package com.example.elasticsearch.config;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author guanglin.ma
 * @date 2023-12-16 22:52
 */
@Configuration
public class MallElasticSearchConfiguration {

    public static final RequestOptions COMMON_OPTIONS;

    static {
        RequestOptions.Builder builde = RequestOptions.DEFAULT.toBuilder();
//        builde.addHeader("Authorization", "Bearer" + TOKEN);
//        builde.setHttpAsyncResponseConsumerFactory(
//                new HttpAsyncResponseConsumerFactory.
//                        HeapBufferedResponseConsumerFactory(30 * 1024 * 1024 * 1024));
        COMMON_OPTIONS = builde.build();
    }

    @Bean
    public RestHighLevelClient restHighLevelClient() {
        RestClientBuilder builder = RestClient.builder(new HttpHost("192.168.56.10", 9200, "http"));
        RestHighLevelClient client = new RestHighLevelClient(builder);
        return client;
    }
}
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.4.2</version>
</dependency>
package com.example.elasticsearch;

import com.example.elasticsearch.config.MallElasticSearchConfiguration;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
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.common.xcontent.XContentType;
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.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder;
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;

@SpringBootTest
class ElasticsearchApplicationTests {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Test
    void contextLoads() {
        System.out.println("------>" + restHighLevelClient);
    }

    /**
     * 测试保存文档
     */
    @Test
    void saveIndex() throws Exception {
        IndexRequest indexRequest = new IndexRequest("system");
        indexRequest.id("1");
        // indexRequest.source("name","bobokaoya","age",18,"gender","男");
        User user = new User();
        user.setName("bobo");
        user.setAge(22);
        user.setGender("男");
        // 用Jackson中的对象转json数据
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(user);
        indexRequest.source(json, XContentType.JSON);
        // 执行操作
        IndexResponse index = restHighLevelClient.index(indexRequest, MallElasticSearchConfiguration.COMMON_OPTIONS);
        // 提取有用的返回信息
        System.out.println(index);
    }

    class User {
        private String name;
        private Integer age;
        private String gender;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        public String getGender() {
            return gender;
        }

        public void setGender(String gender) {
            this.gender = gender;
        }
    }

    //检索出所有的bank索引的所有文档
    @Test
    void searchIndexAll() throws IOException {
        // 1.创建一个 SearchRequest 对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("bank"); // 设置我们要检索的数据对应的索引库
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        /*sourceBuilder.query();
        sourceBuilder.from();
        sourceBuilder.size();
        sourceBuilder.aggregation();*/
        searchRequest.source(sourceBuilder);

        // 2.如何执行检索操作
        SearchResponse response = restHighLevelClient.search(searchRequest, MallElasticSearchConfiguration.COMMON_OPTIONS);
        // 3.获取检索后的响应对象,我们需要解析出我们关心的数据
        System.out.println("ElasticSearch检索的信息:" + response);
    }

    //    根据address全文检索
    @Test
    void searchIndexByAddress() throws IOException {
        // 1.创建一个 SearchRequest 对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("bank"); // 设置我们要检索的数据对应的索引库
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 查询出bank下 address 中包含 mill的记录
        sourceBuilder.query(QueryBuilders.matchQuery("address", "mill"));
        searchRequest.source(sourceBuilder);
        // System.out.println(searchRequest);

        // 2.如何执行检索操作
        SearchResponse response = restHighLevelClient.search(searchRequest, MallElasticSearchConfiguration.COMMON_OPTIONS);
        // 3.获取检索后的响应对象,我们需要解析出我们关心的数据
        System.out.println("ElasticSearch检索的信息:" + response);
    }

//    嵌套的聚合操作:检索出bank下的年龄分布和每个年龄段的平均薪资
    @Test
    void searchIndexAggregation() throws IOException {
        // 1.创建一个 SearchRequest 对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("bank"); // 设置我们要检索的数据对应的索引库
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 查询出bank下 所有的文档
        sourceBuilder.query(QueryBuilders.matchAllQuery());
        // 聚合 aggregation
        // 聚合bank下年龄的分布和每个年龄段的平均薪资
        AggregationBuilder aggregationBuiler = AggregationBuilders.terms("ageAgg")
                .field("age")
                .size(10);
        // 嵌套聚合
        aggregationBuiler.subAggregation(AggregationBuilders.avg("balanceAvg").field("balance"));

        sourceBuilder.aggregation(aggregationBuiler);
        sourceBuilder.size(0); // 聚合的时候就不用显示满足条件的文档内容了
        searchRequest.source(sourceBuilder);
        System.out.println(sourceBuilder);

        // 2.如何执行检索操作
        SearchResponse response = restHighLevelClient.search(searchRequest, MallElasticSearchConfiguration.COMMON_OPTIONS);
        // 3.获取检索后的响应对象,我们需要解析出我们关心的数据
        System.out.println(response);
    }

    //并行的聚合操作:查询出bank下年龄段的分布和总的平均薪资
    @Test
    void searchIndexAggregation1() throws IOException {
        // 1.创建一个 SearchRequest 对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("bank"); // 设置我们要检索的数据对应的索引库
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 查询出bank下 所有的文档
        sourceBuilder.query(QueryBuilders.matchAllQuery());
        // 聚合 aggregation
        // 聚合bank下年龄的分布和平均薪资
        AggregationBuilder aggregationBuiler = AggregationBuilders.terms("ageAgg")
                .field("age")
                .size(10);

        sourceBuilder.aggregation(aggregationBuiler);
        // 聚合平均年龄
        AvgAggregationBuilder balanceAggBuilder = AggregationBuilders.avg("balanceAgg").field("age");
        sourceBuilder.aggregation(balanceAggBuilder);

        sourceBuilder.size(0); // 聚合的时候就不用显示满足条件的文档内容了
        searchRequest.source(sourceBuilder);
        System.out.println(sourceBuilder);

        // 2.如何执行检索操作
        SearchResponse response = restHighLevelClient.search(searchRequest, MallElasticSearchConfiguration.COMMON_OPTIONS);
        // 3.获取检索后的响应对象,我们需要解析出我们关心的数据
        System.out.println(response);
    }

    @Test
    void searchIndexResponse() throws IOException {
        // 1.创建一个 SearchRequest 对象
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("bank"); // 设置我们要检索的数据对应的索引库
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 查询出bank下 address 中包含 mill的记录
        sourceBuilder.query(QueryBuilders.matchQuery("address","mill"));
        searchRequest.source(sourceBuilder);
        // System.out.println(searchRequest);

        // 2.如何执行检索操作
        SearchResponse response = restHighLevelClient.search(searchRequest, MallElasticSearchConfiguration.COMMON_OPTIONS);
        // 3.获取检索后的响应对象,我们需要解析出我们关心的数据
        // System.out.println("ElasticSearch检索的信息:"+response);
        RestStatus status = response.status();
        TimeValue took = response.getTook();
        SearchHits hits = response.getHits();
        float maxScore = hits.getMaxScore(); // 相关性的最高分
        SearchHit[] hits1 = hits.getHits();
        for (SearchHit documentFields : hits1) {
            /*"_index" : "bank",
                    "_type" : "account",
                    "_id" : "970",
                    "_score" : 5.4032025*/
            //documentFields.getIndex(),documentFields.getType(),documentFields.getId(),documentFields.getScore();
            String json = documentFields.getSourceAsString();
            //System.out.println(json);
            // JSON字符串转换为 Object对象
            ObjectMapper mapper = new ObjectMapper();
            Account account = mapper.readValue(json, Account.class);
            System.out.println("account = " + account);
        }
        //System.out.println(relation.toString()+"--->" + value + "--->" + status);
    }

    static class Account {

        private int account_number;
        private int balance;
        private String firstname;
        private String lastname;
        private int age;
        private String gender;
        private String address;
        private String employer;
        private String email;
        private String city;
        private String state;

        public int getAccount_number() {
            return account_number;
        }

        public void setAccount_number(int account_number) {
            this.account_number = account_number;
        }

        public int getBalance() {
            return balance;
        }

        public void setBalance(int balance) {
            this.balance = balance;
        }

        public String getFirstname() {
            return firstname;
        }

        public void setFirstname(String firstname) {
            this.firstname = firstname;
        }

        public String getLastname() {
            return lastname;
        }

        public void setLastname(String lastname) {
            this.lastname = lastname;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getGender() {
            return gender;
        }

        public void setGender(String gender) {
            this.gender = gender;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public String getEmployer() {
            return employer;
        }

        public void setEmployer(String employer) {
            this.employer = employer;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }

        public String getState() {
            return state;
        }

        public void setState(String state) {
            this.state = state;
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1423958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么样的评论更容易得到别人的关注

要发表吸引人的评论&#xff0c;可以注意这些个方面&#xff1a; 合适的软件&#xff1a;用DT浏览器的笔记本写文本&#xff0c;保存为图片&#xff0c;用图片的方式评论更容易得到别人的关注。 特别的观点&#xff1a;发表与众不同的观点&#xff0c;或者从不同的角度看待问…

上岸国考有多难?

国考笔试成绩已于2024年1月13日公布&#xff0c;听说宇宙的尽头是编制&#xff0c;今天用一份2024国考的数据帮大家探探路。数据来自和鲸平台&#xff0c;数据主要包括招考省市和部门、专业和学历要求、招考和报考人数。 经过一番探索&#xff0c;我发现一个上岸密码&#xff1…

成功解决AttributeError: ‘str‘ object has no attribute ‘keys‘

成功解决AttributeError: ‘str’ object has no attribute ‘keys’。 &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333;报错分析及解决方案&#x1f333;&#x1f333;字典对象的keys方法&#x1f333;&#x1f333;结尾&#x1f333; &…

01、全文检索 ------ 反向索引库 与 Lucene 的介绍

目录 全文检索 ------ 反向索引库 与 LuceneSQL模糊查询的问题反向索引库反向索引库的查询 Lucene&#xff08;全文检索技术&#xff09;Lucene能做什么Lucene存在的问题Solr 和 Elasticsearch 与 Lucene 的关系 全文检索 ------ 反向索引库 与 Lucene MySQL一些索引词汇解释 …

MIMIC-IV-ED数据集介绍

MIMIC-IV-ED v2.2 Abstract MIMIC-IV-ED 是一个大型的免费数据库&#xff0c;记录了2011年至2019年间急诊部门(ED)贝斯以色列女执事医疗中心的入院情况。该数据库包含约425,000个 ED 停留。生命体征&#xff0c;分类信息&#xff0c;药物协调&#xff0c;药物管理和出院诊断是…

android 自定义下拉框

一、 简介&#xff1a; 原生Android 提供的spinner下拉框不怎么方便&#xff0c;样式有点丑。修改起来麻烦&#xff0c;于是就自己动手写了一下拉列表。 实现原理使用的是&#xff0c;popwindow弹框&#xff0c;可实现宽高自定义&#xff0c;下拉列表使用listview. 二、pop弹框…

python爬虫2

1.table 是表格&#xff0c;tr是行&#xff0c;td是列 ul li是无序列标签用的较多&#xff0c;ol li是有序列标签 最基本的结构 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title> Title </title>…

《区块链简易速速上手小册》第6章:区块链在金融服务领域的应用(2024 最新版)

文章目录 6.1 金融服务中的区块链6.1.1 金融服务中区块链的基础6.1.2 主要案例&#xff1a;跨境支付6.1.3 拓展案例 1&#xff1a;去中心化金融&#xff08;DeFi&#xff09;6.1.4 拓展案例 2&#xff1a;代币化资产 6.2 区块链在支付系统中的作用6.2.1 支付系统中区块链的基础…

2024-02-01 Unity Shader 开发入门4 —— ShaderLab 语法

文章目录 1 材质和 Shader1.1 Unity Shader 和 Shader 的区别1.2 Unity 中的材质和 Shader1.3 创建材质1.4 创建 Shader 2 ShaderLab 的基本结构2.1 什么是 ShaderLab2.2 ShaderLab 的基本结构 3 Shader 名称4 Shader 属性4.1 Shader 属性的作用4.2 Shader 属性的基本语法4.3 数…

飞桨paddlespeech语音唤醒推理C INT8 定点实现

前面的文章&#xff08;飞桨paddlespeech语音唤醒推理C定点实现&#xff09;讲了INT16的定点实现。因为目前商用的语音唤醒方案推理几乎都是INT8的定点实现&#xff0c;于是我又做了INT8的定点实现。 实现前做了一番调研。量化主要包括权重值量化和激活值量化。权重值由于较小且…

操作方法:将 PDF 转换为 Word 文档的 7 种方法

只要您有合适的工具&#xff0c;将 PDF 另存为 Word 文档就是小菜一碟。为了简化 PDF 转 Word 的转换&#xff0c;从而提高工作效率&#xff0c;这篇文章将围绕 Windows、Mac 和在线上的几个免费 PDF 转 Word 转换器&#xff0c;轻松帮助您学习如何将 PDF转换为 Word。 Windows…

AIGC,ChatGPT4 实际需求效办公自动化函数应用

用实际需求来给大家演示一下ChatGPT如何助力办应用。 首先我们来提取年份值 我们将公式复制到表格即可。 接下来进行向下填充。 就得到了所有年份&#xff0c; 接下来我们完成第二个需求&#xff0c;按年份统计销售额。 Prompt&#xff1a;有一个表格C列是年份&#xff0c;D列…

java+springboot电影订票选座及评论网站影评系统ssm+vue

广大观影消费者需要知道自己的空闲时间&#xff0c;在自己可以接受的地理距离范围内&#xff0c;是否有感兴趣的影片可供观看&#xff0c;也需要清楚哪家影院在销售自己需要的电影票&#xff1b;同时手握电影排期及上映信息的电影院的运营者也急需根据消费者的观影需求实时调整…

FFMPEG 之 DXVA2 硬解

一&#xff1a;FFMPEG 支持的硬解方式有很多&#xff1a; DXVA2、D3D11VA、CUDA、QSV、OPENCL、DRM、VAAPI、VDPAU、VIDEOTOOLBOX、MEDIACODEC。 有的支持 Windows 平台&#xff0c;有的支持 linux 平台&#xff0c;有的支持 apple ios 平台&#xff0c;…

Java 面向对象进阶 03 static 注意事项、重新认识main方法(黑马)

案例代码如下&#xff1a; 非静态的方法虚拟机会自带一个关键字&#xff0c;如下&#xff1a; 但是不能手动出来&#xff0c;它的类型就是当前类&#xff1b; 通过输出this&#xff0c;得到当前s1,s2 的地址&#xff1b; 所以在输出时会有隐藏的this&#xff0c;所以this所指向…

LaTeX 文本对齐:ragged2e 宏包

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

268. Missing Number(丢失的数字)

题目描述 给定一个包含 [0, n] 中 n 个数的数组 nums &#xff0c;找出 [0, n] 这个范围内没有出现在数组中的那个数。 问题分析 因为这些数是在[0, n] 范围内的数&#xff0c;我们要是一一标记其是否出现很麻烦&#xff0c;但是我们考虑到如果用所有数的和&#xff0c;减去…

【网络安全|漏洞挖掘】ChatGPT 再曝安全漏洞,被指泄露私密对话

风靡全球的聊天机器人 ChatGPT 近日再次陷入安全风波&#xff0c;被曝泄露用户同机器人的私密对话&#xff0c;其中包含用户名、密码等敏感信息。 ArsTechnica 网站援引其读者提供的截图报道称&#xff0c;ChatGPT 泄露了多段非用户本人的对话内容&#xff0c;包含了大量敏感信…

Web前端入门 - HTML JavaScript Vue

ps&#xff1a;刚开始学习web前端开发&#xff0c;有什么不正确、不标准的内容&#xff0c;欢迎大家指出~ Web简介 90年代初期&#xff0c;Web1.0&#xff0c;静态页面&#xff0c;不和服务器交互&#xff0c;网页三剑客指Dreamweaver、Fireworks、Flash2000年代中期&#xf…

HiveSQL题——数据炸裂和数据合并

目录 一、数据炸裂 0 问题描述 1 数据准备 2 数据分析 3 小结 二、数据合并 0 问题描述 1 数据准备 2 数据分析 3 小结 一、数据炸裂 0 问题描述 如何将字符串1-5,16,11-13,9" 扩展成 "1,2,3,4,5,16,11,12,13,9" 且顺序不变。 1 数据准备 with da…