一、QueryString查询
即通过get形式查询,条件都在url中
Domain Specific Language,格式化查询语句
1、请求参数的查询(QueryString)
- 请求(GET)
192.168.56.101:9200/shop/_search?q=desc:一纸家书网&q=age:20
GET /shop/_search?q=desc:一纸家书网
GET /shop/_search?q=nickname:家书&q=age:25
- 注意:
text与keyword搜索对比测试(keyword不会被倒排索引,不会被分词)
二、DSL语法查询
QueryString用的很少,一旦参数复杂就难以构建,所以大多查询都会使用dsl来进行查询更好。
- Domain Specific Language
- 特定领域语言
- 基于JSON格式的数据查询
- 查询更灵活,有利于复杂查询
1、DSL格式语法
# 查询
POST 192.168.56.101:9200/shop/_search
{
"query": {
"match": {
"desc": "一纸家书网"
}
}
}
# 判断某个字段是否存在
{
"query": {
"exists": {
"field": "desc"
}
}
}
- 语法格式为一个json object,内容都是key-value键值对,json可以嵌套。
- key可以是一些es的关键字,也可以是某个field字段,后面会遇到
2、查询所有数据
-
查询索引中的所有字段
- QS查询方式
GET xxx.xxx.xx.xxx:9200/shop/_search
- DSL查询方式
POST xxx.xxx.xx.xxx:9200/shop/_search
{ "query": { "match_all": {} } }
-
查询部分字段信息
- QS查询方式
GET xxx.xxx.xx.xxx:9200/shop/_search?_source=id,name
- DSL查询方式
POST xxx.xxx.xx.xxx:9200/shop/_search
{ "query": { "match_all": {} }, "_source": ["id", "name"] }
3、分页查询
"from": 0,
"size": 10
-
请求(POST)
POST xxx.xxx.xx.xxx:9200/shop/_search
-
请求参数
- 查询所有字段信息
{ "query": { "match_all": {} }, "from": 0, "size": 10 }
- 查询部分字段信息
{ "query": { "match_all": {} }, "_source": [ "id", "nickname", "age" ], "from": 5, "size": 5 }
三、term
和match
term精确搜索
match分词搜索
1、用例
- 请求方式(POST)
POST xxx.xxx.xx.xxx:9200/shop/_search
- json参数对比
# term形式,“一纸家书”会作为一个完整的词去匹配查询,不会对其进行分词
{
"query": {
"term": {
"desc": "一纸家书"
}
}
}
# match形式:会对搜索条件“一纸家书”进行分词查询,比如:一、纸、家、书、家书等等
{
"query": {
"match": {
"desc": "一纸家书"
}
}
}
2、terms
多词查询
- 请求方式(POST)
POST xxx.xxx.xx.xxx:9200/shop/_search
- json参数
{
"query": {
"terms": {
"desc": ["一纸家书", "天子", "无敌"]
}
}
}
3、match_phrase
短语匹配
match:分词后只要有匹配就返回
match_phrase:分词结果必须在text字段分词中都包含,而且顺序必须相同,而且必须都是连续的。(搜索比较严格,这是当slop为0或者没有设置的时候,如果设置了slop的值大于0,顺序可以不同,也可以不连续)
slop:match_phrase 短语匹配时,允许词语间跳过的数量
-
测试用例:
词条一: 我大学毕业后去上海工作,薪资还不错。
词条二: 大学生活真好,毕业后就开始忙碌了
词条三: 毕业已经一年了,大学同学都没有再见到过
词条四: 毕业大学生都去大城市打拼了
词条五: 大学生不断的走入社会,那些还没有毕业的也蠢蠢欲动
请求方式(POST)
POST xxx.xxx.xx.xxx:9200/shop/_search
- json参数(无slop形式)
{
"query": {
"match_phrase": {
"desc": {
"query": "大学 毕业"
}
}
}
}
此时只能查到“词条一”,
“词条一”中的“大学”和“毕业”顺序对,又连续
“词条二”、“词条五”中的“大学”和“毕业”虽然顺序对,但是不连续
“词条三”中的“大学”和“毕业”顺序都不对所以无法查询
“词条四”中的“大学”和“毕业”顺序也不对,所以无法查询
-
json参数(添加slop形式)
"slop": 2,表示 大学 和 毕业两个词中间最多可以间隔2个分词
{
"query": {
"match_phrase": {
"desc": {
"query": "大学 毕业",
"slop": 3
}
}
}
}
- slop的值以及查询结果
* slop:0,结果为-->词条一
* slop:3,结果为-->词条一、词条二
* slop:4,结果为-->词条一、词条二、词条四
* slop:8,结果为-->词条一、词条二、词条四、词条三
* slop:10(大于等于10),结果为-->词条一、词条二、词条四、词条三、词条五
4、operator
条件查询
-
operator
or:搜索内容分词后,只要存在一个词语匹配就展示结果,比如查询“一纸家书”,数据中有“一”就能被查出来
and:搜索内容分词后,都要满足词语匹配,比如查询“一纸家书”,数据中必须含有“一”、“纸”、“家书”才能查出来,顺序和是否连续到无所谓
请求方式(POST)
POST xxx.xxx.xx.xxx:9200/shop/_search
- json参数
{
"query": {
"match": {
"desc": "xbox游戏机"
}
}
}
# 等同于
{
"query": {
"match": {
"desc": {
"query": "xbox游戏机",
"operator": "or"
}
}
}
}
# 相当于 select * from shop where desc='xbox' or|and desc='游戏机'
5、minimum_should_match
- 最低匹配精度
至少有[分词后的词语个数]x百分百,得出一个数据值取整。
举个例子:当前属性设置为70,若一个用户查询检索内容分词后有10个词语,那么匹配度按照 10x70%=7,则desc中至少需要有7个词语匹配,就展示;若分词后有8个,则8x70%=5.6,则desc中至少需要有5个词语匹配,就展示。
- 也能设置具体的数字
表示个数
- 请求方式(POST)
POST xxx.xxx.xx.xxx:9200/shop/_search
- json参数
{
"query": {
"match": {
"desc": {
"query": "女友生日送我好玩的xbox游戏机",
"minimum_should_match": "60%"
}
}
}
}
6、根据文档主键ids
搜索
同QS查询: GET xxx.xxx.xx.xxx:9200/shop/_doc/1001
- 请求方式(POST)
POST xxx.xxx.xx.xxx:9200/shop/_search
- json参数
查询文档id为:"1001", "1010", "1008"相关信息
{
"query": {
"ids": {
"values": ["1001", "1010", "1008"]
}
}
}
7、multi_match
multi_match
满足使用match在多个字段中进行查询的需求,例如:我需要查名字
username
有“小天”以及别名'aliasname'有“小天”的用户,就需要用到该查询
boost
权重,为某个字段设置权重,权重越高,文档相关性得分就越高。通畅来说搜索商品名称要比商品简介的权重更高。如:"aliasname10",加数值
- 请求方式(POST)
查名字
username
有“小天”以及别名'aliasname'有“小天”的用户
POST xxx.xxx.xx.xxx:9200/shop/_search
- json参数
{
"query": {
"multi_match": {
"query": "小天",
"fields": ["username", "aliasname"]
}
}
}
- json参数(设置权重boost)
{
"query": {
"multi_match": {
"query": "小天",
"fields": ["username", "aliasname^10"]
}
}
}
aliasname^10 代表搜索提升10倍相关性,也就是说用户搜索的时候其实以这个aliasname为主,username为辅,aliasname的匹配相关度当然要提高权重比例了。
8、布尔查询bool
- must:查询必须匹配搜索条件,类似于mysql中的 and
- must_not:查询搜索条件一个都不满足的数据
- should:查询匹配条件一个或者一个以上的数据,类似于mysql中的 or
8.1 must
查询username 或者 aliasname中包含“小天”,且性别sex为1(男),并且出生日期为:1996-01-14的用户信息(全都要满足)
POST 192.168.56.101:9200/shop/_search
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "小天",
"fields": ["username", "aliasname"]
}
},
{
"term": {
"sex": 1
}
},
{
"term": {
"birthday": "1996-01-14"
}
}
]
}
}
}
8.2 should
查询username 或者 aliasname中包含“小天”,或者性别为0(女)的,或者出生日期为:2000-01-14的用户信息(满足一个就行)
POST 192.168.56.101:9200/shop/_search
{
"query": {
"bool": {
"should": [
{
"multi_match": {
"query": "小天",
"fields": ["username", "aliasname"]
}
},
{
"term": {
"sex": 0
}
},
{
"term": {
"birthday": "2000-01-14"
}
}
]
}
}
}
8.3 must_not
查询username 或者 aliasname中不包含包含“小天”,且性别不是0(女),且出生日期不是:2000-01-14的用户信息(全都不能满足)
POST 192.168.56.101:9200/shop/_search
{
"query": {
"bool": {
"must_not": [
{
"multi_match": {
"query": "小天",
"fields": ["username", "aliasname"]
}
},
{
"term": {
"sex": 0
}
},
{
"term": {
"birthday": "2000-01-14"
}
}
]
}
}
}
8.4 混合使用
查询 username 和 aliasname 中必须包含“小天”,并且年龄不为20,升高为180或者175的用户
POST 192.168.56.101:9200/shop/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"username": "小天"
}
},
{
"match": {
"aliasname": "小天"
}
}
],
"must_not": [
{
"term": {
"age": 20
}
}
],
"should": [
{
"term": {
"height": 180
}
},
{
"term": {
"height": 175
}
}
]
}
}
}
8.5 加权boost
查询 username 包含“小天”,或者年龄为20岁的用户,优先年龄为20岁的(权重更大,获取的数据更靠前)
POST 192.168.56.101:9200/shop/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"username": {
"query": "小天",
"boost": 2
}
}
},
{
"match": {
"age": {
"query": 20,
"boost": 10
}
}
}
]
}
}
}
9、过滤器post_filter
对搜索出来的结果进行数据过滤。不会到es库里去搜,不会去计算文档的相关度分数,所以过滤的性能会比较高,过滤器可以和全文搜索结合在一起使用。
post_filter元素是一个顶层元素,只会对搜索结果进行过滤。不会计算数据的匹配度相关性分数,不会根据分数去排序,query则相反,会计算分数,也会按照分数去排序。
9.1 使用场景
- query:根据用户搜索条件检索匹配记录
- post_filter:用于查询后,对结果数据的筛选
9.2 post_filter关键字
- gte:大于等于
- lte:小于等于
- gt:大于
- lt:小于
- 除此以外还能做其他的match等操作也行
9.3 实例
查询专业包含“软件工程”,年龄在10到20岁的用户
POST 192.168.56.101:9200/shop/_search
{
"query": {
"match": {
"major": "软件工程"
}
},
"post_filter": {
"range": {
"age": {
"gt": 10,
"lt": 20
}
}
}
}
10、排序sort
- desc
- asc
- 组合排序
10.1 用例
查询专业包含“软件工程”,年龄在10到20岁的用户,并按年龄倒序,性别正序排序
POST 192.168.56.101:9200/shop/_search
{
"query": {
"match": {
"major": "软件工程"
}
},
"post_filter": {
"range": {
"age": {
"gt": 10,
"lt": 20
}
}
},
"sort": [
{
"age": "desc"
},
{
"sex": "asc"
}
]
}
10.2 对文本进行排序
当文本被分词,所以去做排序会报错,通常我们可以为这个字段增加额外的一个附属属性,类型为keyword,用于做排序。就相当于对分词后的分词数据进行排序,一般不推荐使用
POST 192.168.56.101:9200/user2/_mapping
- 添加keyword的
"keyword": {
"type": "keyword"
}
{
"properties": {
"id": {
"type": "long"
},
"nickname": {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
- 添加数据
- 对文本进行排序
使用 nickname.keyword 的方式
{
"sort": [
{
"nickname.keyword": "desc"
}
]
}
10.3 深度分页
从9990页开始查询问题
{
"query": {
"match_all": {}
},
"from": 9990,
"size": 10
}
- 查询过程分析
我们在获取第9999条到10009条数据的时候,其实每个分片都会拿到10009条数据,然后集合在一起,总共是10009*3=30027条数据,针对30027数据再次做排序处理,最终会获取最后10条数据
如此一来,搜索得太深,就会造成性能问题,会耗费内存和占用cpu。而且es为了性能,他不支持超过一万条数据以上的分页查询。那么如何解决深度分页带来的性能呢?其实我们应该避免深度分页操作(限制分页页数),比如最多只能提供100页的展示,从第101页开始就没了,毕竟用户也不会搜的那么深,我们平时搜索淘宝或者百度,一般也就看个10来页就顶多了。
- 提升搜索量
通过设置index.max_result_window来突破10000数据,然后就可以使用上面的查询去获取数据
GET 192.168.56.101:9200/shop/_settings
PUT 192.168.56.101:9200/shop/_settings
{
"index.max_result_window": "20000"
}
11、查询关键词高亮highlight
会为查询结果中的关键词添加标签,默认为标签,可以自行配置
11.1 用例一
使用默认标签
POST 192.168.56.101:9200/user2/_search
- 查询条件
{
"query": {
"match": {
"desc": "薪资"
}
},
"highlight": {
"fields": {
"desc": {}
}
}
}
- 查询结果
{
"took": 27,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.0925692,
"hits": [
{
"_index": "test",
"_id": "1001",
"_score": 1.0925692,
"_source": {
"id": 1001,
"desc": "我大学毕业后去上海工作,薪资还不错。"
},
"highlight": {
"desc": [
"我大学毕业后去上海工作,薪资还不错。"
]
}
}
]
}
}
11.2 用例二
自定义标签
POST 192.168.56.101:9200/user2/_search
- 查询条件
{
"query": {
"match": {
"desc": "薪资"
}
},
"highlight": {
"pre_tags": [""],
"post_tags": [""],
"fields": {
"desc": {}
}
}
}
- 查询结果
{
"took": 9,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.0925692,
"hits": [
{
"_index": "test",
"_id": "1001",
"_score": 1.0925692,
"_source": {
"id": 1001,
"desc": "我大学毕业后去上海工作,薪资还不错。"
},
"highlight": {
"desc": [
"我大学毕业后去上海工作,薪资还不错。"
]
}
}
]
}
}
12 前缀查询prefix
根据前缀去查询
POST 192.168.56.101:9200/user2/_search
{
"query": {
"prefix": {
"desc": "baidu"
}
}
}
- head 可视化(待完善)
13、模糊搜索fuzzy
模糊搜索,并不是指的sql的模糊搜索,而是用户在进行搜索的时候的打字错误现象,搜索引擎会自动纠正,然后尝试匹配索引库中的数据。
- 基本查询
把baidu打成了beidu
POST 192.168.56.101:9200/user2/_search
{
"query": {
"fuzzy": {
"desc": "beidu"
}
}
}
- 多字段搜索
{
"query": {
"multi_match": {
"fields": [ "usernem", "alaisname"],
"query": "小天 小民",
"fuzziness": "AUTO"
}
}
}
{
"query": {
"multi_match": {
"fields": [ "desc", "nickname"],
"query": "演说",
"fuzziness": "1"
}
}
}
14、占位符查询wildcard
?:1个字符
*:1个或多个字符
POST 192.168.56.101:9200/user2/_search
{
"query": {
"wildcard": {
"desc": "*oo?"
}
}
}
{
"query": {
"wildcard": {
"desc": "演*"
}
}
}
14、游标查询scroll
一次性查询1万+数据,往往会造成性能影响,因为数据量太多了。这个时候可以使用滚动搜索,也就是 scroll。
滚动搜索可以先查询出一些数据,然后再紧接着依次往下查询。在第一次查询的时候会有一个滚动id,相当于一个锚标记,随后再次滚动搜索会需要上一次搜索的锚标记,根据这个进行下一次的搜索请求。每次搜索都是基于一个历史的数据快照,查询数据的期间,如果有数据变更,那么和搜索是没有关系的,搜索的内容还是快照中的数据。
- 获取
scroll_id
scroll=1m, Elasticsearch 将 “search context” 保存多久,即生成的
scroll_id
多久过期,1m表示搜索保持的上下文时间为1分钟。
POST 192.168.56.101:9200/shop/_search?scroll=1m
{
"query": {
"match_all": {}
},
"sort": ["_doc"],
"size": 5
}
- 后续查询
获取结果中的
_scroll_id
,以便进行下一轮查询(必须携带)
POST 192.168.56.101:9200/_search/scroll
{
"scroll_id": "FGluY2x1ZGVfY29udGV4dF91dWlkDnF1ZXJ5VGhlbkZldGNoAxZmeThnNlFkbVI2LWdMTEF4em1lZXdnAAAAAAAAAFMWYkJ6eFg5alpUTFdpaXdLZ1l4emtYZxZmeThnNlFkbVI2LWdMTEF4em1lZXdnAAAAAAAAAFUWYkJ6eFg5alpUTFdpaXdLZ1l4emtYZxZmeThnNlFkbVI2LWdMTEF4em1lZXdnAAAAAAAAAFQWYkJ6eFg5alpUTFdpaXdLZ1l4emtYZw==",
"scroll": "1m"
}
15、批量操作bulk
批量新增、修改、删除等
15.1 批量操作基本语法
bulk操作和以往的普通请求格式有区别。不要格式化json,不然就不在同一行了,这个需要注意。
- 参数结构如下
\n,表示换行,所以必须回车,否则会报错
{ action: { metadata }}\n
{ request body }\n
{ action: { metadata }}\n
{ request body }\n
-
{ action: { metadata }}
代表批量操作的类型,可以是新增、删除或修改 -
\n
是每行结尾必须填写的一个规范,每一行包括最后一行都要写,用于es的解析 -
{ request body }
是请求body,增加和修改操作需要,删除操作则不需要
15.2 批量操作的类型
action 必须是以下选项之一
-
create
:如果文档不存在,那么就创建它。存在会报错。发生异常报错不会影响其他操作。 -
index
:创建一个新文档或者替换一个现有的文档。 -
update
:部分更新一个文档。 -
delete
:删除一个文档。
metadata 中需要指定要操作的文档的_index 、 _type 和 _id,_index 、 _type也可以在url中指定
15.2 批量操作实例
TODO
四、用例数据
1、创建index
PUT 192.168.56.101:9200/test
{
"settings": {
"index": {
"number_of_shards": "3",
"number_of_replicas": "0"
}
}
}
2、为索引test创建mappings
POST 192.168.56.101:9200/test/_mapping
{
"properties": {
"id": {
"type": "long"
},
"desc": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
3、为索引test添加数据
POST 192.168.56.101:9200/test/_doc/1001(对应的id)
一条条的添加数据,记得对应id,方便识别
{
"id": 1001,
"desc": "我大学毕业后去上海工作,薪资还不错。"
}
{
"id": 1002,
"desc": "大学生活真好,毕业后就开始忙碌了"
}
{
"id": 1003,
"desc": "毕业已经一年了,大学同学都没有再见到过"
}
{
"id": 1004,
"desc": "毕业大学生都去大城市打拼了"
}
{
"id": 1005,
"desc": "大学生不断的走入社会,那些还没有毕业的也蠢蠢欲动"
}