ElasticSearch 学习笔记
前言:
作为一名励志成为全栈web工程师的大龄程序员,ES是必须要学习的,刚好最近项目,也有要使用到的地方。于是在网上找视频教程看一下,看了一圈下来,基本都是照着PPT在念,看了有三四个,没有一个能把全文检索,倒排索引讲明白的,一头雾水,又是安装又是集群的,不适合工作在一线的程序员快速入门,还是自己到官网学习,笔记在此,方便查阅。
学习一个技术最主要是动手用起来,管他什么全文检索,什么倒排索引。先用起来。总之先知道ES是用来检索数据的就可以。
首先是官网地址:https://www.elastic.co/cn/,还有一个中文版的文档地址 https://www.elastic.co/guide/cn/ 这里面是一本权威指南,罗里吧嗦一大堆,直接在quick start开始吧。
安装:
第一步肯定是安装,初学者那肯定是docker安装只需要五行命令,就可以直接看到效果。
// 创建一个网络
docker network create elastic
//获取elastic镜像
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.17.19
//run这个镜像
docker run --name es01-test --net elastic -p 127.0.0.1:9200:9200 -p 127.0.0.1:9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.17.19
直接访问http://localhost:9200就能看到版本信息,elasticsearch有一个可以直接访问的_cat api,可以通过 http://localhost:9200/_cat 来访问。例如:http://localhost:9200/_cat/health?v 可以查看健康状况, ?v 可以显示表头。到这里elasticsearch就算是安装成功了。
//获取kibana镜像
docker pull docker.elastic.co/kibana/kibana:7.17.19
//run这个镜像
docker run --name kib01-test --net elastic -p 127.0.0.1:5601:5601 -e "ELASTICSEARCH_HOMES=http://es01-test:9200" docker.elastic.co/kibana/kibana:7.17.19
一切顺利的话 直接 http://localhost:5601 就可以看到 kibana 的首页。打开就是一大堆看不懂的东西,不管他,直接左侧菜单打开,找到最后一个Management 下面的 Dev tool. 直接输入 GET / 就可以查看版本信息。
接下来就可以开始elasticsearch的学习了,对于开发者来说,最主要要掌握的就是 第一个RestApi,第二个java客户端,第三个就是springdata相关api。
大爷的,坑还是有,上面的我昨天明明跑的好好的但是今天那个Kibana就TM不行了,好,不行就不行,刚好使用curl来练练手,本身ES对外的接口就是基于HTTP的,所以任何能发送http请求的客户端都可以用来测试
在开始之前还是得介绍一下:Apache下面有个开源的全文检索工具包,叫个lucene,他有两个实现一个是solr,再一个就是今天要搞的elasticsearch。
这个lucene对于存储结构的设计是: index,type,document。就是一个lucene里面可以有多个index,一个index里面可以有多个type,然后type存储的是相同类型的数据document。
elasticsearch基于lucene,但是它把type给弱化了,可以有多个index,但是一个index里面只有一个默认的type叫做 _doc,再然后呢这个_doc里面可以随便放数据,不用按照固定的格式。
它会动态映射之前没有出现过的字段。比如:
curl -X PUT "localhost:9200/data/_doc/1?pretty" -H 'Content-Type: application/json' -d '{ "count": 5 }'
无需定义index也无需定义_doc,直接发送PUT请求发送json数据,就能保存成功,它把这个count自动映射称为数字类型,curl -X GET "localhost:9200/data?pretty" 这个命令可以看到mapping属性。之后这个字段只能是数字,否则报错。
{"_index":"data","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":3,"_primary_term":1}
这个自动映射文档类型应该也就是方便测试,至于要写进代码还是应该定义这个index的文档类型,es里面称之为mapping,就是文档字段的类型映射,下面的命令就可以生成相应mapping的index.并且 "dynamic":false 规定不能动态生成mapping。数据还是能保存,但是mapping不会变化
curl -X PUT "localhost:9200/my-index-001" -H "Content-type:application/json" -d '{"mappings":{"dynamic":false,"properties":{"age":{"type":"integer"},"email":{"type":"keyword"},"name":{"type":"text"}}}}'
{"acknowledged":true,"shards_acknowledged":true,"index":"my-index-001"}
记录一下 看起来 创建索引只能用PUT,创建数据POST可以自动生成ID(新增),而PUT创建数据必须指定ID(更新)。
好,新的一天开始 docker安装了新版本的elasticsearch和kibana,顺利启动,还是用kibana吧,这个还是方便一点。还是得手写
这个mapping可以直接修改,注意路径和参数的变化
PUT /my-index-001/_mapping
{
"properties":{
"emp-id":{
"type":"keyword",
"index":false //是否作为索引字段
}
}
}
那这个mapping具体怎么用呢?它其实字段名与类型的映射关系,字段名是开发者自己取得的 name age 那种,这个类型就是ES规定了,看下来有二三十个类型,比如名字是keyword,age是数字类型。
看几个比较常见的类型
比如一个keyword 可以用来排序,聚合,和 term-level 查询。排序,聚合功能还是很强大的。这个term-level的查询,简单理解起来就是相等的数据,不一定是完全相等,跟text的match查询相对应。
再一个 text,这个类型的数据会被分词器分词,然后根据单个词可以匹配包含该词语的文档,建议存储那些非结构化的但是人类可读的数据,比如邮件内容,产品描述等
通常情况下 如果你既想要排序聚合有想要分词查询,那么同一个内容放在两个字段上。
这个mapping映射除了属性的类型,接下来就是属性上面的配置。比如 这个 analyzer 分词器,文档提示除了选择安装合适的分词器插件外,在上线之前要详细的测试这个分词器的分词效果,再比如 boost 相关性分数计算的系数。等等功能很多,配置很多。但是直接上手没有那么复杂。
es还支持别名,就是给index 或者 属性另外起一个名字,具体什么用处,还不清楚。
再接下里介绍es的查询,这个比较关键,毕竟用这个东西就是来检索数据的。
首先是查询语法
GET /<target>/_search
GET /_search
POST /<target>/_search
POST /_search
看起来是既支持GET也支持POST,总之就是灵活奔放,怎么都能查,参数可以拼在url里面也可以放在请求体里面,内容很多,我觉得就学POST的请求就可以,因为有些复杂的查询参数很多,放在body里面看起来更顺眼。
查询操作除了正常的根据字段查询响应的数据之外,还支持聚合,分页,排序,和异步查询,这些在接下来都会整理。
关于查询还有很多特性,比如过滤查询,高亮查询等,例子:
PUT /shirts
{
"mappings": {
"properties": {
"brand": { "type": "keyword"},
"color": { "type": "keyword"},
"model": { "type": "keyword"}
}
}
}
PUT /shirts/_doc/1?refresh
{
"brand": "gucci",
"color": "red",
"model": "slim"
}
创建一个index并且往里面插入一条数据,然后:
POST /shirts/_search
{
"query": {
"bool": { //boolean类型的查询
"filter": [ //这里就是过滤器的语法
{"term": {"brand": "gucci"}}, //可以添加多个
{"term": {"color": "red"}}
]
}
},
"aggs": { //聚合查询
"models": {
"terms": { "field": "model" } //根据model聚合查询 model count
}
}
}
来个更复杂的:
POST /shirts/_search
{
"query": {
"bool": {
"filter": {
"term": { "brand": "gucci" } //只过滤品牌
}
}
},
"aggs": { //聚合查询
"colors": {
"terms": { "field": "color" } //根据颜色count
},
"color_red": {
"filter": {
"term": { "color": "red" } //根据颜色过滤 看红色
},
"aggs": { //基于红色过滤器聚合
"models": {
"terms": { "field": "model" } //根据modelcount
}
}
}
},
"post_filter": {
"term": { "color": "red" } //从最后的搜索结果中只保留红色的。
}
}
ES使用 Query DSL 来配置查询条件,DSL就是 domain specific language,只需要知道这玩意就是基于json来配置查询条件就够了。
之前也写了几个熟悉了一下,
靠,内容太多,看些关键的吧?
官网有一些建议,第一个elasticsearch 是一个搜索引擎,擅长搜索最符合条件的文档,不建议查询所有文档,而且文档不建议太大,默认的http传输限制100M,虽然可以调整。
等等吧 还有好多,接下来看怎么在项目里面使用对于开发来说是最关键的。es 提供了一个专门的 java client,用来连接操作es服务器。
首先肯定是依赖了。
<project>
<dependencies>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.17.22</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<version>2.0.1</version>
</dependency>
</dependencies>
</project>
然后demo:
// Create the low-level client
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
// And create the API client
ElasticsearchClient client = new ElasticsearchClient(transport);
SearchResponse<Product> search = client.search(s -> s
.index("products")
.query(q -> q
.term(t -> t
.field("name")
.value(v -> v.stringValue("bicycle"))
)),
Product.class);
for (Hit<Product> hit: search.hits().hits()) {
processProduct(hit.source());
}
//之前的版本 应该是这个高水平的客户端,在新版本也是可以用的,而且两个客户端可以使用同一个httpclient
RestHighLevelClient hlrc = new RestHighLevelClientBuilder(restClient)
.setApiCompatibilityMode(true)
.build();
//创建index
client.indices().create(c -> c.index("products"));
然后是跟Spring整合
@Configuration
public class MyClientConfig extends ElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder()
.connectedTo("localhost:9200")
.build();
}
}
@Autowired
ElasticsearchOperations operations;
@Autowired
ElasticsearchClient elasticsearchClient;
@Autowired
RestClient restClient;
Spring 还提供了 实体类跟index的映射,使用注解,例如
@Document(indexName = "products",createIndex = false)
public class Product implements Serializable {
@Id
private String sku;
@Field(type = FieldType.Keyword,)
private String name;
@Field(type = FieldType.Text,analyzer = "ik_analyzer")
private String desc;
@Field(type = FieldType.Double)
private Double price;
}
到这里 基本就可以用了,想要熟练那肯定得去实践才行。