Elasticsearch学习笔记

编程入门 行业动态 更新时间:2024-10-13 04:23:27

Elasticsearch<a href=https://www.elefans.com/category/jswz/34/1770117.html style=学习笔记"/>

Elasticsearch学习笔记

目录

一、Elasticsearch的安装、kibana、Ik分词器

二、Elasticsearch所使用的lucene核心

三、Elasticsearch介绍、及和redis、solr的对比

四、新建Elasticsearch项目,文档索引的增删改查

五、模仿京东搜索、爬虫

六、高亮和VUE


Gitee仓库:狂神-Elasticsearch: 跟随狂神视频教程,完成的练习项目 - Gitee

课程地址:1、ElasticSearch课程简介_哔哩哔哩_bilibili

一、Elasticsearch的安装、kibana、Ik分词器

安装资料合集地址:百度网盘 请输入提取码
提取码:qk8p

1、解压缩得到Elasticsearch本体、可视化谷歌插件、ik分词器、kibana交互式工具

JDK版本最低要8

2、在windows上安装Elasticsearch

下载地址:下载 Elastic 产品 | Elastic

点击第一个文件夹,继续点击进入bin目录

点击Elasticsearch.bat启动脚本,运行30秒后出现started代表成功

访问localhost:9200,可以看到版本信息,代表安装成功

3、在windows上安装Elasticsearch-head谷歌可视化插件

因为我本地没有npm环境,所以采用了谷歌商店的安装方式

访问谷歌商店,魔法道具请自行寻找

搜索得到Elasticsearch-head

下载安装,并启动插件

4、在windows上安装ik分词器

下载地址:

找到bin目录下面的plugins文件夹

将之前解压出来的elasticsearch-analysis-ik-7.6.1文件夹复制过来即可

5、在windows上安装kibana

下载地址:下载 Elastic 产品 | Elastic

点击kibana文件夹,继续点击进入bin目录

点击kibana.bat启动脚本,运行30秒后出现running at localhost:5601代表成功

访问 localhost:5601

使用kibana进行测试,点击Dev Tools进入控制台

二、Elasticsearch所使用的lucene核心

1、lucene发展史

 1997 年年末,Doug Cutting (lucene 的原作者) 因为工作不稳定,产生了把自己的一些软件商业化的想法。再加上当时 Java 已经是一门炙手可热的编程语言 , 作者也正好需要一个学习它的理由。因为作者以前有编写搜索软件的经验,于是觉得可以用 Java 写一个搜索软件以维持生计。于是 lucene 就此诞生。

        2000 年 ,作者由于对商业运作并没有什么兴趣 ,就把 lucene 放到了 SourceForge 上 , 希望通过开源的方式来让自己保持程序创作的激情。2000年10月份发布1.0版本,2001年7月发布了最后一个SourceForge版本1.01b。

        2001年9月份Lucene作为高质量的Java开源软件产品加入Apache软件基金会的Jakarta家族。2005年Lucene升级成为Apache顶级项目。目前Lucene包含大量相关项目,具体情况可以访问。

       自从Lucene成为Apache开源项目以后,项目质量得到明显增强,也吸引了越来越多的用户和开发者的关注和参与,Lucene社区规模也不断发展壮大。2010 年 ,原作者已经不再参与 lucene 的日常开发和维护了 , 已经有很多对 lucene 核心有着深入了解的开发者参与到项目中。

        截止到2013年初,最新的Lucene版本是4.1。lucene 发展出了入 C++ , C# , Prel , Python 等其他语言的版本。

2、lucene源码的增删改查

参考博客:Lucene介绍与使用_lucene使用_是小方啦的博客-CSDN博客

创建索引的lucene源代码

 // 创建索引@Testpublic void testCreate() throws Exception{//1 创建文档对象Document document = new Document();// 创建并添加字段信息。参数:字段的名称、字段的值、是否存储,这里选Store.YES代表存储到文档列表。Store.NO代表不存储document.add(new StringField("id", "1", Field.Store.YES));// 这里我们title字段需要用TextField,即创建索引又会被分词。 document.add(new TextField("title", "谷歌地图之父跳槽facebook", Field.Store.YES));//2 索引目录类,指定索引在硬盘中的位置Directory directory = FSDirectory.open(new File("d:\\indexDir"));//3 创建分词器对象Analyzer analyzer = new StandardAnalyzer();//4 索引写出工具的配置对象IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, analyzer);//5 创建索引的写出工具类。参数:索引的目录和配置信息IndexWriter indexWriter = new IndexWriter(directory, conf);//6 把文档交给IndexWriterindexWriter.addDocument(document);//7 提交indexWritermit();//8 关闭indexWriter.close();}

查询索引

@Testpublic void testSearch() throws Exception {// 索引目录对象Directory directory = FSDirectory.open(new File("d:\\indexDir"));// 索引读取工具IndexReader reader = DirectoryReader.open(directory);// 索引搜索工具IndexSearcher searcher = new IndexSearcher(reader);// 创建查询解析器,两个参数:默认要查询的字段的名称,分词器QueryParser parser = new QueryParser("title", new IKAnalyzer());// 创建查询对象Query query = parser.parse("谷歌");// 搜索数据,两个参数:查询条件对象要查询的最大结果条数// 返回的结果是 按照匹配度排名得分前N名的文档信息(包含查询到的总条数信息、所有符合条件的文档的编号信息)。TopDocs topDocs = searcher.search(query, 10);// 获取总条数System.out.println("本次搜索共找到" + topDocs.totalHits + "条数据");// 获取得分文档对象(ScoreDoc)数组.SocreDoc中包含:文档的编号、文档的得分ScoreDoc[] scoreDocs = topDocs.scoreDocs;for (ScoreDoc scoreDoc : scoreDocs) {// 取出文档编号int docID = scoreDoc.doc;// 根据编号去找文档Document doc = reader.document(docID);System.out.println("id: " + doc.get("id"));System.out.println("title: " + doc.get("title"));// 取出文档得分System.out.println("得分: " + scoreDoc.score);}}

修改索引

/* 测试:修改索引* 注意:* 	A:Lucene修改功能底层会先删除,再把新的文档添加。* 	B:修改功能会根据Term进行匹配,所有匹配到的都会被删除。这样不好* 	C:因此,一般我们修改时,都会根据一个唯一不重复字段进行匹配修改。例如ID* 	D:但是词条搜索,要求ID必须是字符串。如果不是,这个方法就不能用。* 如果ID是数值类型,我们不能直接去修改。可以先手动删除deleteDocuments(数值范围查询锁定ID),再添加。*/
@Test
public void testUpdate() throws Exception{// 创建目录对象Directory directory = FSDirectory.open(new File("indexDir"));// 创建配置对象IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());// 创建索引写出工具IndexWriter writer = new IndexWriter(directory, conf);// 创建新的文档数据Document doc = new Document();doc.add(new StringField("id","1",Store.YES));doc.add(new TextField("title","谷歌地图之父跳槽facebook ",Store.YES));/* 修改索引。参数:* 	词条:根据这个词条匹配到的所有文档都会被修改* 	文档信息:要修改的新的文档数据*/writer.updateDocument(new Term("id","1"), doc);// 提交writermit();// 关闭writer.close();
}

删除索引

/** 演示:删除索引* 注意:* 	一般,为了进行精确删除,我们会根据唯一字段来删除。比如ID* 	如果是用Term删除,要求ID也必须是字符串类型!*/
@Test
public void testDelete() throws Exception {// 创建目录对象Directory directory = FSDirectory.open(new File("indexDir"));// 创建配置对象IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());// 创建索引写出工具IndexWriter writer = new IndexWriter(directory, conf);// 根据词条进行删除//		writer.deleteDocuments(new Term("id", "1"));// 根据query对象删除,如果ID是数值类型,那么我们可以用数值范围查询锁定一个具体的ID//		Query query = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);//		writer.deleteDocuments(query);// 删除所有writer.deleteAll();// 提交writermit();// 关闭writer.close();
}

分页查询

// 分页@Testpublic void testPageQuery() throws Exception {// 实际上Lucene本身不支持分页。因此我们需要自己进行逻辑分页。我们要准备分页参数:int pageSize = 2;// 每页条数int pageNum = 3;// 当前页码int start = (pageNum - 1) * pageSize;// 当前页的起始条数int end = start + pageSize;// 当前页的结束条数(不能包含)// 目录对象Directory directory = FSDirectory.open(new File("indexDir"));// 创建读取工具IndexReader reader = DirectoryReader.open(directory);// 创建搜索工具IndexSearcher searcher = new IndexSearcher(reader);QueryParser parser = new QueryParser("title", new IKAnalyzer());Query query = parser.parse("谷歌地图");// 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转如果是false,升序。true降序Sort sort = new Sort(new SortField("id", Type.LONG, false));// 搜索数据,查询0~end条TopDocs topDocs = searcher.search(query, end,sort);System.out.println("本次搜索共" + topDocs.totalHits + "条数据");ScoreDoc[] scoreDocs = topDocs.scoreDocs;for (int i = start; i < end; i++) {ScoreDoc scoreDoc = scoreDocs[i];// 获取文档编号int docID = scoreDoc.doc;Document doc = reader.document(docID);System.out.println("id: " + doc.get("id"));System.out.println("title: " + doc.get("title"));}}

3、lucene的核心原理

 参考博客:Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理_njpjsoftdev的博客-CSDN博客

参考教程:1.光速入门ES底层内核Lucene_哔哩哔哩_bilibili

倒排索引又叫反向索引(右下图)以字或词为文档中出现的位置情况。

在实际的运用中,我们可以对数据库中原始的数据结构(左图),在业务空闲时事先根据左图内容,创建新的倒排索引结构的数据区域(右图)。

用户有查询需求时,先访问倒排索引数据区域(右图),得出文档id后,通过文档id即可快速,准确的通过左图找到具体的文档内容。

这一过程,可以通过我们自己写程序来实现,也可以借用已经抽象出来的通用开源技术来实现。

lucene的文件结构

lucene的数据类型

三、Elasticsearch介绍、及和redis、solr的对比

1、Elasticsearch的历史

        多年前,一个叫做Shay Banon的刚结婚不久的失业开发者,由于妻子要去伦敦学习厨师,他便跟着也去了。在他找工作的过程中,为了给妻子构建一个食谱的搜索引擎,他开始构建一个早期版本的Lucene

        直接基于Lucene工作会比较困难,所以Shay开始抽象Lucene代码以便Java程序员可以在应用中添加搜索功能。他发布了他的第一个开源项目,叫做“Compass”。

        后来Shay找到一份工作,这份工作处在高性能和内存数据网格的分布式环境中,因此高性能的、实时的、分布式的搜索引擎也是理所当然需要的。然后他决定重写Compass库使其成为一个独立的服务叫做Elasticsearch

        第一个公开版本出现在2010年2月,在那之后Elasticsearch已经成为Github上最受欢迎的项目之一,代码贡献者超过300人。一家主营Elasticsearch的公司就此成立,他们一边提供商业支持一边开发新功能,不过Elasticsearch将永远开源且对所有人可用。

        Shay的妻子依旧等待着她的食谱搜索…..

2、什么是Elasticsearch

        Elaticsearch,简称为es,es是一个开源的高扩展分布式全文检索引擎,它可以近乎实时的存储检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别(大数据时代)的数据。es也使用java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。

        据国际权威的数据库产品评测机构DB Engines的统计,在2016年1月,ElasticSearch已超过Solr等,成为排名第一的搜索引擎类应用

谁在使用:

1、维基百科,类似百度百科,全文检索,高亮,搜索推荐/2
2、The Guardian (国外新闻网站) ,类似搜狐新闻,用户行为日志(点击,浏览,收藏,评论) +社交网络数据(对某某新闻的相关看法) ,数据分析,给到每篇新闻文章的作者,让他知道他的文章的公众反馈(好,坏,热门,垃圾,鄙视,崇拜)
3、Stack Overflow (国外的程序异常讨论论坛) , IT问题,程序的报错,提交上去,有人会跟你讨论和回答,全文检索,搜索相关问题和答案,程序报错了,就会将报错信息粘贴到里面去,搜索有没有对应的答案
4、GitHub (开源代码管理),搜索 上千亿行代码
5、电商网站,检索商品
6、日志数据分析, logstash采集日志, ES进行复杂的数据分析, ELK技术, elasticsearch+logstash+kibana
7、商品价格监控网站,用户设定某商品的价格阈值,当低于该阈值的时候,发送通知消息给用户,比如说订阅牙膏的监控,如果高露洁牙膏的家庭套装低于50块钱,就通知我,我就去买
8、BI系统,商业智能, Business Intelligence。比如说有个大型商场集团,BI ,分析一下某某区域最近3年的用户消费 金额的趋势以及用户群体的组成构成,产出相关的数张报表, **区,最近3年,每年消费金额呈现100%的增长,而且用户群体85%是高级白领,开-个新商场。ES执行数据分析和挖掘, Kibana进行数据可视化
9、国内:站内搜索(电商,招聘,门户,等等),IT系统搜索(OA,CRM,ERP,等等),数据分析(ES热门
的一一个使用场景)

3、对比Redis

参考博客:聊聊redis和Elasticsearch_elasticsearch和redis_小叶曲的博客-CSDN博客

项目RedisElasticsearch
介绍Redis是内存中的数据结构存储,用作数据库,缓存和消息代理Elasticsearch是一个基于Apache Lucene的现代搜索和分析引擎
主数据库模型键值存储搜索引擎
DB-Engines排名得分120.41总排名第9,key-value存储排名第7得分120.00总排名第10,搜索引擎排名第1
网站redis.iowww.elastic.co/cn/elasticsearch
技术文档redis.io/documentationwww.elastic.co/cn/elasticsearch/features
由开发Salvatore Sanfilippo
初始发行20092010
当前版本5.0.8,2020年3月7.6.1,2020年3月
许可证信息开源开源
基于云的信息没有没有
实现语言CJava
支持的操作系统BSD Linux OS X Windows所有带有Java VM的操作系统
数据scheme无scheme无scheme
打字局部
XML支持 没有
二级索引没有
SQL没有没有
API和其他访问方法专有协议Java API RESTful HTTP / JSON API
支持的编程语言C C#C ++ Clojure Crystal D Dart Elixir Erlang Fancy Go Haskell Haxe Java JavaScript(Node.js)Lisp Lua MatLab Objective-C OCaml Perl PHP Prolog Python R Rebol Ruby Rust Scala Smalltalk Tcl.Net Clojure Erlang Go Groovy Haskell Java JavaScript Lua Perl PHP Python Ruby Scala
服务器端脚本Lua
触发器没有
分区方法拆分拆分
复制方法主从复制
MapReduce的没有没有
一致性概念最终的一致性最终的一致性
外键没有没有

4、对比Solr

  • Solr是Apache下的一个顶级开源项目,采用Java开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置可扩展,并对索引、搜索性能进行了优化
  • Solr可以独立运行,运行在letty. Tomcat等这些Selrvlet容器中 , Solr 索引的实现方法很简单,<mark>用POST方法向Solr服务器发送一个描述Field及其内容的XML文档, Solr根据xml文档添加、删除、更新索引</mark>。Solr 搜索只需要发送HTTP GET请求,然后对Solr返回xml、json等格式的查询结果进行解析,组织页面布局。
  • Solr不提供构建UI的功能, Solr提供了一个管理界面,通过管理界面可以查询Solr的配置和运行情况。
  • Solr是基于lucene开发企业级搜索服务器,实际上就是封装了lucene.
  • Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交-定格式的文件,生成索引;也可以通过提出查找请求,并得到返回结果。

当单纯的对已有数据进行搜索时,Solr更快

当实时建立索引时,Solr会产生io阻塞,查询性能较差,ElasticSearch具有明显的优势

随着数据量的增加,Solr的搜索效率会变得更低,而ElasticSearch却没有明显的变化

转变我们的搜索基础设施后从Solr ElasticSearch,我们看见一个即时~ 50x提高搜索性能!

总结

1、es基本是开箱即用(解压就可以用!) ,非常简单。Solr安装略微复杂一丢丢!
2、Solr 利用Zookeeper进行分布式管理,而Elasticsearch自身带有分布式协调管理功能
3、Solr 支持更多格式的数据,比如JSON、XML、 CSV ,而Elasticsearch仅支持json文件格式
4、Solr 官方提供的功能更多,而Elasticsearch本身更注重于核心功能,高级功能多有第三方插件提供,例如图形化界面需要kibana友好支撑
5、Solr 查询快,但更新索引时慢(即插入删除慢) ,用于电商等查询多的应用;

  • ES建立索引快(即查询慢) ,即实时性查询快,用于facebook新浪等搜索。
  • Solr是传统搜索应用的有力解决方案,但Elasticsearch更适用于新兴的实时搜索应用。

6、Solr比较成熟,有一个更大,更成熟的用户、开发和贡献者社区,而Elasticsearch相对开发维护者较少,更新太快,学习使用成本较高。

四、Kibana测试增删改查

 1、ELK三剑客

ELK是Elasticsearch、Logstash、 Kibana三大开源框架首字母大写简称。市面上也被成为Elastic Stack。

  • 其中Elasticsearch是一个基于Lucene、分布式、通过Restful方式进行交互的近实时搜索平台框架。
  • Logstash是ELK的中央数据流引擎,用于从不同目标(文件/数据存储/MQ )收集的不同格式数据,经过过滤后支持输出到不同目的地(文件/MQ/redis/elasticsearch/kafka等)。
  • Kibana可以将elasticsearch的数据通过友好的页面展示出来 ,提供实时分析的功能

收集清洗数据(Logstash) ==> 搜索、存储(ElasticSearch) ==> 展示(Kibana)

市面上很多开发只要提到ELK能够一致说出它是一个日志分析架构技术栈总称 ,但实际上ELK不仅仅适用于日志分析,它还可以支持其它任何数据分析和收集的场景,日志分析和收集只是更具有代表性。并非唯一性。

2、Rest风格说明

methodurl地址描述
PUT(创建,修改)localhost:9200/索引名称/类型名称/文档id创建文档(指定文档id)
POST(创建)localhost:9200/索引名称/类型名称创建文档(随机文档id)
POST(修改)localhost:9200/索引名称/类型名称/文档id/_update修改文档
DELETE(删除)localhost:9200/索引名称/类型名称/文档id删除文档
GET(查询)localhost:9200/索引名称/类型名称/文档id查询文档通过文档ID
POST(查询)localhost:9200/索引名称/类型名称/文档id/_search查询所有数据

3、使用kibana测试Elasticsearch增删改查

暂时没写

五、新建Elasticsearch项目,文档索引的增删改查

1、新建项目

新建SpringBoot项目,勾选Elasticsearch依赖

项目结构

导入依赖,本人用的SpringBoot是2.3.2.RELEASE

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><!-- fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.71</version></dependency><!-- lombok 需要安装插件 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

编写ElasticsearchConfig.java配置类

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

编写User.java实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {private static final long serialVersionUID = -3843548915035470817L;private String name;private Integer age;
}

编写索引操作方法

@SpringBootTest
class KuangApplicationTests {@Autowiredpublic RestHighLevelClient restHighLevelClient;// 测试索引的创建@Testpublic void textCreateIndex() throws IOException {CreateIndexRequest request = new CreateIndexRequest("乱码测试");CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);System.out.println(response.isAcknowledged());restHighLevelClient.close();}// 测试索引的查询@Testpublic void textCreateIsExists() throws IOException {GetIndexRequest request = new GetIndexRequest("sly1");boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);System.out.println(exists);restHighLevelClient.close();}// 测试索引的删除@Testpublic void textCreateDelete() throws IOException {DeleteIndexRequest request = new DeleteIndexRequest("sly");AcknowledgedResponse response = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);System.out.println(response.isAcknowledged());restHighLevelClient.close();}
}

2、进行文档的增删改查

新增

    // 测试文档的创建@Testpublic void textAddDocument() throws IOException {User user = new User("小狂狂", 18);// 创建文档IndexRequest request = new IndexRequest("文档1号");// 设置文档的IDrequest.id("2");// 设置超时时间1秒request.timeout(TimeValue.timeValueMillis(1000));// 将请求放入request.source(JSON.toJSONString(user), XContentType.JSON);IndexResponse index = restHighLevelClient.index(request, RequestOptions.DEFAULT);// 获取索引的状态System.out.println(index.status());System.out.println(index);/***  CREATED* IndexResponse[index=文档1号,type=_doc,id=1,version=1,result=created,seqNo=0,primaryTerm=1,* shards={"total":2,"successful":1,"failed":0}]*/restHighLevelClient.close();}

查询

    // 测试文档的查询@Testpublic void textGetDocument() throws IOException {GetRequest request = new GetRequest("文档1号", "1");GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);// 打印文档的内容System.out.println(response.getSourceAsString());System.out.println(response);/***  {"age":18,"name":"小狂狂"}* {"_index":"文档1号","_type":"_doc","_id":"1","_version":1,"_seq_no":0,"_primary_term":1,* "found":true,"_source":{"age":18,"name":"小狂狂"}}*/restHighLevelClient.close();}

修改

    // 测试文档的更新@Testpublic void textUpdateDocument() throws IOException {UpdateRequest request = new UpdateRequest("文档1号", "1");User user = new User("lmk", 11);request.doc(JSON.toJSONString(user), XContentType.JSON);UpdateResponse response = restHighLevelClient.update(request, RequestOptions.DEFAULT);System.out.println(response.status());System.out.println(response);/***  OK* UpdateResponse[index=文档1号,type=_doc,id=1,version=4,seqNo=3,primaryTerm=1,result=updated,* shards=ShardInfo{total=2, successful=1, failures=[]}]*/restHighLevelClient.close();}

删除

    // 测试文档的删除@Testpublic void textDeleteDocument() throws IOException {DeleteRequest request = new DeleteRequest("文档1号", "2");request.timeout("1s");DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);System.out.println(response.status());System.out.println(response);/***  OK* DeleteResponse[index=文档1号,type=_doc,id=1,version=2,result=deleted,shards=ShardInfo{total=2, successful=1, failures=[]}]*/restHighLevelClient.close();}

判断是否存在

    // 测试文档的存在@Testpublic void textExistsDocument() throws IOException {GetRequest request = new GetRequest("文档1号", "1");request.fetchSourceContext(new FetchSourceContext(false));request.storedFields("_nine_");boolean exists = restHighLevelClient.exists(request, RequestOptions.DEFAULT);System.out.println(exists);restHighLevelClient.close();}

3、进行复杂查询新增操作

复杂查询、高亮

    // 查询// SearchRequest 搜索请求// SearchSourceBuilder 条件构造// HighlightBuilder 高亮// TermQueryBuilder 精确查询// MatchAllQueryBuilder// xxxQueryBuilder ...@Testpublic void testSearch() throws IOException {// 1.创建查询请求对象SearchRequest searchRequest = new SearchRequest();// 2.构建搜索条件SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// (1)查询条件 使用QueryBuilders工具类创建// 精确查询TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "liuyou");// 匹配查询MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();// (2)其他<可有可无>:(可以参考 SearchSourceBuilder 的字段部分)// 设置高亮searchSourceBuilder.highlighter(new HighlightBuilder());// 分页,默认已经配置了searchSourceBuilder.from();searchSourceBuilder.size();searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));// (3)条件投入searchSourceBuilder.query(matchAllQueryBuilder);// 3.添加条件到请求searchRequest.source(searchSourceBuilder);// 4.客户端查询请求SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 5.查看返回结果SearchHits hits = searchResponse.getHits();System.out.println(JSON.toJSONString(hits));for (SearchHit documentFields : hits.getHits()) {System.out.println(documentFields.getSourceAsMap());}/*** {"fragment":true,"hits":[],"maxScore":null,"totalHits":{"relation":"EQUAL_TO","value":0}}*/}

失败的批量新增

    // 上面的这些api无法批量增加数据(只会保留最后一个source)@Testpublic void testBatchAdd() throws IOException {IndexRequest request = new IndexRequest("bulk");// 没有id会自动生成一个随机IDrequest.id("3");request.source(JSON.toJSONString(new User("liu", 1)), XContentType.JSON);request.source(JSON.toJSONString(new User("min", 2)), XContentType.JSON);request.source(JSON.toJSONString(new User("kai", 3)), XContentType.JSON);IndexResponse index = restHighLevelClient.index(request, RequestOptions.DEFAULT);System.out.println(index.status());System.out.println(index);/*** CREATED* IndexResponse[index=bulk,type=_doc,id=3,version=1,result=created,seqNo=1,primaryTerm=1,shards={"total":2,"successful":1,"failed":0}]*/}

成功的批量新增

    // 特殊的,真的项目一般会 批量插入数据@Testpublic void testBulk() throws IOException {BulkRequest bulkRequest = new BulkRequest();bulkRequest.timeout("10s");ArrayList<User> users = new ArrayList<>();users.add(new User("liuyou-1", 1));users.add(new User("liuyou-2", 2));users.add(new User("liuyou-3", 3));users.add(new User("liuyou-4", 4));users.add(new User("liuyou-5", 5));users.add(new User("liuyou-6", 6));// 批量请求处理for (int i = 0; i < users.size(); i++) {bulkRequest.add(// 这里是数据信息new IndexRequest("bulk").id("" + (i + 1)) // 没有设置id 会自定生成一个随机id.source(JSON.toJSONString(users.get(i)), XContentType.JSON));}BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);System.out.println(bulk.status());System.out.println(bulk);/*** OK* org.elasticsearch.action.bulk.BulkResponse@77ab5214*/}

五、模仿京东搜索、爬虫

1、解决控制台乱码

打开config文件夹,找到jvm.options文件

在文件里加上 -Dfile.encoding=GBK

2、项目构建

追加导入依赖

        <!-- thymeleaf --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId><version>2.2.13.RELEASE</version></dependency><!-- jsoup解析页面 --><!-- 解析网页 爬视频可 研究tiko --><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.10.2</version></dependency><!-- devtools热部署 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!-- @Configuration --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency>

打开第一节下载的文件,将其中的前端素材复制过来

编写application.properties配置文件

# 防止端口冲突
server.port=9999
# 关闭thymeleaf缓存
spring.thymeleaf.cache=false

编写IndexController.java

@Controller
public class IndexController {@RequestMapping({"toIndex","/"})public String toIndex() {System.out.println("toIndex方法");return "/index";}
}

启动项目测试

3、爬虫原理分析

搜索京东搜索页面,并分析页面

=java

F12打开审查元素,找到目标元素img、name、price

创建HtmlParseUtil,并简单编写​​​​​​​
public class HtmlParseUtil {public static void main(String[] args) throws IOException {/// 使用前需要联网// 请求urlString url = "=java";// 1.解析网页(jsoup 解析返回的对象是浏览器Document对象)Document document = Jsoup.parse(new URL(url), 30000);// 使用document可以使用在js对document的所有操作// 2.获取元素(通过id)Element j_goodsList = document.getElementById("J_goodsList");// 3.获取J_goodsList ul 每一个 liElements lis = j_goodsList.getElementsByTag("li");// 4.获取li下的 img、price、namefor (Element li : lis) {String img = li.getElementsByTag("img").eq(0).attr("src");// 获取li下 第一张图片String name = li.getElementsByClass("p-name").eq(0).text();String price = li.getElementsByClass("p-price").eq(0).text();System.out.println("=======================");System.out.println("img : " + img);System.out.println("name : " + name);System.out.println("price : " + price);}}
}

运行结果

图片没有,一般图片特别多的网站,所有的图片都是通过延迟加载的

4、编写爬虫接口

编写Content.java实体类

Data
@AllArgsConstructor
@NoArgsConstructor
public class Content {private String name;private String img;private String price;
}

编写ContentService.java业务类

@Service
public class ContentService {@Autowiredprivate RestHighLevelClient restHighLevelClient;// 1、解析数据放入 es 索引中public Boolean parseContent(String keyword) throws IOException {/* 获取内容 */List<Content> contents = HtmlParseUtil.parseJD(keyword);// 内容放入 es 中BulkRequest bulkRequest = new BulkRequest();bulkRequest.timeout("2m"); // 可更具实际业务是指for (int i = 0; i < contents.size(); i++) {bulkRequest.add(new IndexRequest("jd_goods").id("" + (i + 1)).source(JSON.toJSONString(contents.get(i)), XContentType.JSON));}BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);restHighLevelClient.close();return !bulk.hasFailures();}// 2、根据keyword分页查询结果public List<Map<String, Object>> search(String keyword, Integer pageIndex, Integer pageSize) throws IOException {if (pageIndex < 0) {pageIndex = 0;}SearchRequest jd_goods = new SearchRequest("jd_goods");// 创建搜索源建造者对象SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 条件采用:精确查询 通过keyword查字段nameTermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", keyword);searchSourceBuilder.query(termQueryBuilder);searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));// 分页searchSourceBuilder.from(pageIndex);searchSourceBuilder.size(pageSize);// 高亮// ....// 搜索源放入搜索请求中jd_goods.source(searchSourceBuilder);// 执行查询,返回结果SearchResponse searchResponse = restHighLevelClient.search(jd_goods, RequestOptions.DEFAULT);restHighLevelClient.close();// 解析结果SearchHits hits = searchResponse.getHits();List<Map<String, Object>> results = new ArrayList<>();for (SearchHit documentFields : hits.getHits()) {Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();results.add(sourceAsMap);}// 返回查询的结果return results;}
}

编写ContentController.java接口类

@Controller
public class ContentController {@Autowiredprivate ContentService contentService;@ResponseBody@GetMapping("/parse/{keyword}")public Boolean parse(@PathVariable("keyword") String keyword) throws IOException {System.out.println("parse方法");return contentService.parseContent(keyword);}@ResponseBody@GetMapping("/search/{keyword}/{pageIndex}/{pageSize}")public List<Map<String, Object>> parse(@PathVariable("keyword") String keyword,@PathVariable("pageIndex") Integer pageIndex,@PathVariable("pageSize") Integer pageSize) throws IOException {System.out.println("search方法");return contentService.search(keyword, pageIndex, pageSize);}
}

编写HtmlParseUtil.java爬虫工具类

public class HtmlParseUtil {public static void main(String[] args) throws IOException {System.out.println(parseJD("java"));}public static List<Content> parseJD(String keyword) throws IOException {// 使用前需要联网String url = "=" + keyword;// 设置cookieMap<String, String> cookies = new HashMap<String, String>();cookies.put("thor", "C4A4AD9C3168F09250467E0EBD6AFE81A42CD20AE853DEC1D45F02E03249B2C2444F82111661DD7FE1D03F0503C174C86D2B4457D8FA8AE63E8896434CEA43FD780830239AB7A0F929F6FF5BA158823DA7DF84FD28FEC9056BCEB61204EB5F8F9BAE613681CCDAAFAFA768D2C91C43C8DC7070192A62C517CDA347D47230FFE416A71AAC377DD76257F49FCBA4A67C92684CA45C5A778594E076C028233D601D;");// 1.解析网页(jsoup 解析返回的对象是浏览器Document对象)Document document = Jsoup.connect(url).cookies(cookies).get();
//        Document document = Jsoup.parse(new URL(url), 30000);// 使用document可以使用在js对document的所有操作// 2.获取元素(通过id)Element j_goodsList = document.getElementById("J_goodsList");// 3.获取J_goodsList ul 每一个 liElements lis = j_goodsList.getElementsByTag("li");
//        System.out.println(lis);// 4.获取li下的 img、price、name// list存储所有li下的内容List<Content> contents = new ArrayList<Content>();int i = 0;for (Element li : lis) {if (i < 10) {i++;// 由于网站图片使用懒加载,将src属性替换为data-lazy-imgString img = li.getElementsByTag("img").eq(0).attr("data-lazy-img");// 获取li下 第一张图片String name = li.getElementsByClass("p-name").eq(0).text();String price = li.getElementsByClass("p-price").eq(0).text();// 封装为对象Content content = new Content(name, img, price);// 添加到list中contents.add(content);System.out.println(content);}}// 5.返回 listreturn contents;}
}

启动项目测试,调用parse接口

看到数据存入了ES

调用search方法

六、高亮和VUE

1、高亮

Controller添加高亮接口

    @ResponseBody@GetMapping("/h_search/{keyword}/{pageIndex}/{pageSize}")public List<Map<String, Object>> highlightParse(@PathVariable("keyword") String keyword,@PathVariable("pageIndex") Integer pageIndex,@PathVariable("pageSize") Integer pageSize) throws IOException {System.out.println("h_search方法");return contentService.highlightSearch(keyword, pageIndex, pageSize);}

Service编写相关业务

    // 3、 在2的基础上进行高亮查询public List<Map<String, Object>> highlightSearch(String keyword, Integer pageIndex, Integer pageSize) throws IOException {SearchRequest searchRequest = new SearchRequest("jd_goods");SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 精确查询,添加查询条件TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", keyword);searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));searchSourceBuilder.query(termQueryBuilder);// 分页searchSourceBuilder.from(pageIndex);searchSourceBuilder.size(pageSize);// 高亮 =========HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.field("name");highlightBuilder.preTags("<span style='color:red'>");highlightBuilder.postTags("</span>");searchSourceBuilder.highlighter(highlightBuilder);// 执行查询searchRequest.source(searchSourceBuilder);SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 解析结果 ==========SearchHits hits = searchResponse.getHits();List<Map<String, Object>> results = new ArrayList<>();for (SearchHit documentFields : hits.getHits()) {// 使用新的字段值(高亮),覆盖旧的字段值Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();// 高亮字段Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();HighlightField name = highlightFields.get("name");// 替换if (name != null) {Text[] fragments = name.fragments();StringBuilder new_name = new StringBuilder();for (Text text : fragments) {new_name.append(text);}sourceAsMap.put("name", new_name.toString());}results.add(sourceAsMap);}return results;}

2、VUE

从第一节下载的课件里找到js文件

复制到项目里

 引入js

<script th:src="@{/js/vue.min.js}"></script>
<script th:src="@{/js/axios.min.js}"></script>

修改后的静态页面总览

<!DOCTYPE html>
<html xmlns:th=""><head><meta charset="utf-8"/><title>狂神说Java-ES仿京东实战</title><link rel="stylesheet" th:href="@{/css/style.css}"/><script th:src="@{/js/jquery.min.js}"></script>
</head><body class="pg">
<div class="page"><div id="app" class=" mallist tmall- page-not-market "><!-- 头部搜索 --><div id="header" class=" header-list-app"><div class="headerLayout"><div class="headerCon "><!-- Logo--><h1 id="mallLogo"><img th:src="@{/images/jdlogo.png}" alt=""></h1><div class="header-extra"><!--搜索--><div id="mallSearch" class="mall-search"><form name="searchTop" class="mallSearch-form clearfix"><fieldset><legend>天猫搜索</legend><div class="mallSearch-input clearfix"><div class="s-combobox" id="s-combobox-685"><div class="s-combobox-input-wrap"><input v-model="keyword" type="text" autocomplete="off" id="mq"class="s-combobox-input" aria-haspopup="true"></div></div><button type="submit" @click.prevent="searchKey" id="searchbtn">搜索</button></div></fieldset></form><ul class="relKeyTop"><li><a>狂神说Java</a></li><li><a>狂神说前端</a></li><li><a>狂神说Linux</a></li><li><a>狂神说大数据</a></li><li><a>狂神聊理财</a></li></ul></div></div></div></div></div><!-- 商品详情页面 --><div id="content"><div class="main"><!-- 品牌分类 --><form class="navAttrsForm"><div class="attrs j_NavAttrs" style="display:block"><div class="brandAttr j_nav_brand"><div class="j_Brand attr"><div class="attrKey">品牌</div><div class="attrValues"><ul class="av-collapse row-2"><li><a href="#"> 狂神说 </a></li><li><a href="#"> Java </a></li></ul></div></div></div></div></form><!-- 排序规则 --><div class="filter clearfix"><a class="fSort fSort-cur">综合<i class="f-ico-arrow-d"></i></a><a class="fSort">人气<i class="f-ico-arrow-d"></i></a><a class="fSort">新品<i class="f-ico-arrow-d"></i></a><a class="fSort">销量<i class="f-ico-arrow-d"></i></a><a class="fSort">价格<i class="f-ico-triangle-mt"></i><i class="f-ico-triangle-mb"></i></a></div><!-- 商品详情 --><div class="view grid-nosku"><div class="product" v-for="result in results"><div class="product-iWrap"><!--商品封面--><div class="productImg-wrap"><a class="productImg"><img :src="result.img"></a></div><!--价格--><p class="productPrice"><em v-text="result.price"></em></p><!--标题--><p class="productTitle"><a v-html="result.name"></a></p><!-- 店铺名 --><div class="productShop"><span>店铺: 狂神说Java </span></div><!-- 成交信息 --><p class="productStatus"><span>月成交<em>999笔</em></span><span>评价 <a>3</a></span></p></div></div></div></div></div></div>
</div><script th:src="@{/js/vue.min.js}"></script>
<script th:src="@{/js/axios.min.js}"></script>
<script>new Vue({el: "#app",data: {keyword: '', // 搜索的关键字results: [] // 后端返回的结果},methods: {searchKey() {var keyword = this.keyword;console.log(keyword);axios.get('h_search/' + keyword + '/1/20').then(response => {console.log(response.data);this.results = response.data;})}}});
</script></body>
</html>

启动项目测试

更多推荐

Elasticsearch学习笔记

本文发布于:2024-02-26 18:20:33,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1703438.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:学习笔记   Elasticsearch

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!