一、相关概念
1.1 倒排索引
Elasticsearch使用倒排索引来达到加速检索的目的。
倒排索引:根据属性值来确定记录位置
倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。
在Elasticsearch中,每一个字段的数据都是默认被索引的。也就是说,每个字段专门有一个反向索引用于快速检索。而且,与其它数据库不同,它可以在同一个查询中利用所有的这些反向索引,以惊人的速度返回结果。
通常,我们可以认为对象(object)和文档(document)是等价相通的。不过,他们还是有所差别:对象(Object)是一个JSON结构体——类似于哈希、hashmap、字典或者关联数组;对象(Object)中还可能包含其他对象(Object)。 在Elasticsearch中,文档(document)这个术语有着特殊含义。它特指最顶层结构或者根对象(root object)序列化成的JSON数据(以唯一ID标识并存储于Elasticsearch中)。
1.2 分析器
当我们索引(index)一个文档,全文字段会被分析为单独的词来创建倒排索引。不过,当我们在全文字段搜索(search)时,我们要让查询字符串经过同样的分析流程处理,以确保这些词在索引中存在。
全文查询我们将在稍后讨论,理解每个字段是如何定义的,这样才可以让它们做正确的事:
- 当你查询全文(full text)字段,查询将使用相同的分析器来分析查询字符串,以产生正确的词列表。
- 当你查询一个确切值(exact value)字段,查询将不分析查询字符串,但是你可以自己指定。
1.3 指定分析器
当Elasticsearch在你的文档中探测到一个新的字符串字段,它将自动设置它为全文string
字段并用standard
分析器分析。
你不可能总是想要这样做。也许你想使用一个更适合这个数据的语言分析器。或者,你只想把字符串字段当作一个普通的字段——不做任何分析,只存储确切值,就像字符串类型的用户ID或者内部状态字段或者标签。
为了达到这种效果,我们必须通过映射(mapping)人工设置这些字段。
小提示
错误的映射,例如把age
字段映射为string
类型而不是integer
类型,会造成查询结果混乱。
要检查映射类型,而不是假设它是正确的!
1.4 查询和过滤--性能差异
使用过滤语句得到的结果集 -- 一个简单的文档列表,快速匹配运算并存入内存是十分方便的, 每个文档仅需要1个字节。这些缓存的过滤结果集与后续请求的结合使用是非常高效的。
查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比 过滤语句更耗时,并且查询结果也不可缓存。
幸亏有了倒排索引,一个只匹配少量文档的简单查询语句在百万级文档中的查询效率会与一条经过缓存 的过滤语句旗鼓相当,甚至略占上风。 但是一般情况下,一条经过缓存的过滤查询要远胜一条查询语句的执行效率。
过滤语句的目的就是缩小匹配的文档结果集,所以需要仔细检查过滤条件。
1.5 查询和过滤--什么情况下使用
原则上来说,使用查询语句做全文本搜索或其他需要进行相关性评分的时候,剩下的全部用过滤语句
前面我们讲到的是关于结构化查询语句,事实上我们可以使用两种结构化语句: 结构化查询(Query DSL)和结构化过滤(Filter DSL)。 查询与过滤语句非常相似,但是它们由于使用目的不同而稍有差异。
一条过滤语句会询问每个文档的字段值是否包含着特定值:
-
created
的日期范围是否在2013
到2014
? -
status
字段中是否包含单词 "published" ? -
lat_lon
字段中的地理位置与目标点相距是否不超过10km ?
一条查询语句与过滤语句相似,但问法不同:
查询语句会询问每个文档的字段值与特定值的匹配程度如何?
查询语句的典型用法是为了找到文档:
-
查找与
full text search
这个词语最佳匹配的文档 -
查找包含单词
run
,但是也包含runs
,running
,jog
或sprint
的文档 -
同时包含着
quick
,brown
和fox
--- 单词间离得越近,该文档的相关性越高 -
标识着
lucene
,search
或java
--- 标识词越多,该文档的相关性越高
一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分 _score
,并且 按照相关性对匹配到的文档进行排序。 这种评分方式非常适用于一个没有完全配置结果的全文本搜索。
二、最重要的查询过滤语句
2.1 term
过滤
term
主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed
的字符串(未经分析的文本数据类型):
2.2 terms
过滤
terms
跟 term
有点类似,但 terms
允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配:
2.3 range
过滤
range
过滤允许我们按照指定范围查找一批数据:
2.4 exists
和 missing
过滤
exists
和 missing
过滤可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的IS_NULL
条件
2.5 bool
过滤
bool
过滤可以用来合并多个过滤条件查询结果的布尔逻辑,它包含以下操作符:
must
:: 多个查询条件的完全匹配,相当于 and
。
must_not
:: 多个查询条件的相反匹配,相当于 not
。
should
:: 至少有一个查询条件匹配, 相当于 or
。
这些参数可以分别继承一个过滤条件或者一个过滤条件的数组:
2.6 match_all
查询
使用match_all
可以查询到所有文档,是没有查询条件下的默认语句。
2.7 match
查询
match
查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
2.8 multi_match
查询
multi_match
查询允许你做match
查询的基础上同时搜索多个字段:
2.9 bool
查询
bool
查询与 bool
过滤相似,用于合并多个查询子句。不同的是,bool
过滤可以直接给出是否匹配成功, 而bool
查询要计算每一个查询子句的 _score
(相关性分值)。
must
:: 查询指定文档一定要被包含。
must_not
:: 查询指定文档一定不要被包含。
should
:: 查询指定文档,有则可以为文档相关性加分。
2.10 查询与过滤条件的合并
查询语句和过滤语句可以放在各自的上下文中。 在 ElasticSearch API 中我们会看到许多带有 query
或filter
的语句。 这些语句既可以包含单条 query 语句,也可以包含一条 filter 子句。 换句话说,这些语句需要首先创建一个query
或filter
的上下文关系。
复合查询语句可以加入其他查询子句,复合过滤语句也可以加入其他过滤子句。 通常情况下,一条查询语句需要过滤语句的辅助,全文本搜索除外。
所以说,查询语句可以包含过滤子句,反之亦然。 以便于我们切换 query 或 filter 的上下文。这就要求我们在读懂需求的同时构造正确有效的语句。
三、常用REST请求示例
针对es对外开放的http接口,下面列举除了一些常用的rest请求示例,同时它们在tcp接口中都有对应的java api实现
3.1 查询集群信息
localhost:9200
3.2 查看集群健康状态
localhost:9200/_cat/health?v
3.3 查看集群节点
localhost:9200/_cat/nodes?v
3.4 查看集群分片
localhost:9200/_cat/indices?v
3.5 创建索引
PUT localhost:9200/lulijun
3.6 索引文档
PUT localhost:9200/poi/doc/8?pretty
{
"poi_id": 2,
"poi_name": "3_name",
"time":1,
"price": 5
}
3.7 查询文档
localhost:9200/lulijun/doc/1?pretty
3.8 删除索引
DELETE localhost:9200/lulijun
3.9 更新文档
POST localhost:9200/lulijun/doc/1/_update
{
"doc":{
"name":"lulijun02"
}
}
3.10 以脚本更新文档
localhost:9200/lulijun/doc/1/_update
{
"script":"ctx._source.gener = 'male'"
}
3.11 删除文档
DELETE localhost:9200/lulijun/doc/1
3.12 批量操作
POST localhost:9200/lulijun/doc/_bulk
{"index":{"_id":"1"}}
{"name": "lulijun01","gender":"female" }
{"index":{"_id":"2"}}
{"name": "lulijun02", "gender":"male" }
3.13 查询全部-request param
localhost:9200/_search?q=*
3.14 查询全部-request body
POST localhost:9200/poi/_search
{
"query": { "match_all": {} }
}
3.15 limit
localhost:9200/lulijun/_search
{
"query": { "match_all": {} },
"size" : 1
}
3.16 Paging
localhost:9200/poi/_search
{
"query": { "match_all": {} },
"from" : 1,
"size":1
}
3.17 指定返回字段
POST localhost:9200/lulijun/_search
{
"query":{
"match_all":{}
},
"_source":["gender"]
}
3.18 match查询
localhost:9200/lulijun/_search
{
"query":{
"match":{
"gender":"male"
}
}
}
3.19 bool查询-must
localhost:9200/lulijun/_search
{
"query":{
"bool":{
"must":[
{
"match":{
"gender":"male"
}
},
{
"match":{
"name":"lulijun"
}
}
]
}
}
}
3.20 bool查询-should
localhost:9200/lulijun/_search
{
"query":{
"bool":{
"should":[
{
"match":{
"gender":"male"
}
},
{
"match":{
"name":"lulijun"
}
}
]
}
}
}
3.21 bool查询-must not
localhost:9200/lulijun/_search
{
"query":{
"bool":{
"must_not":[
{
"match":{
"gender":"xx"
}
},
{
"match":{
"name":"xy"
}
}
]
}
}
}
3.22 groupby+count
localhost:9200/poi/_search
{
"size":0,
"aggs":{
"group_by_state":{
"terms":{
"field":"poi_id"
}
}
}
}
3.23 groupby+sum
localhost:9200/poi/_search
{
"aggregations": {
"poi_id": {
"terms": {
"field": "poi_id",
"size": 2
},
"aggregations": {
"poi_name": {
"terms": {
"field": "time",
"size": 200
},
"aggregations": {
"SUM(price)": {
"sum": {
"field": "price"
}
}
}
}
}
}
}
}
3.24 script field
localhost:9200/poi/_search
{
"from": 0,
"size": 100,
"query":{
"bool":{
"must":[
{
"match":{
"poi_id":1
}
}
]
}
},
"aggregations": {
"poi_id": {
"terms": {
"script": {
"inline": "doc['poi_id'].value + 1"
}
},
"aggregations": {
"price": {
"sum": {
"field": "price"
}
}
}
}
}
}