目录

1、Elasticsearch 简介

2、Docker 安装 Elasticsearch

2.1 使用Docker安装ElasticSearch7.6.2

2.2Elasticsearch 目录详解

2.3 使用Docker安装elasticSearch--head

通过Chrome 插件安装ElasticSearch-head

2.4 了解ELK

ELK 功能结构图

Docker 安装Kibana

3 ElasticSearch 核心概念

3.1索引 index

3.2 映射 mapping

3.3 字段Field

3.4 字段类型 Type

3.5 文档 document

3.6 集群 cluster

3.7 节点 node

3.8 分片和副本 shards&replicas

分片

副本

ElasticSearch 分词原理之倒排索引

ElasticSearch 集成IK分词

IK分词效果:

4、ElasticSearch 数据管理

4.1 ElasticSearch 基本操作

1) 创建索引

2) 查询索引

3) 删除索引

4) 添加文档

5) 修改文档

6) 查询文档

7) 删除文档

8) 查询操作

9) 文档批量操作

5、ElasticSearch之DSL

DSL 思维导图

DSL 操作

(1)无查询条件

(2)有查询条件

2.2 组合条件查询(多条件查询)

2.3 连接查询(多文档合并查询)

DSL 检索实例

6、ElasticSearch 之文档映射

动态映射实操

静态映射实操

核心类型(Core datatype)

静态映射指定text类型的ik分词器

7、Elasticsearch架构原理

Elasticsearch节点类型

Master节点

DataNode节点

分片和副本机制

分片(Shard)

副本

8、Elasticsearch重点工作流程

Elasticsearch文档写入原理

Elasticsearch检索原理

Elasticsearch准实时索引实现

1.溢写到文件系统缓存

2.写translog保障容错

3. flush到磁盘

4. segment合并

9、ElasticSearch 进级

人工控制搜索结果精准度

实现方式二:使用should+bool搜索,控制搜索条件的匹配度。

Boost 权重控制

基于dis_max实现best fields策略进行多字段搜索

基于tie_breaker参数优化dis_max搜索效果

使用multi_match简化dis_max+tie_breaker

cross fields搜索

copy_to fields 搜索

match phrase 搜索

前缀搜索 prefix search

通配符搜索

正则搜索

搜索推荐

fuzzy模糊搜索

高亮显示

10、ElasticSearch聚合搜索

bucket和metric概念简介

聚合操作案例

根据color分组统计销售数量

统计不同color车辆的平均价格

统计不同color不同brand中车辆的平均价格

统计不同color中的最大和最小价格、总价

统计不同品牌汽车中价格排名最高的车型

histogram 区间统计

date_histogram区间分组

_global bucket

aggs+order

search+aggs

filter+aggs

聚合中使用filter

11、ElasticSearch 嵌套对象

nested object  嵌套对象

父子关系数据建模

父子关系


1、Elasticsearch 简介

        Elasticsearch是一个基于Lucene库的搜索引擎。它提供了一个分布式、支持多租户的全文搜索引擎,具有HTTP Web接口和无模式JSON文档。Elasticsearch是用Java开发的,并在Apache许可证下作为开源软件发布。官方客户端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和许多其他语言中都是可用的。[5]根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr,也是基于Lucene。

2、Docker 安装 Elasticsearch

Elasticsearch的发展是非常快速的,所以在ES5.0之前,ELK的各个版本都不统一,出现了版本号混乱的状态,所以 从5.0开始,所有Elastic Stack中的项目全部统一版本号。目前最新版本是8.3.2,我们将基于这Elasticsearch7.6.2版本进行学习。

2.1 使用Docker安装ElasticSearch7.6.2

# elasticsearch 镜像检索
docker search elasticsearch# 下载elasticsearch 指定版本7.6.2
docker pull elasticsearch:7.6.2# 创建elasticsearch 容器挂载目录地址(配置文件、数据和插件)
mkdir -p /usr/local/es/config /usr/local/es/data /usr/local/es/plugins# 简单运行elasticsearch 容器
docker run -d \
--name elasticsearch \
-p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
elasticsearch:7.6.2# 拷贝elasticsearch 容器中的配置文件
docker cp elasticsearch:/usr/share/elasticsearch/config /usr/local/es
docker cp elasticsearch:/usr/share/elasticsearch/data /usr/local/es
docker cp elasticsearch:/usr/share/elasticsearch/plugins /usr/local/es# 通过SFTP将下载的IK插件上传至/usr/local/es/plugins 目录下。
[root@localhost plugins]# ls -a
.  ..  elasticsearch-analysis-ik-7.6.2# 修改jvm 运行参数
[root@localhost plugins]# vim /usr/local/es/config/jvm.options
# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space-Xms64m
-Xmx512m# 解决跨越配置
[root@localhost plugins]# vim /usr/local/es/config/elasticsearch.yml
添加如下代码片段
http.cors.enabled: true
http.cors.allow-origin: "*"# 移除简单elasticsearch 容器
[root@localhost plugins]# docker rm -f elasticsearch
elasticsearch# 再重新启动挂卷elasticsearch 容器实例
[root@localhost config]# docker run -d \
> --name elasticsearch \
> -p 9200:9200 -p 9300:9300 \
> -e "discovery.type=single-node" \
> -v /usr/local/es/config:/usr/share/elasticsearch/config \
> -v /usr/local/es/data:/usr/share/elasticsearch/data \
> -v /usr/local/es/plugins:/usr/share/elasticsearch/plugins \
> elasticsearch:7.6.2# 检查elasticsearch 服务是否正常启动
[root@localhost config]# curl http://localhost:9200
{"name" : "0f6f368c4c1d","cluster_name" : "docker-cluster","cluster_uuid" : "B_p4MWplSiO2cNp3EE9AOg","version" : {"number" : "7.6.2","build_flavor" : "default","build_type" : "docker","build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f","build_date" : "2020-03-26T06:34:37.794943Z","build_snapshot" : false,"lucene_version" : "8.4.0","minimum_wire_compatibility_version" : "6.8.0","minimum_index_compatibility_version" : "6.0.0-beta1"},"tagline" : "You Know, for Search"
}

MobaXterm 实际操作:

# 查看本地镜像资源
[root@localhost docker]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              0e901e68141f        3 weeks ago         142MB
mysql               5.7                 2a0961b7de03        3 weeks ago         462MB
delron/fastdfs      latest              8487e86fc6ee        4 years ago         464MB# 检索elasticsearch 镜像资源
[root@localhost docker]# docker search elasticsearch
NAME                                               DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
elasticsearch                                      Elasticsearch is a powerful open source sear…   5665                [OK]
kibana                                             Kibana gives shape to any kind of data — str…   2421                [OK]
bitnami/elasticsearch                              Bitnami Docker Image for Elasticsearch          54                                      [OK]
bitnami/elasticsearch-exporter                     Bitnami Elasticsearch Exporter Docker Image     4                                       [OK]# 下载elasticsearch 镜像资源指定版本7.6.2
[root@localhost docker]# docker pull elasticsearch:7.6.2
7.6.2: Pulling from library/elasticsearch
ab5ef0e58194: Pull complete
c4d1ca5c8a25: Pull complete
941a3cc8e7b8: Pull complete
43ec483d9618: Pull complete
c486fd200684: Pull complete
1b960df074b2: Pull complete
1719d48d6823: Pull complete
Digest: sha256:1b09dbd93085a1e7bca34830e77d2981521a7210e11f11eda997add1c12711fa
Status: Downloaded newer image for elasticsearch:7.6.2# 创建elasticsearch 容器挂载宿主主机文件目录创建
[root@localhost docker]# mkdir -p /usr/local/es/config /usr/local/es/data /usr/local/es/plugins
[root@localhost docker]# cd /usr/local/es
[root@localhost es]# ls -al
总用量 0
drwxr-xr-x.  5 root root  47 6月  21 16:29 .
drwxr-xr-x. 16 root root 192 6月  21 16:29 ..
drwxr-xr-x.  2 root root   6 6月  21 16:29 config
drwxr-xr-x.  2 root root   6 6月  21 16:29 data
drwxr-xr-x.  2 root root   6 6月  21 16:29 plugins# 简单运行elasticsearch 容器,为了拷贝elasticsearch 容器相关配置文件
[root@localhost es]# docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.6.2# 进入elasticsearch 容器,下载IK 分词插件
[root@localhost es]# docker exec -it elasticsearch /bin/bash
[root@93e38d04a341 elasticsearch]# 温馨提示:es 从 v5.5.1 版本开始支持自带的 es 插件命令来安装。
[root@93e38d04a341 elasticsearch]#./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
-> Installing https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
-> Downloading https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
-> Failed installing https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
-> Rolling back https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
-> Rolled back https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip
Exception in thread "main" java.net.UnknownHostException: github.comat java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:567)at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:339)at java.base/java.net.Socket.connect(Socket.java:603)无法解析github.com网站,导致elasticsearch-analysis-ik-7.6.2.zip 指定资源无法下载。
解决办法:宿主主机下载elasticsearch-analysis-ik-7.6.2.zip 压缩包,解压压缩包并上传宿主主机的 /usr/local/es/plugins 文件目录下。# 退出elasticsearch 容器
[root@93e38d04a341 elasticsearch]# exit
exit# 拷贝elasticsearch 容器配置文件
[root@localhost ~]# docker cp elasticsearch:/usr/share/elasticsearch/config /usr/local/es
[root@localhost ~]# docker cp elasticsearch:/usr/share/elasticsearch/data /usr/local/es
[root@localhost ~]# docker cp elasticsearch:/usr/share/elasticsearch/plugins /usr/local/es#编辑jvm.option
[root@localhost config]# vim jvm.options
#编辑elasticsearch.yml
[root@localhost config]# vim elasticsearch.yml#移除简单elasticsearch 容器
[root@localhost plugins]# docker rm -f elasticsearch
elasticsearch#重新 启动elasticsearch 容器
[root@localhost config]# docker run -d \
> --name elasticsearch \
> -p 9200:9200 -p 9300:9300 \
> -e "discovery.type=single-node" \
> -v /usr/local/es/config:/usr/share/elasticsearch/config \
> -v /usr/local/es/data:/usr/share/elasticsearch/data \
> -v /usr/local/es/plugins:/usr/share/elasticsearch/plugins \
> elasticsearch:7.6.2
0f6f368c4c1d57046f9476bfe63a436c4ee153a4bc677be774a6b0145cf33881
[root@localhost config]# docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                                            NAMES
0f6f368c4c1d        elasticsearch:7.6.2   "/usr/local/bin/dock…"   4 seconds ago       Up 3 seconds        0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp   elasticsearch#验证elasticsearch 服务是否正常
[root@localhost config]# curl http://localhost:9200
{"name" : "0f6f368c4c1d","cluster_name" : "docker-cluster","cluster_uuid" : "B_p4MWplSiO2cNp3EE9AOg","version" : {"number" : "7.6.2","build_flavor" : "default","build_type" : "docker","build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f","build_date" : "2020-03-26T06:34:37.794943Z","build_snapshot" : false,"lucene_version" : "8.4.0","minimum_wire_compatibility_version" : "6.8.0","minimum_index_compatibility_version" : "6.0.0-beta1"},"tagline" : "You Know, for Search"
}

2.2Elasticsearch 目录详解

ElasticSearch 官网地址:https://www.elastic.co

下载ElasticSearch-7.6.2.zip 包,解压至当前文件目录

目录文件说明:

bin: 启动命令
config: 配置文件log4j2: 日志配置文件jvm.options: Java虚拟机 相关配置elasticsearch.yml: elasticsearch 配置文件lib: 依赖的相关jar包log: 日志输出modules:功能模块plugins: 插件存放位置,如IK

2.3 使用Docker安装elasticSearch--head

由于ES官方并没有为ES提供界面管理工具,仅仅是提供了后台的服务。elasticsearch-head是一个为ES开发的一个 页面客户端工具,其源码托管于GitHub,地址为:https://github.com/mobz/elasticsearch-head
head提供了4种安装方式:
  • 源码安装,通过npm run start启动(不推荐)
  • 通过docker安装(推荐)
  • 通过chrome插件安装(推荐)
  • 通过ES的plugin方式安装(不推荐)

通过Chrome 插件安装ElasticSearch-head

ElasticSearch-Head 插件安装后的效果:

启动ElasticSearch-Head 插件,连接ElasticSearch 服务。

启动ElasticSearch-Head 插件后, 插件默认首页。

点击New 按钮,添加ElasticSearch 服务的IP地址。

至此,ElasticSearch-Head 插件安装成功,并成功连接ElasticSearch 服务。

ElasticSearch-Head 之Google 插件下载地址:

2.4 了解ELK

ELK 是ElasticSearch、Logstash、Kibana 三大开源框架首字母。市面上也被称为Elastic Stack。其中ElasticSearch 是一个基于Lucene、分布式、通过Restful方式进行交互的近实时检索平台框架。Logstash 是ELK的中央数据流引擎,用于从不同的目标(文件/数据 存储/MQ)收集不同格式的数据,经过过滤后支持输出到不同的目的地(文件/MQ/Redis/ElasticSearch/KaKa等)。Kibana 可以将ElasticSearch 的数据通过友好的页面展示出来,提供实时分析的功能。

ELK 功能结构图

Docker 安装Kibana

1、下载Kibana

# 检索Kibana 镜像
docker search kibana# 下载Kibana 镜像指定版本7.4.1
docker pull kibana:7.4.1# 查看本地镜像资源
docker images

2、编辑kibana的配置文件kibana.yml

在宿主主机上 创建/usr/local/kibana/conf 文件目录用于存放kibana.yml 配置文件。

mkdir -p /usr/local/kibana/conf

编辑kibana.yml 文件内容如下:

# Default Kibana configuration for docker target
server.name: kibana
server.host: "0"
elasticsearch.hosts: [ "http://192.168.43.10:9200" ]
xpack.monitoring.ui.container.elasticsearch.enabled: true

注意:elasticsearch.hosts为Elasticsearch实例。

3、运行 Kibana

# Kibana 运行命令
docker run -d --restart=always --log-driver json-file --log-opt max-size=100m --log-opt max-file=2 --name es-kibana -p 5601:5601 -v /usr/local/kibana/conf/kibana.yml:/usr/share/kibana/config/kibana.yml kibana:7.4.1# 查看容器启动状态
docker ps
4、运行Kibana 效果截图
访问 http://192.168.43.10:5601 (启动可能会较慢,如失败等几秒再尝试刷新一下)

3 ElasticSearch 核心概念

3.1索引 index

一个索引就是一个拥有几分相似特征的文档的集合。比如说,可以有一个客户数据
的索引,另一个产品目录的索引,还有一个订单数据的索引。
一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对对应于这
个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。

3.2 映射 mapping

ElasticSearch中的映射(Mapping)用来定义一个文档。
mapping是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认
值、分词器、是否被索引等等,这些都是映射里面可以设置的

3.3 字段Field

相当于是数据表的字段|列

3.4 字段类型 Type

每一个字段都应该有一个对应的类型,例如:Text、Keyword、Byte等

3.5 文档 document

一个文档是一个可被索引的基础信息单元,类似一条记录。文档以JSON(
Javascript Object Notation)格式来表示;

3.6 集群 cluster

一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提
供索引和搜索功能。

3.7 节点 node

一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索
引和搜索功能
        一个节点可以通过配置集群名称的方式来加入一个指定的集群。默认情况下,每
个节点都会被安排加入到一个叫做“elasticsearch”的集群中
这意味着,如果在网络中启动了若干个节点,并假定它们能够相互发现彼此,它们
将会自动地形成并加入到一个叫做“elasticsearch”的集群中
        在一个集群里,可以拥有任意多个节点。而且,如果当前网络中没有运行任何
Elasticsearch节点,这时启动一个节点,会默认创建并加入一个叫
做“elasticsearch”的集群

3.8 分片和副本 shards&replicas

分片

  • 一个索引可以存储超出单个结点硬件限制的大量数据。比如,一个具有10 亿文档的索引占据1TB的磁盘空间,而任一节点都没有这样大的磁盘空间;或 者单个节点处理搜索请求,响应太慢。
  • 为了解决这个问题,Elasticsearch提供了将索引划分成多份的能力,这些 份就叫做分片。
  • 当创建一个索引的时候,可以指定你想要的分片的数量。
  • 每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以 被放置到集群中的任何节点上。
  • 分片很重要,主要有两方面的原因
  • 允许水平分割/扩展你的内容容量
  • 允许在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量
  • 至于一个分片怎样分布,它的文档怎样聚合回搜索请求,是完全由 Elasticsearch管理的,对于作为用户来说,这些都是透明的

副本

在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎 么的就处于离线状态,或者由于任何原因消失了,这种情况下,有一个故障转 移机制是非常有用并且是强烈推荐的。为此目的,Elasticsearch允许你创建分 片的一份或多份拷贝,这些拷贝叫做副本分片,或者直接叫副本 
副本之所以重要,有两个主要原因 
    1) 在分片/节点失败的情况下,提供了高可用性。 注意到复制分片从不与原/主要(original/primary)分片置于同一节点 上是非常重要的 
    2) 扩展搜索量/吞吐量,因为搜索可以在所有的副本上并行运行 
        每个索引可以被分成多个分片。一个索引有0个或者多个副本。
        一旦设置了副本,每个索引就有了主分片和副本分片,分片和副本的数 量可以在索引创建的时候指定 
                   在索引创建之后,可以在任何时候动态地改变副本的数量,但是不能改变 分片的数量

ElasticSearch 分词原理之倒排索引

ElasticSearch 集成IK分词

# 首先官网下载elasticsearch-analysis-ik-7.6.2 分词
官网地址:https://github.com/medcl/elasticsearch-analysis-ik/releases# 将elasticsearch-analysis-ik-7.6.2 解压文件目录,通过ftp/sftps上传至es plugin挂载卷地址
/usr/local/es/plugins[root@localhost plugins]# pwd
/usr/local/es/plugins
[root@localhost plugins]# ls -al
总用量 0
drwxr-xr-x. 3 root root  45 6月  21 17:09 .
drwxr-xr-x. 5 root root  47 6月  21 16:29 ..
drwxr-xr-x. 3 root root 243 6月  21 17:09 elasticsearch-analysis-ik-7.6.2# 重新启动elasticSearch 容器
[root@localhost ~]# docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                                            NAMES
0f6f368c4c1d        elasticsearch:7.6.2   "/usr/local/bin/dock…"   40 hours ago        Up 40 hours         0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp   elasticsearch
[root@localhost ~]# docker restart 0f6f368
0f6f368

IK分词效果:

4、ElasticSearch 数据管理

ES是面向文档(document oriented)的,这意味着它可以存储整个对象或文档 (document)。 然而它不仅仅是存储,还会使用索引(index)每个文档的内容使之可以被搜索。
在ES中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。
ES使用JSON作为文档序列化格式。
JSON现在已经被大多语言所支持,而且已经成为NoSQL领域的标准格式。
ES存储的一个员工文档的格式示例:
{"email": "zhouzhiwengang@163.com","name": "周志文","age": 30,"interests": ["厨艺", "阅读"]
}

4.1 ElasticSearch 基本操作

1) 创建索引

语法格式: PUT /索引名称
举例: PUT /es_db

2) 查询索引

语法格式: GET /索引名称
举例: GET /es_db

3) 删除索引

语法格式: DELETE /索引名称1
举例: DELETE /es_db

4) 添加文档

语法格式: PUT /索引名称/类型/id
PUT http://192.168.43.10:9200/es_db/_doc/1
{"email": "zhouzhiwen@163.com","name": "周志文","age": 30,"interests": ["厨艺", "阅读"]
}PUT http://192.168.43.10:9200/es_db/_doc/2
{"email": "zhouzhiwenxiong@163.com","name": "周志雄","age": 30,"interests": ["游戏"]
}PUT http://192.168.43.10:9200/es_db/_doc/3
{"email": "zhouzhiwengang@163.com","name": "周志刚","age": 30,"interests": ["登山","美食"]
}

5) 修改文档

格式: PUT /索引名称/类型/id
PUT http://192.168.43.10:9200/es_db/_doc/3{"email": "zhouzhiwengang@163.com","name": "周志刚修改","age": 29,"interests": ["编程","美食"]
}

6) 查询文档

格式: GET /索引名称/类型/id
GET http://192.168.43.10:9200/es_db/_doc/3

7) 删除文档

格式: DELETE /索引名称/类型/id
DELETE http://192.168.43.10:9200/es_db/_doc/3

8) 查询操作

8.1 查询当前类型中的所有文档 _search

格式: GET /索引名称/类型/_search
GET http://192.168.43.10:9200/es_db/_doc/_search

8.2 条件查询, 如要查询age等于30岁的 _search?q=*:***

格式: GET /索引名称/类型/_search?q=*:***
GET http://192.168.43.10:9200/es_db/_doc/_search?q=age:30

8.3 范围查询, 如要查询age在25至26岁之间的 _search?q=***[** TO **] 注意: TO 必须为大写

格式: GET /索引名称/类型/_search?q=***[25 TO 26]
8.4 根据多个ID进行批量查询 _mget
格式:  POST  /索引名称/类型/_mget
POST http://192.168.43.10:9200/es_db/_doc/_mget{"ids": [1, 2]
}

 8.5 查询年龄小于等于28岁的 :<=

格式: GET /索引名称/类型/_search?q=age:<=**
GET http://192.168.43.10:9200/es_db/_doc/_search?q=age:<=28

8.6 查询年龄大于28前的 :>=

格式: GET /索引名称/类型/_search?q=age:>=**
GET http://192.168.43.10:9200/es_db/_doc/_search?q=age:>=28

8.7 分页查询 from=*&size=*
格式: GET /索引名称/类型/_search?q=age:>=28&from=0&size=1
GET http://192.168.43.10:9200/es_db/_doc/_search?q=age:>=28&from=0&size=1

8.8 对查询结果只输出某些字段 _source=字段,字段

格式: GET /索引名称/类型/_search?_source=字段,字段
GET http://192.168.43.10:9200/es_db/_doc/_search?_source=name

8.9 对查询结果排序 sort=字段:desc/asc

格式: GET /索引名称/类型/_search?sort=字段:desc
http://192.168.43.10:9200/es_db/_doc/_search?sort=age:desc

9) 文档批量操作

多个文档是指,批量操作多个文档。

9.1 批量获取文档数据

说明:批量获取文档数据是通过_mget的API来实现的 。

格式: POST /索引名称/类型/_mget

POST http://192.168.43.10:9200/es_db/_doc/_mget{"ids":[1, 2]
}

9.2  批量操作文档数据

说明:批量对文档进行写操作是通过_bulk的API来实现的。
格式:   POST /_bulk
 {"actionName":{"_index":"indexName", "_type":"typeName","_id":"id"}}{"field1":"value1", "field2":"value2"}温馨提示:actionName:表示操作类型,主要有create,index,delete和update

(1) 批量新增create

POST http://192.168.43.10:9200/_bulkContent-Type:application/json{"create": {"_index": "es_db", "_type": "_doc", "_id": 3}}
{"email": "zhouzhiwengang@163.com", "name": "周志刚", "age": 30, "interests": ["登山","美食"]}

 (2) 批量新增或替换index

POST http://192.168.43.10:9200/_bulkContent-Type:application/json{"index": {"_index": "es_db", "_type": "_doc", "_id": 3}}
{"email": "zhouzhiwengang@163.com", "name": "周志刚修改", "age": 28, "interests": ["登山","美食"]}

 (3)批量删除delete

POST http://192.168.43.10:9200/_bulkContent-Type:application/json{"delete": {"_index": "es_db", "_type": "_doc", "_id": 3}}

 (4)批量修改update

POST http://192.168.43.10:9200/_bulk
Content-Type:application/json{"update": {"_index": "es_db", "_type": "_doc", "_id": 2}}
{"doc": {"name":"修改"}}

5、ElasticSearch之DSL

Elasticsearch提供了基于JSON的完整查询DSL(特定于域的语言)来定义查询。将查询DSL视为查询的AST(抽象语法树),它由两种子句组成:

  • 叶子查询子句:
    叶查询子句中寻找一个特定的值在某一特定领域,如 match,term或 range查询。这些查询可以自己使用。
  • 复合查询子句
    复合查询子句包装其他叶查询或复合查询,并用于以逻辑方式组合多个查询(例如 bool或dis_max查询),或更改其行为(例如 constant_score查询)。
    查询子句的行为会有所不同,具体取决于它们是在 查询上下文中还是在过滤器上下文中使用。

DSL 思维导图

DSL 操作

(1)无查询条件

  无查询条件是查询所有,默认是查询所有的,或者使用match_all表示所有。
POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"match_all": {}}
}

(2)有查询条件

2.1、叶子条件查询(单字段查询条件)
2.1.1 模糊匹配
        模糊匹配主要是针对文本类型的字段,文本类型的字段会对内容进行分 词,对查询时,也会对搜索条件进行分词,然后通过倒排索引查找到匹 配的数据,模糊匹配主要通过match等参数来实现。
  • match : 通过match关键词模糊匹配条件内容
  • prefix : 前缀匹配
  • regexp : 通过正则表达式来匹配数据
    match的特殊用法
        query : 指定匹配的值operator : 匹配条件类                               and : 条件分词后都要匹配                                or : 条件分词后有一个匹配即可(默认)minmum_should_match : 指定最小匹配的数量
2.1.2 精确匹配
  • term : 单个条件相等
  • terms : 单个字段属于某个值数组内的值
  • range : 字段属于某个范围内的值
  • exists : 某个字段的值是否存在
  • ids : 通过ID批量查询

2.2 组合条件查询(多条件查询)

组合条件查询是将叶子条件查询语句进行组合而形成的一个完整的查询条件。
bool : 各条件之间有and,or或not的关系must : 各个条件都必须满足,即各条件是and的关系should : 各个条件有一个满足即可,即各条件是or的关系must_not : 不满足所有条件,即各条件是not的关系filter : 不计算相关度评分,它不计算_score即相关度评分,效率更高
constant_score : 不计算相关度评分

温馨提示:must/filter/shoud/must_not 等的子条件是通 过 term/terms/range/ids/exists/match 等叶子条件为参数的 注:以上参数,当只有一个搜索条件时,must等对应的是一个对象,当 是多个条件时,对应的是一个数组。

2.3 连接查询(多文档合并查询)

  • 父子文档查询:parent/child
  • 嵌套文档查询: nested

DSL 检索实例

查询所有(match_all)

POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"match_all": {}}
}

查询结果中返回指定条数(size)

POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"match_all": {}},"size":1
}

 分页查询(from)

from 关键字: 用来指定起始返回位置,和size关键字连用可实现分页效果

POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"match_all": {}},"size":1,"from":1
}

 查询结果中返回指定字段(_source)

source 关键字: 是一个数组,在数组中用来指定展示那些字段

POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"match_all": {}},"_source":["name","age"],"size":1,"from":1
}

term 精准查询
根据名称精确查询姓名 term, term查询不会对字段进行分词查询,会采用精确匹配。
        注意: 采用term精确查询, 查询字段映射类型属于为keyword
POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"term": {"name.keyword": "周志文"}},"_source":["name","age"]
}

match 模糊匹配
根据备注信息模糊查询 match, match会根据该字段的分词器,进行分词查询。
POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"match": {"name": "周"}},"_source":["name","age"]
}

multi_match 多字段模糊匹配查询与精准查询
POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"multi_match": {"query": 30,"fields":["age"]}},"_source":["name","age"]
}

等于SQL:

SQL: select * from t_user where age like '%30%'
query_string 字段条件查询
  • 未指定字段条件查询 query_string , 含 AND 与 OR 条件
POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"query_string": {"query": "周 or 30"}},"_source":["name","age"]
}

  • 指定字段条件查询 query_string , 含 AND 与 OR 条件
POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"query_string": {"query": "周 and zhou","fields":["name"]}},"_source":["name","age"]
}

Rang 范围查询

大于、等于和小于转义

  • gte 大于等于
  • lte 小于等于
  • gt 大于
  • lt 小于
  • now 当前时间
POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"range": {"age":{"gte": 25,"lte":31}}},"_source":["name","age"]
}

等于SQL:

select * from t_user where age between 25 and 31

 前缀查询(prefix)

prefix 关键字: 用来检索含有指定前缀的关键词的相关文档

POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"prefix": {"name":{"value": "修"}}},"_source":["name","age"]
}

 通配符查询(wildcard)

wildcard 关键字: 通配符查询 ? 用来匹配一个任意字符 * 用来匹配多个任意字符

POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"wildcard": {"name":{"value": "周*"}}},"_source":["name","age"]
}

 多id查询(ids)

ids 关键字 : 值为数组类型,用来根据一组id获取多个对应的文档

POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"ids": {"values":[1,2]}},"_source":["name","age"]
}

模糊查询(fuzzy)

fuzzy 关键字: 用来模糊查询含有指定关键字的文档。

POST http://192.168.43.10:9200/es_db/_doc/_search?pretty{"query": {"fuzzy": {"email.keyword":"zhouzhiwenxiong@163.com"}},"_source":["name","age"]
}

6、ElasticSearch 之文档映射

ES中映射可以分为动态映射和静态映射 。
动态映射:
        在关系数据库中,需要事先创建数据库,然后在该数据库下创建数据表,并创建
表字段、类型、长度、主键等,最后才能基于表插入数据。而Elasticsearch中不
需要定义Mapping映射(即关系型数据库的表、字段等),在文档写入
Elasticsearch时,会根据文档字段自动识别类型,这种机制称之为动态映射。
动态映射规则如下:
静态映射:
        静态映射是在Elasticsearch中也可以事先定义好映射,包含文档的各字段类型、分词器等,这种方式称之为静态映射。

动态映射实操

删除原创建的索引 :
DELETE http://192.168.43.10:9200/es_db
创建索引:
PUT http://192.168.43.10:9200/es_db
创建文档(ES根据数据类型, 会自动创建映射)
PUT http://192.168.43.10:9200/es_db/_doc/1{"name":"周志文","sex":1,"age":31,"book":"java入门至精通","address":"广东深圳"
}
获取文档映射
GET http://192.168.43.10:9200/es_db/_mapping响应结果:
{"es_db": {"mappings": {"properties": {"address": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}},"age": {"type": "long"},"book": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}},"name": {"type": "text","fields": {"keyword": {"type": "keyword","ignore_above": 256}}},"sex": {"type": "long"}}}}
}

静态映射实操

删除原创建的索引

DELETE http://192.168.43.10:9200/es_db
创建索引和设置文档映射
PUT http://192.168.43.10:9200/es_db{"mappings":{"properties":{"address":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"age":{"type":"long"},"book":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"name":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"sex":{"type":"long"}}}
}
根据静态映射创建文档

PUT http://192.168.43.10:9200/es_db/_doc/1{"name":"周志刚","sex":1,"age":31,"book":"elasticSearch入门至精通","address":"广东深圳"
}

获取文档映射

GET http://192.168.43.10:9200/es_db/_mapping

核心类型(Core datatype)

字符串:string,string类型包含 text 和 keyword。
        text:该类型被用来索引长文本,在创建索引前会将这些文本进行分词,转化为 词的组合,建立索引;允许es来检索这些词,text类型不能用来排序和聚合。
        keyword:该类型不能分词,可以被用来检索过滤、排序和聚合,keyword类型不可用text进行分词模糊检索。
数值型:long、integer、short、byte、double、float
日期型:date
布尔型:boolean

静态映射指定text类型的ik分词器

设置ik分词器的文档映射,先删除之前的es_db,再创建新的es_db,重新定义ik_smart的映射。

PUT http://192.168.43.10:9200/es_db/{"mappings":{"properties":{"address":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"age":{"type":"long"},"book":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"name":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}},"index":true,"store":true,"analyzer":"ik_smart","search_analyzer": "ik_smart"},"sex":{"type":"long"}}}
}

7、Elasticsearch架构原理

Elasticsearch节点类型

在Elasticsearch主要分成两类节点,一类是Master,一类是DataNode。

Master节点

在Elasticsearch启动时,会选举出来一个Master节点。当某个节点启动后,然后使用Zen Discovery机制找到集群中的其他节点,并建立连接。
        discovery.seed_hosts: ["192.168.21.130", "192.168.21.131", "192.168.21.132"]
并从候选主节点中选举出一个主节点。
        cluster.initial_master_nodes: ["node1", "node2","node3"]
Master节点主要负责:
  • 管理索引(创建索引、删除索引)、分配分片
  • 维护元数据
  • 管理集群节点状态
  • 不负责数据写入和查询,比较轻量级
一个Elasticsearch集群中,只有一个Master节点。在生产环境中,内存可以相对小一点,但机器要稳定。

DataNode节点

在Elasticsearch集群中,会有N个DataNode节点。DataNode节点主要负责:
        数据写入、数据检索,大部分Elasticsearch的压力都在DataNode节点上。
在生产环境中,内存最好配置大一些

分片和副本机制

分片(Shard)

        Elasticsearch是一个分布式的搜索引擎,索引的数据也是分成若干部分,分布在不同的服务器节点中分布在不同服务器节点中的索引数据,就是分片(Shard)。Elasticsearch会自动 管理分片,如果发现分片分布不均衡,就会自动迁移。
        一个索引(index)由多个shard(分片)组成,而分片是分布在不同的服务器上的。

副本

        为了对Elasticsearch的分片进行容错,假设某个节点不可用,会导致整个索引库都将不可用。所以,需要对分片进行副本容错。每一个分片都会有对应的副本。在Elasticsearch中,默认创建的索引为1个分片、每个分片有1个主分片和1个副本 分片。
        每个分片都会有一个Primary Shard(主分片),也会有若干个Replica Shard(副本分片)
        Primary Shard和Replica Shard不在同一个节点上

创建索引映射,指定分片、副本数量
PUT http://192.168.43.10:9200/es_db/{"mappings":{"properties":{"address":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"age":{"type":"long"},"book":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"name":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}},"index":true,"store":true,"analyzer":"ik_smart","search_analyzer": "ik_smart"},"sex":{"type":"long"}}},"settings":{"number_of_shards":1, "number_of_replicas":1}
}

8、Elasticsearch重点工作流程

Elasticsearch文档写入原理

1.选择任意一个DataNode发送请求,例如:node2。此时,node2就成为一个coordinating node(协调节点)
2.计算得到文档要写入的分片
        `shard = hash(routing) % number_of_primary_shards`
    routing 是一个可变值,默认是文档的 _id
3.coordinating node会进行路由,将请求转发给对应的primary shard所在的DataNode(假设primary shard在node1、replica shard在node2
4.node1节点上的Primary Shard处理请求,写入数据到索引库中,并将数据同步到Replica shard
5.Primary Shard和Replica Shard都保存好了文档,返回client

Elasticsearch检索原理

1.client发起查询请求,某个DataNode接收到请求,该DataNode就会成为协调节点 (Coordinating Node)
2.协调节点(Coordinating Node)将查询请求广播到每一个数据节点,这些数据节点的分片会处理该查询请求
3.每个分片进行数据查询,将符合条件的数据放在一个优先队列中,并将这些数据的文档ID、节点信息、分片信息返回给协调节点
4.协调节点将所有的结果进行汇总,并进行全局排序
5.协调节点向包含这些文档ID的分片发送get请求,对应的分片将文档数据返回给协调节点,最后协调节点将数据返回给客户端

Elasticsearch准实时索引实现

1.溢写到文件系统缓存

        当数据写入到ES分片时,会首先写入到内存中,然后通过内存的buffer生成一个
segment,并刷到文件系统缓存中,数据可以被检索(注意不是直接刷到磁盘)
ES中默认1秒,refresh一次

2.写translog保障容错

        在写入到内存中的同时,也会记录translog日志,在refresh期间出现异常,会根
据translog来进行数据恢复
        等到文件系统缓存中的segment数据都刷到磁盘中,清空translog文件

3. flush到磁盘

        ES默认每隔30分钟会将文件系统缓存的数据刷入到磁盘

4. segment合并

        Segment太多时,ES定期会将多个segment合并成为大的segment,减少索引查询时IO开销,此阶段ES会真正的物理删除(之前执行过的delete的数据)

9、ElasticSearch 进级

人工控制搜索结果精准度

演示数据:

PUT http://192.168.43.10:9200/db/_doc/1{"name":"周志熊","sex":1,"age":31,"remark":"java developer","address":"广东深圳"
}PUT http://192.168.43.10:9200/db/_doc/2{"name":"周志文","sex":1,"age":26,"remark":"vue developer","address":"广东广州"
}PUT http://192.168.43.10:9200/db/_doc/3{"name":"周志钢","sex":1,"age":29,"remark":"elasticsearch developer","address":"广东广州"
}

功能实现:在索引库(db)中,文档(document)中的remark字段包含java或developer词组,都符合搜索条件

POST http://192.168.43.10:9200/db/_search{"query": {"match": {"remark": "java developer"}}
}

功能升级:需要搜索的文档(document)中的remark字段,同时包含java和developer词组,则需要使
用下述查询语局:
POST  http://192.168.43.10:9200/db/_search{"query": {"match": {"remark": {"query": "java developer","operator": "and"}}}
}

温馨提示:如果将operator的值改为or。则与第一个案例搜索语法效果一致。默认的ES执行搜索的时候,operator就是or。

功能升级:如果要求在搜索的文档document中,需要remark字段中包含多个搜索词条中的一定比 例,可以使用下述方式实现。

实现方式一:minimum_should_match百分比或固定数字。

POST http://192.168.43.10:9200/db/_search{"query": {"match": {"remark": {"query": "java developer","minimum_should_match": "50%"}}}
}

更新db/1 的索引 数据

PUT http://192.168.43.10:9200/db/_doc/1{"name":"周志熊","sex":1,"age":31,"remark":"java developer china","address":"广东深圳"
}

温馨提示:搜索条件中词条百分比,如果无法整除,向下匹配 (如,query条件有3个单词,如果使用百分比提供精准度计算,那么是无法除尽的,如果需要至少匹配两个单词,则需要用67%来进行描述。如果使用66%描述,ES则认为匹配一个单词即可。)

POST http://192.168.43.10:9200/db/_search{"query": {"match": {"remark": {"query": "java developer china","minimum_should_match": "68%"}}}
}

POST http://192.168.43.10:9200/db/_search{"query": {"match": {"remark": {"query": "java developer china","minimum_should_match": "30%"}}}
}

实现方式二:使用should+bool搜索,控制搜索条件的匹配度

具体操作如下:下述案例代表搜索的document中的remark字段中,必须匹配java、developer、 China三个词条中的至少2个。

POST http://192.168.43.10:9200/db/_search{"query": {"bool": {"should": [{"match": {"remark": "java"}}, {"match": {"remark": "developer"}}, {"match": {"remark": "china"}}],"minimum_should_match": 2}}
}

Boost 权重控制

        搜索document中remark字段中包含java的数据,如果remark中包含developer 或china,则包含china的document优先显示。(就是将china数据匹配时的相关度分数增加)。
        一般用于搜索时相关度排序使用。如:电商中的综合排序。将一个商品的销量,广告投放,评价值,库存,单价比较综合排序。在上述的排序元素中,广告投放权重最高,库存权重最低.
POST  http://192.168.43.10:9200/db/_search{"query": {"bool": {"must":[{"match":{"remark":"java"}}],"should": [{"match": {"remark": {"query":"developer","boost": 1}}}, {"match": {"remark":{"query":"china","boost": 3}}}]}}
}

基于dis_max实现best fields策略进行多字段搜索

        best fields策略: 搜索的document中的某一个field,尽可能多的匹配搜索条件。与之相反的是,尽可能多的字段匹配到搜索条件(most fields策略)。如百度搜索使用这种策略。
        优点:精确匹配的数据可以尽可能的排列在最前端,且可以通过minimum_should_match来去除长尾数据,避免长尾数据字段对排序结果的影响。
        长尾数据比如说我们搜索4个关键词,但很多文档只匹配1个,也显示出来了,这些文档其实不是我们想要的。
        缺点:相对排序不均匀。
        dis_max语法: 直接获取搜索的多条件中的,单条件query相关度分数最高的数据,以这个数据做相关度排序。
下述的案例中,就是找name字段中“周志熊”匹配相关度分数或remark字段中java developer匹配相关度分数,哪个高,就使用哪一个相关度分数进行结果排序。
POST http://192.168.43.10:9200/db/_search{"query": {"dis_max": {"queries": [{"match": {"name": "周志熊"}}, {"match": {"remark": "java developer"}}]}}
}

基于tie_breaker参数优化dis_max搜索效果

        dis_max是将多个搜索query条件中相关度分数最高的用于结果排序,忽略其他 query分数,在某些情况下,可能还需要其他query条件中的相关度介入最终的结果排序,这个时候可以使用tie_breaker参数来优化dis_max搜索。tie_breaker参数代表的含义是:将其他query搜索条件的相关度分数乘以参数值,再参与到结果排序中。如果不定义此参数,相当于参数值为0。所以其他query条件的相关度分数被忽略.
POST http://192.168.43.10:9200/db/_search{"query": {"dis_max": {"queries": [{"match": {"name": "周志熊"}}, {"match": {"remark": "java developer"}}],"tie_breaker":0.5}}
}

使用multi_match简化dis_max+tie_breaker

ES中相同结果的搜索也可以使用不同的语法语句来实现。

POST http://192.168.43.10:9200/db/_search{"query": {"dis_max": {"queries": [{"match": {"name": "周志熊"}}, {"match": {"remark":{"query":  "java developer","boost" : 2,"minimum_should_match": 2}}}],"tie_breaker":0.5}}
}

cross fields搜索

        cross fields : 一个唯一的标识,分部在多个fields中,使用这种唯一标识搜索数据就称为cross fields搜索。如:人名可以分为姓和名,地址可以分为省、 市、区县、街道等。那么使用人名或地址来搜索document,就称为cross fields搜索。
        实现这种搜索,一般都是使用most fields搜索策略。因为这就不是一个field的问题。
        Cross fields搜索策略,是从多个字段中搜索条件数据。默认情况下,和most fields搜索的逻辑是一致的,计算相关度分数是和best fields策略一致的。一般来说,如果使用cross fields搜索策略,那么都会携带一个额外的参数operator。 用来标记搜索条件如何在多个字段中匹配。
        当然,在ES中也有cross fields搜索策略。具体语法如下:
POST  http://192.168.43.10:9200/db/_search{"query": {"multi_match": {"query": "java developer","fields": ["name", "remark"],"type": "cross_fields","operator": "and"}}
}

上述功能说明:搜索条件中的java必须在name或remark字段中匹配, developer也必须在name或remark字段中匹配。

温馨提示:

most field策略问题:most fields策略是尽可能匹配更多的字段,所以会导致精确搜索结果排序问题。又因为cross fields搜索,不能使用minimum_should_match来去除长尾数据。所以在使用most fields和cross fields策略搜索数据的时候,都有不同的缺陷。所以商业项目开发中,都推荐使用best fields策略实现搜索。

copy_to fields 搜索

        京东中,如果在搜索框中输入“手机”,点击搜索,那么是在商品的类型名称、商品的名称、商品的卖点、商品的描述等字段中,哪一个字段内进行数据的匹配?如果使用某一个字段做搜索不合适,那么使用_all做搜索是否合适?也不合适,因为_all字段中可能包含图片,价格等字段。
        假设,有一个字段,其中的内容包括(但不限于):商品类型名称、商品名称、商品卖点等字段的数据内容。是否可以在这个特殊的字段上进行数据搜索匹配?
演示数据:
{"category_name":"手机","product_name":"一加6T手机","price":568800,"sell_point":"国产最好的Android手机","tags":["8G+128G","256G可扩展"],"color":"红色","keyword":"手机 一加6T手机 国产最好的Android手机"
}
        copy_to : 就是将多个字段,复制到一个字段中,实现一个多字段组合。copy_to可以解决cross fields搜索问题,在商业项目中,也用于解决搜索条件默认字段问题。
        如果需要使用copy_to语法,则需要在定义index的时候,手工指定mapping映射策略。
copy_to语法:

PUT http://192.168.43.10:9200/es_product/
{"mappings":{"properties":{"provice":{"type":"text","analyzer":"ik_smart","copy_to":"address"},"city":{"type":"text","analyzer":"ik_smart","copy_to":"address"},"street":{"type":"text","analyzer":"ik_smart","copy_to":"address"},"address":{"type":"text","analyzer":"ik_smart"}}}
}
上述的mapping定义中,是新增了4个字段,分别是provice、city、street、 address,其中provice、city、street三个字段的值,会自动复制到address字段中,实现一个字段的组合。那么在搜索地址的时候,就可以在address字段中做条件匹配,从而避免most fields策略导致的问题。在维护数据的时候,不需对address字段特殊的维护。因为address字段是一个组合字段,是由ES自动维护的。 类似java代码中的推导属性。在存储的时候,未必存在,但是在逻辑上是一定存在
的,因为address是由3个物理存在的属性province、city、street组成的。

copy_to 演示数据
PUT http://192.168.43.10:9200/es_product/_doc/1{"provice":"广东.深圳.华为技术有限公司","city":"深圳.龙华区.华为技术研发中心","street":"天安街道.华为大道","address":"华为产业园"
}

copy_to 查询:

POST  http://192.168.43.10:9200/es_product/_doc/_search?pretty{"query": {"match": {"address": "广东"}}
}

match phrase 搜索

短语搜索。就是搜索条件不分词。代表搜索条件不可分割。
POST http://192.168.43.10:9200/db/_doc/_search?pretty{"query":{"match_phrase":{"remark":"vue developer"}}
}

前缀搜索 prefix search

        使用前缀匹配实现搜索能力。通常针对keyword类型字段,也就是不分词的字段。
POST http://192.168.43.10:9200/db/_doc/_search{"query":{"prefix":{"address.keyword":{"value":"广"}}}
}
注意:针对前缀搜索,是对keyword类型字段而言。而keyword类型字段数据大小写敏感。

        前缀搜索效率比较低。前缀搜索不会计算相关度分数。前缀越短,效率越低。 如果使用前缀搜索,建议使用长前缀。因为前缀搜索需要扫描完整的索引内容,所以前缀越长,相对效率越高。

通配符搜索

        ES中也有通配符。但是和java还有数据库不太一样。通配符可以在倒排索引中使用,也可以在keyword类型字段中使用。
        常用通配符:
  • ? - 一个任意字符
  • * - 0~n个任意字符

POST http://192.168.43.10:9200/db/_doc/_search{"query":{"wildcard":{"address.keyword":{"value":"?东*"}}}
}

不推荐使用。

正则搜索

        ES支持正则表达式。可以在倒排索引或keyword类型字段中使用。
        常用符号:
  • [] - 范围,如: [0-9]是0~9的范围数字
  • . - 一个字符
  • + - 前面的表达式可以出现多次。
POST http://192.168.43.10:9200/db/_doc/_search{"query":{"regexp":{"address.keyword":"[A‐z].+"}}
}

搜索推荐

效率较低,这里就不做过多讲解。

fuzzy模糊搜索

搜索的时候,可能搜索条件文本输入错误,如:hello world -> hello word。这种拼写错误还是很常见的。fuzzy技术就是用于解决错误拼写的(在英文中很有效,在中文中几乎无效。)。其中fuzziness代表value的值word可以修改多少个字母来进行拼写错误的纠正(修改字母的数量包含字母变更,增加或减少字母。)。
POST http://192.168.43.10:9200/db/_doc/_search{"query":{"fuzzy":{"remark":{"value":"develop","fuzziness": 2}}}
}

高亮显示

        在搜索中,经常需要对搜索关键字做高亮显示,高亮显示也有其常用的参数,在这个案例中
做一些常用参数的介绍。
        现在搜索db索引中address字段中包含“广东”的document。并对“XX关键字”做高亮显
示,高亮效果使用html标签<span>,并设定字体为红色。

POST http://192.168.43.10:9200/db/_doc/_search{"query":{"match":{"address":"广东"}},"highlight":{"fields":{"address":{}}}
}

10、ElasticSearch聚合搜索

bucket和metric概念简介

        bucket就是一个聚合搜索时的数据分组。如:销售部门有员工张三和李四,开发
部门有员工王五和赵六。那么根据部门分组聚合得到结果就是两个bucket。销售部门
bucket中有张三和李四,开发部门 bucket中有王五和赵六。
        metric就是对一个bucket数据执行的统计分析。
        如上述案例中,开发部门有2个员工,销售部门有2个员工,这就是metric。
        
        metric有多种统计,如:求和,最大值,最小值,平均值等。
创建es_cars索引库
POST http://192.168.43.10:9200/es_cars/{"mappings":{"properties":{"price":{"type":"long"},"color":{"type":"keyword"},"brand":{"type":"keyword"},"model":{"type":"keyword"},"sold_date":{"type":"date","format": "yyyy-MM-dd||epoch_millis"},"remark":{"type":"text","analyzer":"ik_max_word"}}}
}

添加验收数据:

PUT http://192.168.43.10:9200/es_cars/_doc/1
{
"price": 123000,
"color": "金色",
"brand": "大众",
"model": "大众速腾",
"sold_date": 1636076363000,
"remark": "大众神车"
}PUT http://192.168.43.10:9200/es_cars/_doc/2
{
"price": 123000,
"color": "金色",
"brand": "大众",
"model": "大众速腾",
"sold_date": 1636076363000,
"remark": "大众神车"
}PUT http://192.168.43.10:9200/es_cars/_doc/3
{
"price": 239800,
"color": "白色",
"brand": "标志",
"model": "标志508",
"sold_date": 1621301963000,
"remark": "标志品牌全球上市车型"
}
PUT http://192.168.43.10:9200/es_cars/_doc/4
{
"price": 148800,
"color": "白色",
"brand": "标志",
"model": "标志408",
"sold_date": 1625296980000,
"remark": "比较大的紧凑型车"
}PUT http://192.168.43.10:9200/es_cars/_doc/5
{
"price": 1998000,
"color": "黑色",
"brand": "大众",
"model": "大众辉腾",
"sold_date": 1629444180000,
"remark": "大众最让人肝疼的车"
}PUT http://192.168.43.10:9200/es_cars/_doc/7
{
"price": 218000,
"color": "红色",
"brand": "奥迪",
"model": "奥迪A4",
"sold_date": 1636183380000,
"remark": "小资车型"
}PUT http://192.168.43.10:9200/es_cars/_doc/8
{
"price": 489000,
"color": "黑色",
"brand": "奥迪",
"model": "奥迪A6",
"sold_date": 1641108180000,
"remark": "政府专用?"
}PUT http://192.168.43.10:9200/es_cars/_doc/9
{
"price": 1899000,
"color": "黑色",
"brand": "奥迪",
"model": "奥迪A 8",
"sold_date": 1644736980000,
"remark": "很贵的大A6。。。"
}

聚合操作案例

根据color分组统计销售数量

        只执行聚合分组,不做复杂的聚合统计。在ES中最基础的聚合为terms,相当于
SQL中的count。
        在ES中默认为分组数据做排序,使用的是doc_count数据执行降序排列。可以使用
_key元数据,根据分组后的字段数据执行不同的排序方案,也可以根据_count元数
据,根据分组后的统计值执行不同的排序方案。
POST http://192.168.43.10:9200/es_cars/_search{"aggs": {"group_by_color": {"terms": {"field": "color","order": {"_count": "desc"}}}}
}

结果:

统计不同color车辆的平均价格

        先根据color执行聚合分组,在此分组的基础上,对组内数据执行聚合统计,这个组内数据的聚合统计就是metric。同样可以执行排序,因为组内有聚合统计,且对统计数据给予了命名avg_by_price,所以可以根据这个聚合统计数据字段名执行排序逻辑。
POST http://192.168.43.10:9200/es_cars/_search{"aggs": {"group_by_color": {"terms": {"field": "color","order": {"avg_by_price": "asc"}},"aggs": {"avg_by_price": {"avg": {"field": "price"}}}}}
}

温馨提示:size可以设置为0,表示不返回ES中的文档,只返回ES聚合之后的数据,提高查询速度,当然如果你需要这些文档的话,也可以按照实际情况进行设置。

POST http://192.168.43.10:9200/es_cars/_search{"size":0,"aggs": {"group_by_color": {"terms": {"field": "color","order": {"avg_by_price": "asc"}},"aggs": {"avg_by_price": {"avg": {"field": "price"}}}}}
}

统计不同color不同brand中车辆的平均价格

        先根据color聚合分组,在组内根据brand再次聚合分组,这种操作可以称为下钻分析。
        Aggs如果定义比较多,则会感觉语法格式混乱,aggs语法格式,有一个相对固定的结构,简单定义:aggs可以嵌套定义,可以水平定义。
        嵌套定义称为下钻分析。水平定义就是平铺多个分组方式。
POST /index_name/type_name/_search{"aggs": {"定义分组名称(最外层)": {"分组策略如:terms、avg、sum": {"field": "根据哪一个字段分组","其他参数": ""},"aggs": {"分组名称1": {},"分组名称2": {}}}}
}
POST http://192.168.43.10:9200/es_cars/_search{"size":0,"aggs": {"group_by_color": {"terms": {"field": "color","order": {"avg_by_price_color": "asc"}},"aggs": {"avg_by_price_color": {"avg": {"field": "price"}},"group_by_brand": {"terms": {"field": "brand","order": {"avg_by_price_brand": "desc"}},"aggs": {"avg_by_price_brand": {"avg": {"field": "price"}}}}}}}
}

统计不同color中的最大和最小价格、总价

POST http://192.168.43.10:9200/es_cars/_search{"aggs": {"group_by_color": {"terms": {"field": "color"},"aggs": {"max_price": {"max": {"field": "price"}},"min_price": {"min": {"field": "price"}},"sum_price": {"sum": {"field": "price"}}}}}
}
        在常见的业务常见中,聚合分析,最常用的种类就是统计数量,最大,最小,平均, 总计等。通常占有聚合业务中的60%以上的比例,小型项目中,甚至占比85%以上。

统计不同品牌汽车中价格排名最高的车型

        在分组后,可能需要对组内的数据进行排序,并选择其中排名高的数据。那么可以使用s来实现:top_top_hithits中的属性size代表取组内多少条数据(默认为10);sort代表组内使用什么字段什么规则排序(默认使用_doc的asc规则排序); source代表结果中包含document中的那些字段(默认包含全部字段)。
POST http://192.168.43.10:9200/es_cars/_search{"size": 0,"aggs": {"group_by_brand": {"terms": {"field": "brand"},"aggs": {"top_car": {"top_hits": {"size": 1,"sort": [{"price": {"order": "desc"}}],"_source": {"includes": ["model", "price"]}}}}}}
}

histogram 区间统计

        histogram类似terms,也是进行bucket分组操作的,是根据一个field,实现数据区间分组。
        如:以100万为一个范围,统计不同范围内车辆的销售量和平均价格。那么使用histogram的聚合的时候,field指定价格字段price。区间范围是100万-interval : 1000000。这个时候ES会将price价格区间划分为: [0, 1000000), [1000000, 2000000), [2000000, 3000000)等,依次类推。在划分区间的同时,histogram会类似 terms进行数据数量的统计(count),可以通过嵌套aggs对聚合分组后的组内数据做再次聚合分析。
POST http://192.168.43.10:9200/es_cars/_search{"size":0,"aggs": {"histogram_by_price": {"histogram": {"field": "price","interval": 1000000},"aggs": {"avg_by_price": {"avg": {"field": "price"}}}}}
}

date_histogram区间分组

        date_histogram可以对date类型的field执行区间聚合分组,如每月销量,每年销量等。
        如:以月为单位,统计不同月份汽车的销售数量及销售总金额。这个时候可以使用date_histogram实现聚合分组,其中field来指定用于聚合分组的字段,interval指定区间范围(可选值有:year、quarter、month、week、day、hour、minute、 second),format指定日期格式化,min_doc_count指定每个区间的最少document(如果不指定,默认为0,当区间范围内没有document时,也会显示bucket分组), extended_bounds指定起始时间和结束时间(如果不指定,默认使用字段中日期最小值所在范围和最大值所在范围为起始和结束时间)。
POST http://192.168.43.10:9200/es_cars/_search{"aggs": {"histogram_by_date": {"date_histogram": {"field": "sold_date","calendar_interval": "month","format": "epoch_millis","min_doc_count": 1,"extended_bounds": {"min": 1609469857000,"max": 1672455457000}},"aggs": {"sum_by_price": {"sum": {"field": "price"}}}}}
}

_global bucket

        在聚合统计数据的时候,有些时候需要对比部分数据和总体数据。
        如:统计某品牌车辆平均价格和所有车辆平均价格。global是用于定义一个全局bucket,这个bucket会忽略query的条件,检索所有document进行对应的聚合统计。
POST http://192.168.43.10:9200/es_cars/_search{"size": 0,"query": {"match": {"brand": "大众"}},"aggs": {"volkswagen_of_avg_price": {"avg": {"field": "price"}},"all_avg_price": {"global": {},"aggs": {"all_of_price": {"avg": {"field": "price"}}}}}
}

aggs+order

对聚合统计数据进行排序。
如:统计每个品牌的汽车销量和销售总额,按照销售总额的降序排列。
POST http://192.168.43.10:9200/es_cars/_search{"size": 0,"aggs": {"group_of_brand": {"terms": {"field": "brand","order": {"sum_of_price": "desc"}},"aggs": {"sum_of_price": {"sum": {"field": "price"}}}}}
}
如果有多层aggs,执行下钻聚合的时候,也可以根据最内层聚合数据执行排序。 如:统计每个品牌中每种颜色车辆的销售总额,并根据销售总额降序排列。这就像SQL中的分组排序一样,只能组内数据排序,而不能跨组实现排序。
POST  http://192.168.43.10:9200/es_cars/_search{"size":0,"aggs": {"group_by_brand": {"terms": {"field": "brand"},"aggs": {"group_by_color": {"terms": {"field": "color","order": {"sum_of_price": "desc"}},"aggs": {"sum_of_price": {"sum": {"field": "price"}}}}}}}
}

search+aggs

        聚合类似SQL中的group by子句,search类似SQL中的where子句。在ES中是完全可
以将search和aggregations整合起来,执行相对更复杂的搜索统计。
        如:统计某品牌车辆每个季度的销量和销售额。
POST http://192.168.43.10:9200/es_cars/_search{"size": 0,"query": {"match": {"brand": "大众"}},"aggs": {"histogram_by_date": {"date_histogram": {"field": "sold_date","calendar_interval": "quarter","min_doc_count": 1},"aggs": {"sum_by_price": {"sum": {"field": "price"}}}}}
}

filter+aggs

        在ES中,filter也可以和aggs组合使用,实现相对复杂的过滤聚合分析。 如:统计10万~50万之间的车辆的平均价格。
POST http://192.168.43.10:9200/es_cars/_search{"query": {"constant_score": {"filter": {"range": {"price": {"gte": 100000,"lte": 500000}}}}},"aggs": {"avg_by_price": {"avg": {"field": "price"}}}
}

聚合中使用filter

filter也可以使用在aggs句法中,filter的范围决定了其过滤的范围。 如:统计某品牌汽车最近一年的销售总额。将filter放在aggs内部,代表这个过滤器只对query搜索得到的结果执行filter过滤。如果filter放在aggs外部,过滤器则会过滤所有的数据。
  • 12M/M 表示 12 个月。
  • 1y/y 表示 1年。
  • d 表示天
POST http://192.168.43.10:9200/es_cars/_search{"query": {"match": {"brand": "大众"}},"aggs": {"count_last_year": {"filter": {"range": {"sold_date": {"gte":1640919457000}}},"aggs": {"sum_of_price_last_year": {"sum": {"field": "price"}}}}}
}

11、ElasticSearch 嵌套对象

案例:设计一个用户document数据类型,其中包含一个地址数据的数组,这种设计方式相对复杂,但是在管理数据时,更加的灵活。

PUT http://192.168.43.10:9200/es_user/{"mappings": {"properties": {"login_name": {"type": "keyword"},"age ": {"type": "short"},"address": {"properties": {"province": {"type": "keyword"},"city": {"type": "keyword"},"street": {"type": "keyword"}}}}}
}
        但是上述的数据建模有其明显的缺陷,就是针对地址数据做数据搜索的时候,经常会搜索
出不必要的数据,如:在下述数据环境中,搜索一个province为北京,city为天津的用户。
PUT http://192.168.43.10:9200/es_user/_doc/1{"login_name": "jack","age": 25,"address": [{"province": "北京","city": "北京","street": "枫林三路"}, {"province": "天津","city": "天津","street": "华夏路"}]
}PUT http://192.168.43.10:9200/es_user/_doc/2{"login_name": "rose","age": 21,"address": [{"province": "河北","city": "廊坊","street": "燕郊经济开发区"}, {"province": "天津","city": "天津","street": "华夏路"}]
}
执行的搜索应该如下:
POST http://192.168.43.10:9200/es_user/_search{"query": {"bool": {"must": [{"match": {"address.province": "北京"}}, {"match": {"address.city": "天津"}}]}}
}
但是得到的结果并不准确,这个时候就需要使用nested object来定义数据建模。

nested object  嵌套对象

使用nested object作为地址数组的集体类型,可以解决上述问题,document模型如下:
PUT http://192.168.43.10:9200/es_user_nested{"mappings": {"properties": {"login_name": {"type": "keyword"},"age": {"type": "short"},"address": {"type": "nested","properties": {"province": {"type": "keyword"},"city": {"type": "keyword"},"street": {"type": "keyword"}}}}}
}
使用nested对应的搜索语法来执行搜索。
POST http://192.168.43.10:9200/es_cars/_search{"query": {"bool": {"must": [{"nested": {"path": "address","query": {"bool": {"must": [{"match": {"address.province": "北京"}}, {"match": {"address.city": "北京"}}]}}}}]}}
}
虽然语法变的复杂了,但是在数据的读写操作上都不会有错误发生,是推荐的设计方式。

父子关系数据建模

        nested object的建模,有个不好的地方,就是采取的是类似冗余数据的方式,将多个数据都放在一起了,维护成本就比较高。每次更新,需要重新索引整个对象(包括跟对象和嵌套对象)
        ES 提供了类似关系型数据库中 Join 的实现。使用 Join 数据类型实现,可以通过Parent / Child 的关系,从而分离两个对象父文档和子文档是两个独立的文档 。更新父文档无需重新索引整个子文档。子文档被新增,更改和删除也不会影响到父文档和其他子文档。
        要点:父子关系元数据映射,用于确保查询时候的高性能,但是有一个限制,就是父子数

据必须存在于一个shard中。

父子关系

定义父子关系的几个步骤
  • 设置索引的 Mapping
  • 索引父文档
  • 索引子文档
  • 按需查询文档

设置Mapping

PUT http://192.168.43.10:9200/my_blogs{"mappings": {"properties": {"blog_comments_relation": {"type": "join","relations": {"blog": "comment"}},"content": {"type": "text"},"title": {"type": "keyword"}}}
}

上述代码建立了一个my_blogs的索引,其中blog_comments_relation是一个用于join的字段,type为join,关系relations为:父为blog, 子为comment
至于建立一父多子关系,只需要改为数组即可:"blog": ["answer", "comment"]

父类文档插入数据
PUT http://192.168.43.10:9200/my_blogs/_doc/blog1{"title": "Learning Elasticsearch","content": "learning ELK is happy","blog_comments_relation": {"name": "blog"}
}温馨提示:可以省略name
PUT http://192.168.43.10:9200/my_blogs/_doc/blog2{"title": "Learning Hadoop","content": "learning Hadoop","blog_comments_relation": {"name": "blog"}
}

插入子文档

子文档的插入语法如下,注意routing是父文档的id,平时我们插入文档时routing的默认就是id此时name为answer,表示这是个子文档。

PUT http://192.168.43.10:9200/my_blogs/_doc/comment1?routing=blog1{"comment": "I am learning ELK","username": "Jack","blog_comments_relation": {"name": "comment","parent": "blog1"}
}PUT http://192.168.43.10:9200/my_blogs/_doc/comment2?routing=blog2{"comment": "I like Hadoop!!!!!","username": "Jack","blog_comments_relation": {"name": "comment","parent": "blog2"}
}PUT http://192.168.43.10:9200/my_blogs/_doc/comment3?routing=blog2{"comment": "Hello Hadoop","username": "Bob","blog_comments_relation": {"name": "comment","parent": "blog2"}
}
Parent / Child 所支持的查询
  • 查询所有文档
  • Parent Id 查询
  • Has Child 查询
  • Has Parent 查询
# 查询所有文档
POST http://192.168.43.10:9200/my_blogs/_search{}#根据父文档ID查看
GET http://192.168.43.10:9200/my_blogs/_doc/blog2# Parent Id 查询
POST http://192.168.43.10:9200/my_blogs/_search{  "query": {  "parent_id": {  "type": "comment",  "id": "blog2"  }  }  }# Has Child 查询,返回父文档
POST http://192.168.43.10:9200/my_blogs/_search{  "query": {  "has_child": {  "type": "comment",  "query" : {  "match": {  "username" : "Jack"  }  } }  }  }# Has Parent 查询,返回相关的子文档
POST http://192.168.43.10:9200/my_blogs/_search{  "query": {  "has_parent": {  "parent_type": "blog",  "query" : {  "match": {  "title" : "Learning Hadoop"  }  }  }  }  }

ElasticSearch 一文读懂相关推荐

  1. 一文读懂大数据平台——写给大数据开发初学者的话!

     一文读懂大数据平台--写给大数据开发初学者的话! 文|miao君 导读: 第一章:初识Hadoop 第二章:更高效的WordCount 第三章:把别处的数据搞到Hadoop上 第四章:把Hado ...

  2. 从实验室走向大众,一文读懂Nanopore测序技术的发展及应用

    关键词/Nanopore测序技术    文/基因慧 随着基因测序技术不断突破,二代测序的发展也将基因检测成本大幅降低.理想的测序方法,是对原始DNA模板进行直接.准确的测序,消除PCR扩增带来的偏差, ...

  3. 一文读懂Faster RCNN

    来源:信息网络工程研究中心本文约7500字,建议阅读10+分钟 本文从四个切入点为你介绍Faster R-CNN网络. 经过R-CNN和Fast RCNN的积淀,Ross B. Girshick在20 ...

  4. 福利 | 一文读懂系列文章精选集发布啦!

    大数据时代已经悄然到来,越来越多的人希望学习一定的数据思维和技能来武装自己,虽然各种介绍大数据技术的文章每天都扑面而来,但纷繁又零散的知识常常让我们不知该从何入手:同时,为了感谢和回馈读者朋友对数据派 ...

  5. ​一文读懂EfficientDet

    一文读懂EfficientDet. 今年年初Google Brain团队在 CVPR 2020 上发布了 EfficientDet目标检测模型, EfficientDet是一系列可扩展的高效的目标检测 ...

  6. 一文读懂序列建模(deeplearning.ai)之序列模型与注意力机制

    https://www.toutiao.com/a6663809864260649485/ 作者:Pulkit Sharma,2019年1月21日 翻译:陈之炎 校对:丁楠雅 本文约11000字,建议 ...

  7. AI洞观 | 一文读懂英特尔的AI之路

    AI洞观 | 一文读懂英特尔的AI之路 https://mp.weixin.qq.com/s/E9NqeywzQ4H2XCFFOFcKXw 11月13日-14日,英特尔人工智能大会(AIDC)在北京召 ...

  8. 一文读懂机器学习中的模型偏差

    一文读懂机器学习中的模型偏差 http://blog.sina.com.cn/s/blog_cfa68e330102yz2c.html 在人工智能(AI)和机器学习(ML)领域,将预测模型参与决策过程 ...

  9. 一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现

    一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现 导读:近日,马云.马化腾.李彦宏等互联网大佬纷纷亮相2018世界人工智能大会,并登台演讲.关于人工智能的现状与未来,他们提出了各自的观点,也引 ...

  10. 一文读懂你该了解的5G知识:现在别买5G手机

    来源: 腾讯科技 2019年是中国全力布局5G的一年:三大运营商纷纷搭建基站,手机厂商发布5G手机,部分城市已经开启了5G测试--在电信日这天,腾讯科技联合知乎推出重磅策划,聚焦和5G相关的小知识,精 ...

最新文章

  1. UVA1025 城市里的间谍 A Spy in the Metro(2003 ICPC world final)(DAG上DP)
  2. 扫地机器人从“玩具”到“工具”,科沃斯、云鲸们要如何扫开屏障?
  3. boost::mp11::mp_find相关用法的测试程序
  4. 迎元旦,庆surging 1.0发布
  5. 《金色梦乡》金句摘抄(十)
  6. c语言1E3是什么数据类型,C语言课件第2章数据类型和表达式.ppt
  7. xmapp mysql打不开_XAMPP 的MYSQL无法启动
  8. autoflowchart软件使用步骤_AutoFlowchart(c语言流程图生成器) V 3.5.3 官方版
  9. LwM2M(轻量级M2M)协议
  10. 机器人无人车项目开发学习 上下位机软硬结合python jetson nano ros
  11. Python 爬取豆瓣电影Top250
  12. 一键去win7快捷方式小箭头
  13. python猫狗大战代码_猫狗大战
  14. /dev/tty、/dev/ttyS/、/dev/ttyUSB区别
  15. 【转载】儒林外史人物——周进(二)
  16. Android11系统丨RK3568开发板运行第一个Android程序
  17. goland 提示doucment contains very longs line,引用包超大的问题
  18. java中Keytool生成证书
  19. Python的egg包(Linux和Windows)
  20. history 清空历史记录 或 history不记录历史命令

热门文章

  1. 详解Django的CSRF认证
  2. java web需要学多久_java框架都有哪些 要学多久
  3. 87个电影调色PR预设包
  4. 计算机网络技术与应用张基温答案,计算机网络技术与应用教程张基温杜勇刘诗瑾廖伟国董兆军第3章接入技术课件教学.ppt...
  5. 阿里P9:聊聊大厂晋升的“潜规则”
  6. 大数据学习——spark运营案例
  7. antony.net
  8. 彩色二维码生成器,带logo文字和中心文字
  9. Vue学习(学习打卡Day16)
  10. CO BW成本报表建立小结