转译:(https://www.elastic.co/guide/en/elasticsearch/guide/current/_finding_exact_values.html#_finding_exact_values

当进行精确值查找时, 我们会使用过滤器(filters)。过滤器很重要,因为它们执行速度非常快,不会计算相关度(直接跳过了整个评分阶段)而且很容易被缓存。不过现在只要记住:请尽可能多的使用过滤式查询。

term 查询数字编辑

我们首先来看最为常用的 term 查询, 可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)。

让我们以下面的例子开始介绍,创建并索引一些表示产品的文档,文档里有字段 `price` 和 `productID` ( `价格` 和 `产品ID` ):
POST /my_store/products/_bulk
{ "index": { "_id": }}
{ "price" : , "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": }}
{ "price" : , "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": }}
{ "price" : , "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": }}
{ "price" : , "productID" : "QQPX-R-3956-#aD8" }

我们想要做的是查找具有某个价格的所有产品,有关系数据库背景的人肯定熟悉 SQL,如果我们将其用 SQL 形式表达,会是下面这样:

SELECT document
FROM products
WHERE price =

在 Elasticsearch 的查询表达式(query DSL)中,我们可以使用 term 查询达到相同的目的。 term 查询会查找我们指定的精确值。作为其本身, term 查询是简单的。它接受一个字段名以及我们希望查找的数值:

{
"term" : {
"price" :
}
}

通常当查找一个精确值的时候,我们不希望对查询进行评分计算。只希望对文档进行包括或排除的计算,所以我们会使用 constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分。

最终组合的结果是一个 constant_score 查询,它包含一个 term 查询:

GET /my_store/products/_search
{
"query" : {
"constant_score" : {

            "filter" : {
"term" : {

                    "price" : 20
}
}
}
}
}
 

我们用 constant_scoreterm 查询转化成为过滤器

我们之前看到过的 term 查询

执行后,这个查询所搜索到的结果与我们期望的一致:只有文档 2 命中并作为结果返回(因为只有 2 的价格是 20 ):

"hits" : [
{
"_index" : "my_store",
"_type" : "products",
"_id" : "2",
"_score" : 1.0,

        "_source" : {
"price" : 20,
"productID" : "KDKE-B-9947-#kL5"
}
}
]

查询置于 filter 语句内不进行评分或相关度的计算,所以所有的结果都会返回一个默认评分 1

term 查询文本编辑

如本部分开始处提到过的一样 ,使用 term 查询匹配字符串和匹配数字一样容易。如果我们想要查询某个具体 UPC ID 的产品,使用 SQL 表达式会是如下这样:

SELECT product
FROM products
WHERE productID = "XHDK-A-1293-#fJ3"

转换成查询表达式(query DSL),同样使用 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"
}

如下:

{
"tokens" : [ {
"token" : "xhdk",
"start_offset" : 0,
"end_offset" : 4,
"type" : "<ALPHANUM>",
"position" : 1
}, {
"token" : "a",
"start_offset" : 5,
"end_offset" : 6,
"type" : "<ALPHANUM>",
"position" : 2
}, {
"token" : "1293",
"start_offset" : 7,
"end_offset" : 11,
"type" : "<NUM>",
"position" : 3
}, {
"token" : "fj3",
"start_offset" : 13,
"end_offset" : 16,
"type" : "<ALPHANUM>",
"position" : 4
} ]
}

这里有几点需要注意:

  • Elasticsearch 用 4 个不同的 token 而不是单个 token 来表示这个 UPC 。
  • 所有字母都是小写的。
  • 丢失了连字符和哈希符( # )。

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

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

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

DELETE /my_store 

PUT /my_store 

{
"mappings" : {
"products" : {
"properties" : {
"productID" : {
"type" : "string",
"index" : "not_analyzed"

                }
}
}
} }
执行顺序说明:

删除索引是必须的,因为我们不能更新已存在的映射。

在索引被删除后,我们可以创建新的索引并为其指定自定义映射。

这里我们告诉 Elasticsearch ,我们不想对 productID 做任何分析。

现在我们可以为文档重建索引:

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

此时, term 查询就能搜索到我们想要的结果,让我们再次搜索新索引过的数据(注意,查询和过滤并没有发生任何改变,改变的是数据映射的方式):

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

因为 productID 字段是未分析过的, term 查询不会对其做任何分析,查询会进行精确查找并返回文档 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)中被忽略。这些小的段即将会消失,所以为它们分配缓存是一种浪费。

实际情况并非如此(执行有它的复杂性,这取决于查询计划是如何重新规划的,有些启发式的算法是基于查询代价的),理论上非评分查询 先于 评分查询执行。非评分查询任务旨在降低那些将对评分查询计算带来更高成本的文档数量,从而达到快速搜索的目的。

从概念上记住非评分计算是首先执行的,这将有助于写出高效又快速的搜索请求。

Elasticsearch-精确查找的更多相关文章

  1. django 在字符串[str(list)]中精确查找

    1.问题描述 1.1表结构 1.2问题 ref_list为id列表的字符串,需要从ref_list中找出包含指定id的数据(eg id=8).如果实用models.objects.filter(ref ...

  2. Lucene全文搜索 分组,精确查找,模糊查找

    http://zm603380946.iteye.com/blog/1827318 完全个人理解,如有更好的方法,欢迎一起讨论 LuceneUtils.java package com.zbiti.l ...

  3. pip list 精确查找某一模块的方法

    1. 今天搜资料的时候get一项技能: pip list精确查找某一模块 命令如下: pip list | findstr "win32" (此处win32可以替换成任意想查找的模 ...

  4. ElasticSearch关联查找

    ElasticSearch是一个基于Lucene的开源搜索引擎,支持全文检索,提供restful接口.在ES中,提供了类似于MongoDB的面向文档存储服务,这种面向文档的存储非常灵活,但是文档与文档 ...

  5. linux精确查找命令

    1. find命令 命令 功能:搜寻文件与目录 功能: 语法: 语法:find 目录名 选项 常用选项有: 常用选项有: -name filename按名字查找 按名字查找 -type x 查找类型为 ...

  6. .Net+SQL Server企业应用性能优化笔记—精确查找瓶颈

    首先我们需要部署一个测试环境,将Web项目的源代码拷到测试环境Web服务器IIS上,使得可以直接通过IE访问我们的网站.SQL Server环境可以部署在同一台机器上,条件允许的话有专门的数据库测试服 ...

  7. FIND_IN_SET 精确查找

    FIND_IN_SET(str,strlist) mysql专为精确匹配字符串而设置的函数 一个字符串列表就是一个由一些被‘,’符号分开的自链组成的字符串 1,2,3,4,5,6,7,8,9: 此函数 ...

  8. ElasticSearch 5学习(10)——结构化查询(包括新特性)

    之前我们所有的查询都属于命令行查询,但是不利于复杂的查询,而且一般在项目开发中不使用命令行查询方式,只有在调试测试时使用简单命令行查询,但是,如果想要善用搜索,我们必须使用请求体查询(request ...

  9. ElasticSearch搜索(一)

    首先从ES的支持的字段说起,ES文档中字段有多种类型 官方文档. 这几个比较常用: text,keyword,integer,float,boolean,object,geo_point(地理坐标), ...

  10. ElasticSearch改造研报查询实践

    背景: 1,系统简介:通过人工解读研报然后获取并录入研报分类及摘要等信息,系统通过摘要等信息来获得该研报的URI 2,现有实现:老系统使用MSSQL存储摘要等信息,并将不同的关键字分解为不同字段来提供 ...

随机推荐

  1. Core2.0知识整理

    概述 Commond-Line ASP.NET结构文件 Startup 配置文件 中间件和依赖注入 依赖注入原理 框架自带的依赖注入(IServiceCollection) 依赖注入生命周期 依赖注入 ...

  2. 如何一步步在生产环境上部署django和vue

    本文由云+社区发表 本文主要讲述了如何一步步在生产环境上部署django和vue,操作系统默认为centos 说明:后文中出现的以下字符串均表示具体的路径或者名称,含义如下: DJANGO_DIR-- ...

  3. Java并发编程-AbstractQueuedSynchronizer源码分析

    简介 提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架.该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础.使用的方法是继承,子类通过 ...

  4. 使用Eclipse创建SpringBoot项目

    1  首先我们需要安装STS插件:Help--> Eclipse Marketplace 安装成功之后,会让你重启eclipse. 2  然后 File-->New--->Proje ...

  5. Jenkins入门之新建任务

    简单了解了Jenkins界面之后,下面我们简单介绍一下如何使用jenkins创建一个任务.打开Jenkins web管理界面之后,点击左侧最上方的NewItem图标 便会进入如下界面 产生要输入一个构 ...

  6. 关于"Could not find acceptable representation"错误

    在项目中调用一个第三方服务,第三方服务是用Spring Boot写成的.结果调用时返回"Could not find acceptable representation"错误. 经 ...

  7. java通过Access_JDBC30读取access数据库时无法获取最新插入的记录

    1.编写了一个循环程序,每几秒钟读取一次,数据库中最新一行数据 连接access数据库的方法和查询的信息.之后开一个定时去掉用. package javacommon.util; import jav ...

  8. Java学习笔记之——继承

    父类(基类):动物 子类:猫.狗.狼.老虎 如果子类继承于父类,则会自动拥有父类的所有非私有属性和方法,不会继承构造方法 父类和子类要满足一种关系:子类是父类的一种 Java中只支持单继承:一个子类只 ...

  9. Verification and validation

    Verification Verification is the process to make sure the product satisfies the conditions imposed a ...

  10. 四个O(n^2)级别的排序性能测试

    测试环境为DEV-C++,并且选择排序,插入排序,冒泡排序,均为优化后的,若想了解具体优化过程,请参照:https://blog.csdn.net/qq_40164152 测试用例: #ifndef ...