还不会Elasticsearch的结构化查询(精确、组合、范围、Null值的查询操作),看完这篇就会了

Elasticsearch的结构化查询



前言

提示:本文测试环境是在ES7.9的环境下,各种命令都是经过亲自测试实现的,所以有兴趣的可以自己实现一下,根据elastic官网中的一些例子做的一些测试操作,官网中一些命令在测试环境中都已经抛弃不用了。
例如type:“string”,以及missing命令也都在7.9版本中抛弃不再使用。


一、精确值查询

在Es中实现精确查询一般通过term实现。可以用term查询处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)。

先插入一些测试数据:

POST /my_store/products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }

然后通过term方式操作数字查询这些数据:

GET /my_store/products/_search
{
    "query" : {
        "constant_score" : { 
            "filter" : {
                "term" : { 
                    "price" : 20
                }
            }
        }
    }
}

以上查询语句中通过constant_score将term转化为过滤器。

在这里插入图片描述
通过term方式操作文本查询:

GET /my_store/products/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "term" : {
                    "productID" : "XHDK-A-1293-#fJ3"
                }
            }
        }
    }
}

但这里有个小问题:我们无法获得期望的结果。为什么呢?问题不在 term 查询,而在于索引数据的方式。 如果我们使用 analyze API (分析 API),我们可以看到这里的 UPC 码被拆分成多个更小的 token。

GET /my_store/_analyze
{
  "field": "productID",
  "text": "XHDK-A-1293-#fJ3"
}

在这里插入图片描述
这里有几点需要注意:
• Elasticsearch 用 4 个不同的 token 而不是单个 token 来表示这个 UPC 。
• 所有字母都是小写的。
• 丢失了连字符和哈希符( # )。

所以当我们用 term 查询查找精确值 XHDK-A-1293-#fJ3 的时候,找不到任何文档,因为它并不在我们的倒排索引中,正如前面呈现出的分析结果,索引里有四个 token 。

显然这种对 ID 码或其他任何精确值的处理方式并不是我们想要的。

为了避免这种问题,我们需要告诉 Elasticsearch 该字段具有精确值,要将其设置成 not_analyzed 无需分析的。 我们可以在 自定义字段映射 中查看它的用法。为了修正搜索结果,我们需要首先删除旧索引(因为它的映射不再正确)然后创建一个能正确映射的新索引:

DELETE /my_store 
 
PUT /my_store  
{
    "mappings" : {
          "properties" : {
              "productID" : {
                  "type" : "keyword",
                  "index" : true  
              }
          }
    }
}

然后新增数据

POST /my_store/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10, "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20, "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30, "productID" : "QQPX-R-3956-#aD8" }

重新执行查询语句:

GET /my_store/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "term" : {
                    "productID" : "XHDK-A-1293-#fJ3"
                }
            }
        }
    }
}

在这里插入图片描述

1.内部过滤器的操作

在内部,Elasticsearch 会在运行非评分查询的时执行多个操作:

  1. 查找匹配文档.
    term 查询在倒排索引中查找 XHDK-A-1293-#fJ3 然后获取包含该 term 的所有文档。本例中,只有文档 1 满足我们要求。
  2. 创建 bitset.
    过滤器会创建一个 bitset (一个包含 0 和 1 的数组),它描述了哪个文档会包含该 term 。匹配文档的标志位是 1 。本例中,bitset 的值为 [1,0,0,0] 。在内部,它表示成一个 “roaring bitmap”,可以同时对稀疏或密集的集合进行高效编码。
  3. 迭代 bitset(s)
    一旦为每个查询生成了 bitsets ,Elasticsearch 就会循环迭代 bitsets 从而找到满足所有过滤条件的匹配文档的集合。执行顺序是启发式的,但一般来说先迭代稀疏的 bitset (因为它可以排除掉大量的文档)。
  4. 增量使用计数.
    Elasticsearch 能够缓存非评分查询从而获取更快的访问,但是它也会不太聪明地缓存一些使用极少的东西。非评分计算因为倒排索引已经足够快了,所以我们只想缓存那些我们 知道 在将来会被再次使用的查询,以避免资源的浪费。
    为了实现以上设想,Elasticsearch 会为每个索引跟踪保留查询使用的历史状态。如果查询在最近的 256 次查询中会被用到,那么它就会被缓存到内存中。当 bitset 被缓存后,缓存会在那些低于 10,000 个文档(或少于 3% 的总索引数)的段(segment)中被忽略。这些小的段即将会消失,所以为它们分配缓存是一种浪费。

二、组合过滤器查询

组个过滤器查询可以理解为sql语句中where后面的条件存在多个。

1.bool过滤器

在es中存在一个bool过滤器,该过滤器由三部分组成

{
   "bool" : {
      "must" :     [],
      "should" :   [],
      "must_not" : [],
   }
}
  • must所有的语句都 必须(must) 匹配,与 AND 等价。
  • must_not所有的语句都 不能(must not) 匹配,与 NOT 等价。
  • should至少有一个语句要匹配,与 OR 等价。

当我们需要多个过滤器时,只须将它们置入 bool 过滤器的不同部分即可。

执行查询语句:

GET /my_store/_search
{
   "query" : {
        "bool" : {
          "should" : [
             { "term" : {"price" : 20}},  
             { "term" : {"productID" : "XHDK-A-1293-#fJ3"}}  
          ],
          "must_not" : {
             "term" : {"price" : 30}  
          }
       }
   }
}

在这里插入图片描述

2.组合bool过滤器

实现语句为:

GET /my_store/_search
{
   "query" : {
        "bool" : {
          "should" : [
            { "term" : {"productID" : "KDKE-B-9947-#kL5"}},  
            { "bool" : {  
              "must" : [
                { "term" : {"productID" : "JODL-X-1937-#pV7"}},  
                { "term" : {"price" : 30}}  
              ]
            }}
          ]
       }
   }
}

在这里插入图片描述

三、多个精确值查询

term 查询对于查找单个值非常有用,但通常我们可能想搜索多个值。 如果我们想要查找价格字段值为 $20 或 $30 的文档该如何处理呢?
不需要使用多个 term 查询,我们只要用单个 terms 查询(注意末尾的 s ), terms 查询好比是 term 查询的复数形式(以英语名词的单复数做比)。

它几乎与 term 的使用方式一模一样,与指定单个价格不同,我们只要将 term 字段的值改为数组即可:

GET /my_store/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "terms" : {  
                    "price" : [20, 30]
                }
            }
        }
    }
}

在这里插入图片描述
一定要了解 term 和 terms 是 包含(contains) 操作,而非 等值(equals) (判断)。 如何理解这句话呢?

如果我们有一个 term(词项)过滤器 { “term” : { “tags” : “search” } } ,它会与以下两个文档 同时 匹配:

{ “tags” : [“search”] }
{ “tags” : [“search”, “open_source”] }

Elasticsearch 会在倒排索引中查找包括某 term 的所有文档,然后构造一个 bitset 。在我们的例子中,倒排索引表如下:
在这里插入图片描述
这里解释一下什么是倒排索引:

倒排索引的理解:正排索引是根据文档查询索引,而倒排索引是根据索引查询文档。举例来说,如果文档与索引是key-value的关系,那么正排索引的key就是文档,value就是文档中的索引值;那么倒排索引则正好反过来,key是文档中的索引,value是文档
对应关系如下:
正排索引:
在这里插入图片描述
倒排索引:
在这里插入图片描述
当 term 查询匹配标记 search 时,它直接在倒排索引中找到记录并获取相关的文档 ID,如倒排索引所示,这里文档 1 和文档 2 均包含该标记,所以两个文档会同时作为结果返回。
所以要实现精确相等时,可以使用term来实现,通过term对应其中的值来实现精确的查询。

GET /my_index/my_type/_search
{
    "query": {
        "constant_score" : {
            "filter" : {
                 "bool" : {
                    "must" : [
                        { "term" : { "tags" : "search" } }, 
                        { "term" : { "tag_count" : 1 } } 
                    ]
                }
            }
        }
    }
}

四、范围查询

对数字范围进行过滤有时会更有用。例如,我们可能想要查找所有价格大于 $20 且小于 $40 美元的产品。
在sql中也经常会使用范围查询比如查询价格大于2小于5的商品。
而在es中则通过range 来实现范围查询
range 查询可同时提供包含(inclusive)和不包含(exclusive)这两种范围表达式,可供组合的选项如下:
• gt: > 大于(greater than)
• lt: < 小于(less than)
• gte: >= 大于或等于(greater than or equal to)
• lte: <= 小于或等于(less than or equal to)
查询语句如下:

GET /my_store/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                    "price" : {
                        "gte" : 20,
                        "lt"  : 40
                    }
                }
            }
        }
    }
}

在这里插入图片描述
range 查询同样可以应用在日期字段上:

GET /my_store/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                  "timestamp" : {
                      "gt" : "2014-01-01 00:00:00",
                      "lt" : "2014-01-07 00:00:00"
                  }
              }
            }
        }
    }
}

range 查询同样可以处理字符串字段:
字符串范围可采用 字典顺序(lexicographically) 或字母顺序(alphabetically)。
例如,下面这些字符串是采用字典序(lexicographically)排序的:
• 5, 50, 6, B, C, a, ab, abb, abc, b
在倒排索引中的词项就是采取字典顺序(lexicographically)排列的,这也是字符串范围可以使用这个顺序来确定的原因。
如果我们想查找从 a 到 b (不包含)的字符串,同样可以使用 range 查询语法:

"range" : {
    "title" : {
        "gte" : "a",
        "lt" :  "b"
    }
}

五、处理Null值

如果一个字段没有值,那么如何将它存入倒排索引中的呢?
这是个有欺骗性的问题,因为答案是:什么都不存。让我们看看之前内容里提到过的倒排索引:
在这里插入图片描述
如何将某个不存在的字段存储在这个数据结构中呢?无法做到!简单的说,一个倒排索引只是一个 token 列表和与之相关的文档信息,如果字段不存在,那么它也不会持有任何 token,也就无法在倒排索引结构中表现。

最终,这也就意味着,null, [] (空数组)和 [null] 所有这些都是等价的,它们无法存于倒排索引中。

显然,数据往往会有缺失字段,或有显式的空值或空数组。为了应对这些状况,Elasticsearch 提供了一些工具来处理空或缺失值。

1.存在查询

exists 存在查询。这个查询会返回那些在指定字段有任何值的文档,让我们索引一些示例文档并用标签的例子来说明:

POST /my_index/posts/_bulk
{ "index": { "_id": "1"              }}
{ "tags" : ["search"]                }  
{ "index": { "_id": "2"              }}
{ "tags" : ["search", "open_source"] }  
{ "index": { "_id": "3"              }}
{ "other_field" : "some data"        }  
{ "index": { "_id": "4"              }}
{ "tags" : null                      }  
{ "index": { "_id": "5"              }}
{ "tags" : ["search", null]          }  

在这里插入图片描述
exits查询如下:

GET /my_index/posts/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "exists" : { "field" : "tags" }
            }
        }
    }
}

在这里插入图片描述

以上文档集合中 tags 字段对应的倒排索引如下:

2.缺失查询

缺失 查询本质上与 exists 恰好相反:它返回某个特定 无 值字段的文档,与以下 SQL 表达的意思类似:

SELECT tags
FROM   posts
WHERE  tags IS NULL

因此es实现时只要通过bool过滤器将exit过滤掉就可以实现缺失查询
es实现语句如下:

GET /my_index/posts/_search
{
    "query" : {
      "bool": {
        "must_not": {
          "exists" : { "field" : "tags" }
        }
      }
    }
}

在这里插入图片描述


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值