初识elasticsearch
elasticsearch是一个非常强大的开源搜索引擎,可以帮助我们从海量的数据中快速搜索出想要的数据。
elasticsearch结合Kibina、Logstash、Beats,也就是elastic stack(ELK)。被广泛应用在日志分析、实时监控等领域。
elasticsearch底层是基于lucene来实现的。
Lucene是一个Java语言的搜索引擎类库,是Apache公司的顶级项目,由DougCutting于1999年研发。官网地址:https://lucene.apache.org/ 。
Lucene的优势:
- 易扩展
- 高性能(基于倒排索引)
Lucene的缺点: - 只限于Java语言开发
- 学习曲线陡峭
- 不支持水平拓展
相比lucene,elasticsearch具备以下优势:
- 支持分布式,可水平拓展
- 提供Restful接口,可被任何语言调用
搜索引擎技术排名:
1、ElasticSearch:开源的分布式搜索引擎
2、Splunk:商业项目
3、Solr: Apache的开源搜索引擎
总结:
什么是elasticsearch?
- 一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能。
什么是elastic stack(ELK)?
- 是以elasticsearch为核心的技术栈,包括Beats、Logstash、Kibana、Elasticsearch
什么是Lucene?
- 是Apache开源的搜索引擎类库,提供了搜索引擎的核心API
什么是文档和词条
- 每一条数据就是一个文档
- 对文档中的内容分词 得到的词语就是词条
什么是正向索引
基于文档id创建索引,查找词条时必须先找到文档,而后判断是否包含词条
什么是倒排索引
对文档内容分词,对词条创建索引,并记录词条所在的文档信息,查询时先根据词条查询到文档id,而后获取到文档。
搜索流程:
举个例子:
搜索 华为手机 =>分词=>得到 “华为”、"手机"两个词条 =>去词条列表查询文档id=>得到每个词条的文档id=>根据文档id查询文档=>将查询出来的文档存入结果集
文档
elasticsearch时面向文档存储的,可以是数据库中的一条商品数据,一个订单信息。
文档数据会被序列化为json格式后存储在elasticsearch中
索引
索引: 相同类型的文档的集合
映射: 索引中文档的字段约束信息,类似表的结构约束
mysql和elasticsearch的概念对比:
mysql | elasticsearch | 说明 |
Table | Index | 索引(index)就是文档的集合,类似数据库的表(Table) |
Row | Document | 文档(document)就是数据库中的一条条数据,类似数据库中 |
的行(Row),文档都是JSON格式 | ||
Column | Field | 字段(Field),就是JSON文档中的字段,类似数据库中的列(Column) |
Schema | Mapping | 映射(Mapping)是索引中文档的约束,例如字段类型的约束,类似数据库中的表结构(Schema) |
SQL | DSL | DSL是elasticsearch提供的JSON风格的请求语句,用来操作elasticsearch,实现CRUD |
架构区别
Mysql:擅长事务类型操作,可以确保数据的安全和一致性
Elasticsearch:擅长海量数据的搜索、分析、计算
安装es
//创建用户定义的网络(用于连接到同一网络的其他服务(例如 Kibana))
docker network create es-net
//安装es
docker run -d --name elasticsearch --net es-net -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.17.6
安装kibana 并且绑定es
docker run -d --name kibana -e ELASTICSEARCH_HOSTS=http://es:9200 --network=elasticsearch-net -p 5601:5601 kibana:7.17.6
使用kibana模拟请求
到kibana的devtools的菜单 使用GET / 可以实现和localhost:9200 一样的效果 因为kibana绑定了es,所以不用写ip和端口,es请求都是走restFful风格的http请求
ik分词器
采用默认的分词器分词
安装ik分词器要注意的一点是ik分词器的版本一定要和elasticsearch版本完全一致,不然安装不上
ik分词器有两种模式 分别是 ik_smart和ik_max_word
ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query;
ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase 查询
拓展词库和停止词库
要拓展ik分词器的词库,只需要修改ik分词器目录中的config/analysis-ik/目录中的IKAnalyzer.cfg.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 这里我填的是 ext.dic-->
<entry key="ext_dict">ext.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典 *** 添加停用词词典 这里我填的是stopword.dic-->
<entry key="ext_stopwords">stopword.dic</entry>
</properties>
在同级目录下创建ext.dic和stopword.dic
你要拓展的词放在ext.dic,你要停用的词放在stopword.dic即可
ik分词器的作用:
- 创建倒排索引时对文档进行分词
- 用户搜索时,对输入的内容分词
ik分词器有几种模式:
- ik_smart:智能切分,粗粒度
- ik_max_word:最细切分,细粒度
ik分词器如何拓展词条,如何停用词条:
- 利用config目录下analysis-ik 下的IKAnalyzer.cfg.xml 添加拓展词典和停用词典实现的
索引库操作
mapping属性
mapping属性是对索引库中文档的约束,常见的mapping属性包括:
- type:字段数据类型,常见的简单类型有:
- 字符串: text(可分词的文本)、keyword(精确值,例如:品牌、国家、ip地址) 考虑字段时,如果有些词拆了没有意义,比如国家,品牌,那就选keyword 不能拆,否则就选text
*数值: long、integer、short、byte、double、float
*布尔: boolean
*日期: date
*对象: object
- index: 是否创建索引,默认为true
- analyzer: 使用哪种分词器
- properties: 该字段的子字段
索引库操作
创建索引库
elasticsearch 通过Restful请求操作索引库,文档。请求内容用DSL语句来表示。创建索引库和mapping的DSL语法如下:
基本语法:
- 请求方式: PUT
- 请求路径: /索引库名,可自行定义
- 请求参数: mapping映射
格式:
PUT /索引库名称
{
"mappings": {
"properties": {
"字段名":{
"type": "text",
"analyzer": "ik_smart"
},
"字段名2":{
"type": "keyword",
"index": "false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
},
// ...略
}
}
}
举个例子
PUT /christinaya
{
"mappings": {
"properties": {
"info": {
"type": "text",
"analyzer": "ik_smart"
},
"email":{
"type": "keyword"
},
"name":{
"type": "object",
"properties": {
"firstName": {
"type":"text",
"analyzer":"ik_smart",
"copy_to":"all"
},
"lastName":{
"type":"text",
"analyzer":"ik_smart",
"copy_to":"all"
}
}
},
"location":{
"type": "geo_point"
},
"all":{
"type":"text",
"analyzer":"ik_max_word"
}
}
}
}
几个特殊字段说明:
- location:地理坐标,里面包含精度、纬度
- all:一个组合字段,其目的是将多字段的值 利用copy_to合并,提供给用户搜索
查看索引库
GET 索引库
GET liwenzhu
删除索引库
DELETE 索引库名
DELETE liwenzhu
修改索引库
索引库和mapping一旦创建就无法修改,但是可以添加新的字段,语法如下:
PUT /索引库名/_mapping
{
"properties":{
"新字段名"{
"type":"integer"
}
}
}
举个例子:
PUT /liwenzhu/_mapping
{
"properties":{
"age":{
"type":"integer"
}
}
}
文档操作
添加文档:
新增文档的DSL语句如下:
POST /索引库名/_doc/文档id
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子属性1": "值3",
"子属性2": "值4"
},
// ...
}
举个例子
# 新增文档
POST /christinaya/_doc/1
{
"info":"李文铸学习java",
"email":"237872788",
"name":{
"firstName":"李",
"lastName":"文铸"
}
}
查询文档
GET /{索引库名称}/_doc/{id}
举个例子
GET /christinaya/_doc/1
删除文档
DELETE /christinaya/_doc/1
java操作elasticsearch
主要使用的是spring-data-elasticsearch 因为感觉这个很好用 其他的工具也可以,主要是顺心顺手( •̀ ω •́ )
添加pom.xml依赖
<!--elasticsearch data-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!--我study主要写在测试类里面 所以导入测试类依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.5</version>
<scope>test</scope>
</dependency>
<!-- 单元测试依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
编写yml文件
server:
port: 8080
spring:
elasticsearch:
uris: localhost:9200
编写实体类
package com.example.elasticsearchstudy.domain;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* @author liwenzhu
*/
@Component
@Data
@Document(indexName = "hotel")
public class Hotel implements Serializable {
/** 酒店id*/
@Id
private String id;
/** 酒店名称*/
@Field(type = FieldType.Keyword)
private String name;
/** 酒店地址 */
@Field(type = FieldType.Keyword)
private String address;
/** 酒店价格 */
@Field(type = FieldType.Double)
private Integer price;
/** 酒店评分 */
@Field(type = FieldType.Double)
private Double score;
/** 酒店品牌 */
@Field(type = FieldType.Keyword)
private String brand;
/** 所在城市 */
@Field(type = FieldType.Keyword)
private String city;
/** 酒店星级 酒店星级,1星到5星,1钻到5钻 */
@Field(type = FieldType.Keyword)
private String starName;
/** 商圈 */
@Field(type = FieldType.Text,analyzer = "ik_max_word")
private String business;
/** 纬度 */
@Field(type = FieldType.Keyword)
private String latitude;
/** 经度 */
@Field(type = FieldType.Keyword)
private String longitude;
/** 酒店图片 */
@Field(type = FieldType.Keyword)
private String pic;
}
新建一个repository/HotelRepository
package com.example.elasticsearchstudy.repository;
import com.example.elasticsearchstudy.domain.Hotel;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Service;
/**
* @author liwenzhu
*/
@Service
public interface HotelRepository extends ElasticsearchRepository<Hotel,String> {
}
编写测试类
package com.example.elasticsearchstudy;
import com.example.elasticsearchstudy.domain.Hotel;
import com.example.elasticsearchstudy.repository.HotelRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class LiwenzhuTest {
@Resource
private HotelRepository repository;
@Test
public void testCreateDoc() {
Hotel hotel = new Hotel();
hotel.setName("京西大酒店");
hotel.setAddress("xxx路");
hotel.setPrice(2000);
hotel.setScore(4.0);
hotel.setBrand("李文铸牌");
hotel.setCity("福州");
hotel.setStarName("四星");
hotel.setBusiness("福州儿童公园路");
hotel.setLatitude("30.251433");
hotel.setLongitude("120.47522");
hotel.setPic("https://m.tuniucdn.com/filebroker/cdn/res/07/36/073662e1718fccefb7130a9da44ddf5c_w200_h200_c1_t0.jpg");
repository.save(hotel);
}
}
因为是spring-data-elasticsearch 所以crud是遵循spring-data的风格的
增加和修改都是save方法
删除就是delete方法,不过他删除都是根据id删的,所以要么deleteById,要么就是delete实体类的id有值
查询遵循spring-data风格
比如你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。
最后附上代码
github: https://github.com/liwenzhu5113/elasticsearch-study.git
如果对你有帮助,点个赞👍吧