目录
第2关:Elasticsearch多精确值查找以及Null值处理
第1关:Elastisearch精确值查找
任务描述
本关任务:根据所学知识,查找产品 ID 为 KDKE-B-9947-#kL5 或者(产品 ID 为 ODL-X-1937-#pV7 并且价格为 30)的所有产品。
相关知识
为了完成本关任务,你需要掌握:
如何通过精确值查找数字和文本;
使用过滤器的方式进行精确查找;
如何组合多种过滤器。
当进行精确值查找时, 我们会使用过滤器(filters)。过滤器很重要,因为它们执行速度非常快,不会计算相关度(直接跳过了整个评分阶段)而且很容易被缓存。因此我们需要记住:请尽可能多的使用过滤式查询。
精确查找数字
我们首先来看最为常用的 term 查询,可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)。
首先我们可以创建并索引一些表示产品的文档:
a.json 内容如下:
{ "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" }
插入文档:
curl -H "Content-Type: application/json" -XPOST 'http://127.0.0.1:9200/my_store/products/_bulk?pretty' --data-binary @a.json
如果我们想要找到具有某个价格的所有产品,我们可以使用 term 查询达到该目的。
term 查询会查找我们指定的精确值。作为其本身,term 查询是简单的。它接受一个字段名以及我们希望查找的数值:
{
"query" : {
"term" : {
"price" : 20
}
}
}
通常当查找一个精确值的时候,我们不希望对查询进行评分计算。只希望对文档进行包括或排除的计算,所以我们会使用 constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分。
最终组合的结果是一个 constant_score 查询,它包含一个 term 查询:
curl -H "Content-Type: application/json" -XGET 'http://localhost:9200/my_store/products/_search?pretty' -d '
{
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"price" : 20
}
}
}
}
}'
我们用 constant_score 将 term 查询转化成为过滤器。
执行后,这个查询所搜索到的结果与我们期望的一致:只有文档 2 命中并作为结果返回(因为只有 2 的价格是 20):
{
...
"hits" : {
"total" : 1,
"max_score" : 1.0,
"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 的产品:
curl -H "Content-Type: application/json" -XGET 'http://localhost:9200/my_store/products/_search?pretty' -d '
{
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"productID" : "XHDK-A-1293-#fJ3"
}
}
}
}
}'
但是,令我们惊讶的是,我们并没有查找到所需结果,这是为什么呢?
{
"took" : 6,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : null,
"hits" : [ ]
}
}
问题不在 term 查询,而在于索引数据的方式。如果我们使用 analyze API (分析 API),我们可以看到这里的 UPC 码被拆分成多个更小的 token :
curl -H "Content-Type: application/json" -XGET 'http://localhost:9200/my_store/_analyze?pretty' -d '
{
"field": "productID",
"text": "XHDK-A-1293-#fJ3"
}'
执行结果:
{
"tokens" : [
{
"token" : "xhdk",
"start_offset" : 0,
"end_offset" : 4,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "a",
"start_offset" : 5,
"end_offset" : 6,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "1293",
"start_offset" : 7,
"end_offset" : 11,
"type" : "<NUM>",
"position" : 2
},
{
"token" : "fj3",
"start_offset" : 13,
"end_offset" : 16,
"type" : "<ALPHANUM>",
"position" : 3
}
]
}
我们会发现以下几点不同:
Elasticsearch 用 4 个不同的 token 而不是单个 token 来表示这个 UPC;
所有字母都是小写的;
丢失了连接字符的 - 和 #。
所以当我们用 term 查询查找精确值 XHDK-A-1293-#fJ3 的时候,找不到任何文档,因为它并不在我们的倒排索引中,正如前面呈现出的分析结果,索引里有四个 token 。
显然这种对 ID 码或其他任何精确值的处理方式并不是我们想要的。
为了避免这种问题,我们需要告诉 Elasticsearch 该字段具有精确值,要将其设置成精确值,无需分析的。 我们可以在自定义字段映射中查看它的用法。为了修正搜索结果,我们需要首先删除旧索引(因为它的映射不再正确)然后创建一个能正确映射的新索引:
# 首先删除旧索引
curl -XDELETE 'http://localhost:9200/my_store?pretty'
# 创建 _mapping 映射,设置其类型为 keyword,我们不想对 product

最低0.47元/天 解锁文章
13

被折叠的 条评论
为什么被折叠?



