注:此文档仅适用于 Elasticsearch > 5.0 版本
Elasticsearch 的功能之一就是搜索,搜索主要分为两种类型,结构化搜索和全文搜索。结构化搜索是指有关探询那些具有内在结构数据的过程。比如日期、时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作。比较常见的操作包括比较数字或时间的范围,或判定两个值的大小。
结构化搜索的特征是『非是即否』,一个文档要么存于集合之中,要么存在集合之外。
EndPoints
经常的情况下,需要在一个或多个特殊的索引并且在一个或者多个特殊的类型中进行搜索。我们可以通过在URL中指定特殊的索引和类型达到这种效果,如下所示:
/_search
:在所有的索引中搜索所有的类型
/gb/_search
:在 gb
索引中搜索所有的类型
/gb,us/_search
:在 gb
和 us
索引中搜索所有的文档
/g*,u*/_search
:在任何以 g
或者 u
开头的索引中搜索所有的类型
/gb/user/_search
:在 gb
索引中搜索 user
类型
/gb,us/user,tweet/_search
:在 gb
和 us
索引中搜索 user
和 tweet
类型
/_all/user,tweet/_search
:在所有的索引中搜索 user
和 tweet
类型
精确值查询
在 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 } }
}
}
}
常用的过滤器包含 term
、 terms
、 range
、 prefix
、 wildcard
、 regexp
、 exists
、 missing
。下面进行一一介绍。
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
主要用于数字和日期类型的字段查询。例如查询年龄在 10
到 20
岁的用户:
{
"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" }
}
}
}
}
missing
与 exists
正相反,它返回指定字段为 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" }}
]
}
有了排序功能,就需要分页。分页有 size
和 from
两个参数指定。
-
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 的方式。
参考资料: