ElasticSearch安装
声明 : JDK1.8!ElasticSearch 客户端,界面工具!
基于Java开发,ElasticSearch的版本和Java的核心jar包对应着!
下载地址
下载地址:https://www.elastic.co/cn/downloads/elasticsearch
在这里是安装windows版的
1、解压
2、熟悉目录
bin 启动文件
config 配置文件
log4j2.properties 日志配置文件
jvm.options java 虚拟机相关的配置
elasticsearch.yml elasticsearch 的配置文件! 默认端口9200
lib 相jar包
logs 日志
modules 功能模块
plugins 插件
3、启动,访问9200
4、访问测试
安装可视化界面elasticsearch head
1、下载地址:https://github.com/NavyShu/Elasticsearch-Head
需要有配置node环境
2、启动
npm install
npm run start
3、连接测试发现,存在跨域问题:配置ElasticSearch
http.cors.enabled: true
http.cors.allow-origin: "*"
4、重启Elasticserach,然后再次连接
安装Kibanna
官网:https://www.elastic.co/cn/kibana/
Kibanna版本要和ElasticSearch一致!
下载完后,解压需要一些时间!
好处 : ELK基本上都是拆箱即用!
启动测试:
1、解压后的目录
2、启动
3、访问
4、开发工具!(Post、curl、head、谷歌浏览器测试!)
我们之后的所有操作都在在这里编写的!
5、汉化!
i18n.locale: "zh-CN"
重新启动Kibana
IK分词器插件
1、下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
2、下载完毕后,放入到我们的elasticsearch插件即可!
3、重启观察elasticsearch,可以看到ik分词器被加载了!
4、elasticsearch-plugin 可以通过这个命令来查看记载进来的插件
5、使用kibana测试!
查看不同的分词效果
其中 ik_smart 为最少切分
ik_max_word为最细粒度划分!穷尽词库的可能!字典
我们输入超级喜欢狂神说Java
发现问题:狂神说被拆开了
这种自己需要的词,需要自己加到我们的分词器的字典中!
ik 分词器增加自己的配置!
重启elasticsearch
再次测试一下狂神说,看下效果!
我们需要自己配置分词的时候就可以自定义dic文件然后去配置即可!
Rest风格说明
一种软件架构风格,而不是标准,只是提供了一组设计原则和约束条件。它主要是用于客户端和服务器交互的软件。基于这个风格设计的软件更简洁,更有层次,更易于实现缓存等机制。
method | url地址 | 描述 |
---|---|---|
PUT | localhost:9200/索引名称/类型名称/文档id | 创建文档(指定文档id) |
POST | localhost:9200/索引名称/类型名称 | 创建文档(随机文档id) |
POST | localhost:9200/索引名称/类型名称/文档id/_update | 修改文档 |
DELETE | localhost:9200/索引名称/类型名称/文档id | 删除文档id |
GET | localhost:9200/索引名称/类型名称/文档id | 查询文档(通过文档id) |
POST | localhost:9200/索引名称/类型名称/_search | 查询所有数据 |
关于索引的基本操作
1、创建一个索引!
PUT /索引名/~类型名~/文档id
{请求体}
完成了自动增加索引!数据也成功添加了!
3、字段类型
- 字符串类型:text、keyword
- 数值类型:long、integer、short、byte、double、float、half float、scaled float
- 日期类型:date
- te布尔值类型:boolean
- 二进制类型:binary
等等…
4、指定字段的类型
PUT /test2
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "long"
},
"birthday":{
"type": "date"
}
}
}
}
获得这个规则!可以通过GET请求获取具体信息!
5、查看默认的信息!
如果自己的文档字段没有指定,那么elasticsearch就会给我吗默认配置字段类型!
扩展:通过命令elasticsearch索引情况!通过GET _cat可以获得elasticsearch当前的很多信息!
修改 提交使用PUT覆盖值或者使用POST修改!
使用PUT覆盖的方法
使用POST修改的方法
删除索引!
通过DELETE命令实现删除、根据你的请求来判断是还是删除文档记录!
#删除索引
DELETE /索引名称
#删除文档记录
DELETE /索引名称/类型名称/文档id
使用RESTFUL风格是Elasticsearch推荐大家使用的!
关于文档的基本操作(重点)
基本操作
1、添加数据
PUT /me/user/1
{
"name": "me",
"age": 1,
"desc": "一顿操作猛如虎,一看工资2500",
"tage": ["技术宅","温暖","直男"]
}
2、获取数据 GET
3、更新数据 PUT
4、POST _update,推荐使用这种更新方式!
简单的搜索!
GET me/user/1
简单的条件查询!
复杂操作搜索 select (排序, 分页, 高亮, 模糊查询, 精确查询!)
输出结果,不想要那么多!
GET me/user/_search
{
"query": {
"match_phrase": {
"name": "狂神"
}
},
"_source": ["name","desc"]
}
我们之后使用Java操作ElasticSearch,所有的方法和对象都是这里面的key!
排序
- asc : 降序
- desc : 升序
分页查询
"from": 从第几个数据开始
"size": 返回多少个数据(单页数据)
数据下标是从0开始的,和学的所有数据结构是一样的!!!
/search/{current}/{pagesize}
布尔值查询
GET me/user/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "狂神说"
}
},
{
"match": {
"age": 23
}
}
]
}
}
}
must(and),所有的条件都要符合比如Sql语句中 where id = 1 and name = xxx
should(or),所有的条件都要符合比如Sql语句中 where id = 1 or name = xxx
must_not (not)
过滤器 filter
- gt 大于
- gte 大于等于
- lt 小于
- lte 小于等于
匹配多个条件
精确查询
term 查询是直接通过倒排索引指定的词条进程去精确查找的!
关于分词:
- term ,直接查询精确的
- match ,会使用分词器解析!(先分析文档,然后在通过分析的文档进行查询!)
两个类型 text keyword
多个值匹配的精确查询
高亮查询
这些其实MySQL页可以做,只是MySQL效率比较低!
- 匹配
- 按照条件匹配
- 精确匹配
- 区间范围匹配
- 匹配字段过滤
- 多条件查询
- 高亮查询
集成SpringBoot
找官方文档
1、找到原生的依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.15.2</version>
</dependency>
2、找对象
3、分析这个类中的方法即可!
配置基本的项目
一定要保证我们导入的依赖和我们的es版本一致
本人用的SpringBoot版本是2.6.1 ElasticSearch版本是7.15.2
如果出现版本不一致需要自定义版本使和ElasticSearch版本一致
配置一个Config
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
// IP 端口 协议
new HttpHost("127.0.0.1", 9200, "http")));
return client;
}
}
// 可以配置多个或单个ElasticSearch
// 官方的配置代码
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http"),
new HttpHost("localhost", 9201, "http")));
具体API测试
1、创建索引
2、判断索引是否存在
3、删除索引
4、创建文档
5、操作文档
@Autowired
@Qualifier("restHighLevelClient")
private RestHighLevelClient client;
// 测试索引的创建
@Test
void testCreateIndex() throws IOException {
// 1. 创建索引请求
CreateIndexRequest request = new CreateIndexRequest("me_index");
// 2、客户端执行请求 IndicesClient,请求后获得响应
CreateIndexResponse createIndexResponse =
client.indices().create(request, RequestOptions.DEFAULT);
System.out.println(createIndexResponse);
}
// 测试获取索引,判断其是否存在
@Test
void testExistIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("me_index");
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
}
// 测试删除索引
@Test
void testDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("me_index");
// 删除
AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(delete.isAcknowledged());
}
// 测试添加文档
@Test
void testAddDocument() throws IOException {
// 创建对象
User user = new User("狂神说", 3);
// 创建亲求
IndexRequest request = new IndexRequest("me_index");
// 规则 PUT /me_index/_doc/1
request.id("1");
request.timeout(TimeValue.timeValueSeconds(1));
request.timeout("1s");
// 将我们的数据放入请求 json
request.source(JSON.toJSONString(user), XContentType.JSON);
// 客户端发送请求,获得响应结果
IndexResponse index = client.index(request, RequestOptions.DEFAULT);
System.out.println(index);
System.out.println(index.status());
}
// 获取文档,判断是否存在
@Test
void testIsExists() throws IOException {
GetRequest getRequest = new GetRequest("me_index", "1");
// 不获取返回的 _source 的上下文
getRequest.fetchSourceContext(new FetchSourceContext(false));
getRequest.storedFields("_none_");
boolean exists = client.exists(getRequest, RequestOptions.DEFAULT);
System.out.println(exists);
}
// 获取文档的信息
@Test
void testGetDocument() throws IOException {
GetRequest getRequest = new GetRequest("me_index", "1");
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
System.out.println(getResponse.getSourceAsString()); // 打印文档的内容
System.out.println(getResponse); // 返回的全部内容和命令是一样的
}
// 更新文档的信息
@Test
void testUpdateRequest() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("me_index", "1");
updateRequest.timeout("1s");
User user = new User("狂神说Java", 18);
updateRequest.doc(JSON.toJSONString(user), XContentType.JSON);
UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(updateResponse.status());
}
// 删除文档记录
@Test
void testDeleteRequest() throws IOException {
DeleteRequest deleteRequest = new DeleteRequest("me_index", "1");
deleteRequest.timeout("1s");
DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(deleteResponse.status());
}
// 批量插入数据!
@Test
void testBulkRequest() throws IOException {
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("10s");
ArrayList<User> userList = new ArrayList<>();
userList.add(new User("me1", 1));
userList.add(new User("me2", 1));
userList.add(new User("me3", 1));
userList.add(new User("kuangshen1", 3));
userList.add(new User("kuangshen2", 3));
userList.add(new User("kuangshen3", 3));
// 批处理请求
for (int i = 0; i < userList.size(); i++) {
// 批量更新和批量删除,就在这里修改对应的请求就可以了
bulkRequest.add(new IndexRequest("me_index")
.id("" + (i + 1))
.source(JSON.toJSONString(userList.get(i)), XContentType.JSON));
}
BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulkResponse.hasFailures()); // 是否失败,返回 false 代表成功!
}
// 查询
// SearchRequest 搜索请求
// SearchSourceBuilder 条件构造
// HighlightBuilder 构建高亮
// TermQueryBuilder 精确查询
// MatchAllQueryBuilder
@Test
void testSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest("me_index");
// 构建搜索条件
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
// 查询条件,我们可以使用 QueryBuilders 工具来实现
// QueryBuilders.termQuery 精确
// QueryBuilders.matchAllQuery 匹配所有
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("naem", "me1");
//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());
}
}
实战
确保ElasticSearch保持开启状态
最终效果
创建一个新的项目
爬虫
数据问题?数据库获取,消息队列中获取,都可以成为数据源,爬虫!
爬取数据:(获取请求返回的页面信息,筛选出我们想要的数据就可以了!)
jsoup包
<!--jSoup解析网页-->
<!--解析网页jsoup-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.3</version>
</dependency>
// 获取请求 https://search.jd.com/Search?keyword=java
// 前提,需要联网!
String url = "https://search.jd.com/Search?keyword=java";
// 解析网页。(Jsoup返回DOcument就是浏览器Document对象)
Document document = Jsoup.parse(new URL(url), 30000);
// 所有你在js中可以使用的方法,这里都能使用!
Element element = document.getElementById("J_goodsList");
// 获取所有的lj元素
Elements elements = element.getElementsByTag("li");
// 获取元素中的内容,这里el就是每一个li标签了!
for (Element el : elements) {
// 关于这种图片特别多的网站,所有图片都是延迟加载的!
// 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();
System.out.println("===================");
System.out.println(img);
System.out.println(price);
System.out.println(title);
}
前后端分离
搜索高亮
完成后端效果
pojo类代码
@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("心理学").forEach(System.out::println);
}
public List<Content> parseJD(String keywords) throws IOException {
String url = "https://search.jd.com/Search?keyword=" + keywords;
Document document = Jsoup.parse(new URL(url), 30000);
Element element = document.getElementById("J_goodsList");
Elements elements = element.getElementsByTag("li");
ArrayList<Content> goodsList = new ArrayList<>();
// 获取元素中的内容,这里el就是每一个li标签了!
for (Element el : elements) {
// 关于这种图片特别多的网站,所有图片都是延迟加载的!
// 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();
Content content = new Content();
content.setTitle(title);
content.setImg(img);
content.setPrice(price);
goodsList.add(content);
}
return goodsList;
}
}
业务层代码
// 业务编写
@Service
public class ContentService {
@Autowired
private RestHighLevelClient restHighLevelClient;
// 1、解析数据放入 ElasticSearch 索引中
public Boolean parseContent(String keyWord) throws IOException {
List<Content> contents = new HtmlParseUtil().parseJD(keyWord);
// 把查询到的数据放入ElasticSearch中
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("2m");
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、获取这些数据实现搜索功能
public List<Map<String,Object>> searchPageHighlightBuilder(String keyWord,int pageNo,int pageSize) throws IOException {
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);
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 执行搜索
searchRequest.source(sourceBuilder);
SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 解析结果
ArrayList<Map<String, Object>> list = new ArrayList<>();
for (SearchHit documentFields : response.getHits().getHits()) {
list.add(documentFields.getSourceAsMap());
}
return list;
}
// 3、获取这些数据实现高亮功能
public List<Map<String,Object>> searchPage(String keyWord,int pageNo,int pageSize) throws IOException {
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);
sourceBuilder.query(termQueryBuilder);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.requireFieldMatch(false); // 多个高亮显示
highlightBuilder.preTags("<span style='color: red;'>");
highlightBuilder.postTags("</span>");
sourceBuilder.highlighter(highlightBuilder);
// 执行搜索
searchRequest.source(sourceBuilder);
SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 解析结果
ArrayList<Map<String, Object>> list = new ArrayList<>();
for (SearchHit hit : response.getHits().getHits()) {
// 解析高亮的字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
HighlightField title = highlightFields.get("title");
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
if (title != null){
Text[] fragments = title.fragments();
String n_title = "";
for (Text text : fragments) {
n_title += text;
}
sourceAsMap.put("title",n_title);
}
list.add(sourceAsMap);
}
return list;
}
}
控制层代码
// 请求编写
@RestController
public class ContentController {
@Autowired
private ContentService contentService;
@GetMapping("/parse/{keyword}")
public Boolean parse(@PathVariable("keyword") String keyWord) throws IOException {
return contentService.parseContent(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 IOException {
return contentService.searchPage(keyWord,pageNo,pageSize);
}
}
原视频地址:https://www.bilibili.com/video/BV17a4y1x7zq?spm_id_from=333.999.0.0