一.相关概念
ES资料地址:链接:https://pan.baidu.com/s/1PT3jLvCksOhq7kgAKzQm7g 提取码:s824
1.什么是Elasticsearch
ES是一个高扩展分布式搜索和分析引擎。是对lucene的一个封装,隐藏了lucene的复杂操作,对外提供简易的api接口(RESTfUL API)。
2.Elasticsearch特点
(1)可以作为一个大型分布式集群技术(默认集群名:elasticsearch),处理BP级数据,也可以单机运行。
(2)将全文检索、数据分析以及分布式技术融为一体。
(3)对用户而言,开箱即用(解压就可以用)。
(4)是对传统数据库的一个补充:全文检索,同义词处理,相关度排名,复杂数据分析,海量数据近实时处理。(近实时:两个意思,从写入数据到数据可以被搜索到有一个小延迟(大概1秒);基于es执行搜索和分析可以达到秒级)。
3.原理:全文检索,倒排索引
(1)全文检索:是指计算机索引程序通过扫描文章的每一个词,对每个词建立索引,记录该词在文章出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找结果反馈给用户的检索方式。
(2)倒排索引:文章落库时,会先进行分词,每个词会建立索引,记录词与文章的映射关系。当查找是,将查找语句进行分词,分好的词再根据之前的词语文章映射关系去查找文章的过程就是倒排索引。
4.elasticsearch与数据库的类比
关系型数据库(比如Mysql) | 非关系型数据库(Elasticsearch) |
---|---|
数据库Database | 索Index |
表Table | 类型Type |
数据行Row | 文档Document (默认_doc)不需要写 |
数据列Column | 字段Field |
约束 Schema | 映射Mapping |
Mapping(字段类型):数据如何存放到索引对象上,需要有一个映射配置,包括:数据类型、是否存储、是否分词等。
5. solr和ES对比
- Solr利用Zookeeper进行分布式管理,而ES自带分布式协调管理功能。
- Solr支持更多格式数据,比如JSON,XML,CSV;而ES仅支持json格式。
- Solr官方提供的功能更多,而ES注重核心功能,高级功能多用第三方插件提供,例如图形化界面需要Kibana友好支持。
- Solr查询快,但更新索引慢(即删除慢)。用于电商查询多的应用。ES建立索引快(即查询慢),实时性查询快,用于facebook,新浪等搜索。
- Solr更成熟,有更大更成熟的用户、开发者和贡献者社区。ES相对开发维护者较少,更新快,学习成本高(趋势)。
二.安装启动
1.官网下载:https://www.elastic.co/products/elasticsearch
2.双击启动
3.访问
安装可视化界面 head插件
1.下载地址:(依赖node.js环境)
2.安装依赖
(1)npm run satart 启动
(2)解决跨域(elasticsearch.yml):配置重启ES
#开启跨域
http.cors.enabled: true
#允许所有人访问
http.cors.allow-origin: "*"
安装kibana
它是针对ES的开源分析及可视化平台,用来搜索,查看交互存储在ES索引中的数据。使用kibana可以通过各种图表进行高级数据分析及展示。
双击启动:kibana.bat
汉化(kibana.yml):
#汉化
i18n.locale: “zh-CN”
IK分词器(中文分词器)
(1)下载完放入ES的plugins目录下重启:elasticsearch-7.6.1\plugins\ik
(2)在cmd处查看安装的插件:elasticsearch-plugin
(3)IK分词器提供了两种算法:ik_smart和ik_max_word.其中ik_smart为最少切分,ik_max_word为最细粒度划分(划分多个词)。IK分词器添加词(ik的conf目录下)
1.编写自己的词典文件。
2.配置词典文件。
kibana测试
GET _analyze
{
"analyzer": "ik_smart",
"text":"中国共产党"
}
GET _analyze
{
"analyzer": "ik_max_word",
"text":"中国共产党"
}
三.基本使用
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] | 查询文档.[ ]代表可选 |
POST | localhost:9200/索引名称/类型名称/_search | 查询所有数据 |
支持的字段类型
- 字符串类型:text、keyword
- 数值类型:long、Integer、short、byte、double、float、half float、scaled float
- 日期类型:date
- 布尔类型:boolean
- 二进制类型:binary
- 等等。。。
测试
PUT /test1/type1/1
{
"name":"狂人说",
"age":"18"
}
指定字段类型
PUT /test2
{
"mappings": {
"properties": {
"name":{
"type": "text"
},
"age":{
"type": "long"
},
"birthday":{
"type": "date"
}
}
}
}
默认类型(很智能)
PUT /test3/_doc/1
{
"name":"zs",
"age":18,
"birth":"1998-02-01"
}
其它命令
修改
曾经做法
现在做法
post /test3/_doc/1/_update
{
"doc":{
"name":"张三"
}
}
四.文档的基本操作(重点)
添加数据
PUT /qyang/user/1
{
"name":"小不点",
"age":18,
"decs":"一顿操作猛如虎,一看工资2500",
"tags":["王者大神","技术胖","强势"]
}
获取数据
GET /qyang/user/1
更新数据 PUT (这种类似于替换)
PUT /qyang/user/2
{
"name":"小不点啊"
}
(推荐)更新数据 POST _update
POST /qyang/user/1/_update
{
"name":"我也是小不点啊"
}
简单条件查询
精确匹配
GET /qyang/user/_search?q=name:小不点
复杂搜索(排序,分页,高亮,模糊查询,精准查询)
关于分词
- term,精确查询,根据倒排索引精确查找。term用法和match一样,只是查询原理不一样。
- match,使用分词器解析查询条件和文档文档,然后通过分析的文档进行查询。
两个类型(对应mapping中的type) text(分词器解析进行分词) keyword(分词器不解析,整体当成一个词)
//term 不会对查询条件进行分词,整体当成一个词
GET /qyang/user/_search
{
"query": {
"term": {
"name": "狂人说前端"
}
}
}
高亮查询
GET /qyang/user/_search
{
"query": {
"match": {
"name": "狂人"
}
},
"highlight": {
"pre_tags": "<font style='color:red>",
"post_tags": "</font>",
"fields": {
"name":{}
}
}
}
查询总结
- 匹配
- 按照条件匹配
- 精确匹配
- 区间范围匹配
- 匹配字段过滤
- 条件查询
- 高亮查询
五.集成Spring Boot
1.原生依赖
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.11.2</version>
</dependency>
2.找对象
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http"),
new HttpHost("localhost", 9201, "http")));
//用完关闭
client.close();
3.创建spring boot应用,引入相关依赖
4.在config包中配置客户端对象,放入spring容器
@Configuration //相当于xml文件
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")));
return client;
}
}
5.apI测试
@SpringBootTest
class EsApiApplicationTests {
@Autowired//按照类型匹配
//@Qualifier("restHighLevelClient")//指定名称,默认方法名(有多个类型的时候使用)
RestHighLevelClient client;
//测试索引
@Test
void testCreateIndex() throws IOException {
//1.创建请求
CreateIndexRequest index = new CreateIndexRequest("test_index");
//2.执行请求,获得结果 indices(index的复数)
CreateIndexResponse createIndexResponse = client.indices().create(index, RequestOptions.DEFAULT);
System.out.println(createIndexResponse.index());
}
@Test
void testExistIndex() throws IOException {
GetIndexRequest request = new GetIndexRequest("test_index");
System.out.println(client.indices().exists(request,RequestOptions.DEFAULT));
}
@Test
void testDeleteIndex() throws IOException {
DeleteIndexRequest request = new DeleteIndexRequest("test_index");
AcknowledgedResponse acknowledgedResponse = client.indices().delete(request, RequestOptions.DEFAULT);
System.out.println(acknowledgedResponse.isAcknowledged());
}
//测试文档
//添加文档
@Test
public void testAddDocument() throws IOException {
//实体类如果用 lombok的化 需要@Data注解
User user = new User("小李飞刀", 22);
//创建请求
IndexRequest request = new IndexRequest("test_index");
// put /test_index/_doc/1
request.id("1");
//request.timeout("1s"); 设置超时时间的方式
request.timeout(TimeValue.timeValueSeconds(1));
//放入数据 json格式
request.source(JSON.toJSONString(user),XContentType.JSON);
//客户端发送请求
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
System.out.println(indexResponse.toString());
//CREATED 和命令行一样
System.out.println(indexResponse.status());
}
//测试文档是否存在
@Test
public void testIsExists() throws IOException {
// get /index/_doc/1
GetRequest getRequest = new GetRequest("test_index", "1");
//不获取返回的 _source上下文
getRequest.fetchSourceContext(new FetchSourceContext(false));
System.out.println(client.exists(getRequest,RequestOptions.DEFAULT));
}
//获取文档
@Test
public void testGetDocument() throws IOException {
GetRequest getRequest = new GetRequest("test_index", "1");
GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
System.out.println(getResponse.getSourceAsString());
System.out.println(getResponse);
}
//更新文档 增量更新
@Test
public void testUpdateDocument() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("test_index","1");
updateRequest.timeout("1s");
User user = new User("小李飞刀2", 18);
updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);
UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(updateResponse.status());
}
//删除文档
@Test
public void testDeleteDocument() throws IOException {
DeleteRequest deleteRequest = new DeleteRequest("test_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<>();
for(int i=0;i<100;i++){
//默认类型 "type":"text" 会进行分词
userList.add(new User("摩西摩西"+i,i));
}
for (int i = 0; i < userList.size(); i++) {
bulkRequest.add(
new IndexRequest("test_index").id(""+(i+1)).source(JSON.toJSONString(userList.get(i)),XContentType.JSON)
);
}
BulkResponse bulkResponse = client.bulk(bulkRequest,RequestOptions.DEFAULT);
System.out.println(bulkResponse.hasFailures());
}
/**
* 查询
* SearchRequest 搜索请求
* SearchSourceBuilder 条件构造
* HighlightBuilder 构建高亮
* TermQueryBuilder 构建精确查询
* xxxQueryBuilder 对应我们的所有命令
*/
@Test
void testSearch() throws IOException {
SearchRequest searchRequest = new SearchRequest("test_index");
//构建搜索条件
SearchSourceBuilder builder = new SearchSourceBuilder();
//高亮
// builder.highlighter(HighlightBuilder)
//查询条件,我们可以使用 QueryBuilders 工具类,里面的方法对应前面学的命令
// QueryBuilders.termQuery() 精确查询
// QueryBuilders.matchAllQuery() 匹配所有
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("age", "15");
builder.query(termQueryBuilder);
builder.timeout(new TimeValue(60,TimeUnit.SECONDS));
searchRequest.source(builder);
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.getSourceAsString());
}
}
}
六.项目实战
1.controller
@Controller
public class indexController {
@Autowired
IndexService indexService;
@GetMapping({"/","/index"})
public String toIndex(){
return "index";
}
@GetMapping("/parse/{keyword}")
@ResponseBody
public boolean parse(@PathVariable("keyword")String keyword){
return indexService.parseContent(keyword);
}
@GetMapping("/search/{keyword}/{pageNumber}/{pageSize}")
@ResponseBody
public List<Map<String,Object>> search(@PathVariable("keyword")String keyword,
@PathVariable("pageNumber")int pageNumber,
@PathVariable("pageSize")int pageSize) throws IOException {
return indexService.search(keyword,pageNumber,pageSize);
}
}
2.service
@Service
public class IndexService {
@Autowired
RestHighLevelClient restHighLevelClient;
public boolean parseContent(String keyword) {
try {
//Content类的属性需要有get/set方法
List<Content> contents = HtmlParseUtil.parseJD(keyword);
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();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public List<Map<String,Object>> search(String keyword, int pageNumber, int pageSize) throws IOException {
if(pageNumber<1){
pageNumber=1;
}
if(pageSize<=0){
pageSize = 10;
}
//条件搜索
SearchRequest searchRequest = new SearchRequest("jd_goods");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//分页
sourceBuilder.from((pageNumber-1)*pageSize);
sourceBuilder.size(pageSize);
//精准匹配
TermQueryBuilder termQuery = QueryBuilders.termQuery("title", keyword);
sourceBuilder.query(termQuery);
sourceBuilder.timeout(new TimeValue(60,TimeUnit.SECONDS));
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.requireFieldMatch(false);//多个高亮显示
highlightBuilder.preTags("<font style='color:red'>");
highlightBuilder.postTags("</font>");
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.getFragments();
String n_title="";
for(Text text: fragments){
n_title += text;
}
sourceAsMap.put("title",n_title);
}
list.add(sourceAsMap);
}
return list;
}
}
3.config
@Configuration
public class ElasticSearchClientConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")));
return client;
}
}
4.utils(解析网页用jsoup,爬取视频,音乐用tika)
@Component
public class HtmlParseUtil {
public static void main(String[] args) throws Exception {
//不支持中文,设置 new URL(url) encoding...
parseJD("vue").forEach(System.out::println);
}
public static List<Content> parseJD(String keywords) throws Exception {
String url = "https://search.jd.com/Search?keyword="+keywords;
//解析网页(jsoup返回的Document 就是js中的Document对象)
Document document = Jsoup.parse(new URL(url), 30000);
Element element = document.getElementById("J_goodsList");
//获取所有的li元素
Elements lisElement = element.getElementsByTag("li");
//System.out.println(lisElement.html());
//获取元素内容
List<Content> goods = new ArrayList<>();
for (Element e : lisElement) {
String img = e.getElementsByTag("img").eq(0).attr("data-lazy-img");
String price = e.getElementsByClass("p-price").eq(0).text();
String title = e.getElementsByClass("p-name").eq(0).text();
Content content = new Content();
content.setImg(img);
content.setPrice(price);
content.setTitle(title);
goods.add(content);
}
return goods;
}
}