ElasticSearch从入门到精通(二)

ElasticSearch 高级操作

bulk批量操作

批量操作-脚本

#批量操作
#1.删除5号
#新增8号
#更新2号 name为2号
POST _bulk
{"delete":{"_index":"person1","_id":"5"}}
{"create":{"_index":"person1","_id":"8"}}
{"name":"八号","age":18,"address":"北京"}
{"update":{"_index":"person1","_id":"2"}}
{"doc":{"name":"2号"}}

批量操作-java

/**
* Bulk 批量操作
*/
@Test
public void test2() throws IOException {
//创建bulkrequest对象,整合所有操作BulkRequest bulkRequest =new BulkRequest();/*# 1. 删除5号记录# 2. 添加6号记录# 3. 修改3号记录 名称为 “三号”*///添加对应操作//1. 删除5号记录DeleteRequest deleteRequest=new DeleteRequest("person1","5");bulkRequest.add(deleteRequest);//2. 添加6号记录Map<String, Object> map=new HashMap<>();map.put("name","六号");IndexRequest indexRequest=new IndexRequest("person1").id("6").source(map);bulkRequest.add(indexRequest);//3. 修改3号记录 名称为 “三号”Map<String, Object> mapUpdate=new HashMap<>();mapUpdate.put("name","三号");UpdateRequest updateRequest=new UpdateRequest("person1","3").doc(mapUpdate);bulkRequest.add(updateRequest);//执行批量操作BulkResponse response = client.bulk(bulkRequest,RequestOptions.DEFAULT);System.out.println(response.status());
}

导入数据(从mysql导入ES)

第一步:创建索引
PUT goods
{"mappings": {"properties": {"title": {"type": "text","analyzer": "ik_smart"},"price": {"type": "double"},"createTime": {"type": "date"},"categoryName": {"type": "keyword"},"brandName": {"type": "keyword"},"spec": {"type": "object"},"saleNum": {"type": "integer"},"stock": {"type": "integer"}}}
}

第二步:java代码进行数据导入

/**
* 从Mysql 批量导入 elasticSearch
*/
@Test
public void test3() throws IOException {//1.查询所有数据,mysqlList<Goods> goodsList = goodsMapper.findAll();//2.bulk导入BulkRequest bulkRequest=new BulkRequest();//2.1 循环goodsList,创建IndexRequest添加数据for (Goods goods : goodsList) {//2.2 设置spec规格信息 Map的数据 specStr:{}String specStr = goods.getSpecStr();//将json格式字符串转为Map集合Map map = JSON.parseObject(specStr, Map.class);//设置spec mapgoods.setSpec(map);//将goods对象转换为json字符串String data = JSON.toJSONString(goods);IndexRequest indexRequest=newIndexRequest("goods").source(data,XContentType.JSON);bulkRequest.add(indexRequest);}BulkResponse response = client.bulk(bulkRequest,RequestOptions.DEFAULT);System.out.println(response.status());
}

ElasticSearch查询

matchAll

脚本

# 默认情况下,es一次展示10条数据,通过from和size来控制分页
# 查询结果详解
GET goods/_search
{"query": {"match_all": {}},"from": 0,"size": 100
}
GET g

java

/**
* 查询所有
* 1. matchAll
* 2. 将查询结果封装为Goods对象,装载到List中
* 3. 分页。默认显示10条
*/
@Test
public void matchAll() throws IOException {//2. 构建查询请求对象,指定查询的索引名称SearchRequest searchRequest=new SearchRequest("goods");//4. 创建查询条件构建器SearchSourceBuilderSearchSourceBuilder sourceBuilder=new SearchSourceBuilder();//6. 查询条件QueryBuilder queryBuilder= QueryBuilders.matchAllQuery();//5. 指定查询条件sourceBuilder.query(queryBuilder);//3. 添加查询条件构建器 SearchSourceBuildersearchRequest.source(sourceBuilder);// 8 . 添加分页信息 不设置 默认10条// sourceBuilder.from(0);// sourceBuilder.size(100);//1. 查询,获取查询结果SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT);//7. 获取命中对象 SearchHitsSearchHits hits = searchResponse.getHits();//7.1 获取总记录数Long total= hits.getTotalHits().value;System.out.println("总数:"+total);//7.2 获取Hits数据 数组SearchHit[] hits1 = hits.getHits();//获取json字符串格式的数据List<Goods> goodsList = new ArrayList<>();for (SearchHit searchHit : hits1) {String sourceAsString = searchHit.getSourceAsString();//转为java对象Goods goods = JSON.parseObject(sourceAsString, Goods.class);goodsList.add(goods);}for (Goods goods : goodsList) {System.out.println(goods);}
}
设置条件的疑问点

termQuery和matchQuery
term 查询和字段类型有关系,首先回顾一下 ElasticSearch 两个数据类型
ElasticSearch 两个数据类型(做映射时候用于区别是否对这个字段的内容进行分词)
text :会分词,不支持聚合
keyword :不会分词,将全部内容作为一个词条,支持聚合

termQuery和matchQuery作用类似,但是是用在查询条件上的,分别代表的不同的查询方式,termQuery不会对查询条件进行分词,matchQuery则会对查询条件进行分词

term 查询:会将华为手机作为一个整体查询
GET goods/_search
{"query": {"term": {"title": {"value": "华为手机"}}}
}
match查询:会将华为手机分为:华为和手机进行查询
# match查询
GET goods/_search
{"query": {"match": {"title": "华为手机"}},"size": 500
}
总结:
term query 会去倒排索引中寻找确切的 term ,它并不知道分词器的存在。这种查询适合 keyword 、numeric date类型
match query 知道分词器的存在。并且理解是如何被分词的
模糊查询
wildcard 查询:会对查询条件进行分词。还可以使用通配符 ? (任意单个字符) 和 * 0 个或多个字符)
"* *" 包含华字的
" *" 华字后边多个字符
" ?" 华字后边多个字符
"* " "? " 会引发全表(全索引)扫描 注意效率问题
# wildcard 查询。查询条件分词,模糊查询
GET goods/_search
{"query": {"wildcard": {"title": {"value": "华*"}}}
}
正则查询
\W :匹配包括下划线的任何单词字符,等价于 [A-Z a-z 0-9_] 开头的反斜杠是转义符
+ 号多次出现
(.)* 为任意字符
正则查询取决于正则表达式的效率
GET goods/_search
{"query": {"regexp": {"title": "\\w+(.)*"}}
}
前缀查询
keyword 类型支持比较好
GET goods/_search
{"query": {"prefix": {"brandName": {"value": "三"}}}
}
模糊查询-JavaAPI
//模糊查询
WildcardQueryBuilder query = QueryBuilders.wildcardQuery("title", "华*");//华后多
个字符
//正则查询
RegexpQueryBuilder query = QueryBuilders.regexpQuery("title", "\\w+(.)*");
//前缀查询
PrefixQueryBuilder query = QueryBuilders.prefixQuery("brandName", "三");
范围&排序查询
# 范围查询
GET goods/_search
{"query": {"range": {"price": {"gte": 2000,"lte": 3000}}},"sort": [{"price": {"order": "desc"}}]}
queryString查询
queryString 多条件查询
会对查询条件进行分词。
然后将分词后的查询条件和词条进行等值匹配
默认取并集( OR
可以指定多个查询字段
query_string :识别 query 中的连接符( or and
# queryString
GET goods/_search
{"query": {"query_string": {"fields": ["title","categoryName","brandName"],"query": "华为 AND 手机"}}
}
simple_query_string :不识别 query 中的连接符( or and ),查询时会将 华为 "and" 手机 分别进行查询
GET goods/_search
{"query": {"simple_query_string": {"fields": ["title","categoryName","brandName"],"query": "华为 AND 手机"}}
}
query_string :有 default_operator 连接符的脚本
GET goods/_search
{"query": {"query_string": {"fields": ["title","brandName","categoryName"],"query": "华为手机 ", "default_operator": "AND"}}
}
java 代码
QueryStringQueryBuilder query = QueryBuilders.queryStringQuery("华为手
机").field("title").field("categoryName")
.field("brandName").defaultOperator(Operator.AND);
simple_query_string :有 default_operator 连接符的脚本
GET goods/_search
{"query": {"simple_query_string": {"fields": ["title","brandName","categoryName"],"query": "华为手机 ", "default_operator": "OR"}}
}
注意: query 中的 or and 是查询时 匹配条件是否同时出现 ----or 出现一个即可, and 两个条件同时出现 default_operator or and 是对结果进行 并集( or )、交集( and
布尔查询
boolQuery :对多个查询条件连接。连接方式:
•must and ):条件必须成立
•must_not not ):条件必须不成立
•should or ):条件可以成立
•filter :条件必须成立,性能比 must 高。不会计算得分
得分 : 即条件匹配度 , 匹配度越高,得分越高
# boolquery
#must和filter配合使用时,max_score(得分)是显示的
#must 默认数组形式
GET goods/_search
{"query": {"bool": {"must": [{"term": {"brandName": {"value": "华为"}}}],"filter": [{"term": {"title": "手机"}},{"range": {"price": {"gte": 2000,"lte": 3000}}}]}}
}
#filter 单独使用 filter可以是单个条件,也可多个条件(数组形式)
{"query": {"bool": {"filter": [{"term": {"brandName": {"value": "华为"}}}]}}
}
布尔查询 -JavaAPI
布尔查询: boolQuery
1. 查询品牌名称为 : 华为
2. 查询标题包含:手机
3. 查询价格在: 2000-3000
must filter 为连接方式
term match 为不同的查询方式
//1.构建boolQuery
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//2.构建各个查询条件
//2.1 查询品牌名称为:华为
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brandName","华为");
boolQuery.must(termQueryBuilder);
//2.2. 查询标题包含:手机
MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("title", "手机");
boolQuery.filter(matchQuery);
//2.3 查询价格在:2000-3000
RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
rangeQuery.gte(2000);
rangeQuery.lte(3000);
boolQuery.filter(rangeQuery);
sourceBuilder.query(boolQuery);
聚合查询
指标聚合:相当于 MySQL 的聚合函数。 max min avg sum
桶聚合:相当于 MySQL group by 操作。不要对 text 类型的数据进行分组,会失败。

# 聚合查询
# 指标聚合 聚合函数
GET goods/_search
{"query": {"match": {"title": "手机"}},"aggs": {"max_price": {"max": {"field": "price"}}}
}
# 桶聚合 分组
GET goods/_search
{"query": {"match": {"title": "手机"}},"aggs": {"goods_brands": {"terms": {"field": "brandName","size": 100}}}
}
聚合查询 -JavaAPI
聚合查询:桶聚合,分组查询
1. 查询 title 包含手机的数据
2. 查询品牌列表
/**
* 聚合查询:桶聚合,分组查询
* 1. 查询title包含手机的数据
* 2. 查询品牌列表
*/
@Test
public void testAggQuery() throws IOException {SearchRequest searchRequest=new SearchRequest("goods");SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();//1. 查询title包含手机的数据MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("title", "手机");sourceBuilder.query(queryBuilder);//2. 查询品牌列表 只展示前100条AggregationBuilder               aggregation=AggregationBuilders.terms("goods_brands").field("brandName").size(100);sourceBuilder.aggregation(aggregation);searchRequest.source(sourceBuilder);SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT);//7. 获取命中对象 SearchHitsSearchHits hits = searchResponse.getHits();//7.1 获取总记录数Long total= hits.getTotalHits().value;System.out.println("总数:"+total);// aggregations 对象Aggregations aggregations = searchResponse.getAggregations();//将aggregations 转化为mapMap<String, Aggregation> aggregationMap = aggregations.asMap();//通过key获取goods_brands 对象 使用Aggregation的子类接收 buckets属性在Terms接口中体现// Aggregation goods_brands1 = aggregationMap.get("goods_brands");Terms goods_brands =(Terms) aggregationMap.get("goods_brands");//获取buckets 数组集合List<? extends Terms.Bucket> buckets = goods_brands.getBuckets();Map<String,Object>map=new HashMap<>();//遍历buckets key 属性名,doc_count 统计聚合数for (Terms.Bucket bucket : buckets) {System.out.println(bucket.getKey());map.put(bucket.getKeyAsString(),bucket.getDocCount());}System.out.println(map);
}
高亮查询
高亮三要素:
高亮字段
前缀
后缀
默认前后缀 : em
<em> 手机 </em>
GET goods/_search
{"query": {"match": {"title": "电视"}},"highlight": {"fields": {"title": {"pre_tags": "<font color='red'>","post_tags": "</font>"}}}
高亮查询 -JavaAPI
实施步骤:
高亮查询:
1. 设置高亮
        高亮字段
        前缀
        后缀
2. 将高亮了的字段数据,替换原有数据
/**
*
* 高亮查询:
* 1. 设置高亮
* * 高亮字段
* * 前缀
* * 后缀
* 2. 将高亮了的字段数据,替换原有数据
*/
@Test
public void testHighLightQuery() throws IOException {SearchRequest searchRequest = new SearchRequest("goods");SearchSourceBuilder sourceBulider = new SearchSourceBuilder();// 1. 查询title包含手机的数据MatchQueryBuilder query = QueryBuilders.matchQuery("title", "手机");sourceBulider.query(query);//设置高亮HighlightBuilder highlighter = new HighlightBuilder();//设置三要素highlighter.field("title");//设置前后缀标签highlighter.preTags("<font color='red'>");highlighter.postTags("</font>");//加载已经设置好的高亮配置sourceBulider.highlighter(highlighter);searchRequest.source(sourceBulider);SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT);SearchHits searchHits = searchResponse.getHits();//获取记录数long value = searchHits.getTotalHits().value;System.out.println("总记录数:"+value);List<Goods> goodsList = new ArrayList<>();SearchHit[] hits = searchHits.getHits();for (SearchHit hit : hits) {String sourceAsString = hit.getSourceAsString();//转为javaGoods goods = JSON.parseObject(sourceAsString, Goods.class);// 获取高亮结果,替换goods中的titleMap<String, HighlightField> highlightFields = hit.getHighlightFields();HighlightField HighlightField = highlightFields.get("title");Text[] fragments = HighlightField.fragments();//highlight title替换 替换goods中的titlegoods.setTitle(fragments[0].toString());goodsList.add(goods);}for (Goods goods : goodsList) {System.out.println(goods);}
}
重建索引&索引别名
#查询别名 默认别名无法查看,默认别名同索引名
GET goods/_alias/
#结果
{"goods" : {"aliases" : { }}
}
1. 新建 student_index_v1 索引
# -------重建索引-----------
# 新建student_index_v1。索引名称必须全部小写
PUT student_index_v1
{"mappings": {"properties": {"birthday":{"type": "date"}}}
}
#查看 student_index_v1 结构
GET student_index_v1
#添加数据
PUT student_index_v1/_doc/1
{
"birthday":"1999-11-11"
}
#查看数据
GET student_index_v1/_search
#添加数据
PUT student_index_v1/_doc/1
{
"birthday":"1999年11月11日"
}
2. 重建索引 : student_index_v1 数据拷贝到 student_index_v2
# 业务变更了,需要改变birthday字段的类型为text
# 1. 创建新的索引 student_index_v2
# 2. 将student_index_v1 数据拷贝到 student_index_v2
# 创建新的索引 student_index_v2
PUT student_index_v2
{"mappings": {"properties": {"birthday":{"type": "text"}}}
}
# 将student_index_v1 数据拷贝到 student_index_v2
# _reindex 拷贝数据
POST _reindex
{"source": {"index": "student_index_v1"},"dest": {"index": "student_index_v2"}
}
GET student_index_v2/_search
PUT student_index_v2/_doc/2
{
"birthday":"1999年11月11日"
}
3. 创建索引库别名:
注意: DELETE student_index_v1 这一操作将删除 student_index_v1 索引库,并不是删除别名
# 思考: 现在java代码中操作es,还是使用的实student_index_v1老的索引名称。
# 1. 改代码(不推荐)
# 2. 索引别名(推荐)
# 步骤:
# 0. 先删除student_index_v1
# 1. 给student_index_v2起个别名 student_index_v1
# 先删除student_index_v1
#DELETE student_index_v1 这一操作将删除student_index_v1索引库
#索引库默认的别名与索引库同名,无法删除
# 给student_index_v1起个别名 student_index_v11
POST student_index_v2/_alias/student_index_v11
#测试删除命令
POST /_aliases
{"actions": [{"remove": {"index": "student_index_v1", "alias": "student_index_v11"}}]
}
# 给student_index_v2起个别名 student_index_v1
POST student_index_v2/_alias/student_index_v1
#查询别名
GET goods/_alias/
GET student_index_v1/_search
GET student_index_v2/_search

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

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

相关文章

Android 10.0 系统开启和关闭黑白模式主题功能实现

1. 概述 在10.0的rom系统开发定制化中,在系统SystemUI的下拉状态栏中,产品开发功能需求要求添加黑白模式功能开关的功能,就是打开黑白模式,系统颜色就会变成黑白颜色, 关闭黑白模式开关系统就会变成彩色模式,所以就需要了解下系统是怎么设置黑白模式和彩色模式的,然后添…

docker下redis备份文件dump.rdb获取

1.查看镜像 docker ps -a 2.进入redis客户端 docker exec -it redis redis-cli 3.保存备份文件 save 4.查看文件存放位置 CONFIG GET dir 5.将docker中文件拷出 docker cp id或name:容器中文件的路径 目标目录地址

研究铜互连的规模能扩大到什么程度

随着领先的芯片制造商继续将finFET以及很快的纳米片晶体管缩小到越来越小的间距&#xff0c;使用铜及其衬垫和阻挡金属&#xff0c;较小的金属线将变得难以维持。接下来会发生什么以及何时发生&#xff0c;仍有待确定。 自从IBM在20世纪90年代向业界引入采用双镶嵌工艺的铜互连…

一文教你彻底理解什么是协程!

作为程序员&#xff0c;想必你多多少少听过协程这个词&#xff0c;这项技术近年来越来越多的出现在程序员的视野当中&#xff0c;尤其高性能高并发领域。当你的同学、同事提到协程时如果你的大脑一片空白&#xff0c;对其毫无概念。。。 那么这篇文章正是为你量身打造的。话不…

flink集群与资源@k8s源码分析-回顾

本章是分析系列最后一章,作为回顾,以运行架构图串联起所有分析场景 1 启动集群,部署集群(提交k8s),新建作业管理器组件 2 构建和启动flink master组件 3 提交作业,N/A

性能测试分析调优必备的java虚拟机知识

Java虚拟机 Java虚拟机&#xff08;Java Virtual Machine&#xff0c;简称JVM&#xff09;是一种用于执行Java字节码的虚拟计算机。它是Java平台的关键组成部分&#xff0c;负责将Java源代码编译为可在不同计算机体系结构上执行的字节码。 JVM起到了中间层的作用&#xff0c…

IP转地理位置:探讨技术与应用

IP地址是互联网上设备的唯一标识符&#xff0c;而将IP地址转换为地理位置信息是网络管理、安全监控和市场定位等领域中的一项重要任务。本文将深入探讨IP转地理位置的技术原理和各种应用场景。 IP地址与地理位置 IP地址&#xff08;Internet Protocol Address&#xff09;是一…

面试官:你了解axios的原理吗?有看过它的源码吗?

面试官&#xff1a;你了解axios的原理吗&#xff1f;有看过它的源码吗&#xff1f; 一、axios的基本使用 关于 axios 的基本使用&#xff0c;上篇文章已经有所涉及&#xff0c;这里再稍微回顾一下&#xff1a; 发送请求 import axios from axios;axios(config) // 直接传入…

一百八十六、大数据离线数仓完整流程——步骤五、在Hive的DWS层建动态分区表并动态加载数据

一、目的 经过6个月的奋斗&#xff0c;项目的离线数仓部分终于可以上线了&#xff0c;因此整理一下离线数仓的整个流程&#xff0c;既是大家提供一个案例经验&#xff0c;也是对自己近半年的工作进行一个总结。 二、数仓实施步骤 &#xff08;五&#xff09;步骤五、在Hive的…

《从菜鸟到大师之路 MySQL 篇》

《从菜鸟到大师之路 MySQL 篇》 数据库是什么 数据库管理系统&#xff0c;简称为DBMS&#xff08;Database Management System&#xff09;&#xff0c;是用来存储数据的管理系统。 DBMS 的重要性 无法多人共享数据 无法提供操作大量数据所需的格式 实现读取自动化需要编程…

docker安装使用xdebug

docker安装使用xdebug 1、需要先安装PHP xdebug扩展 1.1 到https://pecl.php.net/package/xdebug下载tgz文件&#xff0c;下载当前最新稳定版本的文件。然后把这个tgz文件放到php/extensions目录下&#xff0c;记得install.sh中要替换解压的文件名&#xff1a; installExtensio…

Vue 组件开发总结

Vue 组件开发思路 1. 组件划分 首先&#xff0c;你需要明确定义组件的划分。将大型界面划分为小型、可重用的组件是一个关键步骤。这有助于提高代码的可维护性和可复用性。 2. 组件设计 在设计组件时&#xff0c;考虑组件的输入&#xff08;props&#xff09;和输出&#xf…

数据库常用指令

检查Linux系统是否已经安装了MySQL&#xff1a; sudo service mysql start

手把手教你使用PLSQL远程连接Oracle数据库【内网穿透】

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…

浅谈智能照明控制系统在体育馆中的应用

【摘要】在社会经济日益发展的今天&#xff0c;人们的物质文化水平都有着不同程度上的提高。与此同时人们更加追求高质量的工作和生活环境。照明在现代化的多功能体育场馆中是非常重要的一个环节。智能化的照明控制系统能够根据环境的变化以及客户的需求等条件来自动调节照明系…

数据中心市场现状及发展趋势分析

随着数字化转型的加速&#xff0c;数据中心在中国的市场需求日益增长。本文将分析2023年中国数据中心市场的现状&#xff0c;并探讨其未来发展趋势。 一、2023年中国数据中心市场现状 市场规模&#xff1a;2023年中国数据中心市场的规模已经相当庞大&#xff0c;达到了数百亿美…

Bigemap如何查看历史影像

工具 Bigemap gis office地图软件 BIGEMAP GIS Office-全能版 Bigemap APP_卫星地图APP_高清卫星地图APP 很多人都在寻找历史影像图&#xff0c;这块的需求是非常大&#xff0c;历史影像一般可以用于历史地貌的变迁分析&#xff0c;还原以前的生态场景&#xff0c;对范围面积…

由于找不到MSVCP140.dll,无法继续执行代码,重新安装程序可能会解决此问题的”修复方案

在Windows操作系统中&#xff0c;msvcp140.dll是一个非常重要的动态链接库文件&#xff0c;它是Microsoft Visual C 2015 Redistributable的一部分。这个文件主要用于支持许多应用程序的正常运行&#xff0c;例如Microsoft Office、SQL Server等。当计算机上缺少msvcp140.dll文…

Django(21):使用Celery任务框架

目录 Celery介绍Celery安装Celery使用项目文件和配置启动Celery编写任务调用异步任务查看任务执行状态及结果 设置定时和周期性任务配置文件添加任务Django Admin添加周期性任务启动任务调度器beat Flower监控任务执行状态Celery高级用法与注意事项给任务设置最大重试次数不同任…

ChatGPT详细搭建教程+支持AI绘画

一、AI创作系统 SparkAi系统是基于很火的GPT提问进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT系统&#xff1f;小编这里写一个详细图文教程吧&#x…