Juconcurrent 学而不思则罔,思而不学则殆。

Elasticsearch学习笔记(14) - 结构化数据的查询


概念

结构化数据,在Elasticsearch中叫Structured Data,用于表示一个不可分割的值。相对于全文本的分词处理,结构化数据本身可看成一个整体。

结构化数据包括:

  1. 日期
  2. 布尔类型
  3. 数字
  4. 分词无意义的文本,包括可枚举的文本
    1. 颜色的种类,如:红、黄、蓝、绿、黑
    2. 博客的标签,如:Java、Elasticsearch、C语言
    3. 性别,如:男性、女性、保密、未知
    4. 订单的id

因此,结构化数据的搜索叫做结构化搜索,可以做精准匹配或者部分匹配。精准匹配使用Term查询,部分匹配使用Prefix前缀查询。结构化搜索的结果只有两个结果。实际场景中,我们根据需要,可以选择是否对搜索的结果进行相关性算分。

例子

先批量导入一些数据。

# 结构化搜索,精确匹配
DELETE products
POST /products/_bulk
{ "index": { "_id": 1 }}
{ "price" : 10,"avaliable":true,"date":"2018-01-01", "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": 2 }}
{ "price" : 20,"avaliable":true,"date":"2019-01-01", "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": 3 }}
{ "price" : 30,"avaliable":true, "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": 4 }}
{ "price" : 30,"avaliable":false, "productID" : "QQPX-R-3956-#aD8" }

再来看看字段的类型,price为long类型,avaliable为boolean类型,date为日期类型,productID为text全文本类型。

GET products/_mapping

对boolean类型查询如下所示。同时,我们可以通过constant score 转成 filtering的方式,来避免算分的过程,也能有效地利用缓存来提高性能。

# 对布尔值 match 查询,有算分
POST products/_search
{
  "profile": "true",
  "explain": true,
  "query": {
    "term": {
      "avaliable": true
    }
  }
}

# 对布尔值,通过constant score 转成 filtering,没有算分
POST products/_search
{
  "profile": "true",
  "explain": true,
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "avaliable": true
        }
      }
    }
  }
}

数字类型的查询如下所示。

# 数字类型 Term
POST products/_search
{
  "profile": "true",
  "explain": true,
  "query": {
    "term": {
      "price": 30
    }
  }
}

# 数字类型 terms
POST products/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "terms": {
          "price": [
            "20",
            "30"
          ]
        }
      }
    }
  }
}

# 数字 Range 查询
GET products/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                    "price" : {
                        "gte" : 20,
                        "lte"  : 30
                    }
                }
            }
        }
    }
}

我们可以对日期类型进行范围查询。有意思的是,这儿我们可以使用now、y、M、h等特殊的单词,加上一些操作符号来完成一些简单的计算。下面的例子表示查找过去一年的数据。

# 日期 range
POST products/_search
{
    "query" : {
        "constant_score" : {
            "filter" : {
                "range" : {
                    "date" : {
                      "gte" : "now-1y"
                    }
                }
            }
        }
    }
}

判断字段是否存在,可如下查询。

# exists查询
POST products/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "exists": {
          "field": "date"
        }
      }
    }
  }
}

对于多值查询,通常是包含,而非等于。若想达到等于的效果,需要使用boolean查询+数量判断来间接实现。

# 处理多值字段
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"] }

# 处理多值字段,term 查询是包含,而不是等于
POST movies/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "genre.keyword": "Comedy"
        }
      }
    }
  }
}

总结

通过学习,我们知道了结构化数据的含义,也了解了结构化数据的查询(其实就是term查询及其各种变种)。对于多值查询,通常是包含关系,而非等于。若要实现多值查询的等于功能,需要使用boolean查询+数量判断来间接实现。


Content