Elasticsearch权威指南 阅读笔记(4) 结构化查询

时间:Oct. 31, 2018 分类:

目录:

结构化查询

请求体查询

GET /_search

查询多个索引或者类型

GET /index_2014*/type1,type2/_search

可以使用from及size参数进行分页

GET /_search
{
  "from": 30,
  "size": 10
}

因为HTTP语义和内容RPC中未规定GET请求中允许携带交互数据,所以有些HTTP服务支持,而另一些(特别是缓存代理)则不允许这种情况,所以可以使用

POST /_search
{
  "from": 30,
  "size": 10
}

同理适用于GET请求的API中

结构化查询

结构化查询依赖的就是Lucene的能力,适用query参数

GET /_search
{
    "query": YOUR_QUERY_HERE
}

空查询可以匹配的就是

GET /_search
{
    "query": {
        "match_all": {}
    }
}

使用match_all查询子句

查询字句

{
    QUERY_NAME: {
        ARGUMENT: VALUE,
        ARGUMENT: VALUE,...
    }
}

或者向一个指定字段FIELD_NAME

{
    QUERY_NAME: {
        FIELD_NAME: {
            ARGUMENT: VALUE,
            ARGUMENT: VALUE,...
        }
    }
}

match查询子句用来找寻在tweet字段中找寻包含elasticsearch的成员

GET /_search
{
    "query": {
        "match": {
            "tweet": "elasticsearch"
        }
    }
}

合并多子句

合并简单的子句为一个复杂的查询语句

  • 叶子子句(leaf clauses)(比如match子句)用以在将查询字符串与一个字段(或多字段)进行比较
  • 复合子句(compound)用以合并其他的子句。例如,bool子句允许你合并其他的合法子句,must,must_not或者should,如果可能的话:
{
    "bool": {
        "must":     { "match": { "tweet": "elasticsearch" }},
        "must_not": { "match": { "name":  "mary" }},
        "should":   { "match": { "tweet": "full text" }}
    }
}

复合子句能合并任意其他查询子句,包括其他的复合子句。 这就意味着复合子句可以相互嵌套,从而实现非常复杂的逻辑。

实例查询的是邮件正文中含有“business opportunity”字样的星标邮件或收件箱中正文中含有“business opportunity”字样的非垃圾邮件:

{
    "bool": {
        "must": { "match":      { "email": "business opportunity" }},
        "should": [
             { "match":         { "starred": true }},
             { "bool": {
                   "must":      { "folder": "inbox" }},
                   "must_not":  { "spam": true }}
             }}
        ],
        "minimum_should_match": 1
    }
}

查询与过滤

结构化查询(Query DSL)和结构化过滤(Filter DSL)

过滤语句会询问每个文档的字段值是否包含着特定值

  • created的日期范围是否在2013到2014 ?
  • status字段中是否包含单词"published" ?
  • lat_lon字段中的地理位置与目标点相距是否不超过10km ?

过滤比查询效率要高,并且易于缓存。

  • 使用过滤语句得到的结果集是一个简单的文档列表,快速匹配运算并存入内存是十分方便的, 每个文档仅需要1个字节。
  • 查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比 过滤语句更耗时,并且查询结果也不可缓存。
  • 过滤语句的目的就是缩小匹配的文档结果集

最重要的查询过滤语句

term过滤

主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed的字符串(未经分析的文本数据类型)

    { "term": { "age":    26           }}
    { "term": { "date":   "2014-09-01" }}
    { "term": { "public": true         }}
    { "term": { "tag":    "full_text"  }}

terms过滤

允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配

{
    "terms": {
        "tag": [ "search", "full_text", "nosql" ]
        }
}

range过滤

按照指定范围查找一批数据

{
    "range": {
        "age": {
            "gte":  20,
            "lt":   30
        }
    }
}
  • gt 大于
  • gte 大于等于
  • lt 小于
  • lte 小于等于

exists 和 missing过滤

查找文档中是否包含指定字段或没有某个字段

{
    "exists":   {
        "field":    "title"
    }
}

bool过滤

来合并多个过滤条件查询结果的布尔逻辑

  • must 多个查询条件的完全匹配,相当于 and。
  • must_not 多个查询条件的相反匹配,相当于 not。
  • should 至少有一个查询条件匹配, 相当于 or。

match_all查询

查询到所有文档,没有查询条件下的默认语句

{
    "match_all": {}
}

match查询

一个标准查询,如果你使用match查询一个全文本字段,它会在真正查询之前用分析器先分析match一下查询字符

{
    "match": {
        "tweet": "About Search"
    }
}

如果用match下指定了一个确切值,在遇到数字,日期,布尔值或者not_analyzed的字符串时,它将为你搜索你给定的值

不过如果是过滤的话,最好用term

multi_match查询

match查询的基础上同时搜索多个字段

{
    "multi_match": {
        "query":    "full text search",
        "fields":   [ "title", "body" ]
    }
}

bool查询

bool查询与bool过滤相似,用于合并多个查询子句。不同的是,bool过滤可以直接给出是否匹配成功, 而bool查询要计算每一个查询子句的 _score(相关性分值)

查询将会找到title字段中包含"how to make millions",并且"tag" 字段没有被标为spam。 如果有标识为 "starred" 或者发布日期为2014年之前,那么这些匹配的文档将比同类网站等级高

{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }},
            { "range": { "date": { "gte": "2014-01-01" }}}
        ]
    }
}

查询与过滤条件的合并

带过滤的查询语句

{
    "match": {
        "email": "business opportunity"
    }
}

让这条语句加入 term 过滤

{
    "term": {
        "folder": "inbox"
    }
}

search API中只能包含query语句,所以我们需要用filtered来同时包含"query"和"filter"子句

GET /_search
{
    "query": {
        "filtered": {
            "query":  { "match": { "email": "business opportunity" }},
            "filter": { "term": { "folder": "inbox" }}
        }
    }
}

单条过滤语句

GET /_search
{
    "query": {
        "filtered": {
            "filter":   { "term": { "folder": "inbox" }}
        }
    }
}

一条查询语句没有指定查询范围,那么它默认使用match_all查询

GET /_search
{
    "query": {
        "filtered": {
            "query":    { "match_all": {}},
            "filter":   { "term": { "folder": "inbox" }}
        }
    }
}

查询语句中的过滤

一条带有查询功能 的过滤语句, 这条语句可以过滤掉看起来像垃圾邮件的文档

GET /_search
{
    "query": {
        "filtered": {
            "filter":   {
                "bool": {
                    "must":     { "term":  { "folder": "inbox" }},
                    "must_not": {
                        "query": {
                            "match": { "email": "urgent business proposal" }
                        }
                    }
                }
            }
        }
    }
}

过滤语句中可以使用query查询的方式代替bool过滤子句。在filter中只能有过滤字句吧,所以用query,我的理解是这样的

很少用到的过滤语句中包含查询,保留这种用法只是为了语法的完整性。 只有在过滤中用到全文本匹配的时候才会使用这种结构。

验证查询

validate API 可以验证一条查询语句是否合法

GET /gb/tweet/_validate/query
{
   "query": {
      "tweet" : {
         "match" : "really powerful"
      }
   }
}

请求的返回值告诉我们这条语句是非法的

{
  "valid" :         false,
  "_shards" : {
    "total" :       1,
    "successful" :  1,
    "failed" :      0
  }
}

语句非法的具体错误信息,需要加上 explain 参数

GET /gb/tweet/_validate/query?explain
{
   "query": {
      "tweet" : {
         "match" : "really powerful"
      }
   }
}

响应结果为

{
  "valid" :     false,
  "_shards" :   { ... },
  "explanations" : [ {
    "index" :   "gb",
    "valid" :   false,
    "error" :   "org.elasticsearch.index.query.QueryParsingException:
                 [gb] No query registered for [tweet]"
  } ]
}

所以这边正确的请求应该是

GET /_validate/query?explain
{
   "query": {
      "match" : {
         "tweet" : "really powerful"
      }
   }
}

返回的结果是带有查询语句的可阅读描述, 可以帮助了解查询语句在ES中是如何执行的

{
  "valid" :         true,
  "_shards" :       { ... },
  "explanations" : [ {
    "index" :       "us",
    "valid" :       true,
    "explanation" : "tweet:really tweet:powerful"
  }, {
    "index" :       "gb",
    "valid" :       true,
    "explanation" : "tweet:really tweet:power"
  } ]
}

explanation会为每一个索引返回一段描述,match 是如何为查询字符串 "really powerful" 进行查询的

  • 首先,它被拆分成两个独立的词分别在 tweet 字段中进行查询。
  • 而且,在索引us中这两个词为"really"和"powerful",在索引gb中被拆分成"really" 和 "power"。 这是因为我们在索引gb中使用了english分析器