简介
当前官方提供对外服务有两种方式:
1.REST API(直接Http请求)
2.客户端TransportClient 和jest (JAVA语言环境)
elasticsearch提供了一整套基于JSON的查询DSL语言来定义查询,以上两种方式中REST查询最终都是通过Query DSL处理后进行实际查询服务的。
Query DSL结构化查询
Query DSL是一个Java开源框架用于构建类型安全的SQL查询语句。采用API代替传统的拼接字符串来构造查询语句。目前Querydsl支持的平台包括JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Search。
Query DSL当作是一系列的抽象的查询表达式树(AST)特定查询能够包含其它的查询,(如 bool ), 有些查询能够包含过滤器(如 constant_score), 还有的可以同时包含查询和过滤器 (如 filtered). 都能够从ES支持查询集合里面选择任意一个查询或者是从过滤器集合里面挑选出任意一个过滤器, 这样的话,我们就可以构造出任意复杂(maybe 非常有趣)的查询了,是不是很灵活啊。
TransportClient方式和jest 两种方式对比
- 在JAVA 下的两种Elasticsearch客户端
- JAVA API 使用的netty协议TransportClient 端口9300 性能好 上手麻烦,需熟悉API和ES DSL,适合大量频繁数据查询
JAVA REST Client使用http协议 端口9200 上手简单,懂ES DSL查询即可
Rest API使用了HTTP协议。Rest API的核心是url和post数据,url直接需传入字符串,这样就不能使用IDE的查错功能。
目前官方在6.0版本中提示如下:
官方已经建议用REST,而 TransportClient 在7.0 开始废弃,在8.0版本移除. 详情请见:https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/client.html
数据准备
单单介绍理论和API是乏味和低效率的,本文将结合一个实际的例子来介绍这些REST API。下表是本文数据表的表结构,表名(type)为“student”。注意,studentNo是本表的id,也就是_id字段的值与studentNo的值保持一致。
字段名 | 字段含义 | 类型 | 是否能被索引 备注 |
---|---|---|---|
studentNo | 学号 | string | 是 |
name | 姓名 | string | 是 |
sex | 性别 | string | 是 |
age | 年龄 | integer | 是 |
birthday | 出生年月 | date | 是 |
address | 家庭住址 | string | 是 |
classNo | 班级 | string | 是 |
isLeader | 是否为班干部 | boolean | 是 |
上面的表结构所对应的mapping如下,将数据保存在索引名为“student”的索引中。
{
"student": {
"properties": {
"studentNo": {
"type": "string",
"index": "not_analyzed"
},
"name": {
"type": "string",
"index": "not_analyzed"
},
"male": {
"type": "string",
"index": "not_analyzed"
},
"age": {
"type": "integer"
},
"birthday": {
"type": "date",
"format": "yyyy-MM-dd"
},
"address": {
"type": "string",
"index": "not_analyzed"
},
"classNo": {
"type": "string",
"index": "not_analyzed "
},
"isLeader": {
"type": "boolean"
}
}
}
}
索引中保存的数据如下,下面介绍的所有API都将基于这个数据表。
studentNo | name | male | age | birthday | classNo | address | isLeader |
---|---|---|---|---|---|---|---|
1 | 刘备 | 男 | 24 | 1985-02-03 | 1 | 湖南省长沙市 | true |
2 | 关羽 | 男 | 22 | 1987-08-23 | 2 | 四川省成都市 | false |
3 | 糜夫人 | 女 | 19 | 1990-06-12 | 1 | 上海市 | false |
4 | 张飞 | 男 | 20 | 1989-07-30 | 3 | 北京市 | false |
5 | 诸葛亮 | 男 | 18 | 1992-04-27 | 2 | 江苏省南京市 | true |
6 | 孙尚香 | 女 | 16 | 1994-05-21 | 3 | false | |
7 | 马超 | 男 | 19 | 1991-10-20 | 1 | 黑龙江省哈尔滨市 | false |
8 | 赵云 | 男 | 23 | 1986-10-26 | 2 | 浙江省杭州市 | false |
全文查询-full text query和词条查询-term query的区别
词条(term)查询和全文(fulltext)查询最大的不同之处是:
- full text query:全文查询首先分析(Analyze)查询字符串,使用默认的分析器分解成一系列的分词,term1,term2,termN,然后从索引中搜索是否有文档包含这些分词中的一个或多个,如果执行的match查询,默认的操作符(operator)只要文档的字段值能够匹配任意一个词条,就匹配查询条件,返回该文档;如果不包含任意一个分词,表示没有任何文档匹配查询条件。
- term query词条查询是字符的完全匹配,只有当字段的字符完全匹配查询字符串时,ElasticSearch引擎才判定文档匹配查询条件,词条查询不会分析查询条件,只有当词条和查询字符串完全匹配时,才匹配搜索。当在未被分析的字段中进行搜索时,和查询字符串完全匹配的文档才会被返回;如果在已分析(Analyzed)的字段中进行搜索,词条必须是小写的单个词条,否则,匹配不到任何文档;
词条查询和全文查询对比实例
通过下面的示例进行对比
词条查询
对未分析的字段进行词条查询
GET my_index/my_type/_search
{
"query": {
"term": {
"exact_value": "Quick Foxes!"
}
}
}
对于”exact_value”: “Quick Foxes!” ,文档匹配该查询条件,如果将查询条件修改为”exact_value”: “Quick Foxes”,或”exact_value”: “Quick”,那么文档值不匹配查询条件,不会返回任何文档。
对已分析(analyzed)的字段进行词条查询
GET my_index/my_type/_search
{
"query": {
"term": {
"full_text": "Quick Foxes!"
}
}
}
对于查询条件 “full_text”: “Quick Foxes!”,不会返回任何文档,词条查询不会分析”Quick Foxes!”,对于已分析的字段中,只有小写的单个词条,这些分词都不会匹配含有多个分词的词条。修改查询条件,”full_text”: “quick”,由于文档中包含该分词,因此文档匹配查询条件。
全文查询
对已分析的字段进行全文查询
GET my_index/my_type/_search
{
"query": {
"match": {
"full_text": "Quick Foxes!"
}
}
}
对于全文查询条件 “full_text”: “Quick Foxes!”,ElasticSearch引擎首先分析查询字符串,将其拆分成两个小写的分词:quick 和 foxes,由于字段full_text中包含这两个分词,因此,文档匹配匹配(match)查询。
term query查询API
根据词条进行查询,查询是字符的完全匹配,只有当字段的字符完全匹配查询字符串时才会有匹配的结果。
当在未被分析的字段中进行搜索时,和查询字符串完全匹配的文档才会被返回;如果在已分析(Analyzed)的字段中进行搜索,词条必须是小写的单个词条,否则,匹配不到任何文档;
以下示例基本全是基于字段进行不分拆(un_analyzed)的处理。
Query和Filter
ES为用户提供两类查询API,一类是在查询阶段就进行条件过滤的query查询,另一类是在query查询出来的数据基础上再进行过滤的filter查询。这两类查询的区别是:
query方法会计算查询条件与待查询数据之间的相关性,计算结果写入一个score字段,类似于搜索引擎。filter仅仅做字符串匹配,不会计算相关性,类似于一般的数据查询,所以filter得查询速度比query快。
filter查询出来的数据会自动被缓存,而query不能。
query和filter可以单独使用,也可以相互嵌套使用,非常灵活。
Query查询
下面的情况下适合使用query查询:
需要进行全文搜索。
查询结果依赖于相关性,即需要计算查询串和数据的相关性。
(1)Match All Query
查询所有的数据,相当于不带条件查询。下面的代码是一个典型的match_all查询的调用方式。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"match_all": {}
}
}
'
查询结果如下。其他所有的查询都是返回这种格式的数据。
{
"took": 156, // 查询耗时(毫秒)
"timed_out": false, // 是否超时
"_shards": {
"total": 5, // 总共查询的分片数
"successful": 5, // 查询成功的分片数
"failed": 0 // 查询失败的分片数
},
"hits": {
"total": 8, // 本次查询的记录数
"max_score": 1, // 查询所有数据中的最大score
"hits": [ // 数据列表
{
"_index": "student", // 数据所属的索引名
"_type": "student", // 数据所属的type
"_id": "4", // 数据的id值
"_score": 1, // 该记录的score
"_source": { // ES将原始数据保存到_source字段中
"studentNo": "4",
"name": "张飞",
"male": "男",
"age": "20",
"birthday": "1989-07-30",
"classNo": "3",
"isLeader": "F"
}
},
{
…… // 其他的数据格式相同,就不列出来了
}
]
}
}
查询结果会显示最前面的10条记录。
match_none则作用相反,不匹配任何记录,返回的数据是个空集
查询时,你会发现无论数据量有多大,每次最多只能查到10条数据。这是因为ES服务端默认对查询结果做了分页处理,每页默认的大小为10。如果想自己指定查询的数据,可使用from和size字段,并且按指定的字段排序。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"match_all": {}
},
"from": 2, // 从2条记录开始取
"size": 4, // 取4条数据
"sort": {
"studentNo": { // 按studentNo字段升序
"order": "asc"// 降序为desc
}
}
}
'
注意:不要把from设得过大(不超过10000,ES最大记录为10000条),否则会导致ES服务端因频繁GC而无法正常提供服务。其实实际项目中也没有谁会翻那么多页,但是为了ES的可用性,务必要对分页查询的页码做一定的限制。
(2)term query
词语查询,如果是对未分词的字段进行查询,则表示精确查询。查找名为“诸葛亮”的学生,查询结果为学号为5的记录。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"term": {
"name": "诸葛亮"
}
}
}
'
(3)Bool Query
Bool(布尔)查询是一种复合型查询,它可以结合多个其他的查询条件。主要有3类逻辑查询:
- must:查询结果必须符合该查询条件(列表)。
- should:类似于in的查询条件。如果bool查询中不包含must查询,那么should默认表示必须符合查询列表中的一个或多个查询条件。
- must_not:查询结果必须不符合查询条件(列表)。
查找2班的班干部,查询结果为学号为5的记录。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"bool": {
"must": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
}
'
(4)Ids Query
id字段查询。查询数据id值为1和2的同学,由于id的值与studentNo相同,故查询结果为学号为1和2的学生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"ids": {
"type": "student",
"values": [
"1",
"2"
]
}
}
}
'
(5)Prefix Query
前缀查询。查找姓【赵】的同学,查询结果是学号为8的赵云。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"prefix": {
"name": "赵"
}
}
}
'
(6)Range Query
范围查询,针对date和number类型的数据。查找年龄到18~20岁的同学,查询结果是学号为3、4、5、7的记录。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"range": {
"age": {
"gte": "18", // 表示>=
"lte": "20" // 表示<=
}
}
}
}
'
标识 | 说明 |
---|---|
gte | Greater-than or equal to |
gt | Greater-than |
lte | Less-than or equal to |
lt | Less-than |
boost | Sets the boost value of the query, defaults to 1.0 |
对日期类型的范围查询
当对日期类型的数据进行范围查询时应该使用 Date Math语法日期比较表达式
实际上,对于date类型的数据,ES中以其时间戳(长整形)的形式存放的。
在数学日期表达式中,now就是现在的时间,比如,现在时间是2016.09.07 12:20:00。
- now/d,就是向一天取整,即2016.09.07 00:00:00。
- now/M,就是向一个月取整,即2016.09.01 00:00:00
- now+1h,就是2016.09.07 13:20:00
now-1d,就是2016.09.06 12:20:00
date_format 格式化,默认是YYYY.MM.dd
- time_zone 时区,默认是utc: +01:00 表示在当前时区基础上加一个小时
查询日期为大于等于前一天小于当前天的数据:
GET _search
{
"query": {
"range" : {
"date" : {
"gte" : "now-1d/d",
"lt" : "now/d"
}
}
}
}
日期格式化
日期格式化并取年份大于等于2012/01/01小于等于2013
GET _search
{
"query": {
"range" : {
"born" : {
"gte": "01/01/2012",
"lte": "2013",
"format": "dd/MM/yyyy||yyyy"
}
}
}
}
(7)Terms Query
多词语查询,查找符合词语列表的数据。如果要查询的字段索引为not_analyzed类型,则terms查询非常类似于关系型数据库中的in查询。下面查找学号为1,3的学生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"terms": {
"studentNo": [
"1",
"3"
]
}
}
}
'
(8)Wildcard Query
通配符查询,是简化的正则表达式查询,包括下面两类通配符:
- - 代表任意(包括0个)多个字符
- ? 代表任意一个字符
查找名字的最后一个字是“亮”的同学,查询结果是学号为5的诸葛亮。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"wildcard": {
"name": "*亮"
}
}
}
'
(9)Regexp Query
正则表达式查询,这是最灵活的字符串类型字段查询方式。查找家住长沙市的学生,查询结果为学号为1的学生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"query": {
"regexp": {
"address": ".*长沙市.*" // 这里的.号表示任意一个字符
}
}
}
'
Filter查询
下面的情况下适合使用filter查询:
- yes/no的二元查询
- 针对精确值进行查询
- filter和query的查询方式有不少是重叠的,所以本节仅仅介绍API的调用,一些通用的注意的事项就不再重复了。
(1)Term Filter
词语查询,如果是对未分词的字段进行查询,则表示精确查询。查找名为“诸葛亮”的学生,查询结果为学号为5的记录。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"term": {
"name": "诸葛亮",
"_cache" : true // 与query主要是这里的区别,可以设置数据缓存
}
}
}
'
filter查询方式都可以通过设置_cache为true来缓存数据。如果下一次恰好以相同的查询条件进行查询并且该缓存没有过期,就可以直接从缓存中读取数据,这样就大大加快的查询速度。
(2)Bool Filter
查找2班的班干部,查询结果为学号为5的记录。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"bool": {
"must": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
}
'
(3)And Filter
And逻辑连接查询,连接1个或1个以上查询条件。它与bool查询中的must查询非常相似。实际上,and查询可以转化为对应的bool查询。查找2班的班干部,查询结果为学号为5的学生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"and": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
'
(4)Or Filter
Or连接查询,表示逻辑或。。查找2班或者是班干部的学生名单,查询结果为学号为1、2、5、8的学生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"or": [
{
"term": {
"classNo": "2"
}
},
{
"term": {
"isLeader": "true"
}
}
]
}
}
'
(5)Exists Filter
存在查询,查询指定字段至少包含一个非null值的数据。如果字段索引为not_analyzed类型,则查询sql中的is not null查询方式。查询地址存在学生,查询结果为除了6之外的所有学生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"exists": {
"field": "address"
}
}
}
'
(6)Missing Filter
缺失值查询,与Exists查询正好相反。查询地址不存在的学生,查询结果为学号为6的学生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"missing": {
"field": "address"
}
}
}
'
(7)Prefix Filter
前缀查询。查找姓【赵】的同学,查询结果是学号为8的赵云。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"prefix": {
"name": "赵"
}
}
}
'
(8)Range Filter
范围查询,针对date和number类型的数据。查找年龄到18~20岁的同学,查询结果是学号为3、4、5、7的记录。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"range": {
"age": {
"gte": "18",
"lte": "20"
}
}
}
}
'
(9)Terms Filter
多词语查询,查找符合词语列表的数据。如果要查询的字段索引为not_analyzed类型,则terms查询非常类似于关系型数据库中的in查询。下面查找学号为1,3的学生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"terms": {
"studentNo": [
"1",
"3"
]
}
}
}
'
(10)Regexp Filter
正则表达式查询,是最灵活的字符串类型字段查询方式。查找家住长沙市的学生,查询结果为学号为1的学生。
curl -XPOST "192.168.1.101:9200/student/student/_search" -d
'
{
"filter": {
"regexp": {
"address": ".*长沙市.*"
}
}
}
'
全文查询full text query
对于全文搜索而言,最重要的两个方面是:
相关度(Relevance)
查询的结果按照它们对查询本身的相关度进行排序的能力,相关度可以通过TF/IDF,参见什么是相关度,地理位置的邻近程度(Proximity to a Geo-location),模糊相似性(Fuzzy Similarity)或者其它算法进行计算。解析(Analysis)
解析用来将一块文本转换成单独的,规范化的词条(Tokens),参见解析和解析器(Analysis and Analyzers),用来完成:(a)倒排索引(Inverted Index)的创建;(b)倒排索引的查询。
一旦我们开始讨论相关度或者解析,也就意味着我们踏入了查询(Query)的领域,而不再是过滤器(Filter)。
全文查询主要包括如下查询类型
- Match Query
在你需要对任何字段进行查询时,match查询应该是你的首选。它是一个高级全文查询,意味着它知道如何处理全文字段(Full-text, analyzed)和精确值字段(Exact-value,not_analyzed),主要使用场景是全文搜索 - Match Phrase Query
- Match Phrase Prefix Query
- Multi Match Query
- Common Terms Query
- Query String Query
- Simple Query String Query
match
匹配查询是布尔型的。这意味着对所提供的文本进行分析,分析过程从所提供的文本构造一个布尔查询。可以将operator标志设置为or或and控制布尔条件(默认为OR)。可匹配的最低数量可以设置minimum_should_match参数。
可以设置analyzer来控制哪一个分析器在文本上执行分析过程。它默认为字段显式映射定义,也默认为默认搜索分析器。
lenient的参数可以设置为true,以忽略数据类型不匹配造成的异常,例如试图用文本查询字符串查询数字字段。默认为false。
fuzziness
模糊查询允许根据所查询字段的类型进行模糊匹配。参考fuzziness设置。
prefix_length和max_expansions可以设置在这种情况下的模糊控制过程。如果模糊选项fuzziness设置了值将使用top_terms_blended_freqs_ $ { max_expansions }为fuzzy_rewrite的重写方法,fuzzy_rewrite参数可以控制如何将重写查询。
数据准备
DELETE /my_index
PUT /my_index
{ "settings": { "number_of_shards": 1 }}
POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "The quick brown fox" }
{ "index": { "_id": 2 }}
{ "title": "The quick brown fox jumps over the lazy dog" }
{ "index": { "_id": 3 }}
{ "title": "The quick brown fox jumps over the quick dog" }
{ "index": { "_id": 4 }}
{ "title": "Brown fox brown dog" }
查询title中包含QUICK!的文本
GET /my_index/my_type/_search
{
"query": {
"match": {
"title": "QUICK!"
}
}
}
ES会按照如下的方式执行上面的match查询:
检查字段类型
title字段是一个全文字符串字段(analyzed),意味着查询字符串也需要被分析。解析查询字符串
查询字符串”QUICK!”会被传入到标准解析器中,得到的结果是单一词条”quick”。因为我们得到的只有一个词条,match查询会使用一个term低级查询来执行查询。找到匹配的文档
term查询会在倒排索引中查询”quick”,然后获取到含有该词条的文档列表,在这个例子中,文档1,2,3会被返回。对每份文档打分
term查询会为每份匹配的文档计算其相关度分值_score,该分值通过综合考虑词条频度(Term Frequency)(“quick”在匹配的每份文档的title字段中出现的频繁程度),倒排频度(Inverted Document Frequency)(“quick”在整个索引中的所有文档的title字段中的出现程度),以及每个字段的长度(较短的字段会被认为相关度更高)来得到。参考什么是相关度(What is Relevance?)
match_phrase查询
短语查询,slop定义的是关键词之间隔多少未知单词
GET /library/books/_search
{
"query":{
"match_phrase" :{
"query":"Elasticsearch,distributed",
"slop":2 #表示Elasticsearch和distributed之间隔多少单词
}
}
}
multi_match查询
可以指定多个字段查询
查询title和preview这两个字段都包含Elasticsearch关键词的文档
GET /library/books/_search
{
"query":{
"multi_match":{
"query":"Elasticsearch"
"fields":["title","preview"]
}
}
}
更多API使用讲解参考http://blog.youkuaiyun.com/bigbigtreewhu/article/details/53353738
总结
本文介绍了ES中的部分查询API,这些API是一些常用的简单API,如果需要使用更加复杂一点的API,请查阅官网文档。下一篇文章准备介绍ES中的聚合API的使用。
参考
本文转载自
http://blog.youkuaiyun.com/xialei199023/article/details/48227247