之前的查询直接指定id,这里的查询只是输入条件
先来一个没有条件的查询:
返回的结果很容易理解,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
查看映射:GET http://localhost:9200/user/_mapping/doc
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被如何被分析:
测试输入GOOD-NAME,会被分析为两个token:小写的good name
使用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,增加同意词条
先实验一把,发现并不好使:
并没有去掉h1,a、the,也没有把&转换为and,不过大小写倒是都变为小写的,其是字符过滤、词条过滤默认是不开启的,需要手动配置:
再添加索引时就设置了一个分析器(my_analyzer),测试一下它的分词效果:
这一步仅仅添加了一个分词器,需要在字段映射使用这个分词器:
使用字段分词:
查询结果:
not换成a the肯定查询不到任何结果,另外通过_source可以指定返回那些字段
回到映射,一个字段被映射后,若新增一个文档,字段类型不一致会如何?
es会尝试转换,如输入的是“true”则帮你转换为true,但输入数字或字符就会报错了,es在遇到新字段时自动做了映射,当然可以控制这一行为:
通过设置dynamic为strict,es遇到新类型字段会抛出异常,注意properties里面的json,可以覆盖外层的dynamic的值:
下面的put会成功:
当然如果新加一个字段就会抛异常了。
term不分析查询:
term和match的区别,match查询会对查询的字符做分析,而term不会,上图的查询若不是name.keyword而是name,则查的是空,因为name已经被分析为good和name了,对于GOOD-Name而言是找不到的,
我们在插入几个文档,新文档增加一个字段nullable
对于原来的文档,这个字段是null,查询这个字段非空的文档:
反过来,查询这个字段为空的文档,es2有missing,es6已经没有了,不过我们可以用must_not:
验证查询是否正确:
这个分析某个有问题的查询体时有作用,另外也解释了我们的查询实际执行情况,这里GOOD-NAME已经被分词器分析了
排序:
和query同级加上sort,sort的value是一个数组(若只有一个元素可以用对象),sort结果不计算评分,当然,要使用评分来排序,评分肯定会被计算的:
若按文本类型的字段排序:
mode是text类型,在es中为倒排索引,不支持排序的,解决上面的问题把mode修改为mode.keyword即可,或者设置该字段映射的fielddata为true???todo
多字段排序,可以使用
min
、
max
、
avg
或是
sum
若没有sort,es对结果集按照_score来进行排序,_score是如何计算的呢?
1:频率,搜索词在搜索字段中出现的次数,次数越高 score就越高
2:文档频率,搜索词在所有文档出现的次数 次数越高 score就越低
3:字段长度,搜索词所在字段的长度 长度越长 score越低
查看评分如何计算:
好吧,公式对我而言有点复杂,有时候需要查看为什么某个文档没有被查询出来:
搜索类型:
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" }}
]
}