1.文档
在Elasticsearch中,文档以JSON格式进行存储,可以是复杂的结构,如:
{ "_index": "haoke", "_type": "user", "_id": "1005", "_version": 1, "_score": 1, "_source": { "id": 1005, "name": "孙七", "age": 37, "sex": "女", "card": { "card_number": "123456789" } } }
其中,card是一个复杂对象,嵌套的Card对象。
元数据(metadata)
一个文档不只有数据。它还包含了元数据(metadata)——关于文档的信息。三个必须的元数据节点是:
_index:
索引(index)类似于关系型数据库里的“数据库”——它是我们存储和索引关联数据的地方。
注意:我们的索引被存储在分片(shards)中,索引只是把一个或多个分片组合在一起的逻辑空间。
_type:
在Elasticsearch中,使用相同类型(type)的文档表示相同的“事物”,因为他们的数据结构也是相同的。
注意:_type 的名字可以是大写或小写,不能包含下划线或逗号。
_id:
id仅仅是一个字符串,它与 _index 和 _type 组合时,就可以在Elasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义 _id ,也可以让Elasticsearch帮你自动生成。
2.查询响应
访问:http://192.168.43.182:9200/haoke/user/69AyEXEBVAiLr6jRzMgR
{ "_index": "haoke", "_type": "user", "_id": "69AyEXEBVAiLr6jRzMgR", "_version": 1, "found": true, "_source": { "id": 1004, "name": "赵六", "age": 19, "sex": "女" } }
指定响应字段访问:
GET /haoke/user/69AyEXEBVAiLr6jRzMgR?_source=id,name
{ "_index": "haoke", "_type": "user", "_id": "69AyEXEBVAiLr6jRzMgR", "_version": 1, "found": true, "_source": { "name": "赵六", "id": 1004 } }
如不需要返回元数据,仅仅返回原始数据,可以这样:
GET /haoke/user/1005/_source
{ "id": 1004, "name": "赵六", "age": 19, "sex": "女" }
也可以这样:GET /haoke/user/1005/_source?_source=id,name
{ "name": "赵六", "id": 1004 }
3.判断文档是否存在
如果我们只需要判断文档是否存在,而不是查询文档内容,那么可以这样:
HEAD /haoke/user/69AyEXEBVAiLr6jRzMgR
如果不存在则返回404
4.批量操作
批量查询:
POST /haoke/user/_mget
{ "ids" : [ "1001", "1003" ] }
如果,某一条数据不存在,不影响整体响应,需要通过found的值进行判断是否查询到数据。
_bulk操作:
在Elasticsearch中,支持批量的插入、修改、删除操作,都是通过_bulk的api完成的。
批量插入数据:POST /haoke/user/_bulk
{"create":{"_index":"haoke","_type":"user","_id":2001}} {"id":2001,"name":"name1","age": 20,"sex": "男"} {"create":{"_index":"haoke","_type":"user","_id":2002}} {"id":2002,"name":"name2","age": 20,"sex": "男"} {"create":{"_index":"haoke","_type":"user","_id":2003}} {"id":2003,"name":"name3","age": 20,"sex": "男"}
注意:最后必须有一个回车
批量删除:POST /haoke/user/_bulk
{"delete":{"_index":"haoke","_type":"user","_id":2001}} {"delete":{"_index":"haoke","_type":"user","_id":2002}} {"delete":{"_index":"haoke","_type":"user","_id":2003}}
注意:最后必须有一个回车
其他操作类似
一次请求多少性能最高?
整个批量请求需要被加载到接收我们请求节点的内存里,所以请求越大,给其它请求可用的内存就越小。有一个最佳的bulk请求大小。超过这个大小,性能不再提升而且可能降低。
最佳大小,当然并不是一个固定的数字。它完全取决于你的硬件、你文档的大小和复杂度以及索引和搜索的负载。
幸运的是,这个最佳点(sweetspot)还是容易找到的:试着批量索引标准的文档,随着大小的增长,当性能开始降低,说明你每个批次的大小太大了。开始的数量可以在1000~5000个文档之间,如果你的文档非常大,可以使用较小的批次。
通常着眼于你请求批次的物理大小是非常有用的。一千个1kB的文档和一千个1MB的文档大不相同。一个好的批次最好保持在5-15MB大小间。
5.分页
和SQL使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch接受 from 和 size 参数:
size: 结果数,默认10
from: 跳过开始的结果数,默认0
如果你想每页显示5个结果,页码从1到3,那请求如下:
GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10
应该当心分页太深或者一次请求太多的结果,结果在返回前会被排序。记住一个搜索请求常常涉及多个分片,每个分片生成自己排好序的结果,接着需要集中起来排序以确保整体排序正确。
http://192.168.43.182:9200/haoke/user/_search?size=1&from=2
在集群系统中深度分页
假设在一个有5个主分片的索引中搜索。当我们请求结果的第一页(结果1到10)时,每个分片产生自己最顶端10个结果然后返回给它们请求节点(requesting node),它再排序这所有的50个结果以选出顶端的10个结果。
现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果。然后请求节点排序这50050个结果并丢弃50040个!
可以看到在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网络搜索引擎中任何语句不能返回多于1000个结果的原因。
6.映射
前面我们创建的索引以及插入数据,都是由Elasticsearch进行自动判断类型,有些时候我们是需要进行明确字段类型的。
自动判断的规则如下:
Elasticsearch中支持的类型如下:
string类型在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,由text和keyword类型替代。
text 类型,当一个字段是要被全文搜索的,比如Email内容、产品描述,应该使用text类型。设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段不用于排序,很少用于聚合。
keyword类型适用于索引结构化的字段,比如email地址、主机名、状态码和标签。如果字段需要进行过滤(比如查找已发布博客中status属性为published的文章)、排序、聚合建议使用。keyword类型的字段只能通过精确值搜索到。
创建明确类型的索引:
PUT /fan
{ "settings": { "index": { "number_of_shards": "2", "number_of_replicas": "0" } }, "mappings": { "person": { "properties": { "name": { "type": "text" }, "age": { "type": "integer" }, "mail": { "type": "keyword" }, "bobby": { "type": "text" } } } } }
查看映射:
GET /fan/_mapping
插入数据:
POST /fan/_bulk
{"index":{"_index":"fan","_type":"person"}} {"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球、乒乓球、足球"} {"index":{"_index":"fan","_type":"person"}} {"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球、乒乓球、足球、篮球"} {"index":{"_index":"fan","_type":"person"}} {"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球、篮球、游泳、听音乐"} {"index":{"_index":"fan","_type":"person"}} {"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步、游泳"} {"index":{"_index":"fan","_type":"person"}} {"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐、看电影"}
注意:最后必须有一个回车
7.结构化查询
term查询:
term 主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型):
{ "term": { "age": 26}}
{ "term": { "date": "2014-09-01" }}
{ "term": { "public": true }}
{ "term": { "tag": "full_text" }}
POST /fan/person/_search
{ "query": { "term": { "age": 20 } } }
terms查询:
terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去做匹配:
{ "query": { "terms": { "age": [20, 21, 22] } } }
range查询:
range 过滤,允许我们按照指定范围查找一批数据:
范围操作符包含:
gt :: 大于
gte :: 大于等于
lt :: 小于
lte :: 小于等于
{ "query": { "range": { "age": { "gte": 20, "lt": 30 } } } }
exists 查询:
exists 查询可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的 IS_NULL 条件
如果存在则返回相应的数据,否则返回空数据
{ "query": { "exists": { "field": "card" } } }
match查询:
match 查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析再进行查询字符:
{ "query": { "match": { "hobby": "羽毛球 兵乓球" } } }
如果用 match 指定了一个确切值,在遇到数字,日期,布尔值或者 not_analyzed 的字符串时,它将为你搜索你给定的值:
{ "match": { "age": 26 }}
{ "match": { "date": "2014-09-01" }}
{ "match": { "public": true }}
{ "match": { "tag": "full_text" }}
bool查询:
bool 查询可以用来合并前面的多个查询,它包含一下操作符:
must :: 多个查询条件的完全匹配,相当于 and 。
must_not :: 多个查询条件的相反匹配,相当于 not 。
should :: 至少有一个查询条件匹配, 相当于 or 。
{ "query": { "bool": { "must": { "match": { "hobby": "足球" } }, "must_not": { "hobby": "音乐" } } } }
8.过滤查询
查询年龄为20岁的用户:
{ "query": { "bool": { "filter": { "term": { "age": 20 } } } } }
查询和过滤的对比:
一条过滤语句会询问每个文档的字段值是否包含着特定值。
查询语句会询问每个文档的字段值与特定值的匹配程度如何。
一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分 _score,并且按照相关性对匹配到的文档进行排序。
这种评分方式非常适用于一个没有完全配置结果的全文本搜索。
一个简单的文档列表,快速匹配运算并存入内存是十分方便的,每个文档仅需要1个字节。这些缓存的过滤结果集与后续请求的结合使用是非常高效的。
查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比过滤语句更耗时,并且查询结果也不可缓存。
建议:做精确匹配搜索时,最好用过滤语句,因为过滤语句可以缓存数据。