上节记了ES简单的增删改查操作,但是作为搜索引擎来看,查的操作是最全面的:

1、ES中高级检索(Query)

ES官方提供了两种检索方式:一种是通过 URL 参数进行搜索,另一种是通过 DSL(Domain Specified Language) 进行搜索官方更推荐使用第二种方式第二种方式是基于传递JSON作为请求体(request body)格式与ES进行交互,这种方式更强大,更简洁

全文检索 索:对文档创建索引的过程 检索 查询条件

使用语法

(1)URL查询: GET /索引/类型/_search?参数

(2)DSL查询: GET /索引/类型/_search {}

添加一下测试数据:

PUT /ems
{
  "mappings":{
    "emp":{
      "properties":{
        "name":{
          "type":"text"
        },
        "age":{
          "type":"integer"
        },
        "bir":{
          "type":"date"
        },
        "content":{
          "type":"text"
        },
        "address":{
          "type":"keyword"
        }
      }
    }
  }
}

GET /ems/_mapping

PUT /ems/emp/_bulk
  {"index":{}}
  {"name":"小黑","age":23,"bir":"2012-12-12","content":"为开发团队选择一款优秀的MVC框架是件难事儿,在众多可行的方案中决择需要很高的经验和水平","address":"北京"}
  {"index":{}}
  {"name":"王小黑","age":24,"bir":"2012-12-12","content":"Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式","address":"上海"}
  {"index":{}}
  {"name":"张小五","age":8,"bir":"2012-12-12","content":"Spring Cloud 作为Java 语言的微服务框架,它依赖于Spring Boot,有快速开发、持续交付和容易部署等特点。Spring Cloud 的组件非常多,涉及微服务的方方面面,井在开源社区Spring 和Netflix 、Pivotal 两大公司的推动下越来越完善","address":"无锡"}
  {"index":{}}
  {"name":"win7","age":9,"bir":"2012-12-12","content":"Spring的目标是致力于全方位的简化Java开发。 这势必引出更多的解释, Spring是如何简化Java开发的?","address":"南京"}
  {"index":{}}
  {"name":"梅超风","age":43,"bir":"2012-12-12","content":"Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API","address":"杭州"}
  {"index":{}}
  {"name":"张无忌","age":59,"bir":"2012-12-12","content":"ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口","address":"北京"}

URL检索,毕竟ES是基于RestFul的,url检索非常好理解:

# 1.QueryString方式查询 
# 查询所有
GET /ems/emp/_search?q=*
# 排序 sort
GET /ems/emp/_search?q=*&sort=age:desc
# 分页 from size
GET /ems/emp/_search?q=*&sort=age:desc&size=5&from=16
# 只返回需要的字段 _source
GET /ems/emp/_search?q=*&sort=age:desc&size=5&from=16&_source=name

DSL检索,这是重要且关键的检索方式,用的很多,也比URL检索方式清晰许多:

对比url检索的四种方式:

# 查询所有
GET /ems/emp/_search
{
  "query": {
    "match_all": {
  
    }
  }
}

# 查询所有并排序 sort
GET /ems/emp/_search
{
  "query": {
    "match_all": {}
    },
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    },
    {
      "address": {
        "order": "desc"
      }
    }
  ]
}

# 分页查询 size from
GET /ems/emp/_search
{
  "query": {
    "match_all": {
  
    }
  },
  "size": 2,
  "from": 3,
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]
}

# 指定查询结果中返回指定字段 _source
GET /ems/emp/_search
{
  "query": {
    "match_all": {
  
    }
  },
  "_source": ["name","bir"]
}

基于关键字的检索查询,这里要注意ES默认是使用standard分词器的,对英文是单词分词,中文则是单字分词,对中文不太友好(比如wangba你是大王八会拆成 wangba+你+是+大+王+八),这里简单做个记录,下文会使用IK分词器做替换:

# queryDSL中term查询  基于关键词进行查询
GET /ems/emp/_search
{
  "query": {
    "term": {
      "content": {
        "value": "spring"
      }
    }
  }
}
# 1.type:text 类型分词 小黑会拆成 小 和 黑
# 2.ES中默认使用分词器是标准分词器 strandard 中文 单字分词 英文分词则是单词分词
# 3.type:keyword 不分词 所以查北京能查到 integer也不分词 date也不分词

GET /_analyze
{
  "text":"Redis is open source DB"
}

GET /_analyze
{
  "text":"wangba你是大王八"
}

其他高级检索方式,应用最广泛的是布尔查询(其实是布尔带着其他检索方式一块用),因为后续的高级检索一般都会是filter+query的方式,布尔查询一定会用:

# 范围查询 range
GET /ems/emp/_search
{
  "query":{
    "range": {
      "age": {
        "gte": 5,
        "lte": 20
      }
    }
  }
}

# 前缀查询 基于关键词前缀查询 prefix 张小五 张小 小五
GET /ems/emp/_search
{
  "query": {
    "prefix": {
      "address": {
        "value": "北"
      }
    }
  }
}

# 通配符查询 wildcard ?只能匹配一个任意字符 *匹配0到任意多个
GET /ems/emp/_search
{
  "query": {
    "wildcard": {
      "address": {
        "value": "北?"
      }
    }
  }
}

# 多个id查询 ids查询
GET /ems/emp/_search
{
  "query": {
    "ids": {
      "values": ["sdGK33wB7JXPW5S2Hf74","tdGK33wB7JXPW5S2Hf74","s9GK33wB7JXPW5S2Hf74"]
    }
  }
}

# fuzzy 模糊查询 最大模糊错误必须在0-2之间
# 搜索关键词长度为2 不允许存在模糊 0
# 搜索关键词长度为3-5 允许一次模糊 0 1
# 搜索关键词长度大于5 允许最大两个模糊
GET /ems/emp/_search
{
  "query": {
    "fuzzy": {
      "content": "elasticsoorch"
    }
  }
}

# 布尔查询 bool must should must_not (且或非的关系)
GET /ems/emp/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "age": {
              "value": 23
            }
          }
        },
        {
          "term": {
            "address": {
              "value": "北平"
            }
          }
        }
      ]
    }
  }
}

# 高亮查询 highlight 查询结果做二次渲染 高亮
GET /ems/emp/_search
{
  "query": {
    "term": {
      "content": {
        "value": "redis"
      }
    }
  },
  "highlight": {
    "pre_tags": ["<span style='color:red'>"],
    "post_tags": ["</span>"],
    "fields": {
      "content": {}
    }
  }
}

# 多字段查询 multi_match (有分词的) 搜索比较智能
# 如果搜索的字段分词 他会对query进行先 分词再搜索
# 如果搜索的字段不分词,他会直接使用query整体进该字段搜索
GET /ems/emp/_search
{
  "query": {
    "multi_match": {
      "query": "中国",
      "fields": ["name","content"]
    }
  }
}

# 多字段分词查询 query_string
GET /ems/emp/_search
{
  "query": {
    "query_string": {
      "query": "小Redis",
      "fields": ["name","content"]
    }
  }
}
2、索引库底层原理分析

ElasticSearch相对于MySQL数据库,更好的我们叫它为索引库(加入分词器后,我就彻底叫他搜索引擎了。。),它分为两个过程:先是建立索引,再是检索,先贴张图:

3.ES中索引的库的底层原理.jpg

简单缕下流程:ES服务开启,现在有一条文档插入到ES服务的索引库中,索引库会根据文档中的类型、映射关系将文档进行拆分(分词),将细化的数据存放到索引区中;然后再把整个文档数据存入到元数据区一份(顺序可能反了,好像是先存元数据),以索引的_id作为连接,进行先在索引区查询,拿到_id后去元数据区查询的二次查询。

贴一个查询场景: 现在用户想要查找address为"北京"的文档,ES会进行两次搜索,先是在索引区[分词后存放的数据]找到相匹配的文档的"id",再是按照"id"去找到元数据区对应的文档,最后按照:比如在0文档出现的次数多,就把0文档排在前面,展示给用户。

所以接下来就要引入IK分词器了(我们的中文分词器)

3、IK分词器

默认ES中采用标准分词器进行分词,这种方式并不适用于中文网站,因此需要修改ES对中文友好分词,从而达到更佳的搜索的效果。

IK的github: https://github.com/medcl/elasticsearch-analysis-ik

安装的话分在线安装和本地安装,在线我就不做了,没连上github,而且老师在这里遇到了一个坑,他是把在线和本地两种方式都做了演示,但是在之后做拓展分词配置的时候修改错了配置文件(本地安装和在线安装的这个配置文件默认目录是不一样的),所以拓展分词一直不生效,哈哈哈哈,自己做的时候,选择一种安装方式就好了。

本地安装IK:

1、进入es安装目录中将data目录数据删除:rm -rf data
2、下载对应版本:wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.8.0/elasticsearch-analysis-ik-6.8.0.zip
3、解压:unzip elasticsearch-analysis-ik-6.8.0.zip #先使用yum install -y unzip
4、有个坑是直接unzip 的话文件就都跑出来了 ,建议unzip -d 指定文件夹名(比如elasticsearch)后 再去移动到es 的plugins文件夹下
5、比如这样:mv elasticsearch elasticsearch-6.8.0/plugins/
6、重启es生效,建议把kibana也重启一下
7、本地安装ik配置目录为:es安装目录中/plugins/elasticsearch/config/IKAnalyzer.cfg.xml

测试下IK分词器:

IK分词类型:

IK分词器提供了两种mapping类型用来做文档的分词分别是 ik_max_word 和 ik_smart

ik_max_word 和 ik_smart 什么区别?

ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;
ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”
测试数据:

# 1.使用ik分词器
PUT /ems
{
  "mappings":{
    "emp":{
      "properties":{
        "name":{
          "type":"text",
           "analyzer": "ik_max_word",
           "search_analyzer": "ik_max_word"
        },
        "age":{
          "type":"integer"
        },
        "bir":{
          "type":"date"
        },
        "content":{
          "type":"text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_max_word"
        },
        "address":{
          "type":"keyword"
        }
      }
    }
  }
}

GET /ems/_mapping

PUT /ems/emp/_bulk
  {"index":{}}
  {"name":"小黑","age":23,"bir":"2012-12-12","content":"为开发团队选择一款优秀的MVC框架是件难事儿,在众多可行的方案中决择需要很高的经验和水平","address":"北京"}
  {"index":{}}
  {"name":"王小黑","age":24,"bir":"2012-12-12","content":"Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式","address":"上海"}
  {"index":{}}
  {"name":"张小五","age":8,"bir":"2012-12-12","content":"Spring Cloud 作为Java 语言的微服务框架,它依赖于Spring Boot,有快速开发、持续交付和容易部署等特点。Spring Cloud 的组件非常多,涉及微服务的方方面面,井在开源社区Spring 和Netflix 、Pivotal 两大公司的推动下越来越完善","address":"无锡"}
  {"index":{}}
  {"name":"win7","age":9,"bir":"2012-12-12","content":"Spring的目标是致力于全方位的简化Java开发。 这势必引出更多的解释, Spring是如何简化Java开发的?","address":"南京"}
  {"index":{}}
  {"name":"梅超风","age":43,"bir":"2012-12-12","content":"Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API","address":"杭州"}
  {"index":{}}
  {"name":"张无忌","age":59,"bir":"2012-12-12","content":"ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口","address":"北京"}
  
  GET /ems/emp/_search
  {
    "query": {
      "term": {
        "content": {
          "value": "搜索引擎"
        }
      }
    }
  }

7.PNG

可见我在content中的IK分词粒度设置的是max,检索搜索引擎可以搜索到,没问题,这里有一点要注意:

        "content":{
          "type":"text",
          "analyzer": "ik_max_word",
          "search_analyzer": "ik_max_word"
        }

search_analyzer其实是没必要写的,因为在analyzer中设置了ik_max_word,search_analyzer是默认跟随该粒度的。(不过写上也无所谓)

配置拓展词: IK支持自定义 扩展词典 和 停用词典

扩展词典: 就是有些词并不是关键词,但是也希望被ES用来作为检索的关键词,可以将这些词加入扩展词典。

停用词典: 就是有些词是关键词,但是出于业务场景不想使用这些关键词被检索到,可以将这些词放入停用词典。比如大王八。

几个关键文件和该yml文件位置(我的路径):

8.PNG

main.dic是IK的默认字典。这里配置完yml后重启es,不-d启动的话是可以看到es加载了拓展字典。测试:

  PUT /ems/emp/1
  {
    "name":"惊天秘密","age":23,"bir":"2012-12-12","content":"碰瓷出现杠精事件,蓝瘦","address":"北京"
  }
  
      GET /ems/emp/_search
  {
    "query": {
      "match_all": {}
      }
    }
  
    GET /ems/emp/_search
  {
    "query": {
      "term": {
        "content": {
          "value": "碰瓷"
        }
      }
    }
  }

我刚才在拓展字典加进了"碰瓷",在main.dic里是没有的,重启es后对content中的该词进行检索发现已经可以收到了:

9.PNG

注意:词典的编码必须为UTF-8,否则无法生效

远程配置字典不做了,可以自己建个springboot项目建个字典放web上试一试,可以去参考:8_IK分词器

4、(过滤查询) Filter Query

很关键的一个东西了,以后肯定都是filter和query组合用。

准确来说,ES中的查询操作分为2种: 查询(query)和过滤(filter)

查询即是之前提到的query查询,它 (查询)默认会计算每个返回文档的得分,然后根据得分排序。而过滤(filter)只会筛选出符合的文档,并不计算 得分,且它可以缓存文档 。所以,单从性能考虑,过滤比查询更快。

换句话说,过滤适合在大范围筛选数据,而查询则适合精确匹配数据。一般应用时, 应先使用过滤操作过滤数据, 然后使用查询匹配数据。

10.PNG

基本语法:

# 1.过滤使用filter
  GET /ems/emp/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "content": {
              "value": "框架"
            }
          }
        }
      ],
      "filter": {
        "range": {
          "age": {
            "gte": 0,
            "lte": 23
          }
        }
      }
    }
  }
}

注意: 1.在执行filter和query时,先执行filter在执行query 2.Elasticsearch会自动缓存经常使用的过滤器,以加快性能。 3.使用过滤查询必须使用bool查询

几个常见的过滤器类型:

# 2.基于指定关键词过滤 term terms
GET /ems/emp/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "name": {
              "value": "梅超风"
            }
          }
        }
      ],
      "filter": {
        "terms": {
          "content": ["架构","redis"]
        }
      }
    }
  }
}

  PUT /ems/emp/2
  {
    "name":"惊天秘密","age":23,"bir":"2012-12-12","content":"中国馆国歌","address":"北京","aaa":"999"
  }

# 3.exists 存在指定字段的filter query
GET /ems/emp/_search
{
  "query": {
    "bool": {
      "must": [
        {"term": {
          "content": {
            "value": "中国"
          }
        }}
      ],
      "filter": {
        "exists": {
          "field":"aaa"
        }
      }
    }
  }
}

# 4.ids filter
GET /ems/emp/_search
{
  "query": {
    "bool": {
      "must": [
        {"term": {
          "content": {
            "value": "中国"
          }
        }}
      ],
      "filter": {
        "ids": {
          "values": ["1","2","3"]
        }
      }
    }
  }
}

我在ES(1)里说他是非关系型数据库,这样是没错,但是自打学习了分词的概念后,还是叫他搜索引擎吧,太强大了😆

根据课程,接下来就应该是用java和springboot操作ES和搭建集群了(可能还有个简单项目实操吧),集群和以前一样,不做了,争取在ES(3)中把剩下的操作记录完。


标题:ES(2)
作者:jyl
地址:http://jinyunlong.xyz/articles/2021/11/04/1635993443574.html