ES
https://blog.csdn.net/wthfeng/article/details/52908959
https://blog.csdn.net/wthfeng/article/details/52918003
https://blog.csdn.net/wthfeng/article/details/52953317
https://blog.csdn.net/wthfeng/article/details/53001218
一、定义
ES是一个基于Apache Lucene的开源搜索引擎,使用java开发的,ES并不是一个标准的数据库,侧重于对存储数据进行搜索,Lucene读写是两个分开的句柄,往写句柄写入的数据刷新之后,读句柄重新打开,这才能读到新写入的数据,一般会有1秒钟的延时。ES是一个开源分布式RESTful的搜索引擎。
- 分布式的实时文件存储,每个字段都被索引并可被搜索
- 分布式的实时分析搜索引擎
- 可以扩展到上百台服务器,处理PB级结构化或非结构化数据
所有ES的功能都被集成到一个服务里面,可以通过RESTful API,各语言客户端或命令行与之交互
Elasticsearch索引的文档是JSON形式,MongoDB存储也是以JSON形式。ES内部功能通过Rest API暴露给外部,除了通过HTTP直接访问ES,还可以通过python,java等更多语言的客户端库来访问
关系型数据库,这些数据保存到由行和列组成的关系数据库中,类似于把一个丰富的信息表现力强的对象拆散了放入一个非常大的表格中,不得不拆散对象以适应表模式(通常一列表示一个字段),然后查询的时候重建它们(要集中好几个表中的数据组成)
文档型数据库,ES是面向文档的,意味着它可以存储整个对象或文档,不仅仅是存储,还会索引(类似于目录),可以对文档进行索引、搜索、排序、过滤。
ES是使用javascript对象符号,也就是json,作为文档序列化格式,以下使用json文档来表示一个用户对象:
{
"email": "john@smith.com",
"first_name": "John",
"last_name": "Smith",
"info": {
"bio": "Eco-warrior and defender of the weak",
"age": 25,
"interests": [ "dolphins", "whales" ]
},
"join_date": "2014/05/01"
}
二、安装
1.前提:安装java
2.下载es: elasticsearch.org\/download
3.安装Marvel:
Marvel是es的管理和监控工具,是一个插件,在开发环境下免费试用,它包含了一个叫做sense的交互式控制台,使用户方便的通过浏览器直接与es进行交互(安装不是必须的)
在Elasticsearch目录中运行以下命令来下载和安装:
./bin/plugin -i elasticsearch/marvel/latest
关闭Marvel,禁用监控:
echo 'marvel.agent.enabled: false' >> ./config/elasticsearch.yml
4.启动ES:
./bin/elasticsearch
启动后,如果只有本地可以访问,尝试修改配置文件 elasticsearch.yml中network.host(注意配置文件格式不是以#
开头的要空一格, :
后要空一格) 为network.host: 0.0.0.0
如果想在后台以守护进程模式运行,添加-d
参数。
测试:
curl 'http://localhost:9200/?pretty'
返回信息为:
{
"status": 200,
"name": "Shrunken Bones",
"version": {
"number": "1.4.0",
"lucene_version": "4.10"
},
"tagline": "You Know, for Search"
}
这说明你的ELasticsearch集群已经启动并且正常运行
5.终止es:
curl -XPOST 'http://localhost:9200/_shutdown'
三、与ES交互
所有程序语言都可以使用RESTful API,通过9200端口与es进行通信,向es发出http请求:
curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
VERB HTTP方法:GET, POST, PUT, HEAD, DELETE
PROTOCOL http或者https协议(只有在es签名有https代理的时候可用)
HOST : ES集群中的任何一个节点的主机名,如果是在本地的节点,那么就是localhost
PORT : ES HTTP服务所在的端口,默认是9200
PATH : API路径
QUERY_STRING : 可选的查询请求参数,例如?pretty参数将使请求返回更加美观易读的json数据
BODY : 一个json格式的请求主体
curl -XGET ‘http://localhost:9200/_count?pretty' -d '{"query":{"match_all":{}}}’
返回
{
"count" : 0,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
}
}
四、理解
对比关系型数据库一一对应:
MongoDB---->Database---->Tables--------->Rows---------------->Colums
ES集群------->index索引---->Types类型--->Documents文档---->Fields字段
索引:可以是名词作为数据库,可以是动词是insert插入数据的意思
ES中的查询请求有两种方式:
1.一种是简易版的查询像传递URL参数一样去传递查询语句,被称为简单搜索或查询字符串(query string)搜索
2.另一个使用JSON完整请求体也叫做结构化查询(DSL),DSL查询就是POST过去一个json
方法一、$curl -XGET http://localhost:9200/index/doc/_search //查询index/doc下的所有数据
GET /megacorp/employee/_search?q=last_name:Smith //查询last_name为Smith的员工
方法二、$curl -XPOST http://localhost:9200/index/doc/1 -d'{"content":"美国留给伊拉克的是个烂摊子吗","title":"标题","tags":["美国","伊拉克","烂摊子"]}'
1.搜索一条信息
GET /megacorp/employee/1 索引/类型/文档ID
得到返回
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
}
我们请求的数据结果就存储在返回数据的_source字段中
GET检索文档 DELETE删除文档 HEAD检查文档是否存在 PUT更新文档
2.搜索多个数据
GET /megacorp/employee/_search /索引/类型/搜索
{
"took": 6,
"timed_out": false,
"_shards": { ... },
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "megacorp",
"_type": "employee",
"_id": "3",
"_score": 1,
"_source": {
"first_name": "Douglas",
"last_name": "Fir",
"age": 35,
"about": "I like to build cabinets",
"interests": [ "forestry" ]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_score": 1,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "2",
"_score": 1,
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
hits数组中包含了我们搜索类型下的所有文档,默认情况下搜索会返回前10个结果
3.按条件查询
GET /magacorp/employee/_search?q=last_name:Smith 搜索名字包含smith的员工,q为查询字符串
{
...
"hits": {
"total": 2,
"max_score": 0.30685282,
"hits": [
{
...
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
...
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
4.使用DSL语句查询
ES提供丰富且灵活的查询语言叫做DSL查询,DSL以json请求体的形式出现
上个例子的表现形式可以写为以下形式,返回结果与之前相同
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"last_name" : "Smith"
}
}
}
######################
例子:{ "query": { "match": { "content" : { "query" : "我的宝马多少马力" } } } } 面的查询匹配就会进行分词,比如"宝马多少马力"会被分词为"宝马 多少 马力", 所有有关"宝马 多少 马力", 那么所有包含这三个词中的一个或多个的文档就会被搜索出来。
例子:{ "query": { "match_phrase": { "content" : { "query" : "我的宝马多少马力", "slop" : 1 } } } } 完全匹配可能比较严,我们会希望有个可调节因子,少匹配一个也满足,那就需要使用到slop。
例子:{ "query": { "multi_match": { "query" : "我的宝马多少马力", "fields" : ["title", "content"] } } } 如果我们希望两个字段进行匹配,其中一个字段有这个文档就满足的话,使用multi_match
例子:https://www.cnblogs.com/yjf512/p/4897294.html
5.添加过滤器
查询是query查询,它查询默认会计算每个返回文档的得分,然后根据得分排序,而过滤filter只会筛选出符合的文档,并不计算得分,且它可以缓存文档。所以单从性能考虑,过滤比查询更快。
过滤适合在大范围筛选数据,而查询适合精确匹配数据,一般应用时,应先使用过滤操作过滤数据,然后使用查询匹配数据。
GET /megacorp/employee/_search #查询年龄大于30的名字包含smith的员工
{
"query" : {
"filtered" : { #过滤查询的关键字
"filter" : { #在过滤关键字下的filter过滤逻辑
"range" : {
"age" : { "gt" : 30 } <1> #先执行区间过滤器,查询所有年龄大于30的数据,gt为greater than的缩写
}
},
"query" : { #在过滤关键字下的普通查询query逻辑
"match" : {
"last_name" : "smith" <2> #后执行查询包含smith的数据
}
}
}
}
}
6.相关性
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"about" : "rock climbing"
}
}
}
返回结果为:
{
...
"hits": {
"total": 2,
"max_score": 0.16273327,
"hits": [
{
...
"_score": 0.16273327, <1> #分越高,与查询条件的匹配度越高
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
...
"_score": 0.016878016, <2> #分低于上一个,因为about字段中只有rock匹配上了
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
目前我们可以在字段中搜索单独的一个词,但是有时候你想要确切的匹配若干个单词或者短语(phrases)。例如我们想要查询同时包含"rock"和"climbing"(并且是相邻的)的员工记录。这时我们需要将match查询变更为match_phrase查询即可。这样上面的搜索就只有一条结果数据了
五、scroll
上面的方法用search默认最多搜索出10条数据,而scroll API可以被用来检索大量的结果,甚至所有的结果。为了使用scroll,初始搜索请求应该在查询中指定scroll参数,这可以告诉es需要保持搜索的上下文环境多久
curl -XGET 'localhost:9200/twitter/tweet/_search?scroll=1m' -d '
{
"query": {
"match" : {
"title" : "elasticsearch"
}
}
}
'
上面的请求返回的结果中包含一个scroll_id,这个ID可以被传递给scroll API来检索下一个批次的结果,初始请求和每个后续滚动请求返回一个新的_scroll_id,只有最近的_scroll_id才能被使用
curl -XGET 'localhost:9200/_search/scroll' -d' #检索下一批次的结果,url中不应该包含index和type的值,这些都指定在了原始的search请求中
{
"scroll" : "1m", #告诉es保持搜索的上下文等待另一个1分钟
"scroll_id" : "c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1"
}
'
也可以写成
curl -XGET 'localhost:9200/_search/scroll?scroll=1m' id '
c2Nhbjs2OzM0NDg1ODpzRlBLc0FXNlNyNm5JWUc1
'
六、source字段
使用es时,有时并不期望返回所有source字段,那么此时_source字段就可以派上用场了。
_source_include=['aaa','bbbb'] 返回的数据中包含的字段
_source_exclude=['aaa','bbb'] 返回的数据中不要的字段
若有fields这个参数,则不再返回_source字段,_search?fields=rdcname,skuID
七、search_type
这个字段包含两种
dfs_query_then_fetch
query_then_fetch(默认)
八、size
用search方法默认返回10条数据,可以用size定义返回的数量
https://es.xiaoleilu.com/010_Intro/30_Tutorial_Search.html
九、python操作elasticsearch
9.1 elasticsearch包
es = Elasticsearch(es_host, port=es_port, http_auth=(es_username, es_password), maxsize=15) #有XPACK安全认证的ES集群 es = Elasticsearch(host:port, maxsize=15) #无XPACK安全认证的集群 ## 查看状态 es.ping() es.info() ## es.indices.create(index='my-index',ignore) #创建索引,索引的名字是my-index,如果存在返回个400,也可以在插入数据的时候创建索引 es.search(index="my_index", doc_type="test_type") #查询所有数据 #根据body里的条件查询。body里是DSL bd={ "query":{ "bool":{ "should":[ {"match_phrase_prefix":{"email":"yikai"}} ] } } } es.search(index="ttt",body=bd) # 查单个记录,指定index, type, id es.get(index='test2',id=1,doc_type='_doc') # 查询数据是否存在,结果返回True或False es.exists(index="index1",doc_type='test2',id=1) # 指定ID进行更新单条记录 data={ "doc":{ "age":77 } } es.update(index="test",id=3,doc_type="ttt",body=data) # 根据body里的DSL条件批量更新 data={ "query":{ "match_all":{} }, "script":{ "source":"ctx._source.age=params.age", "lang":"painless", "params":{ "age":88 } }}} es.update_by_qurey(index="",body=data) # 插入一条记录 data={ "name":"rike", "company":"DDD", "age":56 } es.index(index="test2",body=data, doc_type="_doc",id=8) # 指定id删除记录 es.delete(index="ttt",doc_type="eee",id=5) # 根据DSL条件批量删除 bd = {"query":{"bool":{"should":[{"match_phrase_prefix":{"email":"yikai"}}]}}} es.delete_by_qurey(index="test2",body=bd) # 清空,清空索引,不删除索引 trunc={ "query":{"match_all":{}} } es.delete_by_query(index="ts",body=trunc) # 使用buld命令批量插入, JSON数据不能有回车换行 batch_data = [ {"index":{}}, {"name": "王义凯", "age": 11, "email":"wangyikai1@csdn.com", "company":"CSDN1"}, {"index": {}}, {"name": "wang,yi-kai", "age": 22, "email":"wangyikai2@csdn.com", "company":"CSDN2"} ] es.bulk(index="test2", doc_type="ddd",body=batch_data) ## 批量插入更新删除,#批量对不同的索引进行增删改查操作,每个json一行 batch_action = [ {"index":{"_index":"test", "_type": "_doc", "_id": "999"}}, {"name": "rick99", "age": 99, "email":"wangyikai9@csdn.com", "company":"CSDN9" }, {"index": {"_index": "test2", "_type": "_doc", "_id": "888"}}, {"name": "rick88", "age": 88, "email":"wangyikai8@csdn.com", "company":"CSDN8" }, {"delete": {"_index": "test2", "_type": "_doc", "_id": "999"}}, {"create": {"_index" : "test2", "_type" : "_doc", "_id": "000"}}, {"name": "rick00", "age": 100, "email":"wangyikai0@csdn.com", "company":"CSDN0" }, {"update": {"_index": "test2", "_type": "_doc", "_id": "888"}}, {"doc": {"age": "888"}} ] es.bulk(index='test2',doc_type='_doc',body=batch_action) 使用bulk批量操作的时候,对于不同的操作类型,一定要在前面加上与之对应的操作头信息({“index”: {}}, {‘delete’: {…}}, …),否则会报TransportError(400, u’illegal_argument_exception’)的错误。
9.2 requests包
使用request包可以补充elasticsearch包里不方便或者还没有实现的功能,作为对elasticsearch包的一个补充,ES支持Restful接口,我们可以使用curl命令对其进行操作,同样我们也可以使用python里的request包访问操作ES库。
9.2.1 GET查询
auth=('elastic','r12345635x') #tuple格式的账号密码 res=requests.get( 'http://localhost:9200'+'/'+index+'/'+type+'/'+id,auth=auth) #如果没有安全认证则不需要auth参数 res.text #查询该索引下所有数据 res=requests.get('http://localhost:9200'+'/'+index+'/_search',auth=auth) ##使用DSL查询数据 bd={ "query":{ "bool":{ "should":[{"match_phrase_prefix":{"name":"rike"}}] } } } res=requests.get('http://localhost:9200'+'/'+index+'/_search/?pretty',auth=auth,json=bd)#pretty是为了格式化json样式,看起来更好看,可以忽略 print(res.text)
9.2.2 POST
#使用POST方法往ES中插入数据 data={"name": "rick999", "age": 999, "email":"wangyikai999@csdn.com", "company":"CSDN999" } res = requests.post(es_http+'/'+index+'/_doc/999',auth=auth,json=data) #使用POST方法根据DSL对ES进行操作 bd={ "query":{"match_all"{}}, "script":{"source":"cx._source.age"=parmas.age;","lang":"pa","params":{"age":99"} } } res = requests.post(es_http+'/'+index+'/_update_by_query',auth=auth,json=bd) res.text
9.2.3 PUT
#创建索引 res = requests.put(es_http+'/'+'new_index',auth=auth) res.text
9.2.4 DELETE
#使用DELETE方法删除ID为999的记录 requests.delete(es_http+'/'+'new_index'+'/_doc/999',auth=auth) #使用DELETE方法删除new_index的索引 res=requests.delete(es_http+'/'+'new_index',auth=auth) #删除索引
9.3 常用方法
########以下是查询用的方法body######### es.count(index="",doc_type="",body={}) ############
#查询所有数据 body={"query":{"match_all":{}} #查询name=python的所有数据 body={"query":{"term":{"name":"python"}}} #查询出name=python或name=android的所有数据 body={"query":{"term":{"name":["python","android"]}}} #匹配name包含python关键字的数据 body={"query":{"match":{"name":"python"}}} #在name和addr里匹配包含python关键字的数据 body={"query":{"multi_match":{"query":"python","fields":["name","addr"]}}} #搜索出id为1或2d的所有数据 body={"query":{"ids":{"type":"test_type","values":["1","2"]}}} #获取name=python并且age=18的所有数据,bool有3类查询关系must是都满足,should是其中一个满足,must_not是都不满足 body={"query":{"bool":{"must":[{"terms":{"name":"python"}},{"terms":{"age":18}}]}}} #从第2条数据开始,获取4条数据 body={"query":{"match_all":{}}"from":2 "size":4} #查询18<=age<=30的所有数据 body={"query":{"range":{"age":{"gte":18,"lte":30}}}} #查询前缀为p的所有数据 body={"query":{"prefix":{"name":"p"}}} #查询name以id为后缀的所有数据 body={"query":{"wildcard":{"name":"*id"}}} #根据age字段升序排序,asc升序,desc降序 body={"query":{"match_all":{}}"sort":{"age":{"order":"asc"}}} #只获取_id数据,多个条件用逗号隔开 es.search(index="my_index",doc_type="test_type",filter_path=["hits.hits._id"]) #获取数据量 es.count(index="my_index",doc_type"test_type") #搜索所有数据,并获取age最小的值;把min_age和min缓冲max_age和max就是查询最大值 body={"query":{"match_all":{}},"aggs":{"min_age":{"min":{"field":"age"}}}} #搜索所有数据,并获取所有age的和,把sum_age换成avg_age则为平均值 body={"query":{"match_all":{}},"aggs":{"sum_age":{"sum":{"field":"age"}}}}
https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html
通过http访问的一个demo:
查询数据,/ceshi是索引,rui是type,搜索内容时title为jones的数据
方法一
curl http://vim.xiaorui.cc:9200/ceshi/rui/_search?q=title:jones&size=5&pretty=true
方法二
http://vim.xiaorui.cc:9200/ceshi/rui/_search
body -> {"query":{"term":{"title":"jones"}}}
page = es.search(
index='eagle-eye', #database
doc_type=self.dbname, #table
scroll='100m', #游标,将受影响的数据暂时放到了一个内存区域的虚表中,而这个虚表就是游标。参数为保存的时长,100分钟
_source_include=['configuration_date', 'hostname', 'command_name','output',"device_type"], #返回每组包含的参数
search_type='query_then_fetch', #查询并取出
size=5, #每次最多取出5组数据
body={
'query':{'match_phrase':{'configuration_date':configuration_date}} #根据configuration_date查询
})