分布式框架中间件总纲
友情链接
目录
一、ElasticSearch 概述
二、ES 核心概念
三、IK 分词器插件
四、Rest 风格说明
五、关于索引的基本操作
六、关于文档的基本操作
七、集成 SpringBoot
八、案例
1、爬虫
2、前后端分离
3、搜索高亮
一、ElasticSearch 概述
1、什么是 ElasticSearch
The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash(也称为 ELK Stack)。能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。Elaticsearch,简称为ES, ES是一个开源的高扩展的分布式全文搜索引擎,是整个Elastic Stack技术栈的核心。它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。
2、全文搜索引擎
1、Google,百度类的网站搜索,它们都是根据网页中的关键字生成索引,我们在搜索的时候输入关键字,它们会将该关键字即索引匹配到的所有网页返回;还有常见的项目中应用日志的搜索等等。对于这些非结构化的数据文本,关系型数据库搜索不是能很好的支持。
2、一般传统数据库,全文检索都实现的很鸡肋,因为一般也没人用数据库存文本字段。进行全文检索需要扫描整个表,如果数据量大的话即使对SQL的语法优化,也收效甚微。建立了索引,但是维护起来也很麻烦,对于 insert 和 update 操作都会重新构建索引。
3、基于以上原因可以分析得出,在一些生产环境中,使用常规的搜索方式,性能是非常差的:
搜索的数据对象是大量的非结构化的文本数据。
文件记录量达到数十万或数百万个甚至更多。
支持大量基于交互式文本的查询。
需求非常灵活的全文搜索查询。
对高度相关的搜索结果的有特殊需求,但是没有可用的关系数据库可以满足。
对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况。
4、为了解决结构化数据搜索和非结构化数据搜索性能问题,我们就需要专业,健壮,强大的全文搜索引擎
这里说到的全文搜索引擎指的是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。
3、Elasticsearch And Solr 的区别
Lucene是Apache软件基金会Jakarta项目组的一个子项目,提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。但Lucene只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来进行应用。
目前市面上流行的搜索引擎软件,主流的就两款:Elasticsearch和Solr,这两款都是基于Lucene搭建的,可以独立部署启动的搜索引擎服务软件。由于内核相同,所以两者除了服务器安装、部署、管理、集群以外,对于数据的操作 修改、添加、保存、查询等等都十分类似。
在使用过程中,一般都会将Elasticsearch和Solr这两个软件对比,然后进行选型。这两个搜索引擎都是流行的,先进的的开源搜索引擎。它们都是围绕核心底层搜索库 - Lucene构建的 - 但它们又是不同的。像所有东西一样,每个都有其优点和缺点:
image.png
5、Elasticsearch Or Solr 的选型
Google搜索趋势结果表明,与 Solr 相比,Elasticsearch具有很大的吸引力,但这并不意味着Apache Solr已经死亡。虽然有些人可能不这么认为,但Solr仍然是最受欢迎的搜索引擎之一,拥有强大的社区和开源支持。
与Solr相比,Elasticsearch易于安装且非常轻巧。此外,你可以在几分钟内安装并运行Elasticsearch。但是,如果Elasticsearch管理不当,这种易于部署和使用可能会成为一个问题。基于JSON的配置很简单,但如果要为文件中的每个配置指定注释,那么它不适合您。总的来说,如果你的应用使用的是JSON,那么Elasticsearch是一个更好的选择。否则,请使用Solr,因为它的schema.xml和solrconfig.xml都有很好的文档记录。
Solr拥有更大,更成熟的用户,开发者和贡献者社区。ES虽拥有的规模较小但活跃的用户社区以及不断增长的贡献者社区。
Solr贡献者和提交者来自许多不同的组织,而Elasticsearch提交者来自单个公司。
Solr更成熟,但ES增长迅速,更稳定。
Solr是一个非常有据可查的产品,具有清晰的示例和API用例场景。 Elasticsearch的文档组织良好,但它缺乏好的示例和清晰的配置说明。
由于易于使用,Elasticsearch在新开发者中更受欢迎。一个下载和一个命令就可以启动一切。
如果除了搜索文本之外还需要它来处理分析查询,Elasticsearch是更好的选择
如果需要分布式索引,则需要选择Elasticsearch。对于需要良好可伸缩性和以及性能分布式环境,Elasticsearch是更好的选择。
Elasticsearch在开源日志管理用例中占据主导地位,许多组织在Elasticsearch中索引它们的日志以使其可搜索。
如果你喜欢监控和指标,那么请使用Elasticsearch,因为相对于Solr,Elasticsearch暴露了更多的关键指标
6、Elasticsearch 应用案例
GitHub: 2013年初,抛弃了Solr,采取Elasticsearch 来做PB级的搜索。“GitHub使用Elasticsearch搜索20TB的数据,包括13亿文件和1300亿行代码”。
维基百科:启动以Elasticsearch为基础的核心搜索架构
SoundCloud:“SoundCloud使用Elasticsearch为1.8亿用户提供即时而精准的音乐搜索服务”。
百度:目前广泛使用Elasticsearch作为文本数据分析,采集百度所有服务器上的各类指标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常或业务层面异常。目前覆盖百度内部20多个业务线(包括云分析、网盟、预测、文库、直达号、钱包、风控等),单集群最大100台机器,200个ES节点,每天导入30TB+数据。
新浪:使用Elasticsearch分析处理32亿条实时日志。
阿里:使用Elasticsearch构建日志采集和分析体系。
Stack Overflow:解决Bug问题的网站,全英文,编程人员交流的网站。
二、ES 核心概念
1、ElasticSearch 是面向文档的,关系行数据库 和 ElasticSearch 客观对比 【一切皆为JSON】
image.pngelasticsearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包含多个文档(行),每个文档中又包含多个字段(列)。
2、物理设计:
elasticsearch在后台把每个索引划分成多个分片,每片分片可以在集群中的不同服务器间迁移一个人就是一个集群!默认的集群名称就是elaticsearh
image.png
3、逻辑设计:
一个索引类型中,包含多个文档,比如说文档1,文档2。当我们索引一篇文档时,可以通过这样的一各顺序找到它:
索引 -> 类型 -> 文档ID,通过这个组合我们就能索引到某个具体的文档。注意:ID不必是整数,实际上它是个字符串。
1、文档:就是一条数据
1 阿K 18 码农
2 K桑 32 CTO
之前说elasticsearch是面向文档的,那么就意味着索引和搜索数据的最小单位是文档,elasticsearch中,文档有几个重要属性:
(1)自我包含,一篇文档同时包含字段和对应的值,也就是同时包含key:value
(2)可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的!{就是一个json对象! fastijson进行自动转换!}
(3)灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在elasticsearch中,对于字段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新的字段。
尽管我们可以随意的新增或者忽略某个字段,但是,每个字段的类型非常重要,比如一个年龄字段类型,可以是字符串也可以是整型。因为elasticsearch会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型,这也是为什么在 elasticsearch中,类型有时候也称为映射类型。
2、类型:已经废弃,看看就好,没啥用
image.png
类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定义称为映射,比如name映射为字符串类型。我们说文档是无模式的,它们不需要拥有映射中所定义的所有字段,比如新增一个字段,那么elasticsearch是怎么做的呢?elasticsearch会自动的将新字段加入映射,但是这个字段的不确定它是什么类型,elasticsearch就开始猜,如果这个值是18,那么elasticsearch会认为它是整形。但是elasticsearch也可能猜不对,所以最安全的方式就是提前定义好所需要的映射,这点跟关系型数据库殊途同归了,先定义好字段,然后再使用,别整什么鸡儿。
3、索引:就是数据库
索引是映射类型的容器,elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上了。臭宝,我们来研究下分片是如何工作的吧。
物理设计: 节点和分片如何工作?
一个集群至少有一个节点,而一个节点就是一个elasricsearch进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有个5个分片( primary shard ,又称主分片)构成的,每一个主分片会有一个副本( replica shard ,又称复制分片)
image.png
上图是一个有3个节点的集群,可以看到主分片和对应的复制分片都不会在同一个节点内,这样有利于某个节点挂掉了,数据也不至于丢失。实际上,一个分片是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得elasticsearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。不过,等等,倒排索引是什么呢?
4、倒排索引
elasticsearch 使用的是一种称为倒排索引的结构,采用 Lucene倒排索作为底层。这种结构适用于快速的全文搜索,一个索引由文档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。例如,现在有两个文档,每个文档包含如下内容:
study every day,good good up to forever # 文档1包含的内容
To forever,study every day,good good up #文档2包含的内容
为了创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现在哪个文档:
image.png现在,我们试图搜索to forever,只需要查看包含每个词条的文档 分数(权重)
image.png
两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键字的文档都将返回
案例2:
image.pngimage.png
如果要搜索含有python标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快的多。只需要查看标签这一栏,然后获取相关的文章ID即可。|
★ elasticsearch的索引和Lucene的索引对比
在elasticsearch中,索引这个词被频繁使用,这就是术语的使用。在elasticsearch中,索引被分为多个分片,每份分片是一个Lucene的索引。所以一个elasticsearch索引是由多个Lucene索引组成的。别问为什么,谁让elasticsearch使用Lucene作为底层呢!如无特指,说起索引都是指elasticsearch的索引。
三、IK 分词器插件
1、什么是 IK 分词器
分词:即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如“我爱阿K"会被为"我""爱""阿""K”,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。如果要使用中文,建议使用ik分词器
IK提供了两个分词算法:∶ ik_smart和ik_max_word,其中ik_smart为最少切分,ik_max_word为最细粒度划分!
2、安装:直接解压到 es 的 plugins 文件夹下
下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
(1)进入es 控制台:docker exec -it 71a0 /bin/bash
(2)进入 plugins 文件夹下 创建 IK文件夹,下载文件
创建文件夹:mkdir /plugins /plugins
安装wget:yum install wget
下载版本:wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
(3)解压:unzip elasticsearch-analysis-ik-7.4.2.zip
(4)删除:rm -rf elasticsearch-analysis-ik-7.4.2.zip
(5)重启镜像:docker restart 71a0
3、用 kibana 测试 分词器、
(1)测试ik_smart (最少切分) and ik_max_word (最细粒度划分)
GET _analyze
{
"analyzer":"ik_max_word" ,
"text": "我喜欢一个人"
}
image.png
GET _analyze
{
"analyzer":"ik_smart" ,
"text": "我喜欢一个人"
}
(2)测试 阿K,吹牛逼 词条,发现用最少切分也被拆开;随意需要自定义词典
image.png
4、配置自定义分词器
(1)找到配置文件
image.png image.png
(2)创建对应的自定义词典,内容:阿K,吹牛逼;文件名为 ak.dic,在 ik 文件夹下
vi plugins/ik/config/ak.dic
(3)修改配置 IKAnalyzer.cfg.xml
,引入 ak.dic
打开配置文件:vi IKAnalyzer.cfg.xml
如果用 vi打开乱码::set encoding=utf8
,调整
写入配置:
(4)重启镜像:docker restart 71a0
四、Rest 风格说明
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
基本rest命令:
image.png
1、创建一个索引
PUT /索引名/类型名字/文档 id
{
请求体
}
image.png
2、创建索引规则,声明字段类型
image.png
3、查看 创建好的索引规则
image.png
4、7x版本后,废弃了类型,默认类型为 _doc,默认为字段提供类型
image.png
4、查看索引(库)信息
image.png
5、修改一:前面的饿值会被覆盖
image.png
6、修改二:灵活性更好,指定修改对应值
image.png
7、删除
image.png
五、关于文档的基本操作(查询)
1、简单查询
插入测试数据
PUT /mykk/user/1
{
"name":"阿K11",
"age":18,
"bir":"1997-06-06",
"tags":["英俊","帅气","执着"]
}
PUT /mykk/user/2
{
"name":"阿K22",
"age":22,
"bir":"1997-06-06",
"tags":["英俊","帅气","渣男"]
}
PUT /mykk/user/3
{
"name":"阿K33",
"age":23,
"bir":"1997-06-06",
"tags":["英俊","帅气","美丽"]
}
(1)最简单的查询 :
image.png
(2)条件查询:
image.png
2、复杂查询 select(排序,分页,高亮,模糊,精准查询)
(1)简单条件的变种写法
image.png
(2)输出指定字段
image.png
(3)排序:
image.png
(4)分页:from 是从0开始的
image.png
(5)多条件匹配【and】:must 所有条件都要符合 eg: where id =1 and name =mykk
image.png
(6)多条件匹配【or 】:should 所有条件都要符合 eg: where id =1 or name =mykk
image.png
(7)多条件匹配【not 】:must_not 所有条件都要符合 eg: where id !=1
image.png
(8)多条件匹配【 filter 】:filter 所有条件都要符合 【 gt 是大于,gte 是大于等于,lt 小于, lte 小于等于 】
image.png
3、精确查询详解
term 查询是通过倒排索引指定的词条进程精确查找
1、关于分词
(1)term ,直接查询精确的
(2)match,会使用分词器解析(先分析文档,然后再通过分析的文档进行查询)
两个类型 text , keyword
text:会被分词器解析,拆分掉查询
keyword:不会被分词器解析,不可再分
4、高亮搜索
image.png六、集成 SpringBoot
1、看文档:https://www.elastic.co/guide/index.html
(1)找到es的客户端 用于开发连接
image.png
(2)找到java rest版的
image.png
(3)选择高级版
image.png
(4)找到maven依赖
image.png
image.png
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.4.2</version>
</dependency>
(5)初始化对象
image.png
2、实操【索引】
(1)创建springboot新工程勾选
image.png
(2)编写配置
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient() {
return new RestHighLevelClient (
RestClient.builder (
new HttpHost ("101.34.180.131", 9200, "http")));
}
}
(3)创建索引
@SpringBootTest
class ElasticshedemoApplicationTests {
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
// 测试创建索引 request PUT mykk_demo1
@Test
void testCreateIndex() throws Exception{
// 1、创建索引请求
CreateIndexRequest request = new CreateIndexRequest ("mykk_demo1");
// 2、客户端执行请求 IndicesClient ,请求后获取响应
CreateIndexResponse createIndexResponse = client.indices ( ).create (request, RequestOptions.DEFAULT);
System.out.println (createIndexResponse );
}
}
(4)索引是否存在
// 测试获取索引
// 存在返回 true
@Test
void testExistIndex()throws Exception{
GetIndexRequest request = new GetIndexRequest ("mykk_demo1");
boolean exists = client.indices ( ).exists (request, RequestOptions.DEFAULT);
System.out.println (exists );
}
(5)删除索引
// 测试删除索引
@Test
void testDeleteIndex()throws Exception{
DeleteIndexRequest req = new DeleteIndexRequest ("mykk_demo1");
AcknowledgedResponse delete = client.indices ( ).delete (req, RequestOptions.DEFAULT);
System.out.println (delete.isAcknowledged () );
}
3、实操【文档】
(1)添加文档
// 测试文档添加
@Test
void testAddDocument()throws Exception{
User user = new User ("阿K", 18);
// 创建请求
IndexRequest request = new IndexRequest ("mykk_demo1");
// 请求体 put /mykk_demo1/_doc/1
request.id ("1");
request.timeout ("1s"); // 设置超时
// 将数据放入请求 json
request.source (JSON.toJSONString (user),XContentType.JSON);
// 客户端发送请求,获取响应结果
IndexResponse indexResponse = client.index (request, RequestOptions.DEFAULT);
System.out.println (indexResponse.toString () );
System.out.println (indexResponse.status () );
}
(2)判断文档是否存在
// 测试文档是否存在
@Test
void testExitsDocument() throws Exception{
GetRequest getRequest = new GetRequest ("mykk_demo1", "1");
// 不获取返回的 _source 上下文信息(提高效率)
getRequest.fetchSourceContext (new FetchSourceContext (false));
getRequest.storedFields ("_none_");
boolean exists = client.exists (getRequest, RequestOptions.DEFAULT);
System.out.println (exists );
}
(3)获取文档记录
// 测试获取文档信息
@Test
void testGetDocument() throws Exception{
GetRequest getRequest = new GetRequest ("mykk_demo1", "1");
// 获取文档内容
GetResponse response = client.get (getRequest, RequestOptions.DEFAULT);
// 打印文档的内容
System.out.println (response.getSourceAsString () );
System.out.println (response );
}
(4)更新文档记录
// 测试更新文档信息
@Test
void testUpdateDocument() throws Exception{
// 获取文档
UpdateRequest updateRequest = new UpdateRequest ("mykk_demo1", "1");
updateRequest.timeout ("1s");
User user = new User ("阿K", 23);
updateRequest.doc (JSON.toJSONString (user),XContentType.JSON);
UpdateResponse updateResponse = client.update (updateRequest, RequestOptions.DEFAULT);
System.out.println (updateResponse.status () );
}
(6)删除文档记录
// 测试删除文档信息
@Test
void testDeleteDocument() throws Exception{
DeleteRequest deleteRequest = new DeleteRequest ("mykk_demo1", "1");
deleteRequest.timeout ("1s");
DeleteResponse deleteResponse = client.delete (deleteRequest, RequestOptions.DEFAULT);
System.out.println (deleteResponse.status () );
}
(5)插入文档记录
// 测试批量文档记录
@Test
void testBulkDocument() throws Exception{
BulkRequest bulkRequest = new BulkRequest ( );
bulkRequest.timeout ("10s");
ArrayList<User> userList = new ArrayList<> ( );
userList.add (new User ("阿K1",1));
userList.add (new User ("阿K2",3));
userList.add (new User ("阿K3",5));
userList.add (new User ("阿K4",7));
userList.add (new User ("阿K5",9));
// 批处理请求
for (int i = 0; i < userList.size ( ); i++) {
// 批量更新和删除,在这里修改对应请求即可
bulkRequest.add (new IndexRequest ("mykk_demo1")
.id (""+(i+1))
.source (JSON.toJSONString (userList.get (i)),XContentType.JSON));
}
BulkResponse bulkResponse = client.bulk (bulkRequest, RequestOptions.DEFAULT);
System.out.println (bulkResponse.hasFailures () );// 是否成功,false 代表成功
}
(7)查询
// 查询
// SearchRequest 搜索请求
// SearchSourceBuilder 条件构造
// HighlightBuilder 构建高亮
// TermQueryBuilder 精确查询
// MatchAllQueryBuilder 精确所有
@Test
// 记住存入的要查询的不能是中文,因为还没有处理
void testSearch() throws Exception{
SearchRequest searchRequest = new SearchRequest ("mykk_demo1");
// 构建搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder ( );
sourceBuilder.highlighter ();
// 查询条件:可以使用 QueryBuilders 工具来实现
// QueryBuilders.termQuery 精确
// QueryBuilders.matchAllQuery () 匹配所有
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery ("name", "ak1");
// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery ( );
sourceBuilder.query (termQueryBuilder);
sourceBuilder.timeout (new TimeValue (60, TimeUnit.SECONDS));
searchRequest.source (sourceBuilder);
SearchResponse searchResponse = client.search (searchRequest, RequestOptions.DEFAULT);
System.out.println (JSON.toJSONString (searchResponse.getHits ()) );
System.out.println ("===========================================" );
for (SearchHit documentFields : searchResponse.getHits ( ).getHits ( )) {
System.out.println (documentFields.getSourceAsMap () );
}
}
七、案例(模拟京东)
搭建项目
image.png
导入静态文件
狂神百度云链接:https://pan.baidu.com/s/1PT3jLvCksOhq7kgAKzQm7g 提取码:s824
1、爬虫
(1)去京东官网,找到url搜索规则:https://search.jd.com/Search?keyword=java
(2)编写配置文件
# 应用名称
spring.application.name=jingdongelactichserchdemo
# 应用服务 WEB 访问端口
server.port=8080
# THYMELEAF (ThymeleafAutoConfiguration)
# 开启模板缓存(默认值: true )
spring.thymeleaf.cache=false
spring.elasticsearch.rest.uris= 101.34.180.131:9200
(3)导入网页解析的依赖,不支持视频的
<!--解析网页-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
(4)封装对象用于映射每个爬取的商品(li标签)
image.png
(5)爬取京东的搜索工具方法
@Component
public class HtmlParseUtil {
public static void main(String[] args)throws Exception {
parseJD ("阿K").forEach (System.out::println);
}
/**
* 用于搜索返回商品爬取数据
* @param keyword 用于搜索
* @return 返回商品集合爬取的数据
* @throws Exception
*/
public static List<Content> parseJD(String keyword) throws Exception{
// 爬取的url :https://search.jd.com/Search?keyword=java
// &enc=urt8 解决中文传参问题
String url = "https://search.jd.com/Search?keyword="+keyword+"&enc=urt8";
// Document 既是 html的 Document对象
Document document = Jsoup.parse (new URL (url), 30000);
// 所有 js操作 dom的方法这里都有
Element element = document.getElementById ("J_goodsList");
// 获取所有的 li元素
Elements elements = element.getElementsByTag ("li");
// 根据打印找出懒加载图片的 赋值标签,data-lazy-img
//System.out.println (elements );
// 获取元素中的内容,这里 el 就是每一个 li 标签
ArrayList<Content> contents = new ArrayList<> ( );
for (Element el : elements) {
// 关于这种图片特别多的网站,采用的是延迟加载
// source-data-lazy-img
String img = el.getElementsByTag ("img").eq (0).attr ("data-lazy-img");
String price = el.getElementsByClass ("p-price").eq (0).text ();
String title = el.getElementsByClass ("p-name").eq (0).text ();
contents.add (new Content (title,img,price));
}
return contents;
}
}
(6)编写搜索爬的数据,存储到 es 中
Controller
@RestController
public class ContextController {
@Autowired
private ContextService contextService;
// 爬数据,存es
@GetMapping("/parse/{keyword}")
public Boolean parse(@PathVariable("keyword") String keyword)throws Exception{
return contextService.parseContext (keyword);
}
// 分页查询
@GetMapping("/search/{keyword}/{pageNo}/{pageSize}")
public List<Map<String,Object>> search(
@PathVariable("keyword") String keyword,
@PathVariable("pageNo") int pageNo,
@PathVariable("pageSize") int pageSize
)throws Exception{
return contextService.searchPage (keyword,pageNo,pageSize);
}
}
Servoce
@Service
public class ContextService {
@Autowired
private RestHighLevelClient client;
// 1、解析数据放入 es 索引中
public boolean parseContext(String keyword) throws Exception{
List<Content> contents = HtmlParseUtil.parseJD (keyword);
// 将查询的数据存入 es中
BulkRequest bulkRequest = new BulkRequest ( );
bulkRequest.timeout ("2m");// 超时2分钟放弃
for (int i = 0; i < contents.size ( ); i++) {
bulkRequest.add (new IndexRequest ("jd_goods")
.source (JSON.toJSONString (contents.get (i)),XContentType.JSON));
}
BulkResponse bulk = client.bulk (bulkRequest, RequestOptions.DEFAULT);
return !bulk.hasFailures ();
}
// 2、搜索获取数据,分页功能
public List<Map<String,Object>> searchPage(String keyword,int pageNo,int pageSize)throws Exception{
if (pageNo<=1){
pageNo=1;
}
// 条件搜索
SearchRequest searchRequest = new SearchRequest ("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder ( );
// 分页
sourceBuilder.from (pageNo);
sourceBuilder.size (pageSize);
// 精确匹配
// TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword);无法解决中文搜索
MatchQueryBuilder termQueryBuilder = QueryBuilders.matchQuery ("title", keyword);
sourceBuilder.query (termQueryBuilder);
sourceBuilder.timeout (new TimeValue (60, TimeUnit.SECONDS));
// 执行搜索
searchRequest.source (sourceBuilder);
SearchResponse searchResponse = client.search (searchRequest, RequestOptions.DEFAULT);
// 解析结果
ArrayList<Map<String, Object>> list = new ArrayList<> ( );
for (SearchHit documentFields : searchResponse.getHits ( ).getHits ( )) {
list.add (documentFields.getSourceAsMap ());
}
return list;
}
2、前后端分离
(1)运行两条命令
npm install vue
npm install axios
(2)将 node.js 初始化的,找到 两个js 放入工程的静态文件中
image.png
(3)引入编写对应js代码
<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;
axios.get('search/'+keyword+'/1/10').then(response=>{
console.log(response);
this.results = response.data;
});
}
}
});
</script>
image.png
image.png
3、搜索高亮
1、改写 service的搜索分页
image.png
2、改写页面的显示
image.png
3、效果
image.png
网友评论