Elasticsearch - 结构化搜索

注:此文档仅适用于 Elasticsearch > 5.0 版本

Elasticsearch 的功能之一就是搜索,搜索主要分为两种类型,结构化搜索和全文搜索。结构化搜索是指有关探询那些具有内在结构数据的过程。比如日期、时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作。比较常见的操作包括比较数字或时间的范围,或判定两个值的大小。

结构化搜索的特征是『非是即否』,一个文档要么存于集合之中,要么存在集合之外。

EndPoints

经常的情况下,需要在一个或多个特殊的索引并且在一个或者多个特殊的类型中进行搜索。我们可以通过在URL中指定特殊的索引和类型达到这种效果,如下所示:

/_search :在所有的索引中搜索所有的类型

/gb/_search :在 gb 索引中搜索所有的类型

/gb,us/_search :在 gbus 索引中搜索所有的文档

/g*,u*/_search :在任何以 g 或者 u 开头的索引中搜索所有的类型

/gb/user/_search :在 gb 索引中搜索 user 类型

/gb,us/user,tweet/_search :在 gbus 索引中搜索 usertweet 类型

/_all/user,tweet/_search :在所有的索引中搜索 usertweet 类型

精确值查询

在 Elasticsearch 中,过滤器不能够单独使用,它必须和 query 一起使用,例如下面的查询例子。其中 constant_score 表示查询以非评分模式进行,这样可以省掉评分过程,提高查询速度。

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

如果需要查找 object 里面的字段,可以使用点语法,例如:

{
    "query" : {
        "constant_score" : { 
            "filter" : { "term" : { "user.id" : 1 } }
        }
    }
}

常用的过滤器包含 termtermsrangeprefixwildcardregexpexistsmissing。下面进行一一介绍。

term

term 代表完全匹配,也就是精确查询。

注:查询字符串时,查询的字段类型必须是 keyword ,如果查询的是 text, 则有可能查询结果不正确。

{
    "query" : {
        "constant_score" : { 
            "filter" : {"term" : { "price" : 20 } }
        }
    }
}

terms

terms 查询类似于 SQL 中的 in 关键词。例如:

{
    "query": {
        "constant_score" : {
            "filter" : {"terms" : { "user" : ["kimchy", "elasticsearch"]}}
        }
    }
}

range

range 主要用于数字和日期类型的字段查询。例如查询年龄在 1020 岁的用户:

{
    "query": {
        "constant_score" : {
            "filter" : {"range" : { "age" : { "gte" : 10, "lte" : 20 } } }
        }
    }
}

range 接受以下几个参数:

  • gte:大于等于
  • gt:大于
  • lte:小于等于
  • le:小于

当查询字段的类型是日期时,可以添加 format 类型的字段,如果时间字段与时区相关,则还可以添加 time_zone 字段。

{
    "query": {
        "constant_score" : {
            "filter" : {
                "range" : { 
                    "age" : { 
                        "gte" : "2015-01-01 00:00:00", 
                        "lte" : "2015-02-01 00:00:00",
                        "format": "yyyy-MM-dd HH:mm:ss", 
                        "time_zone": "+01:00" 
                    } 
                } 
            }
        }
    }
}

prefix

prefix 又称前缀查询,用于 keyword 类型的字段查询。

{
    "query" : {
        "constant_score" : {
            "filter" : {
                "prefix" : { "name" : "ki" }
            }
        }
    }
}

wildcard

wildcard 又称通配符查询,也是用于 keyword 类型的字段查询。通配符查询主要基于 shell 的通配符: ? 匹配任意字符, * 匹配 0 或多个字符。

{
    "query" : {
        "constant_score" : {
            "filter" : {
                "wildcard" : { "name" : "ki*y" }
            }
        }
    }
}

regexp

regexp 又称正则查询,也是用于 keyword 类型的字段查询。

{
    "query" : {
        "constant_score" : {
            "filter" : {
                "regexp" : { "name" : "s.*y" }
            }
        }
    }
}

exists 和 missing

exists 字段类似于 SQL 中的 IS NOT NULL ,用于过滤指定字段不为 NULL 的文档。

{
    "query" : {
        "constant_score" : {
            "filter" : {
                "exists" : { "field" : "tags" }
            }
        }
    }
}

missingexists 正相反,它返回指定字段为 NULL 的文档。

bool 过滤器

bool 过滤器是一个复合过滤器(Compound filter),它可以接受多个其他过滤器作为参数,并将这些过滤器结合成各式各样的布尔(逻辑)组合。

一个 bool 过滤器由三部分组成:

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

filter 的结果会缓存,因此尽量使用 filter 关键词。

{
   "query" : {
      "filtered" : { 
         "filter" : {
            "bool" : {
              "should" : [
                 { "term" : {"price" : 20}}, 
                 { "term" : {"productID" : "XHDK-A-1293-#fJ3"}} 
              ],
              "must_not" : {
                 "term" : {"price" : 30} 
              }
           }
         }
      }
   }
}

上面的查询等同于 (price = 20 OR productID='XHDK-A-1293-#fJ3') AND price != 30

nested 查询

nested 类型的字段,由于索引在独立的文档中,我们无法通过点语法直接搜索他们。因此 Elasticsearch 开发了 nested 查询,这个查询主要用于查询 nested 类型字段中的对象的字段。

{
  "query": {
    "nested": {
      "path": "comments",
      "query": {
        "bool": {
          "filter": {
            {"term": {"comments.name": "john"}}
          }
        }
      }
    }
  }
}

排序和分页

默认情况下,查询返回的结果会按照 _score 进行排序,值越高越靠前。咱结构化搜索中,_score 的只都是相等的,因此结果会按照随机顺序返回。

Elasticsearch 提供了结果排序的功能,可以使用 sort 字段指定排序的方式。

{
    "query" : {
        "bool" : {
            "filter" : { "term" : { "user_id" : 1 }}
        }
    },
    "sort": { "date": { "order": "desc" }}
}

也可以指定多个字段进行排序:

{
    "query" : {
        "bool" : {
            "filter" : { "term" : { "user_id" : 1 }}
        }
    },
    "sort": [
        { "date":   { "order": "desc" }},
        { "name": { "order": "esc" }}
    ]
}

有了排序功能,就需要分页。分页有 sizefrom 两个参数指定。

  • size :返回的结果数量,默认10
  • from :跳过开始的结果数,默认0

下面的例子表示请求第三页的数据。

{
    "query" : {
        "bool" : {
            "filter" : { "term" : { "user_id" : 1 }}
        }
    },
    "sort": { "date":   { "order": "desc" }},
    "size": 10,
    "from": 20
}

在 Elasticsearch 中,返回的结果随着分页的增长而成倍增长。

现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果。然后请求节点排序这50050个结果并丢弃50040个!

因此尽量避免使用深度分页。如果有大量分页的需求,可以使用 Scrolling 的方式。


参考资料:

  1. Elasticsearch: 权威指南
  2. Elasticsearch 官方文档
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容