ElasticSearch(七) 搜索


title: ElasticSearch(七) 搜索
tags: ElasticSearch
author: Clown95html


搜索

在前面,已经介绍了在ElasticSearch索引中处理数据的基础知识,如今是时候进行核心功能的学习了。
搜索主要有两种方式:node

  • URI Searchgit

    • 操做简便,方便经过命令行测试
    • 可是仅包含部分查询语法
  • Request Body Searchgithub

    • es 最经常使用的方式,查询丰富。
    • 提供的完备查询语法Query DSL(Domain Specific Language)

URI Search

URL检索是经过提供请求参数纯粹使用URI来执行搜索请求。
方法: curl -XGET "http://localhost:9200/movies/doc/_search?参数,多个参数用&分开。web

经常使用参数以下:算法

参数 描述
q 查询字符串(映射到query_string查询)
df 在查询中不指定字段是默认查询的字段,若是不指定字段,ES会查询全部字段
analyzer 分析查询字符串时要使用的分析器名称
sort 排序,能够升序排序和降序排序
timeout 指定超时时间,默认为无超时
from 返回的索引匹配结果的开始值,默认为0
size 要返回的搜索条数,默认为10
default_operator 要使用的默认运算符能够是AND或 OR,默认为OR

详细参数查看官方文档 :https://www.elastic.co/guide/en/elasticsearch/reference/6.2/search-uri-request.htmlsql

下面咱们来了解下怎么使用这些参数:shell

q

咱们首先来了解下 q的使用,如今咱们来查找监狱风云的信息 :json

GET /movies/doc/_search?q=%E7%9B%91%E7%8B%B1%E9%A3%8E%E4%BA%91

E7%9B%91%E7%8B%B1%E9%A3%8E%E4%BA%91 是监狱风云的 url编码
在线转码工具 :http://tool.oschina.net/encode?type=4数组

固然咱们也能够指定字段查询,例如:

GET /movies/doc/_search?q=title:%E7%9B%91%E7%8B%B1%E9%A3%8E%E4%BA%91

响应信息:

{
  "took": 14,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 3,
    "max_score": 5.9509215,
    "hits": [
      {
        "_index": "movies",
        "_type": "doc",
        "_id": "2",
        "_score": 5.9509215,
        "_source": {
          "title": "监狱风云1",
          "director": "林岭东",
          "year": 1987,
          "genres": [
            "犯罪",
            "剧情",
            "动做"
          ],
          "actors": [
            "周润发",
            "梁家辉",
            "张耀扬"
          ]
        }
      },
      {
        "_index": "movies",
        "_type": "doc",
        "_id": "3",
        "_score": 3.8119292,
        "_source": {
          "title": "监狱风云2",
          "director": "林岭东",
          "year": 1991,
          "genres": [
            "犯罪",
            "剧情",
            "动做"
          ],
          "actors": [
            "周润发",
            "徐锦江",
            "陈松勇"
          ]
        }
      },
      {
        "_index": "movies",
        "_type": "doc",
        "_id": "14",
        "_score": 1.9059646,
        "_source": {
          "title": "澳门风云3",
          "director": "王晶",
          "year": 2016,
          "genres": [
            "搞笑",
            "动做"
          ],
          "actors": [
            "周润发",
            "刘德华",
            "张家辉",
            "张学友",
            "余文乐"
          ]
        }
      }
    ]
  }
}

咱们能够看到每一个被匹配出来的索引信息都有多个_score 和一个max_score ,这就是相关性评分。
默认状况下,Elasticsearch根据结果相关性评分来对结果集进行排序,所谓的「结果相关性评分」就是文档与查询条件的匹配程度。很显然,排名第一的监狱风云1title字段明确的写到监狱风云。可是为何澳门风云3也会出如今结果里呢?首先咱们使用的是ES的默认分词器,它会把中文内容分红单个汉字,因此只要包含监狱风云中任意一个字的文档都会被匹配出来。又由于监狱风云1 彻底匹配了监狱风云 四个字,而澳门风云3只匹配了风云两个字,因此监狱风云1_score澳门风云3_score
咱们先了解下这个概念,后面咱们再详细介绍

sort

咱们再来了解下排序sort ,咱们根据year字段升序排列,例如:

GET /movies/doc/_search?q=title:%E7%9B%91%E7%8B%B1&sort=year:asc&pretty

asc 按升序排序,desc 按降序排序

from和size 分页

_serarch返回的内容默认只有10个文档在hits数组中。咱们的movies有20个左右的文档,那么咱们如何看到其余文档?

和SQL使用LIMIT关键字返回只有一页的结果同样,Elasticsearch接受from和size参数,size: 表示查询多少条文档,默认10 ,from: 从第几行开始,默认0

如今咱们想要每页显示5个数据,显示3页内容:

GET /movies/doc/_search?&size=5&from=0
	GET /movies/doc/_search?&size=5&from=5
	GET /movies/doc/_search?&size=5&from=10

_source过滤

一般,GET 请求将返回文档的所有,存储在_source 参数中。可是可能你感兴趣的字段只title。 请求个别字段可使用_source参数。多个字段可使用逗号分隔:

GET	/movies/doc/1?_source=title,actors

操做符

Url Search 支持的操做符有:

  • AND(&&), OR(||), NOT(!)
  • + -分别对应must和must not
  • > < >= <=

注意:+ -等符号url不能直接识别,要使用url encode后的结果才能够。

如今咱们来使用AND查询分类即剧情,又是同性

GET  movies/movie/_search?q=genres:(%e5%89%a7%e6%83%85 AND %e5%90%8c%e6%80%a7)&pretty

或者使用&& ,在下面的命令中咱们把 &转成了%26

GET  movies/movie/_search?q=genres:(%e5%89%a7%e6%83%85 %26%26 %e5%90%8c%e6%80%a7)&pretty

咱们再来使用 +,来进行多个匹配,只要任意知足一个便可匹配到

GET  movies/movie/_search?q=genres:(%e5%89%a7%e6%83%85 %2B %e5%90%8c%e6%80%a7)&pretty

咱们还能够指定查找的范围,例如咱们查找大于1995年的信息:

curl -XGET "http://localhost:9200/movies/doc/_search?q=year:(>1995)"

查找大于1990且<2000年的信息:

GET /movies/doc/_search?q=year:(>1990 AND <2000)

Request Body Search

前面咱们使用的Url search ,咱们也可使用请求正文来搜索信息,咱们须要向请求正文中提供查询,这种方法是咱们最经常使用的搜索方式。

为了使用ElasticSearch进行搜索,咱们使用_search端点,可选择使用索引和类型。也就是说,按照如下模式向URL发出请求:
http://localhost:9200/<index>/<type>/_search。其中,index和type都是可选的。

请求正文是一个JSON对象,除了其它属性之外,它还要包含一个名称为“query”的属性。这种查询方法叫查询DSL。你可能想知道查询DSL是什么。它是ElasticSearch本身基于JSON的域特定语言,能够在其中表达查询和过滤器。

{
    "query": {
           Query DSL here
    }
}

Query DSL基于JSON定义的查询语言,主要包含以下两种类型:

  • 字段类查询 :如term, match, range等,只针对某一 个字段进行查询
    字段类查询主要包括如下两类:

    • 全文匹配
      针对text类型的字段进行全文检索,会对查询语句先进行分词处理,如match,match_ phrase等query类型

    • 单词匹配
      不会对查询语句作分词处理,直接去匹配字段的倒排索引,如term, terms,range等query类型

  • 复合查询:如bool查询等,包含一个或多个字段类查询或者复合查询语句。

query_string查询

query_string 相似 Url serarch 里面的参数q
我使用query_string (查询字符串),如今咱们来查询风云所对应的文档。

POST movies/doc/_search
{
    "query": {
        "query_string": {
            "query": "风云"
        }
    }
}

term查询

term 查询,能够用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text),而不是全文本字段。term是表明彻底匹配,也就是精确查询,搜索前不会再对搜索词进行分词。

下面咱们查询指定的year字段:

GET /movies/_search
{
    "query" : {
      "term": {
        "year": {"value": 1994 }
      }
    }
}

6.X以上版本term好像是没法直接查询字符串内容,须要在字段后面添加.keyword,例如:

GET /movies/doc/_search
{
  "query": {
    "term": {
      "title.keyword": {
        "value": "大话西游"
      }
    }
  }
}

既然已经提到了keyword,咱们就简单的说下,title.keyword,是es最新版本内置创建的field,就是不分词的。因此一个title过来的时候,会创建两次索引,一次是本身自己,是要分词的,分词后放入倒排索引;另一次是基于title.keyword,不分词,保留256个字符最多,直接一个字符串放入倒排索引中。

若是一个字段既须要分词搜索,又须要精准匹配,咱们就能够经过增长keyword字段来支持精准匹配。

{
    "type": "text",
    "fields": {
        "keyword": {
            "type": "keyword",
            "ignore_above": 256
        }
    }
}

这样至关于有addressaddress.keyword两个字段。

terms查询

term查询对于查找单个值很是有用,但一般咱们可能想搜索多个值。若是咱们想搜索多个电影信息的文档该如何处理?

咱们不须要使用多个term 查询,咱们只要用terms查询,它至关于sql中的 in

使用的方法和term同样,如今咱们来查询电影分类是动做剧情的文档:

GET movies/movie/_search
{
  "query": {
    "terms": {
      "genres.keyword": ["剧情","动做"]
    }
  }
}

能够看到咱们只是把terms查询的内容使用数组替代而已,其余基本上同样。

match查询

还记得咱们以前使用URL参数搜索监狱风云的时候,咱们还演示了指定为title字段进行搜索。咱们再来使用match查询来达到这样的效果,match查询它能够被认为是基本的属性搜索查询(就是经过特定的一个或多个属性来搜索)。

match搜索会先对搜索词进行分词,对于最基本的match搜索来讲,只要搜索词的分词集合中的一个或多个存在于文档中便可,例如,当咱们搜索监狱风云,搜索词会先分词为监狱风云,只要文档中包含监狱风云任意一个词,都会被搜索到 。

GET /movies/_search
{
    "query" : {
        "match" : { "title" : "监狱风云" }
    }
}

像这样指定搜索内容为title字段这样的设置称为fields,可用于指定要搜索的字段列表。若是不使用fields字段,ElasticSearch查询将默认自动生成的名为_all的特殊字段,来基于全部文档中的各个字段匹配搜索。

fuzzy查询

咱们再使用搜索引擎的时候,确定出现过拼写错误的状况,好比说咱们搜索elasticsearch 可是我不当心拼错成了elasticsearhc,可是搜索引擎仍然给我搜索出正确的结果。

使用term确定是不行的,由于它是精确查找,因此咱们引出fuzzy模糊查询。

为了方便演示,咱们须要添加几个索引信息。

PUT  myname/doc/1
{
  "name" :"clown"
}
PUT  myname/doc/2
{
  "name" :"clwon"
}

PUT  myname/doc/3
{
  "name" :"colwn"
}

如今咱们须要查询clown 这个文档:

GET myname/doc/_search
{
   "query": {
     "fuzzy": {
       "name": {
         "value": "clown",
         "fuzziness":  2
       }
     }
   }
}

fuzziness是你的搜索文本最多能够纠正几个字母去跟你的数据进行匹配,默认若是不设置,就是2。

wildcard查询

wildcard(通配符)查询和prefix查询相似,也是一个基于词条的低级别查询。可是它可以让你指定一个模式(Pattern),而不是一个前缀(Prefix)。它使用标准的shell通配符:?用来匹配任意字符,*用来匹配零个或者多个字符。

咱们来演示下:

GET myname/doc/_search
{
   "query": {
      "wildcard": {
        "name": {
          "value": "cl*n"
        }
      }
   }
}

multi_match查询

若是咱们须要指定多字段,可使用multi_match ,来指定一个fields属性用来要搜索的字段数组。

GET /movies/_search?pretty
{
  "query": {
        "multi_match" : {
            "query" : "监狱风云",
            "fields" : ["title"]
        }
    }
}

咱们能够看到上面的请求正文,指定了一个查询,而且使用multi_match进行多个匹配。而后经过query 指定查询内容,并经过fields限制查询的字段。

字段也能够经过通配符指定:

GET /movies/_search?pretty
{
  "query": {
        "multi_match" : {
            "query" : "监狱风云",
            "fields" : ["tit*"]
        }
    }
}

filter过滤

filter 相似于 SQL 里面的where 语句,和上面的基础查询比起来,也能实现搜索的功能,同时 filter不计算相关性,而且能够将查询缓存到内存当中,所以,filter速度要快于query。tan

"filter": {
                   Filter to apply to the query
     }

指定条件过滤

这时候咱们就可使用到过滤, 由于我知道监狱风云2是在1991年上映的,因此我使用year过滤出1991年的监狱风云

POST /_search
{
    "query": {
        "bool": {
            "must": {
                "query_string": {
                    "query": "监狱风云"
                }
            },
            "filter": {
                "term": { "year": 1991 }
            }
        }
    }
}

无查询条件过滤

在上面的示例中,使用过滤器限制查询字符串查询的结果。若是咱们想只是单纯的过滤呢?也就是说,咱们但愿全部电影符合必定的标准。
在这种状况下,咱们仍然在搜索请求正文中使用query属性。可是,咱们不能只是添加一个过滤器,须要将它包装在某种查询中。

一个解决方案是修改当前的搜索请求,替换查询字符串 query 过滤查询中的match_all查询,这是一个查询,匹配一切内容。

好比说我想过滤出全部电影中上映时间是1994年的电影:

POST /_search
{
    "query": {
        "bool": {
            "must": {
                "match_all": {
                }
            },
            "filter": {
                "term": { "year": 1994 }
            }
        }
    }
}

过滤非空内容

GET /_search
{
  "query": {
    "bool": {
      "filter": {
        "exists": {
          "field": "title"
        }
      }
    }
  }
}

过滤器缓存

ElasticSearch提供了一种特殊的缓存,即过滤器缓存(filter cache),用来存储过滤器的结果,被缓存的过滤器并不须要消耗过多的内存(由于它们只存储了哪些文档能与过滤器相匹配的相关信息),并且可供后续全部与之相关的查询重复使用,从而极大地提升了查询性能。

注意:ElasticSearch并非默认缓存全部过滤器,如下过滤器默认不缓存:

numeric_range
    script
    geo_bbox
    geo_distance
    geo_distance_range
    geo_polygon
    geo_shape
    and
    or
    not

exists,missing,range,term,terms默认是开启缓存的

开启方式:在filter查询语句后边加上 "_catch":true

constant_score

上面的查询咱们有一个更简单的方法是使用常数分数查询,该查询可以包含一个查询或者一个过滤器,全部匹配文档的相关度分值都为1,不考虑TF/IDF:

curl -H "Content-Type: application/json"  -XPOST "http://localhost:9200/_search" -d' { "query": { "constant_score": { "filter": { "term": { "year": 1994 } } } } }'

range查询

范围查询主要针对数值和日期类型,range 可使用比较关键词,也可使用自带参数。

比较关键词:

  • gte − 大于和等于
  • gt − 大于
  • lte − 小于和等于
  • lt − 小于

参数:

  • from - 从什么时间或者数字开始,等同 gte
  • to - 到什么时间或者数字结束, 等同 lte
  • include_lower - 是否包含范围的左边界,默认是true , 等同gt
  • include_upper - 是否包含范围的右边界,默认是true,等同 lt

咱们先来查询下数值范围

POST movies/movie/_search
{
  "query": {
    "range": {
      "year": {
        "gte": 1990,
        "lte": 2000
      }
    }
  }
}

固然也可使用:

POST movies/movie/_search
{
  "query": {
    "range": {
      "year": {
        "from": 1990,
        "to": 2000
      }
    }
  }
}

由于咱们以前没有含日期的索引,因此咱们如今来建立几个。

PUT  students/doc/1
{
  "name" :"Jay",
   "birth" :"1995-06-07"
}

PUT  students/doc/2
{
  "name" :"Arlis",
   "birth" :"1990-02-02"
}

PUT  students/doc/3
{
  "name" :"Nike",
   "birth" :"1980-02-02"
}
PUT  students/doc/4
{
  "name" :"Yves",
   "birth" :"2004-05-06"
}

好了如今咱们已经有数据,开始查询:

POST students/doc/_search
{
  "query": {
    "range": {
      "birth": {
        "gte": "2000-01-01"
      }
    }
  }
}

日期提供一种更友好的计算方式

POST students/doc/_search
{
  "query": {
    "range": {
      "birth": {
        "gte": "now-20y"
      }
    }
  }
}

now 表明当前系统时间, 20y 表明20年 , now-20y 即在如今的时间基础上减小20年

日期计算的操做符主要有三个 + - /d,例如:

  • +1h 加一小时
  • -1h 减一小时
  • /d 将时间舍入到天

match_phrase查询

目前咱们能够在字段中搜索单独的一个词,可是有时候你想要确切的匹配若干个单词或者短语(phrases). 咱们只须要把match变为match_phrase便可。
match_phrasematch的区别,match_phrase首先分析查询字符串,从分析后的文本中构建短语查询,这意味着必须匹配短语中的全部分词,而且保证各个分词的相对位置不变,通俗的讲就是返回的结果,跟搜索内容的分词的顺序是同样的。

例如:

GET movies/movie/_search
{
  "query": {
    "match_phrase": {
      "title": "罗马假日"
    }
  }
}

上面咱们使用了match_phrase搜索了 罗马假日,可以获得索引信息。

下面咱们搜索假日罗马

GET movies/movie/_search
{
  "query": {
    "match_phrase": {
      "title": "假日罗马"
    }
  }
}


咱们发现没有获得任何结果。

match_phrase_prefix 查询

咱们还可使用match_phrase_prefix 来查询前缀, 为何是match_phrase_prefix 而不是match_prefix其实也很好理解,由于前缀是须要顺序的,必须在前面。

GET movies/movie/_search
{
  "query": {
    "match_phrase_prefix": {
      "title": "霸王"
    }
  }
}

sort排序

当搜索的字段有多个时,能够对指定字段进行排序。注意的是文本内容不能进行排序。

咱们使用sort 指定字段进行排序, 可使用order 指定排序方法。

order的值能够为:

  • asc 进行升序排序
  • desc 进行降序排序

下面咱们对year进行升序排序:

GET /movies/_search?pretty
{
   "query": {
        "match_all":{
        }
    },
  "sort": [
    {"year": {"order": "asc"}}
  ]
}

year进行降序排序:

GET /movies/_search?pretty
{
   "query": {
        "match_all":{
        }
    },
  "sort": [
    {"year": {"order": "desc"}}
  ]
}

当一个字段的内容有多个值的时候,系统支持一些计算进行排序,包括min、max、sum、avg、median (中间值)。

mode 描述
min 选择最低值。
max 选择最高值。
sum 使用全部值的总和做为排序值。仅适用于基于数字的数组字段。
avg 使用全部值的平均值做为排序值。仅适用于基于数字的数组字段。
median 使用全部值的中位数做为排序值。仅适用于基于数字的数组字段。

咱们来演示下用法:

GET /movies/_search?pretty
{
   "query": {
        "match_all":{
        }
    },
  "sort": [
    {"year": {"order": "asc","mode":"min"}}
  ]
}

from size分页

实现分页搜索只须要指定fromsize字段便可,例如:

GET /movies/_search?pretty
{
  "from": 0,
  "size": 15, 
   "query": {
        "match_all":{
        }
    }
}

注意:ES的from、size分页不是真正的分页,称之为浅分页。from+ size不能超过index.max_result_window 默认为10,000 的索引设置

scroll滚动

咱们在前面说过from、size分页是浅分页,并且最多只能显示10000个数据,若是一次性要查出来好比10万条数据,浅分页显然知足不了咱们的要求。此时通常会采起用scoll滚动查询,一批一批的查,直到全部数据都查询完为止。

scoll搜索会在第一次搜索的时候,保存一个当时的视图快照,后续的对文档的改动(索引、更新或者删除)都只会影响后面的搜索请求。scoll采用基于_doc(不使用_score)进行排序的方式,性能较高.

为了提现查询的效果咱们添加点数据。

wget https:   raw.githubusercontent.com/elastic/elasticsearch/master/docs/src/test/resources/accounts.json

curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/account/_bulk" --data-binary "@accounts.json"

为了使用 scroll,初始搜索请求应该在查询中指定scroll 参数,这能够告诉 Elasticsearch 须要保持搜索的上下文环境多久,如 scroll=1m ,1m是一分钟的意思,若是数据很大,那么建议等待时间长一点。

GET /bank/account/_search?scroll=5m
{
  "query": {
    "range": {
      "age": {
        "gte": 20,
        "lte": 40
      }
    }
  }
}

使用上面的请求返回的结果中包含一个scroll_id,这个 ID 能够被传递给 scroll API 来检索下一个批次的结果。

咱们用这个scroll_id来查询

GET /_search/scroll
{
    "scroll" : "1m", 
    "scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAE5FmcxbnNvb0ZKU0Fpb3BMRUFmeEJuaEEAAAAAAAABNxZnMW5zb29GSlNBaW9wTEVBZnhCbmhBAAAAAAAAAToWZzFuc29vRkpTQWlvcExFQWZ4Qm5oQQAAAAAAAAE7FmcxbnNvb0ZKU0Fpb3BMRUFmeEJuaEEAAAAAAAABOBZnMW5zb29GSlNBaW9wTEVBZnhCbmhB" 
}

固然这个响应消息还会返回一个scroll_id,若是咱们须要不断的往下查找就须要不断的查询这些scroll_id

highlight高亮搜索

不少应用喜欢从每一个搜索结果中高亮(highlight)匹配到的关键字,这样用户能够知道为何这些文档和查询相匹配。在Elasticsearch中高亮片断是很是容易的,咱们只须要添加highlight关键字。

请求信息:

GET students/doc/_search
{
    "query" : {
        "match" : {
            "title" : "卢旺达饭店"
        }
    },
    "highlight": {
        "fields" : {
            "title" : {}
        }
    }
}

响应信息:

{
  "took": 6,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 4.9041457,
    "hits": [
      {
        "_index": "movies",
        "_type": "doc",
        "_id": "11",
        "_score": 4.9041457,
        "_source": {
          "title": "卢旺达饭店",
          "director": "特瑞·乔治",
          "year": 2004,
          "genres": [
            "战争",
            "剧情",
            "历史"
          ],
          "actors": [
            "唐·钱德尔",
            "苏菲·奥康内多",
            "杰昆·菲尼克斯"
          ]
        },
        "highlight": {
          "title": [
            "<em>卢</em><em>旺</em><em>达</em><em>饭</em><em>店</em>"
          ]
        }
      }
    ]
  }
}

响应的内容与以前结果相同,可是在返回结果中会有一个新的部分叫作highlight,这里包含了来自titile字段中的文本,而且用<em></em>来标识匹配到的内容。

simple_query_string 查询

Simple_Query_String相似Query_String,可是会忽略错误的查询语法,而且仅支持部分查询语法。

来看下面的例子 ,咱们使用|查询包含罗马或者卢旺达的信息,可是符号附近有一些垃圾字符。

GET  movies/movie/_search
{
  "query": {
    "simple_query_string": {
      "query": "罗马 / | 、卢旺达",
      "fields": ["title"]
    }
  }
}

咱们成功执行了命令。

如今咱们把simple_query_string 换成query_string ,在来试试。

GET  movies/movie/_search
{
  "query": {
    "query_string": {
      "query": "罗马 / | 、卢旺达",
      "fields": ["title"]
    }
  }
}

咱们执行命令,发现报错

bool查询

bool 查询容许咱们使用布尔逻辑将小的查询组成大的查询。

AND查询

好比说咱们须要同时匹配到 title 含有 监狱风云 的索引信息。
请求内容:

GET /movies/_search
{
		"query":{
			"bool":
			{"must":[
				{"match":{"title":"监狱"}},
				{"match":{"title":"风云"}}
				]   
			}   
		}
	}
在上述示例中,`bool` `must` 子句指定了全部匹配文档必须知足的条件。
  • OR查询
    相比之下,bool should 的组合,两个match查询而且返回全部title属性中包含 监狱西游 的任意信息。

    请求内容:

    GET /movies/_search
    {
    	"query":{
    		"bool":
    		{"should":[
    			{"match":{"title":"监狱"}},
    			{"match":{"title":"西游"}}
    			]   
    		}   
    	}
    }

    在上述例子中bool should 子句指定了匹配文档只要知足其中的任何一个条件便可匹配。

AND取反查询

咱们还可使用 `bool` 和`must_not`, 查询属性中不包含匹配内容的文档。
请求内容:
```bash
GET /movies/_search
{
	"query":{
		"bool":
		{"must_not":[
			{"match":{"title":"监狱"}},
			{"match":{"title":"西游"}}
			]   
		}   
	}
}
```
在上述例子中,`bool` `must_not`子句指定了其中的任何一个条件都不知足时便可匹配

布尔组合查询

咱们能够组合 must 、should 、must_not 进行复杂的查询。

  • A AND NOT B
GET /movies/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "监狱风云" } }
      ],
      "must_not": [
        { "match": { "year": 1991 } }
      ]
    }
  }
}
  • A AND (B OR C)
GET /movies/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_all": {}
        },
        {
          "bool": {
            "should": [
              {
                "match": {"genres": "同性"}
              },
              {"match": {"genres": "科幻"}
              }
            ]}
        }
      ]
    }
  }
}

aggs聚合查询

lasticsearch有一个功能叫作聚合(aggregations),它容许你在数据上生成复杂的分析统计。它很像SQL中的GROUP BY可是功能更强大。

准备数据

咱们先插入一些数据:

POST /_bulk
{"index":{"_index":"goods","_type":"doc","_id":1}}
{"gname":"薯片","price":5.00,"classes":"零食","num":100}
{"index":{"_index":"goods","_type":"doc","_id":2}}
{"gname":"方便面","price":3.20,"classes":"零食","num":200}
{"index":{"_index":"goods","_type":"doc","_id":3}}
{"gname":"毛巾" ,"price":13.50, "classes":"日用品","num":60}
{"index":{"_index":"goods","_type":"doc","_id":4}}
{"gname":"面包" ,"price":4.50,"classes":"零食", "num":80}
{"index":{"_index":"goods","_type":"doc","_id":5}}
{"gname":"牙刷" ,"price":4.50, "classes":"日用品","num":100}
{"index":{"_index":"goods","_type":"doc","_id":6}}
{"gname":"可乐" ,"price":3, "classes":"零食","num":100}

Max求最大值

如今咱们来查询刚刚到商品中价格最高的:

GET goods/doc/_search
{
 "aggs": {
   "price_of_max": {
     "max": {
       "field": "price"
     }
   }
 }
}

能够在aggregations中获得查询的结果

{
 ...
  "aggregations": {
    "maxprice": {
      "value": 13.5
    }
  }

Sum求和

如今咱们来统计全部商品的单价和:
若是咱们想要屏蔽查询索引具体内容,可使用size ,把查询数量设为0

GET goods/doc/_search
{
 "size" :0,
  "aggs": {
     "price_of_sum": {
         "sum": {
           "field": "price"
         }
     }
  }
}

响应信息:

{
 ...
  "aggregations": {
    "price_of_sum": {
      "value": 33.700000047683716
    }
  }
}

能够看到和为 33.7

avg求平均值

下面咱们计算商品的平均价格

GET goods/doc/_search
{
  "size":0,
  "aggs": {
     "price_of_avg": {
         "avg": {
           "field": "price"
         }
     }
  }
}

响应信息:

{
 ...
  "aggregations": {
    "price_of_cardi": {
      "value": 5.616666674613953
    }
  }
}

cardinality求基数

GET goods/doc/_search
{
  "size":0,
  "aggs": {
     "price_of_cardi": {
         "cardinality": {
           "field": "price"
         }
     }
  }
}

terms分组

咱们来经过商品类别分组并统计商品数量:

GET goods/doc/_search
{
  "size":0,
  "aggs": {
     "price_group_by": {
         "terms": {
           "field": "classes.keyword"
         }
     }
  }
}

响应信息:

...
...
{
  "aggregations": {
    "price_group_by": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "零食",
          "doc_count": 4
        },
        {
          "key": "日用品",
          "doc_count": 2
        }
      ]
    }
  }
}

咱们能够看到零食的数量为4 ,日用品数量为2

Source过滤

数据列过滤容许在查询的时候不显示原始数据,或者显示部分原始字段。

不显示原始文档

GET /_search
{
    "_source": false,
    "query" : {
        "term": {"year": {"value": 1994}}
    }
}

显示部分文档列

好比说咱们只想显示moviestitleactors信息。

GET /_search
{
    "_source": "actors",
    "query" : {
        "term": {"year": {"value": 1994}}
    }
}

包含或者排除某些列

咱们还可使用 includes 包含某字段,或者使用excludes排除某字段。

例如:

GET /_search
{
    "_source": {
        "includes": [ "title", "director" ],
        "excludes": [ "year" ]
    },
    "query" : {
      "term": {"year": {"value": 1994}}
    }
}

相关性评分

咱们曾经讲过,默认状况下,返回结果是按相关性倒序排列的。 可是什么是相关性? 相关性如何计算?

每一个文档都有相关性评分,用一个相对的浮点数字段_score 来表示 _score的评分越高,相关性越高。查询语句会为每一个文档添加一个 _score 字段,评分的计算方式取决于不一样的查询类型。

不一样的查询语句用于不一样的目的:

  • fuzzy 查询会计算与关键词的拼写类似程度
  • terms查询会计算 找到的内容与关键词组成部分匹配的百分比

可是通常意义上咱们说的全文本搜索是指计算内容与关键词的相似程度。
ElasticSearch的类似度算法被定义为 TF/IDF,即检索词频率/反向文档频率,包括一下内容:

  • 检索词频率::
    检索词在该字段出现的频率,出现频率越高,相关性也越高。 字段中出现过5次要比只出现过1次的相关性高。
  • 反向文档频率::
    每一个检索词在索引中出现的频率,频率越高,相关性越低。 检索词出如今多数文档中会比出如今少数文档中的权重更低, 即检验一个检索词在文档中的广泛重要性。
  • 字段长度准则::
    字段的长度是多少,长度越长,相关性越低。 检索词出如今一个短的 title 要比一样的词出如今一个长的 content 字段。

单个查询可使用TF/IDF评分标准或其余方式,好比短语查询中检索词的距离或模糊查询里的检索词类似度。

相关性并不仅是全文本检索的专属。也适用于yes|no的子句,匹配的子句越多,相关性评分越高。

若是多条查询子句被合并为一条复合查询语句,好比 bool 查询,则每一个查询子句计算得出的评分会被合并到总的相关性评分中。

ElasticSearch提供了一个explain参数,将 explain 设为 true 就能够获得 _score详细的信息。

GET /movies/_search?explain=true
{
  "query": {
    "match": {
      "title": "龙猫"
    }
  }
}

具体的响应消息由于文本太多,本身执行命令查询 ,咱们这里只分析几个重要信息

"_shard": "[movies][3]"
 "_node": "g1nsooFJSAiopLEAfxBnhA",

这里加入了该文档来自于哪一个节点哪一个分片上的信息,这对咱们是比较有帮助的,由于词频率和 文档频率是在每一个分片中计算出来的,而不是每一个索引中。

_explanation会包含在每个入口,告诉你采用了哪一种计算方式,并让你知道计算的结果以及其余详情:

"description":"score(doc=3,freq=1.0 = termFreq=1.0)), product of:"

检索词的频率

"description":"idf, computed as log(1 + (docCount - docFreq + 0.5) / (docFreq + 0.5)) from:"

反向文档频率

"description":"docCount",

字符长度准则

加速检索建议