ElasticSearch教程(详解版)

        本篇博客将向各位详细介绍elasticsearch,也算是对我最近学完elasticsearch的一个总结,对于如何在Kibana中使用DSL指令,本篇文章不会进行介绍,这里只会介绍在java中如何进行使用,保证你看完之后就会在项目中进行上手,那我们开始本篇教程吧~

什么是ElasticSearch

  • Elasticsearch,基于lucene.分布式的Restful实时搜索和分析引擎(实时)
  • 分布式的实时文件存储,每个字段都被索引并可被搜索
  • 高扩展性,可扩展至上百台服务器,处理PB级结构化或非结构化数据
  • Elasticsearch用于全文检索,结构化搜索,分析/合并使用

ElasticSearch是如何做到这么快的

  • 分布式存储:ElasticSearch把数据存储在多个节点上,从而减少单个节点的压力,从而提高性能
  • 索引分片:ElasticSearch把索引分成多个分片,这样可以让查询操作并行化,从而提高性能
  • 全文索引:ElasticSearch把文档转换成可搜索的结构化数据,从而提高效率
  • 倒排索引:ElasticSearch将文档进行分词处理,把每个词在哪个文档中出现过进行映射,并存储这些信息,从而在搜索时,查询这些分词和搜索这些分词存在在哪些文档中,提高查询效率
  • 异步请求处理:ElasticSearch能够在请求到达时立即返回,避免长时间等待,提高效率

全文索引和倒排索引是什么

        全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。全文搜索搜索引擎数据库中的数据。

  • 优点:

    • 可以给多个字段创建索引

    • 根据索引字段搜索、排序速度非常快

  • 缺点:

    • 根据非索引字段,或者索引字段中的部分词条查找时,只能全表扫描。

        倒排索引就和传统的索引结构相反,传统的索引是由文档组成的,每个文档中都包含了若干个词汇,然后根据这些词汇简历索引。而倒排索引则与其相反,倒排索引由词汇构成,每个词汇对应若干个文档,然后根据这些文档建立索引。

  • 优点:

    • 根据词条搜索、模糊搜索时,速度非常快

  • 缺点:

    • 只能给词条创建索引,而不是字段

    • 无法根据字段做排序

ElasticSearch和Mysql之间的映射关系

Mysql类型       ElasticSearch类型说明
VARCHARtext、keyword根据是否需要使用全文搜索或精确搜索选择使用text或keyword
CHAR        

keyword                       

通常映射为keyword,因为它们存储较短的、不经常变化的字符序列
BLOB/TEXT        text大文本块使用text,适用于全文检索
INT,BIGINTlong大多数整数型使用long,以支持更大的数值
TINYINTbyte较小的整数可以映射为byte类型
DECIMAL,FLOAT,DOUBLEdouble,float
DATE,DATETIME,TIMESTAMPdate
BOOLEANboolean

倒排索引建立步骤

es中建立倒排索引需要两步,首先对文档进行分词,其次建立倒排索引

分词

        分词的意思大概就是对文档中的数据通过es的分词器进行分割成一个个词项,比如 “我是银氨溶液” 这句话,经过分词过后就是 “我”、“是”、“银氨”、“溶液”,当然es的分词器分为ik_smart分词器和ik_max_word分词,所以实际操作时这句话会被分解为不同的词段。

es中的一些概念

        文档

        elasticsearch是面向文档(Document)存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在elasticsearch中。

        字段

Json文档中往往包含很多的字段(Field),类似于数据库中的列。

        索引

索引(Index),就是相同类型的文档的集合。因此,我们可以把索引当做是数据库中的表。

        映射

        数据库的表会有约束信息,用来定义表的结构、字段的名称、类型等信息。因此,索引库中就有映射(mapping),是索引中文档的字段约束信息,类似表的结构约束。

对照图表:

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

RestAPI

首先,在这篇文章中不会将所有api都介绍完,所以这了贴上官方文档的地址,以共各位查看:

ElasticSearch官方文档

这里需要先引入es的依赖

 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>

同时,你需要在pom文件中修改es的版本和你本地的一样

 

然后,我们需要把es注入到spring容器中

@Configuration
public class ElasticSearchClientConfig {@Beanpublic RestHighLevelClient restHighLevelClient(){RestHighLevelClient client=new RestHighLevelClient(RestClient.builder(new HttpHost("127.0.0.1", 9200, "http")));return client;}
}

 准备工作完成之后就可以开始接下来的学习啦~

索引的CRUD操作

索引的操作较为简单,而且项目中实际都是对文档进行操作,所以我这里贴出索引的相关操作代码,并做出解释,各位可以了解一下

 @Autowired@Qualifier("restHighLevelClient")private RestHighLevelClient client;//    测试索引的创建  Request@Testvoid testCreateIndex() throws Exception {
//        1、创建索引请求CreateIndexRequest request = new CreateIndexRequest("yinan_index");
//       2、客户端执行请求,请求后获得响应CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);System.out.println(createIndexResponse);}//    测试获取索引@Testvoid testGetIndex() throws IOException {GetIndexRequest getIndex = new GetIndexRequest("yinan_index");boolean exists = client.indices().exists(getIndex, RequestOptions.DEFAULT);System.out.println(exists);}//    删除索引@Testvoid testDeleteIndex() throws Exception {DeleteIndexRequest request = new DeleteIndexRequest("yinan_index");AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);System.out.println(delete.isAcknowledged());}

其中indices中包含了操作索引库的所有方法,在创建索引的时候如果有多个字段,可以提前写好 一个字符串常量,例如:

   public static final String MAPPING_TEMPLATE = "{\n" +"  \"mappings\": {\n" +"    \"properties\": {\n" +"      \"id\": {\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"name\":{\n" +"        \"type\": \"text\",\n" +"        \"analyzer\": \"ik_max_word\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"address\":{\n" +"        \"type\": \"keyword\",\n" +"        \"index\": false\n" +"      },\n" +"      \"price\":{\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"score\":{\n" +"        \"type\": \"integer\"\n" +"      },\n" +"      \"brand\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"city\":{\n" +"        \"type\": \"keyword\",\n" +"        \"copy_to\": \"all\"\n" +"      },\n" +"      \"starName\":{\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"business\":{\n" +"        \"type\": \"keyword\"\n" +"      },\n" +"      \"location\":{\n" +"        \"type\": \"geo_point\"\n" +"      },\n" +"      \"pic\":{\n" +"        \"type\": \"keyword\",\n" +"        \"index\": false\n" +"      },\n" +"      \"all\":{\n" +"        \"type\": \"text\",\n" +"        \"analyzer\": \"ik_max_word\"\n" +"      }\n" +"    }\n" +"  }\n" +"}";

 然后将新增索引中的代码修改成以下:

文档的CRUD操作

对于文档这里将重点介绍查询的相关方法,其它操作只做简单介绍。

查询操作
MatchAll
/*** 查询全部*/@Testvoid testMatchAll() throws IOException {//1.准备requestSearchRequest request = new SearchRequest("hotel");//2.准备参数request.source().query(QueryBuilders.matchAllQuery());//3.发起请求得到响应结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析响应handleResponse(response);}

结果如下:

 从结果我们也可以看到这个api是查询所有数据的方法,但是控制台只显示了10条数据,这是因为这个方法自动进行分页处理,每页10条数据。

Match
   /*** 全文检索*/@Testvoid testMatch() throws IOException {//1.准备requestSearchRequest request = new SearchRequest("hotel");//2.准备参数request.source().query(QueryBuilders.matchQuery("all","如家"));//3.发起请求得到响应结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析响应handleResponse(response);}

结果:

match用来做基本的模糊匹配,在es中会对文本进行分词,在match查询的时候也会对查询条件进行分词,然后通过倒排索引找到匹配的数据。

term
 @Testvoid testMatch() throws IOException {//1.准备requestSearchRequest request = new SearchRequest("hotel");//2.准备参数
//        request.source().query(QueryBuilders.matchQuery("all","如家"));request.source().query(QueryBuilders.termQuery("city","北京"));//3.发起请求得到响应结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析响应handleResponse(response);}

结果:

 从结果来看,term是做精确查询的,所以一般可以用在查询某个具体的属性的时候

multiMatchQuery
 @Testvoid testMatch() throws IOException {//1.准备requestSearchRequest request = new SearchRequest("hotel");//2.准备参数
//        request.source().query(QueryBuilders.matchQuery("all","如家"));
//        request.source().query(QueryBuilders.termQuery("city","北京"));request.source().query(QueryBuilders.multiMatchQuery("如家", "city", "name"));//3.发起请求得到响应结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析响应handleResponse(response);}

 结果:

    multiMatchQuery接受两个参数,一个是text,一个是fieldname,前者表示要查询的内容,后者表示要在哪些字段中进行查询,如果后者中的数据只有一个,那该方法和matchall一致,如果后者有多个,那查询的结果必须要满足其中的一个。
rangeQuery
 @Testvoid testMatch() throws IOException {//1.准备requestSearchRequest request = new SearchRequest("hotel");//2.准备参数
//        request.source().query(QueryBuilders.matchQuery("all","如家"));
//        request.source().query(QueryBuilders.termQuery("city","北京"));
//        request.source().query(QueryBuilders.multiMatchQuery("如家", "city", "name"));request.source().query(QueryBuilders.rangeQuery("price").gte(100).lte(150));//3.发起请求得到响应结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析响应handleResponse(response);}

结果:

该方法主要做范围查询,相当于sql语句中的between....and...

布尔查询
/*** 布尔查询* @throws IOException*/@Testvoid testBool() throws IOException {//1.准备requestSearchRequest request = new SearchRequest("hotel");//2.准备参数BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();boolQuery.must(QueryBuilders.termQuery("city","北京"));boolQuery.filter(QueryBuilders.rangeQuery("price").lte(300));request.source().query(boolQuery);//3.发起请求得到响应结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);System.out.println( "========"+response.getHits());//4.解析响应handleResponse(response);}/*** 解析响应结果* @param response*/private void handleResponse(SearchResponse response) {SearchHits searchHits = response.getHits();//总条数long total = searchHits.getTotalHits().value;System.out.println("共搜索到"+total+"条数据");//文档数组SearchHit[] hits = searchHits.getHits();//遍历for (SearchHit hit : hits){//获取文档sourceString json = hit.getSourceAsString();//反序列化HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);//获取高亮结果Map<String, HighlightField> highlightFields = hit.getHighlightFields();if(!CollectionUtils.isEmpty(highlightFields)){//根据字段名获取高亮结果HighlightField highlightField = highlightFields.get("name");if(highlightField != null){//获取高亮值String name = highlightField.getFragments()[0].string();//替换hotelDoc.setName(name);}}System.out.println("hotDoc = "+hotelDoc);}

这里简单说一下怎么得到的 handleResponse函数

通过以上图片我们就不难看出 response的响应结构,因此我们就可以具体推出相关信息,所以没有学习过在Kibana上使用DSL指令的朋友可以先去学习一下,然后再来运用到java中事半功倍。

当然,如果你只需要获取数据和总条数,可以修改成以下形式:

 private PageResult handleResponse(SearchResponse response) {SearchHits hits = response.getHits();long total = hits.getTotalHits().value;SearchHit[] searchHits = hits.getHits();List<HotelDoc> hotels = new ArrayList<>();for (SearchHit searchHit : searchHits) {String source = searchHit.getSourceAsString();
//            反序列化HotelDoc hotelDoc = JSON.parseObject(source, HotelDoc.class);hotels.add(hotelDoc);}return new PageResult(total,hotels);}
分页和排序 对结果的处理
/*** 分页和排序 对结果的处理*/@Testvoid testPageAndSort() throws IOException {int page = 1,size = 5;//1.准备requestSearchRequest request = new SearchRequest("hotel");//2.准备参数request.source().query(QueryBuilders.matchAllQuery());//排序request.source().sort("price", SortOrder.ASC);//分页request.source().from((page-1)*size).size(size);//3.发起请求得到响应结果SearchResponse response = client.search(request, RequestOptions.DEFAULT);//4.解析响应handleResponse(response);}

当然,还有其它一些api这里还没有介绍到,各位可以去官网进行查看详细文档说明~

 添加文档
@Data
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {private String name;private Integer age;
}
 @Testvoid testAddDocument() throws IOException {
//        1、创建对象User user = new User("yinan", 20);
//        2、创建请求IndexRequest request = new IndexRequest("yinan_index");
//        规则  put /yinan_index/_doc/1request.id("1");request.timeout(TimeValue.timeValueSeconds(1));request.timeout("1s");
//        将数据放入请求  jsonrequest.source(JSON.toJSONString(user), XContentType.JSON);
//        客户端发送请求,获取相应的结果IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);System.out.println(indexResponse.toString());System.out.println(indexResponse.status());  //对应我们命令返回状态}
批量添加文档
 @Testvoid testBulkRequest() throws Exception {BulkRequest bulkRequest = new BulkRequest();bulkRequest.timeout("10s");ArrayList<User> userList = new ArrayList<>();userList.add(new User("yinan1", 21));userList.add(new User("yinan1", 21));userList.add(new User("yinan3", 26));userList.add(new User("yinan4", 24));userList.add(new User("yinan5", 27));userList.add(new User("yinan12", 20));for (int i = 0; i < userList.size(); i++) {bulkRequest.add(new IndexRequest("yinan_index").id("" + (i + 1)).source(JSON.toJSONString(userList.get(i)), XContentType.JSON));}BulkResponse itemResponses = client.bulk(bulkRequest, RequestOptions.DEFAULT);System.out.println(itemResponses.hasFailures());}
修改文档
 @Testvoid testUpdateDocument() throws Exception {UpdateRequest request = new UpdateRequest("yinan_index", "1");request.timeout("1s");User user = new User("yinan_update", 21);request.doc(JSON.toJSONString(user), XContentType.JSON);UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);System.out.println(updateResponse.status());}
删除文档
   @Testvoid testDeleteDocument() throws Exception {DeleteRequest request = new DeleteRequest("yinan_index", "1");request.timeout("1s");DeleteResponse deleteResponse = client.delete(request, RequestOptions.DEFAULT);System.out.println(deleteResponse.status());}

以上就是对es部分api的讲解,当然只看api使用是远远不够的,所以我们需要做一个小训练来巩固我们学习的东西。

资料已经置顶在本篇博客,有需要的请自取~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3072. 将元素分配到两个数组中 II

题目 给你一个下标从 1 开始、长度为 n 的整数数组 nums 。 现定义函数 greaterCount &#xff0c;使得 greaterCount(arr, val) 返回数组 arr 中 严格大于 val 的元素数量。 你需要使用 n 次操作&#xff0c;将 nums 的所有元素分配到两个数组 arr1 和 arr2 中。在第一次操…

四十二、openlayers官网示例Flight Animation扩展——在地图上绘制飞机航线、飞机随航线飞行效果

上篇在地图上绘制了动态的飞机航线&#xff0c;于是我想着&#xff0c;能不能加个飞机的图标跟着航线飞行。 在iconfont上下载一个飞机的svg图形&#xff0c;放在public的data/icons下面 因为图标需要随着航线的方向飞行&#xff0c;需要根据航线调整角度&#xff0c;因此在…

FPGA SPI采集ADC7606数据

一,SPI总线的构成及信号类型 SPI总线只需四条线(如图1所示)就可以完成MCU与各种外围器件的通讯: 1)MOSI – Master数据输出,Slave数据输入 2)MISO – Master数据输入,Slave数据输出 3)SCK – 时钟信号,由Master产生 4)/CS – Slave使能信号,由Master控制。 在一个SPI时…

递归【2】(组合回溯(生成括号)、子集回溯(背包问题))

括号对 &#xff08;组合型回溯&#xff09; 分解成子问题&#xff0c;每一次添加括号分两步&#xff1a; if左括号小于n&#xff0c;加左括号&#xff0c;然后k(index1), if左括号大于有括号&#xff0c;加右括号&#xff0c;k(index1),然后收尾括号单独考虑&#xff0c;到…

core dump核心转储

检查核心转储是否开启&#xff0c;否则无法生成core文件 ulimit -a 如果为0就需要修改 ulimit -c 10240 写一个会触发core命令的程序 以浮点数运算为例 #include <iostream>int main() {int i 1/0; } 在编译时使用-g选项 运行程序&#xff0c;生成core文件 gdb调试 g…

AI大模型在广告领域的应用

深度对谈&#xff1a;广告创意领域中AIGC的应用_生成式 AI_Tina_InfoQ精选文章

ChatGPT-4o, 腾讯元宝,通义千问对比测试中文文化

国内的大模型应用我选择了国内综合实力最强的两个&#xff0c;一个是腾讯元宝&#xff0c;一个是通义千问。其它的豆包&#xff0c;Kimi&#xff0c;文心一言等在某些领域也有强于竞品的表现。 问一个中文文化比较基础的问题,我满以为中文文化chatGPT不如国内的大模型。可事实…

【经典排序算法】堆排序(精简版)

什么是堆排序&#xff1a; 堆排序(Heapsort)是指利用堆&#xff08;完全二叉树&#xff09;这种数据结构所设计的一种排序算法&#xff0c;它是选择排序的一种。需要注意的是排升序要建大堆&#xff0c;排降序建小堆。 堆排序排序的特性总结&#xff1a; 1. 堆排序使用堆来选数…

vivado DIAGRAM、HW_AXI

图表 描述 块设计&#xff08;.bd&#xff09;是在IP中创建的互连IP核的复杂系统 Vivado设计套件的集成商。Vivado IP集成器可让您创建复杂的 通过实例化和互连Vivado IP目录中的IP进行系统设计。一块 设计是一种分层设计&#xff0c;可以写入磁盘上的文件&#xff08;.bd&…

软考架构-计算机网络考点

会超纲&#xff0c;3-5分 网络分类 按分布范围划分 局域网 LAN 10m-1000m左右 房间、楼宇、校园 传输速率高 城域网 MAN 10km 城市 广域网 WAN 100km以上 国家或全球&#xff08;英特网&#xff09; 按拓扑结构划分 总线型&#xff1a;利用率低、干…

(2024,Vision-LSTM,ViL,xLSTM,ViT,ViM,双向扫描)xLSTM 作为通用视觉骨干

Vision-LSTM: xLSTM as Generic Vision Backbone 公和众与号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 2 方法 3 实验 3.1 分类设计 4 结论 0. 摘要 Transformer 被广泛用作计算…

基于深度学习的在线选修课程推荐系统

基于深度学习的在线选修课程推荐系统 1、效果图 点我查看Demo 2、功能 可联系我-微-信(1257309054) 登录注册、点赞收藏、评分评论&#xff0c;课程推荐&#xff0c;热门课程&#xff0c;个人中心&#xff0c;可视化&#xff0c;后台管理&#xff0c;课程选修3、核心推荐代…

Edge浏览器十大常见问题,一次性解决!

Edge曾被称为最好用的浏览器&#xff0c;拳打Chrome脚踢firefox, 可如今却隐藏着像是播放卡顿、下载缓慢、广告繁多等诸多问题&#xff0c;不知道各位还在用吗&#xff1f; 今天小编收集整理了Edge浏览器十大烦人问题&#xff0c;并提供简单有效的解决办法&#xff0c;让你的E…

277 基于MATLAB GUI火灾检测系统

基于MATLAB GUI火灾检测系统&#xff0c;可以实现图片和视频的火苗检测。火焰识别的三个特征&#xff1a;1个颜色特征&#xff0c;2个几何特征颜色特征&#xff1a;HSV颜色空间下&#xff0c;对三个通道值进行阈值滤波&#xff0c;几何特征1&#xff1a;长宽比&#xff0c;几何…

实战 | YOLOv10 自定义数据集训练实现车牌检测 (数据集+训练+预测 保姆级教程)

导读 本文主要介绍如何使用YOLOv10在自定义数据集训练实现车牌检测 (数据集训练预测 保姆级教程)。 YOLOv10简介 YOLOv10是清华大学研究人员在Ultralytics Python包的基础上&#xff0c;引入了一种新的实时目标检测方法&#xff0c;解决了YOLO以前版本在后处理和模型架构方面…

mac M1下安装PySide2

在M1下装不了PySide2, 是因为PySide2没有arm架构的包 1 先在M1上装qt5 安装qt主要是为了能用里面的Desinger, uic, rcc brew install qt5 我装完的路径在/opt/homebrew/opt/qt5 其中Designer就是用来设计界面的 rcc用resource compiler, 编绎rc资源文件的, 生成对应的py文件…

电子电气架构——车载诊断DTC一文通

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站在他人的角度来反对自己。人生在世,最怕的就是把别人的眼光当成自己生活的唯一标…

【模拟-BM99 顺时针旋转矩阵】

题目 BM99 顺时针旋转矩阵 描述 有一个NxN整数矩阵&#xff0c;请编写一个算法&#xff0c;将矩阵顺时针旋转90度。 给定一个NxN的矩阵&#xff0c;和矩阵的阶数N,请返回旋转后的NxN矩阵。 分析 模拟&#xff0c;写几个样例&#xff0c;分析一下新矩阵元素下标与原矩阵元素…

Windows系统问题

Windows系统问题 一、补丁更新提示&#xff1a;0x80070643问题&#xff1a;解决方法&#xff1a;1.以管理员权限运行【cmd】。2.禁用 【Windows RE】&#xff0c;请运行reagentc /disable。3.回收【Windows RE】恢复分区空间。4.准备新的【Windows RE】恢复分区空间。5.配置并启…

并查集进阶版

过关代码如下 #define _CRT_SECURE_NO_WARNINGS #include<bits/stdc.h> #include<unordered_set> using namespace std;int n, m; vector<int> edg[400005]; int a[400005], be[400005]; // a的作用就是存放要摧毁 int k; int fa[400005]; int daan[400005]…