ES之二:Elasticsearch原理
由上图看出, QueryBuilder 是整个查询操作的核心,决定了查询什么样的数据和期望得到什么结果这些核心的问题。
QueryBuilder 只是一个接口,需要具体的实体类才可以。那么如何创建 QueryBuilder 的实例呢?有两种方式
- 通过
QueryBuilder实现类的构造函数 - 使用
QueryBuilders工具类创建
Building Queries
下面就来看下常用的查询及其 API 有哪些
匹配所有的查询
查询语句如下
GET /_search
{
"query": {
"match_all": {}
}
}
对应的 QueryBuilder Class 为 MatchAllQueryBuilder
具体方法为 QueryBuilders.matchAllQuery()
全文查询 Full Text Queries
什么是全文查询?
像使用 match 或者 query_string 这样的高层查询都属于全文查询,
- 查询 日期(
date) 或整数(integer) 字段,会将查询字符串分别作为日期或整数对待。 - 查询一个(
not_analyzed)未分析的精确值字符串字段,会将整个查询字符串作为单个词项对待。 - 查询一个(
analyzed)已分析的全文字段,会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表
组成了词项列表,后面就会对每个词项逐一执行底层查询,将查询结果合并,并且为每个文档生成最终的相关度评分。
Match
match 查询的单个词的步骤是什么?
- 检查字段类型,查看字段是
analyzed,not_analyzed - 分析查询字符串,如果只有一个单词项,
match查询在执行时就会是单个底层的term查询 - 查找匹配的文档,会在倒排索引中查找匹配文档,然后获取一组包含该项的文档
- 为每个文档评分
构建 Match 查询
match 查询可以接受 text/numeric/dates 格式的参数,分析,并构建一个查询。
GET /_search
{
"query": {
"match" : {
"message" : "this is a test"
}
}
}
上面的实例中 message 是一个字段名。
对应的 QueryBuilder class : MatchQueryBuilder
具体方法 : QueryBuilders.matchQuery()
全文查询 API 列表
全部的 API 列表如下(链接均指向 elasticsearch 官网)
基于词项的查询
这种类型的查询不需要分析,它们是对单个词项操作,只是在倒排索引中查找准确的词项(精确匹配)并且使用 TF/IDF 算法为每个包含词项的文档计算相关度评分 _score。
Term
term 查询可用作精确值匹配,精确值的类型则可以是数字,时间,布尔类型,或者是那些 not_analyzed 的字符串。
对应的 QueryBuilder class 是TermQueryBuilder
具体方法是 QueryBuilders.termQuery()
Terms
terms 查询允许指定多个值进行匹配。如果这个字段包含了指定值中的任何一个值,就表示该文档满足条件。
对应的 QueryBuilder class 是 TermsQueryBuilder
具体方法是 QueryBuilders.termsQuery()
Wildcard
wildcard 通配符查询是一种底层基于词的查询,它允许指定匹配的正则表达式。而且它使用的是标准的 shell 通配符查询:
?匹配任意字符*匹配 0 个或多个字符
wildcard 需要扫描倒排索引中的词列表才能找到所有匹配的词,然后依次获取每个词相关的文档 ID。
由于通配符和正则表达式只能在查询时才能完成,因此查询效率会比较低,在需要高性能的场合,应当谨慎使用。
对应的 QueryBuilder class 是 WildcardQueryBuilder
具体方法是 QueryBuilders.wildcardQuery()
基于词项 API 列表
复合查询
什么是复合查询?
复合查询会将其他的复合查询或者叶查询包裹起来,以嵌套的形式展示和执行,得到的结果也是对各个子查询结果和分数的合并。可以分为下面几种:
-
经常用在使用 filter 的场合,所有匹配的文档分数都是一个不变的常量
-
可以将多个叶查询和组合查询再组合起来,可接受的参数如下
- must : 文档必须匹配这些条件才能被包含进来
must_not文档必须不匹配才能被包含进来should如果满足其中的任何语句,都会增加分数;即使不满足,也没有影响filter以过滤模式进行,不评分,但是必须匹配
-
叫做分离最大化查询,它会将任何与查询匹配的文档都作为结果返回,但是只是将其中最佳匹配的评分作为最终的评分返回。
-
允许为每个与主查询匹配的文档应用一个函数,可用来改变甚至替换原始的评分
-
用来控制(提高或降低)复合查询中子查询的权重。
复合查询列表
特殊查询
Wrapper Query
这里比较重要的一个是 Wrapper Query,是说可以接受任何其他 base64 编码的字符串作为子查询。
主要应用场合就是在 Rest High-Level REST client 中接受 json 字符串作为参数。比如使用 gson 等 json 库将要查询的语句拼接好,直接塞到 Wrapper Query 中查询就可以了,非常方便。
Wrapper Query 对应的 QueryBuilder class 是WrapperQueryBuilder
具体方法是 QueryBuilders.wrapperQuery()
小结
本文对 elasticsearch rest high client 中的查询构建进行了总结和整理,对常用的 API 做了简要的介绍。读者如果要查看完整的构建查询的 API 列表,可参考此处
参考文档
一)text字段和keyword字段的区别
所以将字段设置成keyword的时候查询的时候已有的值不会被分词。而text类型的字段会被分词,查询的时候如果用拆开查可以查询的到,但是要是直接全部查,就是查询不到。
注意“1, 2”会被拆分成[1, 2],但是"1,2"是不拆分的,少了个空格。
一、match、match_phrase、query_string和term的区别
1、match和term的区别
1.1、term
1)term查询keyword字段。
term不会分词。而keyword字段也不分词。需要完全匹配才可。
2)term查询text字段
因为text字段会分词,而term不分词,所以term查询的条件必须是text字段分词后的某一个。
1.2.match
1)match查询keyword字段
match会被分词,而keyword不会被分词,match的需要跟keyword的完全匹配可以。
其他的不完全匹配的都是失败的。
2)match查询text字段
match分词,text也分词,只要match的分词结果和text的分词结果有相同的就匹配。
1.3.match_phrase
1)match_phrase匹配keyword字段。
这个同上必须跟keywork一致才可以。
2)match_phrase匹配text字段。
match_phrase是分词的,text也是分词的。match_phrase的分词结果必须在text字段分词中都包含,而且顺序必须相同,而且必须都是连续的。
1.4.query_string
1)query_string查询keyword类型的字段,试过了,无法查询。
2)query_string查询text类型的字段。
和match_phrase区别的是,不需要连续,顺序还可以调换。
二、关于Elasticsearch的精确值查找(term)不生效问题
2.1、问题
常用的 term 查询, 可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)。term查询数字的时候并没有什么问题,但是当我们对字符串类型的字段进行term查询时可能会得到意想不到的情况,可能明明有记录却查询不到,也可能查询出不符合预期的记录。
原因
ES会默认给每个字段进行分词然后建立倒排索引。比如,有两条JSON数据如下:
[
{
"id" :
"searchField" : "abc#def"
},
{
"id" :
"searchField" : "abc#xyz"
}
]
使用ES提供的analyze API 可以看到分词结果如下:
按照上面的分词结果,那么当我们将这两条数据插入ES的时候,建立的倒排索引如下:
Term Counter DocId
abc 2 1,2
def 1 1
xyz 1 2
注意:如果字段内容是大写的,那么在分词生成索引后,索引的项目会变成小写,比如上面的两条数据是ABC#DEF和ABC#XYZ,那么生成的索引也和上面的一样。此时由于索引项是小写,因此term查询ABC是查不到的,必须要查询abc;match查询ABC是可以查询到的,因为match会进行分词然后再匹配。
①使用term精确查询searchField为abc#def的记录:
{
"query": {
"term": {
"searchField":"abc#def"
}
}
}
此时得到的结果是空,我们无法获得期望的结果,问题不在 term 查询,而在于abc#def并不在我们的倒排索引中。
②使用term精确查询searchField为abc的记录:
{
"query": {
"term": {
"searchField":"abc"
}
}
}
此时得到的结果是两条数据都被检索出来。
根据建立的倒排索引不难发现,当精确匹配abc时,那么会命中如下的索引,它的DocId是1,2,因此会查出两条记录。
解决方案
①将字段的type设置为keyword
明确字段是否需要分词,不需要分词的字段就将type设置为keyword,可以节省空间和提高写性能。
ElasticSearch 5.0以后,String字段被拆分成两种新的数据类型: text用于全文搜索,会分词,而keyword用于关键词搜索,不进行分词。对于字符串类型的字段,ES默认会再生成一个keyword字段用于精确索引。默认mapping如下:
"mapping": {
"properties": {
"id": {
"type": "long"
},
"searchField": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above":
}
}
}
}
②将该字段设置成 not_analyzed 无需分析的
告诉 Elasticsearch该字段具有精确值,要将index属性设置成 not_analyzed 无需分析的。也是在mapping中进行设置,例如:
"mapping": {
"properties": {
"id": {
"type": "long"
},
"searchField": {
"type": "text",
"index": "not_analyzed"
}
}
如果是使用Java High Level REST Client 操作Elasticsearch的话可以参考官方API进行设置。
例如:
XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
builder.startObject("properties");
{
builder.startObject("message");
{
builder.field("type", "text");
}
builder.endObject();
}
builder.endObject();
}
builder.endObject();
request.source(builder);
index 属性控制怎样索引字符串。它可以是下面三个值:
① analyzed:首先分析字符串,然后索引它。换句话说,以全文索引这个域。
② not_analyzed:索引这个域,所以它能够被搜索,但索引的是精确值。不会对它进行分析。
③ no:不索引这个域。这个域不会被搜索到。
注意:其他简单类型(例如 long , double , date 等)也接受 index 参数,但有意义的值只有 no 和 not_analyzed , 因为它们永远不会被分析。
2.2、elasticsearch大小写无法使用term查询的问题
在 Elasticsearch中处理字符串类型的数据时,如果我们想把整个字符串作为一个完整的 term 存储,我们通常会将其类型 type 设定为 keyword。但有时这种设定又会给我们带来麻烦,比如同一个数据再写入时由于没有做好清洗,导致大小写不一致,比如 apple、Apple两个实际都是 apple,但当我们去搜索 apple时却无法返回 Apple的文档。
原因是elasticsearch在创建倒排索引时,就已经将大写转为小写,而后写入索引。解决方法:
- 一种是在传入查询条件的时候,使用toLowerCase()转化为小写,但是条件一多,代码量颇多,不太适用。
- 一种是在设置mapping的时候设置normalizer要解决这个问题。
PUT test_normalizer{
"mappings": {
"doc":{
"properties": {
"type":{
"type":"keyword"
}
}
}
}
}
PUT test_normalizer/doc/{
"type":"apple"}
PUT test_normalizer/doc/{
"type":"Apple"}
# 查询一 GET test_normalizer/_search{
"query": {
"match":{
"type":"apple"
}
}
}
# 查询二GET test_normalizer/_search{
"query": {
"match":{
"type":"aPple"
}
}}

原因:
Docs写入Elasticsearch时由于type是keyword,分词结果为原始字符串- 查询 Query 时分词默认是采用和字段写时相同的配置,因此这里也是
keyword,因此分词结果也是原始字符 - 两边的分词进行匹对,便得出了我们上面的结果
2. Normalizer
normalizer是 keyword的一个属性,可以对 keyword生成的单一 Term再做进一步的处理,比如 lowercase,即做小写变换。使用方法和自定义分词器有些类似,需要自定义,如下所示:
DELETE test_normalizer
# 自定义 normalizer
PUT test_normalizer{
"settings": {
"analysis": {
"normalizer": {
"lowercase": {
"type": "custom",
"filter": [
"lowercase"
]
}
}
}
},
"mappings": {
"doc": {
"properties": {
"type": {
"type": "keyword"
},
"type_normalizer": {
"type": "keyword",
"normalizer": "lowercase"
}
}
}
}}
PUT test_normalizer/doc/{
"type": "apple",
"type_normalizer": "apple"}PUT test_normalizer/doc/{
"type": "Apple",
"type_normalizer": "Apple"}
# 查询三GET test_normalizer/_search{
"query": {
"term":{
"type":"aPple"
}
}}
# 查询四GET test_normalizer/_search{
"query": {
"term":{
"type_normalizer":"aPple"
}
}}
我们第一步是自定义了名为 lowercase的 normalizer,其中filter 类似自定义分词器中的 filter ,但是可用的种类很少,详情大家可以查看官方文档。然后通过 normalizer属性设定到字段type_normalizer中,然后插入相同的2条文档。执行发现,查询三无结果返回,查询四返回2条文档。
文档写入时由于加入了
normalizer,所有的term都会被做小写处理查询时搜索词同样采用有
normalizer的配置,因此处理后的term也是小写的两边分词匹对,就得到了我们上面的结果
原文链接:https://blog.csdn.net/u011821334/article/details/100979286
参考:http://www.imooc.com/article/72059
参考:https://blog.csdn.net/AlbertFly/article/details/100705554?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4
ES之二:Elasticsearch原理的更多相关文章
- ES的集群原理
文章转载自:https://www.cnblogs.com/soft2018/p/10213266.html 一.ES集群原理 查看集群健康状况:URL+ /GET _cat/health (1).E ...
- springboot整合es客户端操作elasticsearch(五)
springboot整合es客户端操作elasticsearch的总结: 客户端可以进行可以对所有文档进行查询,就是不加任何条件: SearchRequest searchRequest = new ...
- Java线程池使用和分析(二) - execute()原理
相关文章目录: Java线程池使用和分析(一) Java线程池使用和分析(二) - execute()原理 execute()是 java.util.concurrent.Executor接口中唯一的 ...
- Kylin系列之二:原理介绍
Kylin系列之二:原理介绍 2018年4月15日 15:52 因何而生 Kylin和hive的区别 1. hive主要是离线分析平台,适用于已经有成熟的报表体系,每天只要定时运行即可. 2. Kyl ...
- Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理
相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...
- ES之四、Elasticsearch集群和索引常用命令
REST API用途 elasticsearch支持多种通讯,其中包括http请求响应服务,因此通过curl命令,可以发送http请求,并得到json返回内容. ES提供了很多全面的API,常用的RE ...
- 终于有人把Elasticsearch原理讲透了!
终于有人把Elasticsearch原理讲透了! http://developer.51cto.com/art/201904/594615.htm 小史是一个非科班的程序员,虽然学的是电子专业,但是通 ...
- (转)终于有人把Elasticsearch原理讲透了!
终于有人把Elasticsearch原理讲透了! 小史是一个非科班的程序员,虽然学的是电子专业,但是通过自己的努力成功通过了面试,现在要开始迎接新生活了. 来源:互联网侦察 | 2019-04-08 ...
- es的分布式架构原理是什么?
es的分布式架构原理是什么? 1.首先说一些分片(shard)是什么? ES中所有数据均衡的存储在集群中各个节点的分片中,会影响ES的性能.安全和稳定性 每个shard都是一个最小工作单元,承载部分数 ...
随机推荐
- create_workqueue和create_singlethread_workqueue【转】
本文转载自:http://bgutech.blog.163.com/blog/static/18261124320116181119889/ 1. 什么是workqueueLinux中的Workque ...
- 高通平台MSM8916LCM模块移植(一)-bootloader部分【转】
本文转载自:http://www.mobile-open.com/2016/970947.html 高通平台中的bootloader叫做LK(Little Kernel,对于LCM来说LK部分相当重要 ...
- Luogu-1527 [国家集训队]矩阵乘法
Luogu-1527 [国家集训队]矩阵乘法 题面 Luogu-1527 题解 昨天学CDQ分治时做了一些题,但是因为题(wo)太(tai)水(lan)了(le)并没有整理 学了一晚上的整体二分,拿这 ...
- linux上不能显示Jfreechart的图片文件
出现错误: Jan 23, 2015 4:19:21 PM org.apache.catalina.core.StandardWrapperValve invokeSEVERE: Servlet.s ...
- WEB开发中常见漏洞
1.sql注入 SQL注入在黑客领域是一种非常常见的攻击手段,大家应该都听说过很多数据泄漏的案例,其中大部分都是采用SQL注入来获取数据的. SQL注入一般是前端向后台提交数据的时候,在数据中加入SQ ...
- mysql基础(1)-基本操作
数据库 数据库(Database,DB)是数据的集合,是一个长期存储在计算机内的.有组织的.有共享的.统一管理的数据集合. 存储数据 管理数据 数据库类型 关系型数据库:由二维表及其之间的联系组成的一 ...
- Java日期时间输出格式优化
使用printf格式化日期 printf 方法可以很轻松地格式化时间和日期.使用两个字母格式,它以 %t 开头并且以下面表格中的一个字母结尾. 转 换 符 说 明 示 例 c 包括全部 ...
- 【转载】postgreSQL在linux中安装详解
.编译环境 Linux: CentOS 5.5 gcc: 4.1.2 1. 安装PostgreSQL 1) 解压postgresql-9.1.7.tar.bz2 #tar jxvf postgresq ...
- The tag handler class for "c:forEach" (org.apache.taglibs.standard.tag.rt.core.ForEachTag) was not found on the Java Build Path
.tag出现如上错误 <%@ page language="java" contentType="text/html; charset=GB18030" ...
- (转)android头像设置:从本地照片库或拍照获取并剪裁
本文转载于:http://blog.csdn.net/sheeprunning/article/details/9184021 功能介绍 制作android应用时,用户注册的功能必不可少,往往还需要具 ...
