文章目录

  • 一、ElasticSearch 简介
    • 1.了解创始人 Doug Cutting
    • 2.Lucene 简介
    • 3.ElasticSearch 简介
    • 4.ElasticSearch 和 Solr 的区别
    • 5.了解ELK
  • 二、软件安装
    • 1.ElasticSearch
    • 2.ElasticSearch Head
    • 3.Kibana
  • 三、ElasticSearch 使用详解
    • 1.ES 核心概念
      • 文档
      • 索引
      • 倒排索引
      • ik分词器
    • 2.命令模式的使用
      • Rest风格说明
      • cat命令
      • 关于文档的基本操作(重点)
      • 复杂操作查询
  • 四、SpringBoot 集成 ElasticSearch
    • 1.准备工作
    • 2.API 使用
      • 项目准备
      • 项目初始化
      • 源码分析
      • API 索引操作
      • API 文档操作
  • 五、实现项目-京东搜索
    • 1.项目搭建
    • 2.爬取数据
    • 3.业务编写
    • 4.前端页面展示
    • 5.高亮显示

相关了解:

  • es搜索 6.x 7.x 差别很大,6.x的API(原生 API,RestFul高级)
  • 应用领域很广,包括在大数据相关上面使用

一、ElasticSearch 简介

1.了解创始人 Doug Cutting

  • 1998.9.4,谷歌成立,做搜索引擎起家
  • 一位名叫 Doug Cutting 的工程师,也迷上搜索引擎,做了一个用于文本搜索的函数库,命名为 Lucene
  • Lucene 是 Java 写的,目标是各种中小型应用加入全文检索功能,好用且开源,深受程序员喜爱
  • 2001年底,Lucene 成为 Apache 软件基金会的一个子项目
  • 2004年,Doug Cutting 在 Lucene 基础上开发了开源搜索引擎,Nutch
  • Nutch 建立在 Lucene 基础上,增加了网络爬虫和网页相关功能,在业界影响力比Lucene 更大
  • 随着时间推移,无论是 谷歌还是 Nutch 都面临搜索体积不断增加的问题,谷歌开始优化自己的算法,
  • 2003年,谷歌公开介绍了自己的谷歌文件系统 GFS,这是谷歌为了存储海量搜索数据二设计的专用文件系统
  • 2004年,Doug Cutting 基于谷歌的论文,实现了分布式文件存储系统,简称 NDFS
  • 2004年,谷歌又发布论文,介绍自己的 MapReduce 编程模型,用于大规模数据集的并行分析运算
  • 2005年,Doug Cutting 又基于谷歌的 MapReduce 模型,在Nutch上实现了该功能,大大提高速度
  • 2006年,雅虎公司招了 Doug Cutting
  • Doug Cutting 加盟雅虎之后,将 NDFS 和 MapReduce 进行改造,这就是大名鼎鼎的 Hadoop
  • 2006年,谷歌又发论文了,BigTable,这是一种分布式数据存储系统,用来处理海量数据的非关系型数据库
  • Doug Cutting 当然不会错过,在自己的Hadoop系统里面引入BigTable,命名 HBase
  • 2008年1月,Hadoop 成功上位,正式成为 Apache 基金会的顶级项目

2.Lucene 简介

  • Lucene 是一套信息检索工具包,是 jar 包,不是搜索引擎系统
  • 包含:索引结构,读写索引的工具,排序,搜索规则,工具类

ElasticSearch 和 Lucene 的关系:

  • ElasticSearch 是基于 Lucene 做了一些封装和增强,上手很简单

3.ElasticSearch 简介

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

2016年1月,ElasticSearch 超过 Solr,成为排名第一的搜索引擎应用

谁在使用:

  • 维基百科
  • The Guardian 国外新闻网站
  • Stack Over Flow
  • GitHub
  • 电商网站
  • 日志数据分析,logstash采集日志,ES进行复杂数据分析,ELK技术,ElasticSearch+logstash-kibana
  • 上平价格监控网站
  • BI系统,商业智能,比如分析某些数据趋势
  • 国内:站内搜索,电商,招聘,门户,IT系统搜索,数据分析(ES热门使用的场景)

4.ElasticSearch 和 Solr 的区别

ElasticSearch 用于 全文搜索、结构化搜索、分析

Solr 是 Apache 下的一个顶级开源项目,采用Java开发,基于 Lucene 的全文搜索服务器,Solr 提供了比 Lucene 更为丰富的查询语言,同时实现了可配置,可扩展,并对索引、搜索性能进行了优化

Solr 可以独立运行在 Jetty 等Servlet容器中,

Solr 是一个独立的企业级搜索应用服务器,实际上就是封装了 Lucene,对外提供类似于 Web-service的API接口,用户可以通过http请求,想搜索引擎服务器提交一定格式的文件,生成索引,也可以通过提出查找请求,得到结果

ElasticSearch vs Solr:

  • ES 基本是开箱即用,解压就可以使用,非常简单,Solr 的使用略复杂
  • Solr 利用 Zookeeper 进行分布式管理,ElasticSearch 自身带有分布式协调管理功能
  • Solr 支持更多格式的数据,比如,JSON,XML,CSV ,ElasticSearch 仅支持 JSON 文件格式
  • Solr 官网提供的功能更多,ElasticSearch 本身更专注核心功能,高级功能多由第三方插件提供,如图形化界面需要 kibana 友好支持
  • Solr 查询快,但更新搜索引擎慢,适合用于电商扥个查询多的应用
  • ES 建立搜索引擎模块,查询慢,即时性查询快,适用于facebook、新浪等实时搜索应用
  • Solr 是传统搜索应用的有力解决方案,但ElasticSearch 更适用于新型的实时型搜索应用
  • Solr比较成熟,有一个更大、更成熟的用户、开发贡献社区,而ElasticSearch 相对开发维护者较少,更新又太快,学习使用成本较高
  • ElasticSearch 是未来的使用趋势

5.了解ELK

ELK 是 ElasticSearch、Logstash、Kibana 三大开源框架首字母大写的简称。市面上面也称作 Elastic Stack ,其中 ElasticSearch 是一个基于 Lucene、分布式、通过 ResrFul 方式进行交互的近实时搜索平台台框架。像类似百度、谷歌这种大数据全文搜索引擎的场景都可以使用ElasticSearch 作为底层支持框架,可见 ElasticSearch 提供的搜索功能很强大。

市面上我们简称ElasticSearch 为ES。Logstash 是ELK中央数据流引擎,用于从不同目标(文件/数据存储/MQ)收集的不同格式数据,经过过滤后支持输出到不同目的地(文件/MQ/redis/elastisearch/kafka等),Kibana 可以将 ElasticSearch 的收据通过友好的界面展示出来,提供实时分析的功能。

收集清洗数据 => 搜索、存储 => Kibana

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


大数据:选择

二、软件安装

最低要求 JDK 1.8,maven,web 项目需要有前端环境 node.js相关

官网(比较慢):https://www.elastic.co/cn/downloads/?elektra=home&storm=hero

ElasticSearch: https://mirrors.huaweicloud.com/elasticsearch/?C=N&O=D

logstash: https://mirrors.huaweicloud.com/logstash/?C=N&O=D

可视化界面:elasticsearch-head.https://github.com/mobz/elasticsearch-head

kibana: https://mirrors.huaweicloud.com/kibana/?C=N&O=D

ik分词器 https://github.com/medcl/elasticsearch-analysis-ik

实际生产环境使用 Linux 版本,这里我们学习使用,Windows版本即可

ELK 三件套解压即可使用,

1.ElasticSearch

解压压缩包,

熟悉目录

  • bin 启动目录
  • config 配置目录
    log4j2 日志配置文件,jvm参数默认1g(内存小的需要自己配置),elasticsearch.yml 核心配置,其中默认端口9200
  • lib 相关 jar 包
  • modules 功能模块
  • plugins 插件
  • logs 日志

启动:

进入bin目录,双击启动 elasticsearch.bat

如果启动成功,命令行会显示,可以访问地址为 127.0.0.1:9200,

访问地址可以看到json数据,表示启动成功,访问成功

2.ElasticSearch Head

直接查看后台json数据并不友好,我们需要使用可视化工具

elasticSearch head:https://github.com/mobz/elasticsearch-head

查看目录结构可以得知,这是一个前端项目,需要提前安装node.js

如何启动这个前端项目?查看项目说明


解压缩文件,进入文件根目录,执行命令行 cnpm install ,安装相关依赖,npm 会根据package.json中配置的依赖进行安装

cnpm是淘宝的镜像,安装速度更快一些

依赖下载成功

启动项目 npm run start

启动成功,访问 http://localhost:9100

发现页面没有数据,因为存在跨域问题,9100端口访问9200端口


配置 elasticsearch.yml 核心配置文件,添加配置,注意yml语法空格缩进

http.cors.enabled: true
http.cors.allow-origin: "*"

表示开启可以跨域访问,允许所有都可以访问,保存,关闭文件

关闭 elasticsearch ,重新启动,

先访问测试 9200端口,可以看到数据,再访问 9100,可以看到访问成功

界面简介:

  • 索引先简单理解为一个数据库,可以建立索引,索引中存放文档就相当于存放数据


未来的数据就存放在这里面

这个 head 我们就把他当做数据库连接工具即可,后续我们进行查询可以使用 kibana 工具,效率会更加提高很多

3.Kibana

Kibana 是一个针对 elasticsearch 的开源分析及可视化平台,用来搜索、查看交互存储在 elasticsearch 索引中的数据。使用 Kibana 可以通过各种图表进行高级数据分析及展示,Kibana 让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表盘(dashboard)实时显示 elasticsearch 查询动态,设置 Kibana 非常简单,无需编码或者额外的基础架构,几分钟内就可以完成 Kibana 安装并启动 elasticsearch 索引监测

官网:https://www.elastic.co/cn/downloads/?elektra=home&storm=hero

注意:Kibana 下载的版本一定要和 ElasticSearch 的版本一致

解压缩,可以看到这也是个完整的前端工程项目,

Kibana 已经包含了依赖,且有bin目录,进入bin目录,右键管理员运行 kibana.bat 文件,启动比较慢,


访问 5601 端口

访问页面测试可以使用 Postman、curl、elasticsearch-head、这里我们就使用 kibana

以后的搜索测试 请求语句 可以写在这里,通过kibana 来向搜索引擎 elasticsearch 进行查询请求

kibana 默认英文,如何汉化?

修改插件中的国际化设置,进入目录 kibana-7.6.1-windows-x86_64\x-pack\plugins\translations\translations,可以看到有中文的国家化文件,里面包含了中文翻译对照

我们修改 kibana 核心配置文件,在 kibana-7.6.1-windows-x86_64\config 目录下,kibana.yml 添加国际化配置,默认是英文

i18n.locale: "zh-CN"

重启 kibana,访问测试

三、ElasticSearch 使用详解

1.ES 核心概念

  1. 索引
  2. 字段类型(mapping)
  3. 文档(documents)

集群,节点,索引,类型,文档,分片,映射是什么?

elasticsearch是面向文档,关系型数据库和elasticsearch客观的对比!一切都是json

Relational DB Elasticsearch
数据库(database) 索引(indices)
表(tables) types
行(rows) documents
字段(columns) fields


物理设计

elasticsearch 在后台把每个索引划分成多个分片。每个分片可以在集群中的不同服务器间迁移

初始一个人就是一个集群,默认集群名字 elasticsearch

逻辑设计

一个索引类型中,包含多个文档,当我们索引一篇文档时,可以通过这样的一个顺序找到它:索引 => 类型 => 文档id,通过这个组合我们就能索引到某个具体的文档。注意:ID不必是整数,实际上它是一个字符串。

文档

文档就是我们的一条条的数据

之前说elasticsearch是面向文档的,那么就意味着索弓和搜索数据的最小单位是文档,,elasticsearch中,文档有几个重要属性:

  • 自我包含,一篇文档同时包含字段和对应的值,也就是同时包含 key:value !
  • 可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的! {就是一 个json对象! 可以用fastjson进行自动转换!}
  • 灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中,要提前定义字段才能使用,在elasticsearch中,对于字段是非常灵活的,我们可以忽略某字段,或者动态的添加一个新的字段。

尽管我们可以随意的新增或者忽略某个字段,但每个字段的类型非常重要,比如一个年龄字段类型,可以是字符串也可以是整形。因为elasticsearch会保存字段和类型之间的映射及其他的设置。这种映射具体到每个映射的每种类型,这也是为什么在elasticsearch中,类型有时候也称为映射类型。

索引

索引就是数据库!

索引是映射类型的容器,elasticsearch 中的索引是一个非常大的文档集合。索|存储了映射类型的字段和其他设置。然后它们被存储到了各个分片上了。我们来研究下分片是如何工作的。

物理设计:节点和分片如何工作

一个集群至少有一 个节点,而一个节点就是一个elasricsearch进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有5个分片(primary shard,又称主分片)构成,每一个主分片会有一个副本(replica shard,又称复制分片)


上图是一个有3个节点的集群,可以看到主分片P和对应的复制分片R都不会在同一个节点内,这样有利于某个节点挂掉了,数据也不至于丢失。实际上,一个分片是一个Lucene索引,一个包含倒排索引的文件目录,倒排索引的结构使得elasticsearch在不扫描全部文档的情况下,就能告诉你哪些文档包含特定的关键字。不过,等等,倒排索引是什么鬼?

集群、节点、索引、分片 之间的关系:

  • 一个集群包含一个或多个节点,每个节点独立包含完整数据,节点也分主从节点
  • 一个节点就是一个 Elasticsearch 服务(实例)
  • 节点内创建索引,索引可以分成多个主分片,每个分片包含部分数据
  • 每个主分片还会有对应的从分片(副本),与主分片数据相同,个数自定,主分片不会与他的副本在一个节点内,这是为了增加高可用
  • Elasticsearch 中的分片其实就是 Lucene 索引
  • 一个分片就是一个 Lucene 实例

详细关系参照:Elasticsearch 集群、节点、索引、分片、副本概念

倒排索引

elasticsearch使用的是一种称为倒排索引的结构,采用Lucene倒排索作为底层。这种结构适用于快速的全文搜索,一个索引由文
档中所有不重复的列表构成,对于每一个词,都有一个包含它的文档列表。例如,现在有两个文档,每个文档包含如下内容:

Study every day, good good up to forever  # 文档1包含的内容
To forever, study every day,good good up  # 文档2包含的内容

为为创建倒排索引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现在哪个文档:

term doc_1 doc_2
Study x
To x
every
forever
day
study x
good
every
to x
up

比如,我们搜索 to forever,只需要查看包含每个词条的文档

term doc_1 doc_2
to x
forever
total 2 1

两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键字的文档都将返回。

再来看一个示例,比如我们通过博客标签来搜索博客文章。那么倒排索引列表就是这样的一个结构:

博客文章(原始数据) 博客文章(原始数据) 索引列表(倒排索引) 索引列表(倒排索引)
博客文章ID 标签 标签 博客文章ID
1 python python 1,2,3
2 python linux 3,4
3 linux,python
4 linux

如果要搜索含有python标签的文章,那相对于查找所有原始数据而言,查找倒排索引后的数据将会快的多。只需要查看标签这一栏,然后获取相关的文章ID即可。完全过滤掉无关的所有数据,提高效率!

当然,前提是要先生成索引列表

elasticsearch的索引和Lucene的索引对比

在elasticsearch中,索引(库)这个词被频繁使用,这就是术语的使用。在elasticsearch中,索引被分为多个分片,每份分片是一个Lucene的索引。所以一个elasticsearch索引是由多 个Lucene索引组成的。别问为什么,谁让elasticsearch使用Lucene作为底层呢!如无特指,说起索引都是指elasticsearch的索引。

接下来的一切操作都在kibana中Dev Tools下的Console里完成。基础操作!

ik分词器

什么是IK分词器 ?

分词:

  • 即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词。
  • 比如“我爱狂神”会被分为"我",“爱”,“狂”,“神” ,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。

如果要使用中文,建议使用ik分词器!

IK提供了两个分词算法:

  • ik_ smart和ik_ max_ word
  • ik_ smart为最少切分,ik_ max_ _word为最细粒度划分!

下载:https://github.com/medcl/elasticsearch-analysis-ik

版本不全到这里找:https://elasticsearch.cn/download/

下载完毕后,直接放到目录中 elasticsearch\elasticsearch-7.6.1\plugins 即可,注意将分词器的目录名改为 ik

重启 elasticsearch,在启动过程中可以看到,elasticsearch 加载了 ik 分词器

重启失败原因:

  • elasticsearch 与 ik 分词器版本一定要完全对应上,只对应大版本号不够

  • 如何查看 ik版本号,查看目录下 plugin-descriptor.properties 文件,

  • 解压后,压缩包删除,分词器目录取名ik

  • kabana 和 head 最好也关掉,一起重启

如何确认 elasticsearch 启动成功加载插件?启动 elasticsearch bin 目录中提供的工具 elasticsearch-plugin.bat

进入 elasticsearch bin 目录启动命令行 elasticsearch-plugin list

表示加载成功

测试使用

进入 kibana 界面 Dev Tools 菜单,发起请求,选择分词算法

【ik_smart】(最少切分)测试:

GET _analyze
{"analyzer": "ik_smart","text": "我是社会主义接班人"
}

发起请求,右侧可以看到分词结果

【ik_max_word】(最细粒度划分)测试

GET _analyze
{"analyzer": "ik_max_word","text": "我是社会主义接班人"
}

可以看到二者的拆分区别

思考:

  • ik 分词器为什么知道怎么拆分呢?因为它需要参照分词器字典
  • 如果拆分的词不符合我们的实际需求怎么办?需要自己加到分词器字典里

ik分词器如何增加配置

进入分词器目录 elasticsearch\elasticsearch-7.6.1\plugins\ik\config ,这个目录包含了各种字典,

我们可以创建的自己的字典,然后配置到 IKAnalyzer.cfg.xml 文件中

新建字典文件,比如,swy.dic,编辑内容,比如添加

泥萌好
swy

这样,泥萌好,swy,就会默认成为一个词,被ik分词器识别

在配置文件 IKAnalyzer.cfg.xml 中,添加

<entry key="ext_dict">swy.dic</entry>

重启es,查看启动日志可以看到,我们的新的分词字典已经被加载


开始测试,


可以看到,我们自定义的词已经被看做一个完整的词,以后工作,我们即可配置自己需要的词

2.命令模式的使用

Rest风格说明

一种软件架构风格,而不是标准。只是提供了一组设计原则和约束条件,它主要用于客户端可服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制

method url地址 描述
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/索引名称/类型名称/_search 查询所有的数据

基础测试

  1. 创建一个索引,并添加数据
PUT /索引名/类型名/文档id

注意,PUT 后面有空格

查看 elasticsearch 后台数据

本质上是我们添加了一条数据

完成了自动增加索引,数据成功添加

需要注意的是,只要可以发送规定格式的请求就可完成操作,不一定使用kibana,postman,也可以,但是用kibana很专业,而且输入时有提示,降低出错率

如何给数据中的数据指定类型?

  • 字符串类型:text、keyword
  • 数值类型:long、integer、short、byte、double、float、half float、scaled float
  • 日期类型:date
  • 布尔类型:boolean
  • 二进制类型:binary
  • 等等

官方有详细讲解

指定字段类型,再次添加数据,这里我们先不添加数据,只是制定规则

查看后台控制,可以看到,新增加了一个索引test2,但是没有数据

  1. 获取索引规则信息,GET请求

  1. 查看默认信息

创建索引的时候,如果不指定字段属性类型,则会使用默认类型,当然也可以显示的声明


查看后台数据

发送请求查看数据,可以看到,age默认给了long类型,birthday默认给了date类型,name默认给了text类

  1. 修改操作,第一种,还是直接使用PUT即可,然后覆盖


但是有个缺点,对于修改来说,如果旧的数据有一个属性,新修改的指令里没有添加这个属性,那么修改后这个属性就会消失

由于之前我们己经创建这个数据,所以这次创建直接将原来的覆盖,而且version还显示了版本号增加了

第二种,使用正统的修改指令 POST


删除索引,使用 DELETE

cat命令

获取健康值

其实 elasticsearch-head后台管理页面就是不断地向服务器发送请求,获得的数据

查看所有信息

根据提示可以在 _cat后面添加很多指令进行查看

关于文档的基本操作(重点)

创建文档

  1. 添加数据
PUT /swy/user/1
{"name": "swy01","age": 25,"desc": "every will be ok","tags": ["高","富","帅"]
}

得到结果

{"_index" : "swy","_type" : "user","_id" : "1","_version" : 1,"result" : "created","_shards" : {"total" : 2,"successful" : 1,"failed" : 0},"_seq_no" : 0,"_primary_term" : 5
}

查看后台数据

同样的方法,在添加多条数据

  1. 获取数据 GET
  2. 更新数据


不推荐使用put,推荐使用post,只需该对应的字段即可,不必添加所有的字段

POST swy/user/1/_update
{"doc": {"name": "Mike"}
}


  1. 搜素操作(简单操作)

通过id查询

GET swy/user/_search?q=name:Mike

复杂操作查询

复杂操作查询:select(排序,分页,高亮,模糊查询,精准查询)

查询具体的参数使用 json

  1. 条件查询
GET swy/user/_search
{"query": {"match": {"name": "jack"}}
}

  1. 结果过滤,查询并输出的结果不需要那么多字段
GET swy/user/_search
{"query": {"match": {"name": "jack"}},"_source": ["name","desc"]
}


注意:之后使用Java操作es,所有的方法和对象就是这里的key

  1. 排序操作,sort 表示通过哪个字段排序

这里用的升序,

GET swy/user/_search
{"query": {"match": {"name": "swy"}},"sort": [{"age": {"order": "desc"}}]
}

  1. 分页查询,需要包含从哪里开始,以及页面大小

数据下标从0开始,与其他数据库是一样的

GET swy/user/_search
{"query": {"match": {"name": "swy"}},"sort": [{"age": {"order": "desc"}}],"from": 0,"size": 1
}

  1. 多条件查询,使用bool,

must(相当于and),should(相当于or),must_not(不等于xxx条件)

GET swy/user/_search
{"query": {"bool": {"must": [{"match": {"name": "swy"}},{"match": {"age": 250}}]}}
}

  1. 过滤器 filter,

这里附加了范围

GET swy/user/_search
{"query": {"bool": {"must": [{"match": {"name": "swy"}}],"filter": {"range": {"age": {"gte": 100,"lte": 300}}}}}
}

  1. 匹配多个条件,tags,比如根据性格标签,可以计算出权重

多个条件使用空格分开即可

GET swy/user/_search
{"query": {"match": {"tags": "白 富 美"}}
}

我们可以看到,匹配度越高,分值越高

  1. 精确查询

term查询是直接通过倒排索引指定的词条进程精确查找的

关于分词

  • term,直接查询精确的
  • match,会使用分词器解析,先分析文档,再通过分析的文档进行查询

关于两个类型

  • text类型可以被分词解析
  • keyword不会被分词解析

举个例子,创建索引

PUT testdb
{"mappings": {"properties": {"name": {"type": "text"},"desc": {"type": "keyword"}}}
}

添加数据

PUT testdb/_doc/1
{"name": "狂神说Java name","desc": "狂神说Java desc"
}

改变id,以及属性参数,添加多条数据

当成 keyword 方式分词,是看做一个整体

standard 普通方式,普通分词处理,当然我们也可以使用中文分词

开始查询

当我们搜索 name属性(text类型)时,由于这种数据默认可以被分词器解析,所以,即使我们搜索部分单词,也可以找到完整相关数据

GET testdb/_search
{"query": {"term": {"name": "狂"}}
}


当我们搜素 desc 属性(keyword类型)时,整个属性值被看做一个整体,搜索单个词无法找到,只有搜索整体才有结果

GET testdb/_search
{"query": {"term": {"desc": "狂"}}
}

GET testdb/_search
{"query": {"term": {"desc": "狂神说Java desc"}}
}

结论:keyword 属性的字段不会分词器解析,设置属性的时候需要注意

  1. 多个值匹配的精确查询

先添加一些数据,

PUT testdb/_doc/3
{"t1": "22","t2": "2020-04-06"
}
PUT testdb/_doc/4
{"t1": "33","t2": "2020-04-07"
}

开始查询

GET testdb/_search
{"query": {"bool": {"should": [{"term": {"t1": "22"}},{"term": {"t1": "33"}}]}}
}

  1. 高亮查询

搜索的结果中包含搜索词条的部分,用高亮显示

查询的时候,添加高亮属性,并设置需要高亮的字段

搜索相关的结果,会被自动加上标签

GET swy/user/_search
{"query": {"match": {"name": "swy"}},"highlight": {"fields": {"name": {}}}
}


自定义高亮属性,添加所需要的标签的前缀,后缀

小结:

  • 匹配
  • 按条件匹配
  • 精确匹配
  • 区间范围匹配
  • 匹配字段过滤
  • 多条件匹配
  • 高亮查询

这些 MySQL 也可以做,只不过效率比较低

尤其在海量数据的情况下,es的性能优势非常明显

四、SpringBoot 集成 ElasticSearch

1.准备工作

官方文档:https://www.elastic.co/guide/index.html

找到客户端文档,进入Java Rest Client ,选择高级客户端,

客户端相关:


原生 maven 依赖

<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.6.2</version>
</dependency>

初始化 Initialization

RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(new HttpHost("localhost", 9200, "http"),new HttpHost("localhost", 9201, "http")));

用完关闭

client.close();

2.API 使用

项目准备

创建maven项目作为父项目,删除多余文件,创建子模块springboot项目,添加项目依赖


检查springboot-starter下载的依赖,es依赖版本是否与我们安装的版本一致,如果不一致,将连接不上,一定要确保一致

添加与本地版本相匹配的依赖

<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.6.1</version>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.70</version>
</dependency>

项目初始化

添加配置类,注入bean

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

源码分析

查看依赖 spring-boot-autoconfigure,elasticsearch

源码中提供的rest对象

API 索引操作

使用前确保,elasticsearch 已经启动

编写测试类,有了之前通过 kibana 的使用,我们现在开始转为使用Java API进行操作

@SpringBootTest
class EsApiApplicationTests {@Autowired@Qualifier("restHighLevelClient")private RestHighLevelClient client;// 测试索引的创建@Testvoid testCreateIndex() throws IOException {// 1.创建索引的请求CreateIndexRequest request = new CreateIndexRequest("swy_index");// 2.客户端执行请求,请求后获得响应CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);System.out.println(response);}
}

运行测试,创建成功

查看后台数据,索引已经创建,默认为空

测试获取索引,判断是否存在

 // 测试索引是否存在@Testvoid testExistIndex() throws IOException {// 1.创建索引的请求GetIndexRequest request = new GetIndexRequest("swy_index");// 2.客户端执行请求,请求后获得响应boolean exist =  client.indices().exists(request, RequestOptions.DEFAULT);System.out.println("测试索引是否存在-----"+exist);}


测试删除索引

 // 删除索引@Testvoid testDeleteIndex() throws IOException {DeleteIndexRequest request = new DeleteIndexRequest("swy_index");AcknowledgedResponse delete = client.indices().delete(request,RequestOptions.DEFAULT);System.out.println("删除索引--------"+delete.isAcknowledged());}

API 文档操作

创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class User {private String name;private int age;
}

编写测试类

添加文档

 // 测试添加文档@Testvoid testAddDocument() throws IOException {User user = new User("swy",18);IndexRequest request = new IndexRequest("swy_index");request.id("1");// 设置超时时间request.timeout("1s");// 将数据放到json字符串request.source(JSON.toJSONString(user), XContentType.JSON);// 客户端发送请求,获取响应结果IndexResponse response = client.index(request,RequestOptions.DEFAULT);System.out.println("添加文档-------"+response.toString());System.out.println("返回状态-------"+response.status());}



获取文档,判断是否存在

 // 测试文档是否存在@Testvoid testExistDocument() throws IOException {// 测试文档的 没有indexGetRequest request= new GetRequest("swy_index","1");// 没有indices()了boolean exist = client.exists(request, RequestOptions.DEFAULT);System.out.println("测试文档是否存在-----"+exist);}


获取文档信息

 // 测试获取文档@Testvoid testGetDocument() throws IOException {GetRequest request= new GetRequest("swy_index","1");GetResponse response = client.get(request, RequestOptions.DEFAULT);System.out.println("测试获取文档-----"+response.getSourceAsString());System.out.println("测试获取文档-----"+response);}

返回的内容和命令得到的是一样的

更新文档信息

 // 测试修改文档@Testvoid testUpdateDocument() throws IOException {User user = new User("托克马克", 200);// 修改是id为1的UpdateRequest request= new UpdateRequest("swy_index","1");request.timeout("1s");request.doc(JSON.toJSONString(user),XContentType.JSON);UpdateResponse response = client.update(request, RequestOptions.DEFAULT);System.out.println("测试修改文档-----"+response);System.out.println("测试修改文档-----"+response.status());}



删除文档

 // 测试删除文档@Testvoid testDeleteDocument() throws IOException {DeleteRequest request= new DeleteRequest("swy_index","1");request.timeout("1s");DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);System.out.println("测试删除文档------"+response.status());}


真实的项目一般都会批量插入数据

 //测试批量添加文档@Testvoid testBulkAddDocument() throws IOException {ArrayList<User> userlist = new ArrayList<User>();userlist.add(new User("swy1",5));userlist.add(new User("swy2",6));userlist.add(new User("swy3",40));userlist.add(new User("swy4",25));userlist.add(new User("swy5",15));userlist.add(new User("swy6",35));// 批量操作的RequestBulkRequest request = new BulkRequest();request.timeout("1s");// 批量处理请求for (int i = 0; i < userlist.size(); i++) {request.add(new IndexRequest("swy_index").id(""+(i+1)).source(JSON.toJSONString(userlist.get(i)),XContentType.JSON));}BulkResponse response = client.bulk(request, RequestOptions.DEFAULT);// response.hasFailures()是否是失败的System.out.println("测试批量添加文档-----"+response.hasFailures());}



批量更新、批量删除 都同理

查询操作

  • SearchRequest 搜索请求
  • SearchSourceBuilder 请求条件构造,
  • highlighter 设置高亮
  • TermQueryBuilder 精确查询
  • MatchAllQueryBuilder 匹配全部查询
  • xxxQueryBuilder 对应了我们刚才看到的所有命令
 // 测试查询文档@Testvoid testSearchDocument() throws IOException {SearchRequest request = new SearchRequest("swy_index");// 构建搜索条件SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 设置了高亮sourceBuilder.highlighter();// term name为swy1的TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "swy1");// 匹配所有// MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();sourceBuilder.query(termQueryBuilder);sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));request.source(sourceBuilder);SearchResponse response = client.search(request, RequestOptions.DEFAULT);// 查询结果都封装在hit中System.out.println("测试查询文档-----" + JSON.toJSONString(response.getHits()));System.out.println("=====================");for (SearchHit documentFields : response.getHits().getHits()) {System.out.println("测试查询文档--遍历参数--" + documentFields.getSourceAsMap());}}

五、实现项目-京东搜索

1.项目搭建

新建模块,springboot,添加依赖


修改elasticsearch版本

<properties><java.version>1.8</java.version><elasticsearch.version>7.6.1</elasticsearch.version>
</properties>

添加依赖

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.70</version>
</dependency>

springboot核心配置

server.port=9090
# 关闭thymeleaf缓存
spring.thymeleaf.cache=false

导入页面素材

链接:https://pan.baidu.com/s/1M5uWdYsCZyzIAOcgcRkA_A
提取码:qk8p

下载链接:https://download.csdn.net/download/weixin_47257749/18351882

把 template、static文件复制到resources目录下

创建 controller

@Controller
public class IndexController {@RequestMapping({"/","/index"})public String index() {return "index";}
}

启动项目,访问测试:http://localhost:9090/

2.爬取数据

ElasticSearch 搜索的数据从哪里获取?

  • 数据库、消息队列、爬虫

爬取数据:获取请求返回的页面信息,筛选出我们想要的数据,jsoup 包可以实现,

jsoup可以解析网页,不可以解析媒体

导入依赖

<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.10.2</version>
</dependency>

创建实体类,用于封装商品信息

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Content {private String title;private String img;private String price;// 可以自己添加属性完善功能
}

写一个工具类,解析网页

@Component
public class HtmlParseUtil {// 测试一下public static void main(String[] args) throws IOException {new HtmlParseUtil().parseJD("vue").forEach(System.out::println);}public static List<Content> parseJD(String keyword) throws IOException {/// 使用前需要联网// 请求urlString url = "http://search.jd.com/search?keyword=" + keyword;// 1.解析网页(jsoup 解析返回的对象是浏览器Document对象)Document document = Jsoup.parse(new URL(url), 30000);// 使用document可以使用在js对document的所有操作// 2.获取元素(通过id),id自己查网页Element j_goodsList = document.getElementById("J_goodsList");// 3.获取J_goodsList ul 每一个Elements lis = j_goodsList.getElementsByTag("li");// System.out.println(lis);// 4.获取li下的 img、price、name// list存储所有li下的内容List<Content> contents = new ArrayList<Content>();for (Element li : lis) {// 由于网站图片使用懒加载,将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(contents);// 5.返回 listreturn contents;}
}


抓取的数据交给 elasticsearch 即可用于 es 实现搜索

PS:好好珍惜,爬了一次,再就爬不进去了,getElementById 拿到的只剩 null 了

3.业务编写

添加 elasticsearch 配置类

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

在 elasticsearch 后台管理页面创建一个空索引 jd_goods

创建业务service

@Service
public class ContentService {@Autowiredprivate RestHighLevelClient restHighLevelClient;// 1.解析数据放入 es 中public Boolean parseContent(String keywords) throws Exception {List<Content> contents = new HtmlParseUtil().parseJD(keywords);// 把查询的数据放入es中BulkRequest bulkRequest = new BulkRequest();bulkRequest.timeout("2s");for (int i = 0;i < contents.size();i++) {bulkRequest.add(new IndexRequest("jd_goods").source(JSON.toJSONString(contents.get(i)), XContentType.JSON));}BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);return !bulk.hasFailures();}// 2.到es中查询获取这些数据public List<Map<String, Object>> searchPage(String keyword, int pageNo, int pageSize) throws IOException {if (pageNo <= 1) {pageNo = 1;}// 条件搜索SearchRequest searchRequest = new SearchRequest("jd_godds");SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();// 分页sourceBuilder.from(pageNo);sourceBuilder.size(pageSize);// 精准匹配TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keyword);sourceBuilder.query(termQueryBuilder);sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));// 执行搜索searchRequest.source(sourceBuilder);SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 解析结果ArrayList<Map<String, Object>> list = new ArrayList<>();for (SearchHit documentFields : searchResponse.getHits().getHits()) {list.add(documentFields.getSourceAsMap());}return list;}
}

创建业务controller

@RestController
public class ContentController {@Autowiredprivate ContentService contentService;// 先从京东抓取数据并存入es中@GetMapping("/parse/{keyword}")public Boolean parse(@PathVariable("keyword") String keywords) throws Exception {return contentService.parseContent(keywords);}// 在es中搜索数据返回结果@GetMapping("/search/{keyword}/{pageNo}/{pageSize}")public List<Map<String, Object>> search(@PathVariable("keyword") String keyword,@PathVariable("pageNo") int pageNo,@PathVariable("pageSize") int pageSize) throws IOException {return contentService.searchPage(keyword, pageNo, pageSize);}
}

测试成功后,现拿到了数据,还差前端页面展示,使用 Vue

4.前端页面展示

下载vue相关文件

随便找一处空目录,进入目录cmd,执行命令

npm install vue
npm install axios

找到 node_modules\vue\dist 目录下 vue.js,node_modules\axios\dist 目录下 axios.js

复制到我们项目的静态目录中

改造我们 index.html 页面,实现前后端分离

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<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.title"></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.js}"></script>
<script th:src="@{/js/axios.js}"></script>
<script>new Vue({el:"#app",data:{"keyword": '', // 搜索的关键字"results":[] // 后端返回的结果},methods:{searchKey(){var keyword = this.keyword;console.log(keyword);axios.get('search/'+keyword+'/0/20').then(response=>{console.log(response.data);this.results=response.data;})}}});
</script>
</body>
</html>

5.高亮显示

修改 service业务层,基于方法2的代码,我们创建方法3

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

controller 将接受方法修改为高亮业务

 // 在es中搜索数据返回结果@GetMapping("/search/{keyword}/{pageNo}/{pageSize}")public List<Map<String, Object>> search(@PathVariable("keyword") String keyword,@PathVariable("pageNo") int pageNo,@PathVariable("pageSize") int pageSize) throws IOException {return contentService.highlightSearch(keyword, pageNo, pageSize);}

修改 index.html 页面,使其可以解析高亮标签

<!--标题-->
<p class="productTitle"><a v-html="result.title"></a>
</p>

2021最新版 ElasticSearch 7.6.1 教程详解 爬虫jsoup+es模拟京东搜索(狂神说)相关推荐

  1. Spring MVC 教程详解 个人总结 复习必备 面试宝典 狂神笔记

    文章目录 一.MVC 模式 1.什么是 MVC 2.Servlet MVC 小结 二.Spring MVC 1.Spring MVC 概念 为什么学习 Spring MVC 中央控制器 Dispatc ...

  2. es springboot 不设置id_es(elasticsearch)整合SpringCloud(SpringBoot)搭建教程详解

    注意:适用于springboot或者springcloud框架 1.首先下载相关文件 2.然后需要去启动相关的启动文件 3.导入相关jar包(如果有相关的依赖包不需要导入)以及配置配置文件,并且写一个 ...

  3. 【JavaEE】 IntelliJ IDEA 2022.2最新版Tomcat导入依赖详细教程全解及创建第一个Servlet程序

    目录 一.软件资源 二.放置settings.xml文件 三.创建项目 四.引入依赖 ​五.创建目录 六.编写代码 写在前面:☞What is Servlet? Servlet其实是一种实现动态页面的 ...

  4. Windows系统下nodejs、npm、express的下载和安装教程详解

    这篇文章主要介绍了Windows系统下nodejs.npm.express的下载和安装教程详解,非常不错,具有参考借鉴价值,需要的朋友可以参考下 1. node.js下载 首先进入http://nod ...

  5. linux添加nginx,linux下安装Nginx1.16.0的教程详解

    因为最近在倒腾linux,想安装新版本的nginx,找了一圈教程没有找到对应的教程,在稍微倒腾了一会之后终于成功的安装了最新版. 服务器环境为centos,接下来是详细步骤: 安装必要依赖插件 ? 创 ...

  6. mysql5.7.14安装版_MySql5.7.14安装教程详解(解压版)_MySQL

    下面进入正式的教程: 第一步:下载最近的MySQL文件并且解压: 下载最新版的MySQL–mysql-5.7.12下载地址 将下载到的文件解压缩到自己喜欢的位置,例如我自己的位置是D:\MySQL\m ...

  7. centos 编译安装 mysql_CentOS7编译安装MySQL5.7.24的教程详解

    安装依赖 (1)cmake是新版MySQL的编译工具 sudo yum install gcc gcc-c++ pcre pcre-devel openssl openssl-devel sudo y ...

  8. 戴尔台式计算机怎么安装的,戴尔Dell电脑U盘安装台式机win10系统教程详解

    最近有位戴尔Dell电脑用户,在使用电脑的时候,因为操作失误导致Windows文件出现问题,需要重装系统才可以解决.因此,大白菜整理了一些u盘重装系统的资料,下面就来看看戴尔Dell电脑U盘安装台式机 ...

  9. linux surface pro 4 driver,重置出错?微软Win10平板Surface Pro 4重装系统教程详解

    Surface Pro 4系统重置出错该怎么办? Surface Pro 4无法启动该怎办? Surface Pro 4平板如何重装Win10系统? 在Win10刚刚发布时,很多用户在升级Window ...

最新文章

  1. linux和java_java内存和linux关系
  2. 4 在vCenter Server安装View Composer组件
  3. 深入浅出Yolov5之自有数据集训练超详细教程
  4. jsp jdbc mysql增删改查_使用JSP+SERVLET+JDBC实现对数据库的增删改查(详细)
  5. 【牛客 - 1080B】tokitsukaze and Hash Table(STLset,并查集,Hash)
  6. p2p-如何拯救k8s镜像分发的阿喀琉斯之踵?
  7. Linux第二次作业
  8. BZOJ 2660 (BJOI 2012) 最多的方案
  9. Linux部署安装JDK
  10. SQL语言:DQL,DML,DDL,DCL
  11. matlab寻找直线_matlab寻找直线_Matlab 霍夫变换 ( Hough Transform) 直线检测
  12. ckfinder php 漏洞,编辑器漏洞
  13. pushplus通过企业微信应用给微信发送消息教程
  14. Linux操作系统的基本命令
  15. c语言课程设计--打飞碟源代码,飞碟游戏(飞碟游戏规则)
  16. 网络协议-语义、语法和时序
  17. Ultra-Fast Mathematician
  18. Linux usb设备驱动(2)--- usbmouse.c 源码分析
  19. 磁盘性能分析Disk
  20. PLSQL存储过程定时作业(DBMS_JOB)

热门文章

  1. 你要创业那中国即将发生的重大变化绝对要知道
  2. 基于微信美食菜谱小程序系统设计与实现 开题报告
  3. 【算法自由之路】前缀树 桶排序之计数排序和基数排序
  4. Markdown中图片转PDF之后图片不能显示的问题(已解决)
  5. 区分 Nginx 的 proxy_buffering 与 proxy_cache
  6. tp5 创建直播频道
  7. 树莓派4B与 OpenMediaVault 6 搭建 NAS(一)
  8. 联想小新pro13笔记本外接显示屏没信号
  9. linux中文件的归档压缩
  10. 鸿蒙系统电视k歌,华为电视怎么k歌?全网最详细的教程在这里