ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站

ElasticSearch

安装elasticsearch

官网:elastic.co

https://www.elastic.co/cn/downloads/elasticsearch

官网下载巨慢,下载

目录

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第1张图片

启动,访问9200:
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第2张图片

访问9200接口:
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第3张图片

安装elasticsearch-head:

git clone git://github.com/mobz/elasticsearch-head.git

cd elasticsearch-head

npm install

npm run start

open http://localhost:9100/

存在一个9200和9100的跨域问题!

点击链接,报跨域的错!

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第4张图片

修改elasticsearch中config下的yaml配置文件,修改:

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第5张图片

重启,连接成功:

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第6张图片

把索引当做一个数据库!可以建立索引(库),文档(库中的数据!)

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第7张图片
后面所有的查询,查询功能在Kibana中做!

了解ELK

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第8张图片

安装Kibana

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第9张图片

好处:ELK基本上都是拆箱即用

启动测试:点bin下的kibana

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第10张图片

默认的kibana端口为5601

PS:注意elasticsearch和kibana的版本必须一致 否则出错!

开发工具:(Post curl head 谷歌浏览器插件测试)

汉化,修改kibana yaml中配置 重启,

ES核心概念

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

概述:

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

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

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第11张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第12张图片

类型示例:

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第13张图片

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第14张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第15张图片

IK分词器

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第16张图片
安装https://github.com/medcl/elasticsearch-analysis-ik/releases

放在elasticsearch的plugin(插件)下

重启观察ES

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第17张图片

关于elasticsearch中的命令 如elasticsearch-plugin:

命令行输入:

elasticsearch-plugin list

在kibana中测分词器:

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第18张图片

我们输入 超级喜欢狂神或java

发现问题:狂神说被拆开了!

这种自己需要的词,需要自己加到我们的分词器字典中!

向ik分词器增加自己的配置

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第19张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第20张图片

重启es、kibana

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第21张图片

再次测试一下狂神说,看下效果!ik_max_word最细粒度划分

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第22张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第23张图片

以后自定义dic、导入即可!

索引引擎里面最重要的首先就是分词

Rest风格说明

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

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第24张图片

基础测试

  1. 创建一个索引!也是在kibana终端中试用版PUT命令
 PUT /test1/type1/1{  
 "name": "狂神说", 
 "age": "3"
 } 

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第25张图片
完成了自动增加索引!数据也成功的添加了,这就是可以当做数据库看的原因。。

  1. 那么name字段用不用指定类型呢?毕竟我们关系型数据库 是需要指定类型的啊!

    • 字符串类型 text、keyword
    • 数值类型 long、integer、short、byte、double、float、scaled float
    • 日期类型date
    • te布尔值类型boolean
    • 二进制类型binary
    • 等等。。。
  2. 指定字段的类型——创建规则

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第26张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第27张图片

GET命令得到库的信息:

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第28张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第29张图片

  1. 查看默认的信息

通过命令get _cat/可以获得es当前的很多信息!
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第30张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第31张图片

修改 提交还是使用PUT即可!然后覆盖!最新办法

曾经的办法:

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第32张图片

现在的方法 使用POST:

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第33张图片

删除索引——DELETE
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第34张图片

通过DELETE命令实现删除、根据你的请求来判断是删除索引还是删除文档记录!

使用RESTFUL风格是我们ES推荐大家使用的!

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

基本操作:

添加数据

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第35张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第36张图片

更新数据,将小明改成小红

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第37张图片

PUT更新数据

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第38张图片

version代表被改变的次数

Post,_update自由度更高,PUT必须一次性修改一个个体的全部内容,但是Post可以选择部分修改!

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第39张图片

这个和PUT无异,要在后加_update

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第40张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第41张图片

简单的搜索:

GET Kuangshen/user/_search?q=name:狂神说

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第42张图片

简单的条件查询,可以根据默认的映射规则,产生基本的查询!

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第43张图片

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

GET kuangshen/_search

输出:

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 5,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "kuangshen",
        "_type" : "user",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "name" : "狂神说",
          "age" : 23,
          "desc" : "一顿操作猛如虎,一看工资2500",
          "tags" : [
            "技术宅",
            "温暖",
            "直男"
          ]
        }
      },
      {
        "_index" : "kuangshen",
        "_type" : "user",
        "_id" : "2",
        "_score" : 1.0,
        "_source" : {
          "name" : "张三",
          "age" : 3,
          "desc" : "法外狂徒",
          "tags" : [
            "交友",
            "旅游",
            "渣男"
          ]
        }
      },
      {
        "_index" : "kuangshen",
        "_type" : "user",
        "_id" : "3",
        "_score" : 1.0,
        "_source" : {
          "name" : "李四",
          "age" : 30,
          "desc" : "不知道如何形容",
          "tags" : [
            "篮球",
            "IT",
            "型男"
          ]
        }
      },
      {
        "_index" : "kuangshen",
        "_type" : "user",
        "_id" : "4",
        "_score" : 1.0,
        "_source" : {
          "name" : "王五",
          "age" : 32,
          "desc" : "就是一个屌丝",
          "tags" : [
            "羽毛球",
            "钢琴",
            "渣男"
          ]
        }
      },
      {
        "_index" : "kuangshen",
        "_type" : "user",
        "_id" : "5",
        "_score" : 1.0,
        "_source" : {
          "name" : "狂神说Java8",
          "age" : 34,
          "desc" : "就是一个大帅哥",
          "tags" : [
            "围棋",
            "小提琴",
            "暖男"
          ]
        }
      }
    ]
  }
}
GET kuangshen/_search
{
  "query": {
    "match": {
      "name": "狂神"
    }
  }
}

输出:

{
  "took" : 9,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.9034984,
    "hits" : [
      {
        "_index" : "kuangshen",
        "_type" : "user",
        "_id" : "1",
        "_score" : 1.9034984,
        "_source" : {
          "name" : "狂神说",
          "age" : 23,
          "desc" : "一顿操作猛如虎,一看工资2500",
          "tags" : [
            "技术宅",
            "温暖",
            "直男"
          ]
        }
      },
      {
        "_index" : "kuangshen",
        "_type" : "user",
        "_id" : "5",
        "_score" : 1.6534033,
        "_source" : {
          "name" : "狂神说Java8",
          "age" : 34,
          "desc" : "就是一个大帅哥",
          "tags" : [
            "围棋",
            "小提琴",
            "暖男"
          ]
        }
      }
    ]
  }
}

hit: 索引和文档信息

查询的结果总数

然后就是查询出来的具体文档

数据中的东西都可以遍历出来

分数:通过score判断谁更加符合结果

指定字段查询:

GET kuangshen/_search
{
  "query": {
    "match": {
      "name": "狂神"
    }
  },
  "_source": ["name","desc"]
}

之后使用java操作es,所有的方法和对象就是这里面的key!

复杂操作:

排序

order中的desc降序、asc升序;按照age

GET kuangshen/_search
{
  "query": {
    "match": {
      "name": "狂神"
    }
  }
  ,"sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]
}

分页

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

From:从第几个数据开始,返回多少条数据(单页面的数据)

数据下标还是从0开始的,和学的所有数据结构是一样的

/search/{current}/{pagesize}

布尔值查询

通过布尔值进行更加精确的查询:多条件精确查询

must命令(相当于mysql的and),即所有条件要同时符合;如果将must改为should(相当于or),则只要满足其一即可;

类似的 must not 查询不是。。

GET kuangshen/user/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "狂神说"
          }
        },
        {
          "match": {
            "age": 23
          }
        }
      ]
    }
  }
}

过滤器(filter)

筛选age范围

GET kuangshen/user/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "name": "狂神"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "age": {
              "gte": 10,
              "lte": 40
            }
          }
        }
      ]
    }
  }
}
  • gt 大于
  • gte 大于等于
  • lt 小于
  • lte 小于等于

匹配多个条件

匹配出tags里面只要包含有男的,同时按照上到下分值高到低排列

GET kuangshen/user/_search
{
  "query": {
    "match": {
      "tags": "男 技术"
    }
  }
}

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第44张图片

多个条件使用空格隔开

只要满足其中一个结果即可以被查出

这个时候可以通过分值基本的判断

精确查询

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

关于分词:

term,直接查询精确的

match:会使用分词器解析!(先分析文档,然后再通过分析的文档进行查询!)

两个字段类型text keyword

 #新建db
 PUT testdb
 {
   "mappings": {
     "properties": {
       "name":{
         "type": "text"
       },
        "desc":{
          "type": "keyword"
        }      
     }
   }
 }
 #插入两条数据
 PUT testdb/_doc/1
 {
   "name": "狂神说Java name",
   "desc": "狂神说Java name"
 }
 
  PUT testdb/_doc/2
 {
   "name": "狂神说Java name",
   "desc": "狂神说Java name2"
 }

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第45张图片

 GET _analyze
 {
   "analyzer": "keyword",
   "text": "狂神说Java name"
 }
 
  GET _analyze
 {
   "analyzer": "standard",
   "text": "狂神说Java name"
 }

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第46张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第47张图片

由于desc的type是keyword,当做整体去搜索了

总结:keyword字段类型不会被分词器解析!

多个值匹配的精确查询

精确查询多个值

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第48张图片

高亮查询

 GET kuangshen/user/_search
 {
   "query": {
     "match": {
       "name": "狂神说"
     }
   },
   "highlight": {
     "pre_tags": "

", "post_tags": "

", "fields": { "name": {} } } }

默认是标签

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第49张图片

也可以是自定义标签:设置pre_tags、post_tags

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第50张图片

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

集成Springboot

文档

https://www.elastic.co/guide/index.html

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第51张图片

找原生依赖

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第52张图片

初始化

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第53张图片

配置

新建一个empty project ,再创建普通模块

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第54张图片

创建Springboot 模块:

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第55张图片

勾上依赖:

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第56张图片

由于刚刚建的空project,故要陪JDK环境

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第57张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第58张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第59张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第60张图片

问题:一定要保证我们的导入依赖和我们的es版本一致

默认的导入依赖和我们本地的版本不一致!

可以自定义版本依赖,保证一致

新建config、ElasticSearchConfig.java`

package com.kuang.config;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//狂神的spring两步骤:
//1.找对象
//2.放到spring中用
@Configuration
public class ElasticSearchConfig {

    //
    @Bean
    public RestHighLevelClient restHighLevelClient(){
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("localhost", 9200, "http"),
                        new HttpHost("localhost", 9201, "http")));
        return client;
    }
}

源码

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第61张图片

具体测试es api:

package com.kuang;

import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;


//es 高级客户端测试API
@SpringBootTest
class KuangshenEsApiApplicationTests {
	//面向对象来操作
	@Autowired
	@Qualifier("restHighLevelClient")  //这里的qualifier用来指定下面的client为原始的restHighLevelClient
	private RestHighLevelClient client;

	// 测试索引的创建 Request
	@Test
	void testCreateIndex() throws IOException {
		// 1.创建索引请求 相当于kibana中的PUT
		CreateIndexRequest request = new CreateIndexRequest("kuang_index");
		// 2.执行创建请求IndicesClient ,请求后获得响应
		CreateIndexResponse createIndexResponse =
				client.indices().create(request, RequestOptions.DEFAULT);
		System.out.println(createIndexResponse);
	}
  
  //测试获取索引   判断是否存在某索引
	@Test
	void testExistIndex() throws IOException {
		GetIndexRequest request = new GetIndexRequest("kuang_index");
		boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
		System.out.println(exists);
	}

	// 测试删除索引
	@Test
	void testDeleteIndex()  throws IOException {
		DeleteIndexRequest request = new DeleteIndexRequest("testdb2");
		AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
		System.out.println(delete.isAcknowledged());
	}
}

本来应该是private RestHighLevelClient restHighLevelClient;这里为了简便,所以用Qualifier来限定client为restHighLevelClient;

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第62张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第63张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第64张图片

  • 创建索引
  • 判断索引是否存在
  • 删除索引
  • 创建文档
  • crud文档

创建文档

新建一个pojo,放入User.java

package com.kuang.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

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

由于要将我们的数据放入请求 json,故在pom中导入阿里巴巴fastjson

这里是将对象编写为Json,再放入es的request中

编写test类:

@Test
	void testAddDocument(){
		//创建对象
		User user = new User("狂神说",3);
		IndexRequest request = new IndexRequest("kuang_index");

		// 规则 put /kuang_index/_doc/1
		request.id("1");
		request.timeout(TimeValue.timeValueSeconds(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());//对应我们命令返回的状态
	}

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第65张图片

获取文档

//获取文档的信息
	@Test
	void testGetDocument() throws IOException {
		GetRequest getRequest = new GetRequest("kuang_index", "1");
		GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
		System.out.println(getResponse.getSourceAsString());//打印文档的内容
		System.out.println(getResponse);//返回的全部内容和命令是一样的
	}

在这里插入图片描述

更新文档信息

//更新文档的信息
	@Test
	void testUpdateDocument() throws IOException {
		UpdateRequest updateRequest = new UpdateRequest("kuang_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());

	}

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第66张图片

删除文档信息

//删除文档记录
	@Test
	void testDeleteRequest() throws IOException {
		DeleteRequest request = new DeleteRequest("kuang_index", "1");
		request.timeout("1s");

		DeleteResponse deleteResponse = client.delete(request, 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("psz",11));
		userList.add(new User("psz2",12));
		userList.add(new User("psz3",13));
		userList.add(new User("psz4",14));
		userList.add(new User("psz5",15));
		//批处理请求
		for (int i = 0; i < userList.size(); i++) {
			bulkRequest.add(
					new IndexRequest("kuang_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:成功
	}

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第67张图片

查询

小技巧:

一般企业中,会把index名存在utils里面的ESconst.java文件中:

package com.kuang.utils;

public class ESconst {
    public static final String ES_INDEX = "kuang_index";
}
// 查询
// SearchRequest 搜索请求
// SearchSourceBuilder 条件构造
// HighlightBuilder 构建高亮
// TermQueryBuilder 精确查询
// MatchAllQueryBuilder
// xxx QueryBuilder 对应我们刚才看到的命令

	@Test
	void testSearch() throws IOException {
		SearchRequest searchRequest = new SearchRequest(ESconst.ES_INDEX);
		// 构建搜索的条件
		SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
		
		// 查询条件,可以使用querybuilders工具类实现
		// QueryBuilders.termQuery精确匹配
		// QueryBuilders.matchAllQuery匹配所有
		TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("name", "qinjiang1");
//		MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
		sourceBuilder.query(termQueryBuilder);
		//分页
//		sourceBuilder.from();
//		sourceBuilder.size();
		// 设置查询的时间 希望在60s内查出
		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());
		}
	}

JD商城实战

新建Springboot initializr项目

导入es、fastjson等pom下的依赖

爬虫

数据问题?数据库获取,消息队列中获取,都可以成为数据源,或者爬虫

爬取数据:(获取请求返回的页面信息,筛选出我们想要的数据就可以了)

jsoup包:用于解析网页,不能爬电影

新建一个utils包放网页解析的工具类

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第68张图片

本质的请求是:

https://search.jd.com/Search?keyword=java

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第69张图片

所有在Js中的方法这里都可以使用

package com.kun.utils;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

public class HtmlParseUtil {
    public static void main(String[] args) throws IOException {
        // 获取请求 https://search.jd.com/Search?keyword=java
        String url = "https://search.jd.com/Search?keyword=java";
        //解析网页 Jsoup返回的就是浏览器Document对象
        Document document = Jsoup.parse(new URL(url), 30000);
        Element element = document.getElementById("J_goodsList");
        System.out.println(element.html());
          //获取所有的li元素/标签
        Elements elements = element.getElementsByTag("li");
        //获取元素中的内容  eq获取当前第一个元素,获取src属性
        for (Element el : elements) {
            //关于这种图片特别多的网站,所有的图片都是延迟加载的!
            String img = el.getElementsByTag("img").eq(0).attr("src");
            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);
    }
}

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第70张图片

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第71张图片
ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第72张图片

输出结果

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第73张图片

注意:

在图片较多的网站中,图片往往是延迟加载的,注意看图片的属性:

在这里插入图片描述

将获取到的元素 封装成对象,新建pojo,Content.java

package com.kun.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

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

再次封装工具类:

package com.kun.utils;

import com.kun.pojo.Content;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@Component //丢进Springboot中
public class HtmlParseUtil {
    public static void main(String[] args) throws Exception {
        new HtmlParseUtil().parseJD("java").forEach(System.out::println);
    }


    public List<Content> parseJD(String keywords) throws Exception {
        // 获取请求 https://search.jd.com/Search?keyword=java
        String url = "https://search.jd.com/Search?keyword=" + keywords;
        //解析网页 Jsoup返回的就是浏览器Document对象
        Document document = Jsoup.parse(new URL(url), 30000);
        //所有你在js中可以使用的方法,这里都可以使用
        Element element = document.getElementById("J_goodsList");
//        System.out.println(element.html());
        //获取所有的li元素/标签
        Elements elements = element.getElementsByTag("li");

        //封装对象
        ArrayList<Content> goodsList = new ArrayList<Content>();
        //获取元素中的内容  eq获取当前第一个元素,获取src属性
        for (Element el : elements) {
            //关于这种图片特别多的网站,所有的图片都是延迟加载的!
            String img = el.getElementsByTag("img").eq(0).attr("src");
            String price = el.getElementsByClass("p-price").eq(0).text();
            String title = el.getElementsByClass("p-name").eq(0).text();

            Content content = new Content();
            content.setImg(img);
            content.setPrice(price);
            content.setTitle(title);
            goodsList.add(content);
//            System.out.println("===============================");
//            System.out.println(img);
//            System.out.println(price);
//            System.out.println(title);
        }
        return goodsList;
    }
}

编写业务层service:

package com.kun.service;

import com.alibaba.fastjson.JSON;
import com.kun.pojo.Content;
import com.kun.utils.HtmlParseUtil;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.util.List;
//业务编写
@Service
public class ContentService {
    @Autowired
    private 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("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();
    }
}

测试:由于这个文件中又Autowire,所以就算建了主函数psvm,也不能测,必须启动服务;

直接用controller来测:

package com.kun.controller;


import com.kun.service.ContentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ContentController {
  @Autowired
    private ContentService contentService;

  @GetMapping("/parse/{keyword}")
  public Boolean parse(@PathVariable("keyword") String keywords) throws Exception {
      return  contentService.parseContent(keywords);
  }
}

再在业务层中实现搜索功能:

// 2. 获取数据实现搜索功能
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));
    //执行搜索
    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来测:

@GetMapping("/parse/{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);
  }

前后端分离

先在一个任意包下npm install vue生成vue文件,将内部一些js包导入Springboot项目中;axios.min.js;vue.min.js

在这里插入图片描述

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第74张图片

在前端每个商品中得到result值

ElasticSearch 7.8.1教程(from b站狂神)+JD商城仿站_第75张图片

搜索高亮

修改业务层ContentService.java

 //3. 新增高亮功能
    public List<Map<String,Object>> searchPageHighlightBuilder(String keyword,int pageNo,int pageSize) throws Exception {
        parseContent(keyword);

        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));

        //match匹配 可以支持中文搜索
        MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("title", keyword);
        sourceBuilder.query(matchQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));//超时
        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title"); //高亮的字段
        highlightBuilder.requireFieldMatch(false);//如果一句里面有多个关键词高亮,则只显示第一个
        highlightBuilder.preTags("");
        highlightBuilder.postTags("");
        sourceBuilder.highlighter(highlightBuilder);
        //执行搜索
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //解析结果
        ArrayList<Map<String, Object>> list = new ArrayList<>();
        for (SearchHit hit : searchResponse.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;
    }

效果

你可能感兴趣的:(Java,前端,elasticsearch,java,vue.js,elasticsearch)