天天看点

elasticsearch先实践再理论 三 查询

之前的查询直接指定id,这里的查询只是输入条件

先来一个没有条件的查询:

elasticsearch先实践再理论 三 查询

返回的结果很容易理解,hits的返回的文档数组,_source是文档内容,_score是这此query中该文档的得分,得分越高越靠前。

若user索引中文档有1w个,都返回吗?当然不是,es默认返回10条,也可以自己控制:

{
     "from":1,
     "size":1
}
           

上面的结果是查询第2页,每页1条(mysql limit一模一样)

空查询等同于在请求体中添加如下json:

{
    "query": {
        "match_all": {}
    }
}
           

加上一个查询条件:

{
    "query": {
        "match": {
        	"first_name":"Douglas"
        }
    }
}
           

多个查询条件如何传参呢?使用bool把多个条件嵌套:

{
	"query":{
		 "bool": {
	        "must":     { "match": { "first_name": "Douglas" }},
	        "must_not": { "match": { "last_name":  "mary" }},
	        "should":   { "match": { "about": "abc" }},
	        "filter":   { "range": { "age" : { "gt" : 30 }} }
    	}
	}
}
           

bool查询里面可以有下面几种key

must:必须满足

must_not:必须不满足

should:满足则加分(影响排名)

filter:不满足则过滤掉

进一步了解查询前先看一下映射:

put操作后es会帮助你把字段做映射,就是识别这个字段类型,string、object、date、integer等等,让我们先put一个user

elasticsearch先实践再理论 三 查询

查看映射:GET http://localhost:9200/user/_mapping/doc

elasticsearch先实践再理论 三 查询

age->long birthday->date happy->boolean weght->float,address->object

这几个没啥问题,剩下的都是文本类型 type为text,es2.0版本type是string,我现在用的是es6,文本后门还有个fields,就是说这个字段还有其它字段,字段名为外层字段 点 fields里面的名称,比如name字段也有name.keyword,类型是keyword,ignore_above代表忽略长度大于256的字符。

type为text的字段会被分析,比如name是“GOOD-NAME”,es会用分析器分析

其是keyword和boolean、text医院,也是type的一种,这种类型的字段不会被分析,看一下name被如何被分析:

elasticsearch先实践再理论 三 查询

测试输入GOOD-NAME,会被分析为两个token:小写的good name

elasticsearch先实践再理论 三 查询

使用keyword类型的字段分析文本,它会原封不动的解析为token,es2和es6的区别:

es2
"name":{
    "type":"string"
    "index":"not_analyzed"
}

es6
"name":{
    "type":"keyword"
}
           

es底层是lucene,通过把字段分析为词条(token)并存储为倒排索引:

term position
good doc1,doc2
name doc3
ted doc1
... ...

这样在全文查询时非常高效,假如要搜索博客包含“elasticsearch”,若是用sql:select * from

blog where content like '%elasticsearch%',mysql的索引在like时不会使用的,而用倒排索引就能快速找到那些文章包含你搜索的关键字。

那么为了做倒排索引,es需要把字段分析为一个个词条以插入到倒排索引中,这个工具较分词器,它主要有3个功能:

1:字符过滤,去掉html,&转换为and

2:分词,遇到空格或符号,将文本拆分

3:词条过滤:删除无用的如 a the,增加同意词条

先实验一把,发现并不好使:

elasticsearch先实践再理论 三 查询

并没有去掉h1,a、the,也没有把&转换为and,不过大小写倒是都变为小写的,其是字符过滤、词条过滤默认是不开启的,需要手动配置:

elasticsearch先实践再理论 三 查询

再添加索引时就设置了一个分析器(my_analyzer),测试一下它的分词效果:

elasticsearch先实践再理论 三 查询

这一步仅仅添加了一个分词器,需要在字段映射使用这个分词器:

elasticsearch先实践再理论 三 查询

使用字段分词:

elasticsearch先实践再理论 三 查询

查询结果:

elasticsearch先实践再理论 三 查询

not换成a the肯定查询不到任何结果,另外通过_source可以指定返回那些字段

回到映射,一个字段被映射后,若新增一个文档,字段类型不一致会如何?

elasticsearch先实践再理论 三 查询

es会尝试转换,如输入的是“true”则帮你转换为true,但输入数字或字符就会报错了,es在遇到新字段时自动做了映射,当然可以控制这一行为:

elasticsearch先实践再理论 三 查询

通过设置dynamic为strict,es遇到新类型字段会抛出异常,注意properties里面的json,可以覆盖外层的dynamic的值:

下面的put会成功:

elasticsearch先实践再理论 三 查询

当然如果新加一个字段就会抛异常了。

term不分析查询:

elasticsearch先实践再理论 三 查询

term和match的区别,match查询会对查询的字符做分析,而term不会,上图的查询若不是name.keyword而是name,则查的是空,因为name已经被分析为good和name了,对于GOOD-Name而言是找不到的,

我们在插入几个文档,新文档增加一个字段nullable

elasticsearch先实践再理论 三 查询

对于原来的文档,这个字段是null,查询这个字段非空的文档:

elasticsearch先实践再理论 三 查询

反过来,查询这个字段为空的文档,es2有missing,es6已经没有了,不过我们可以用must_not:

elasticsearch先实践再理论 三 查询

验证查询是否正确:

elasticsearch先实践再理论 三 查询

这个分析某个有问题的查询体时有作用,另外也解释了我们的查询实际执行情况,这里GOOD-NAME已经被分词器分析了

排序:

elasticsearch先实践再理论 三 查询

和query同级加上sort,sort的value是一个数组(若只有一个元素可以用对象),sort结果不计算评分,当然,要使用评分来排序,评分肯定会被计算的:

elasticsearch先实践再理论 三 查询

若按文本类型的字段排序:

elasticsearch先实践再理论 三 查询

mode是text类型,在es中为倒排索引,不支持排序的,解决上面的问题把mode修改为mode.keyword即可,或者设置该字段映射的fielddata为true???todo

多字段排序,可以使用

min

 、 

max

 、 

avg

 或是 

sum

elasticsearch先实践再理论 三 查询

若没有sort,es对结果集按照_score来进行排序,_score是如何计算的呢?

1:频率,搜索词在搜索字段中出现的次数,次数越高 score就越高

2:文档频率,搜索词在所有文档出现的次数 次数越高 score就越低

3:字段长度,搜索词所在字段的长度 长度越长 score越低

查看评分如何计算:

elasticsearch先实践再理论 三 查询

好吧,公式对我而言有点复杂,有时候需要查看为什么某个文档没有被查询出来:

elasticsearch先实践再理论 三 查询

搜索类型:

elasticsearch先实践再理论 三 查询

search_type默认为query_then_fetch,其它选项为query_and_fetch,dfs_query_then_fetch,dfs_query_and_fetch

由于es索引设计为多分片,查询时先发送给协调节点,协调节点把请求转发给各个分片,分片执行query,并把符合条件的id&score(排序需要的值)返回给协调节点,协调节点在发送multi-get查询把那些id对应的数据取回(fetch),这就是query_then_fetch

query_and_fetch指各分片不仅返回id,_source数据也返回,就不需要协调节点再通过id来fetch了

因为每个分片独自计算反向文档频率,dfs会先查询所有分片的文档字段频率并汇总到协调节点,再进行查询

分页查询过程:协调节点初始化from+size大小的数组,各分片也一样,查询后返回给协调节点,协调节点对返回的结果排序,对于深度分页(from很大,如10000),协调节点就需要对(10000+size)*分片数个结果进行排序,非常耗费cpu的,游标查询todo

索引重建:当字段需要修改时,如类型、分析器,就需要把原来所有的数据重新索引一遍了,索引别名能提供一些帮助:

PUT localhost:9200/my_index/_alias/my

上面的操作就给my_index取了个别名my

下面的两个get可以查询my是谁的别名

GET localhost:9200/*/_alias/my

GET localhost:9200/my/_alias/*

那我们可以一直用别名,当需要重建索引时,把新的索引指向这个别名,就完成了索引迁移:

POST /_aliases
{
    "actions": [
        { "remove": { "index": "my_index_v1", "alias": "my_index" }},
        { "add":    { "index": "my_index_v2", "alias": "my_index" }}
    ]
}
           

继续阅读