My Github

Elastic学习之旅 (9) 结构化搜索

大家好,我是Edison。

上一篇:基于Term和全文的ES查询

结构化数据

结构化搜索(Structured Search)是指对结构化数据的搜索,那么,什么数据是结构化的呢?

ES中日期、布尔类型和数字都是结构化的。

另外,文本也可以是结构化的:

  • 比如彩色笔可以有离散的颜色集合:红、蓝、绿等;

  • 一个博客也可能被标记了一些标签:分布式、搜索、架构等;

  • 电商网站上的商品都有UPCs(通用产品码)或其他的唯一标识,它们都需要遵从严格规定的、结构化的格式。

结构化搜索

结构化搜索(Structured Search)是指对结构化数据的搜索,那么我们接下来就看看如何做结构化搜索。在ES中对结构化数据进行匹配,主要使用term查询。

NOTE:对于文本的全文查询,主要使用match哟。

首先,我们先添加一些测试数据,以便后续场景中的示例。请注意里面这些测试数据的结构,后面两条并没有date字段哟。

DELETE products
POST /products/_bulk
{ "index":{"_id":1}}
{ "productID": "XHDK-A-1293-#fJ3", "price":10, "avaliable":true, "date":"2024-04-01"}
{ "index":{"_id":2}}
{ "productID": "KDKE-B-9947-#kL5", "price":20, "avaliable":true, "date":"2023-04-01"}
{ "index":{"_id":3}}
{ "productID": "J0DL-X-1937-#pV7", "price":30, "avaliable":true }
{ "index":{"_id":4}}
{ "productID": "QQPX-R-3956-#aD8", "price":30, "avaliable":false }

场景1:针对布尔、时间、日期和数字类型的结构化数据

这类数据有精确的格式,可以直接对这些格式进行逻辑操作,比如 比较数字或时间的范围,或判定两个值的大小。

(1)布尔值

// 对布尔值进行查询(有算分)
// ES会返回3条记录
POST /products/_search
{
  "profile": "true",
  "query": {
    "term": {
      "avaliable": true
    }
  }
}

ES返回结果:3条记录,有计算分

从之前的学习中,我们知道,可以使用constant_score转成filtering,进而避免算分,提高性能。

// 对布尔值进行查询(使用constant_score,无算分)
// ES会返回3条记录
POST /products/_search
{
  "profile": "true",
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "avaliable": true
        }
      }
    }
  }
}

ES返回结果:3条记录,没有计算分

(2)数字和日期Range

// 数字Range查询价格>=30 并 <=40的记录
POST /products/_search
{
  "profile": "true",
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "price": {
            "gte": 30,
            "lte": 40
          }
        }
      }
    }
  }
}

// 日期Range查询:查询近一年的记录
// y 年 M 月 w 周 d 天 
// H / h 小时 m 分钟 s 秒
POST /products/_search
{
  "profile": "true",
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "date": {
            "gte": "now-1y"
          }
        }
      }
    }
  }
}

(3)处理空值

// exists 可以过滤不包含某字段的记录
POST /products/_search
{
  "profile": "true",
  "query": {
    "constant_score": {
      "filter": {
        "exists": {
          "field": "date"
        }
      }
    }
  }
}

场景2:针对结构化文本做部分匹配 或 精确匹配

为了演示,我们插入两个测试数据来看看处理多值的情况:

POST /movies/_bulk
{ "index":{"_id":1}}
{ "title": "Father of the Bridge Part II", "year": 1995, "genre": "Comedy" }
{ "index":{"_id":2}}
{ "title": "Dave", "year": 1993, "genre": ["Comedy", "Romance"] }

可以看到,第二条数据的genre字段包含了多个值,是一个集合字段。

然后,我们可以通过term+ keyword来处理多值字段的查询:查找genre属于Comedy(喜剧)的记录。这里需要注意的是,term查询的逻辑关系是包含而不是完全相等,所以它做的是部分匹配

// 查询包含单个值的记录
POST /movies/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "genre.keyword": "Comedy"
        }
      }
    }
  }
}

// 查询包含多个值的记录
POST /movies/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "terms": {
          "genre.keyword": [
            "Comedy",
            "Romance"
          ]
        }
      }
    }
  }
}

这里如果我们想做精确匹配,我们可以增加一个count字段并使用bool查询来解决。

Step1.从业务角度,按需改进ES的数据模型

POST /movies_new/_bulk
{ "index":{"_id":1}}
{ "title": "Father of the Bridge Part II", "year": 1995, "genre": "Comedy", "genre_count":1 }
{ "index":{"_id":2}}
{ "title": "Dave", "year": 1993, "genre": ["Comedy", "Romance"], "genre_count":2 }

这里可以看到,我们给genre字段新增一个匹配的genre_count字段,代表genre字段有几个值。

Step2.使用bool查询进行精确匹配

// 方式一:使用must,有算分
POST /movies_new/_search
{
  "query": {
    "bool": {
      "must": [
        {"term":{"genre.keyword": {"value":"Comedy"}}},
        {"term":{"genre_count": {"value":1}}}
      ]
    }
  }
}
// 方式二:使用filter,无算分
POST /movies_new/_search
{
  "query": {
    "bool": {
      "filter": [
        {"term":{"genre.keyword": {"value":"Comedy"}}},
        {"term":{"genre_count": {"value":1}}}
      ]
    }
  }
}

小结

本篇,我们了解了ElasticSearch中的结构化数据和结构化搜索的概念,并通过几个实例了解了如何对结构化数据进行搜索。在ES中,我们主要使用term对结构化数据进行搜索,而主要使用match对文本进行全文搜索。

参考资料

极客时间,阮一鸣,《ElasticSearch核心技术与实战

 

posted @ 2024-04-12 08:30  EdisonZhou  阅读(19)  评论(0编辑  收藏  举报