携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情
⭐️前面的话⭐️
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《王道408》,📚《深入理解 Java 虚拟机-周志明》,📚《Java 核心技术卷》
💬算法刷题:✅力扣🌐牛客网
🎈Github
🎈码云Gitee
1 DSL搜索
• Domain Specific Language
• 特定领域语言
• 基于JSON格式的数据查询
• 查询更灵活,有利于复杂查询
1.0 创建索引、同时创建映射;创建文档
PUT /index_user
{
"settings":{
"index":{
"number_of_shards":5,
"number_of_replicas":0
}
},
"mappings" : {
"properties":{
"user_id":{
"type":"long"
},
"name":{
"type":"text",
"analyzer":"ik_max_word"
},
"login_name":{
"type":"keyword"
},
"age":{
"type":"integer"
},
"birthday":{
"type":"date"
},
"desc":{
"type":"text",
"analyzer":"ik_max_word"
},
"head_url":{
"type":"text",
"index":false
}
}
}
}
添加 3 个文档
POST /index_user/_doc/1
{
"user_id":"1",
"name":"僵尸猎手",
"login_name":"jsls",
"age":25,
"birthday":"1990-03-01",
"desc":"我是一名房产经纪人,现在转行了,目前是一名运输工人",
"head_url":"https://www.zhuifengren.cn/img/jsls.jpg"
}
POST /index_user/_doc/2
{
"user_id":"2",
"name":"夏维尔",
"login_name":"xwe",
"age":28,
"birthday":"1992-06-06",
"desc":"我是一名高级开发经理,每天坐地铁上班,在北京住,从不堵车",
"head_url":"https://www.zhuifengren.cn/img/xwe.jpg"
}
POST /index_user/_doc/3
{
"user_id":"3",
"name":"迪士尼在逃仙柔",
"login_name":"dsnzxr",
"age":10,
"birthday":"2011-06-22",
"desc":"我是一名五年级的小学生,每天专车接专车送,中午在学校入伙,食堂菜可好了,上学期期末考试我拿了三好学生奖",
"head_url":"https://www.zhuifengren.cn/img/dsnzxr.jpg"
}
1.1 查询所有 match_all
POST /index_user/_search
{
"query":{
"match_all":{}
}
}
1.2 match查询
会将关键字先分词,然后用每一个分词去查询,最后将结果取并集。
POST /index_user/_search
{
"query":{
"match":{
"desc":"高级地铁运输"
}
}
}
match查询扩展1(match查询后,结果取交集)
POST /index_user/_search
{
"query":{
"match":{
"desc": {
"query": "一名小学生",
"operator": "and"
}
}
}
}
match查询扩展2(指定匹配率)
POST /index_user/_search
{
"query":{
"match":{
"desc": {
"query": "一名小学生坐地铁",
"minimum_should_match":"6" // 或 "minimum_should_match":"60%"
}
}
}
}
1.3 term查询(不分词)
不会将关键字分词,直接拿来查询。
POST /index_user/_search
{
"query":{
"term":{
"desc":"一名地铁"
}
}
}
1.4 terms查询
与term查询类似,可以写多个关键字,会用每个关键字去查询,最后将结果取并集。
POST /index_user/_search
{
"query":{
"terms":{
"desc":[
"一名",
"小学生"
]
}
}
}
1.5 match的多字段查询
POST /index_user/_search
{
"query":{
"multi_match":{
"query":"一名小学生 僵尸25",
"fields":[
"desc",
"name" // "name^10"
]
}
}
}
1.6 match_phrase查询
与match类似,会先将关键字分词,然后用每个分词去查询,但会对文档中分词间的间隔有一定限制,使用slop属性去限制,默认是0,需要小于设置的间隔,才能匹配文档。
POST /index_user/_search
{
"query":{
"match_phrase":{
"desc":{
"query": "一名 学生",
"slop":8
}
}
}
}
1.7 文档中是否存在某字段 exists
文档中存在该字段,就会被查出来。
POST /index_user/_search
{
"query":{
"exists":{
"field":"name"
}
}
}
1.8 查询部分字段 _source
POST /index_user/_search
{
"query":{
"match_all":{}
},
"_source": [
"user_id",
"name"
]
}
1.9 分页 from size
POST /index_user/_search
{
"query":{
"match_all":{}
},
"from":0, // 从哪条文档开始,文档下标从 0 开始
"size":2 // 每页多少条文档
}
1.10 使用id集合查询
POST /index_user/_search
{
"query": {
"ids":{
"type":"_doc",
"values":["1","3"]
}
}
}
1.11 布尔查询
- 参数中可以包含多种条件的组合。
- 其中 must 块下的条件,文档必须都符合才会被查出来。
- must_not 块下的条件,文档必须都不符合才会被查出来。
- should 块下的条件,文档只要符合一个就能被查出来。
- 最终结果集是 must、must_not、should 块查询结果的交集。
POST /index_user/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"desc": "一名"
}
},
{
"match": {
"desc": "小学生"
}
}
],
"must_not": [],
"should": [
{
"match": {
"desc": "一名"
}
},
{
"match": {
"desc": "小学生"
}
}
]
}
}
}
1.12 为某个查询加权
"match": {
"desc": {
"query": "一名",
"boost":10
}
}
1.13 结果集过滤 post_filter
从ES查询后,再将结果集过滤一次。
- 支持范围查询:
- gte:大于等于
- lte:小于等于
- gt:大于
- lt:小于
- 也支持 term、match 操作。
POST /index_user/_search
{
"query":{
"match":{
"desc":"一名"
}
},
"post_filter": {
"range" : {
"age": {
"gt":10,
"lt":26
}
}
}
}
1.14 排序 sort
POST /index_user/_search
{
"query":{
"match":{
"desc":"一名"
}
},
"sort":[
{
"age":"desc"
},
{
"user_id":"asc"
},
{
"login_name":"asc"
}
]
}
1.15 高亮
高亮效果就是在匹配的关键字上增加标签,便于前端去高亮显示。
POST /index_user/_search
{
"query":{
"match":{
"desc":"一名小学生"
}
},
"highlight":{
"pre_tags":"<span>",
"post_tags":"</span>",
"fields":{
"desc":{}
}
}
}
2 批量操作
mget
3 分词器
下载后解压到 es 的 plugins 文件夹,重启es即可。
分词器是Elasticsearch中很重要的一个组件,用来将一段文本分析成一个一个的词,Elasticsearch再根据这些词去做倒排索引。
3.1 内置分词器
Elasticsearch 中内置了一些分词器,这些分词器只能对英文进行分词处理,无法将中文的词识别出来。
- standard:标准分词器,是Elasticsearch中默认的分词器,可以拆分英文单词,大写字母统一转换成小写。
- simple:按非字母的字符分词,例如:数字、标点符号、特殊字符等,会去掉非字母的词,大写字母统一转换成小写。
- whitespace:简单按照空格进行分词,相当于按照空格split了一下,大写字母不会转换成小写。
- stop:会去掉无意义的词,例如:the、a、an 等,大写字母统一转换成小写。
- keyword:不拆分,整个文本当作一个词。
查看分词效果通用接口
GET /_analyze
{
"analyzer": "standard",
"text": "I am a man."
}
{
"tokens" : [
{
"token" : "i",
"start_offset" : 0,
"end_offset" : 1,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "am",
"start_offset" : 2,
"end_offset" : 4,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "a",
"start_offset" : 5,
"end_offset" : 6,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "man",
"start_offset" : 7,
"end_offset" : 10,
"type" : "<ALPHANUM>",
"position" : 3
}
]
}
3.2 LK 分词器
Elasticsearch中内置的分词器不能对中文进行分词,因此我们需要再安装一个能够支持中文的分词器,IK分词器就是个不错的选择。
ik_max_word: 会将文本做最细粒度的拆分,适合 Term Query;
ik_smart: 会做最粗粒度的拆分,适合 Phrase 查询。
分词效果如下:
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "我是一名Java高级程序员"
}
{
"tokens" : [
{
"token" : "我",
"start_offset" : 0,
"end_offset" : 1,
"type" : "CN_CHAR",
"position" : 0
},
{
"token" : "是",
"start_offset" : 1,
"end_offset" : 2,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "一名",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "一",
"start_offset" : 2,
"end_offset" : 3,
"type" : "TYPE_CNUM",
"position" : 3
},
{
"token" : "名",
"start_offset" : 3,
"end_offset" : 4,
"type" : "COUNT",
"position" : 4
},
{
"token" : "java",
"start_offset" : 4,
"end_offset" : 8,
"type" : "ENGLISH",
"position" : 5
},
{
"token" : "高级",
"start_offset" : 8,
"end_offset" : 10,
"type" : "CN_WORD",
"position" : 6
},
{
"token" : "程序员",
"start_offset" : 10,
"end_offset" : 13,
"type" : "CN_WORD",
"position" : 7
},
{
"token" : "程序",
"start_offset" : 10,
"end_offset" : 12,
"type" : "CN_WORD",
"position" : 8
},
{
"token" : "员",
"start_offset" : 12,
"end_offset" : 13,
"type" : "CN_CHAR",
"position" : 9
}
]
}
3.3 自定义词库
在进行中文分词时,经常出现分析出的词不是我们想要的,这时我们就需要在IK分词器中自定义我们自己词库。
例如:追风人,分词后,只有 追风 和 人,而没有 追风人,导致倒排索引后查询时,用户搜 追风 或 人 可以搜到 追风人,搜 追风人 反而搜不到 追风人。
# cd /usr/local/elasticsearch-7.14.1/plugins/ik/config
# vi IKAnalyzer.cfg.xml
在配置文件中增加自己的字典
# vi my.dic
在文本中加入 追风人,保存。
4 SpringBoot 整合
ElasticsearchTemplate 替换为 ElasticsearchRestTemplate(Transport Client 替换为 RestHighLevelClient )
在 Elasticsearch 7.0 中不建议使用 TransportClient,并且在8.0中会完全删除TransportClient。
因此,官方更建议我们用 Java High Level REST Client,它执行 HTTP 请求,而不是序列化的 Java 请求。
我们的使用的 ElasticsearchRestTemplate 就是基于RestHighLevelClient的再一层封装。
4.1 整合
1个依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
yml 配置
server:
port: 8080
spring:
elasticsearch:
rest:
uris: http://127.0.0.1:9200 #多个地址用逗号分隔
# username: #用户名
# password: #密码
connection-timeout: 6000
read-timeout: 6000
4.2 操作
新建一个ES的entity 类:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "user", shards = 3, replicas = 1, refreshInterval = "30s")
public class UserEsEntity {
@Id
private String id;
@Field(type = FieldType.Text, searchAnalyzer = "ik_max_word", analyzer = "ik_smart")
private String name;
@Field(type = FieldType.Keyword)
private String sex;
@Field(type = FieldType.Integer)
private Integer age;
@Field(type = FieldType.Keyword, index = false)
private String grade;
}
新建一个ElasticsearchRepository,Repository中很多方法都被标了过时,如下
@Repository
public interface UserEsDao extends ElasticsearchRepository<UserEsEntity, Long> {
}
对于查询已经不建议使用ElasticsearchRepository了,直接学习ElasticsearchRestTemplate 的Api。