Elasticsearch 實際上提供了一系列 Restful API 來進行存取和查詢操作,我們可以使用 curl 等命令來進行操作尝抖,但畢竟命令行模式?jīng)]那么方便,所以這里我們就直接介紹利用 Python 來對接 Elasticsearch 的相關(guān)方法迅皇。
Python 中對接 Elasticsearch 使用的就是一個同名的庫昧辽,安裝方式非常簡單:
pip install elasticsearch
本文只做簡單的介紹,更多信息請查閱官方文檔登颓。
創(chuàng)建 Index
我們先來看下怎樣創(chuàng)建一個索引(Index)搅荞,這里我們創(chuàng)建一個名為 news 的索引:
from elasticsearch import Elasticsearch
es = Elasticsearch()
result = es.indices.create(index='news', ignore=400)
print(result)
如果創(chuàng)建成功,會返回如下結(jié)果:
{'acknowledged': True, 'shards_acknowledged': True, 'index': 'news'}
但這時如果我們再把代碼執(zhí)行一次的話,就會返回如下結(jié)果:
{'error': {'root_cause': [{'type': 'resource_already_exists_exception', 'reason': 'index [news/QM6yz2W8QE-bflKhc5oThw] already exists', 'index_uuid': 'QM6yz2W8QE-bflKhc5oThw', 'index': 'news'}], 'type': 'resource_already_exists_exception', 'reason': 'index [news/QM6yz2W8QE-bflKhc5oThw] already exists', 'index_uuid': 'QM6yz2W8QE-bflKhc5oThw', 'index': 'news'}, 'status': 400}
它提示創(chuàng)建失敗咕痛,status 狀態(tài)碼是 400痢甘,錯誤原因是 Index 已經(jīng)存在了。
注意這里我們的代碼里面使用了 ignore 參數(shù)為 400暇检,這說明如果返回結(jié)果是 400 的話产阱,就忽略這個錯誤不會報錯,程序不會執(zhí)行拋出異常块仆。
假如我們不加 ignore 這個參數(shù)的話:
es = Elasticsearch()
result = es.indices.create(index='news')
print(result)
再次執(zhí)行就會報錯了:
raise HTTP_EXCEPTIONS.get(status_code, TransportError)(status_code, error_message, additional_info)
elasticsearch.exceptions.RequestError: TransportError(400, 'resource_already_exists_exception', 'index [news/QM6yz2W8QE-bflKhc5oThw] already exists')
這樣程序的執(zhí)行就會出現(xiàn)問題构蹬,所以說,我們需要善用 ignore 參數(shù)悔据,把一些意外情況排除庄敛,這樣可以保證程序的正常執(zhí)行而不會中斷。
刪除 Index
刪除 Index 也是類似的科汗,代碼如下:
from elasticsearch import Elasticsearch
es = Elasticsearch()
result = es.indices.delete(index='news', ignore=[400, 404])
print(result)
這里也是使用了 ignore 參數(shù)藻烤,來忽略 Index 不存在而刪除失敗導(dǎo)致程序中斷的問題。
如果刪除成功头滔,會輸出如下結(jié)果:
{'acknowledged': True}
如果 Index 已經(jīng)被刪除怖亭,再執(zhí)行刪除則會輸出如下結(jié)果:
{'error': {'root_cause': [{'type': 'index_not_found_exception', 'reason': 'no such index', 'resource.type': 'index_or_alias', 'resource.id': 'news', 'index_uuid': '_na_', 'index': 'news'}], 'type': 'index_not_found_exception', 'reason': 'no such index', 'resource.type': 'index_or_alias', 'resource.id': 'news', 'index_uuid': '_na_', 'index': 'news'}, 'status': 404}
這個結(jié)果表明當前 Index 不存在,刪除失敗坤检,返回的結(jié)果同樣是 JSON兴猩,狀態(tài)碼是 400,但是由于我們添加了 ignore 參數(shù)早歇,忽略了 400 狀態(tài)碼倾芝,因此程序正常執(zhí)行輸出 JSON 結(jié)果,而不是拋出異常箭跳。
插入數(shù)據(jù)
Elasticsearch 就像 MongoDB 一樣晨另,在插入數(shù)據(jù)的時候可以直接插入結(jié)構(gòu)化字典數(shù)據(jù),插入數(shù)據(jù)可以調(diào)用 create() 方法谱姓,例如這里我們插入一條新聞數(shù)據(jù):
from elasticsearch import Elasticsearch
es = Elasticsearch()
es.indices.create(index='news', ignore=400)
data = {'title': '美國留給伊拉克的是個爛攤子嗎', 'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm'}
result = es.create(index='news', doc_type='politics', id=1, body=data)
print(result)
這里我們首先聲明了一條新聞數(shù)據(jù)借尿,包括標題和鏈接,然后通過調(diào)用 create() 方法插入了這條數(shù)據(jù)屉来,在調(diào)用 create() 方法時路翻,我們傳入了四個參數(shù),index 參數(shù)代表了索引名稱奶躯,doc_type 代表了文檔類型帚桩,body 則代表了文檔具體內(nèi)容亿驾,id 則是數(shù)據(jù)的唯一標識 ID嘹黔。
運行結(jié)果如下:
{'_index': 'news', '_type': 'politics', '_id': '1', '_version': 1, 'result': 'created', '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 0, '_primary_term': 1}
結(jié)果中 result 字段為 created,代表該數(shù)據(jù)插入成功。
另外其實我們也可以使用 index() 方法來插入數(shù)據(jù)儡蔓,但與 create() 不同的是郭蕉,create() 方法需要我們指定 id 字段來唯一標識該條數(shù)據(jù),而 index() 方法則不需要喂江,如果不指定 id召锈,會自動生成一個 id,調(diào)用 index() 方法的寫法如下:
es.index(index='news', doc_type='politics', body=data)
create() 方法內(nèi)部其實也是調(diào)用了 index() 方法获询,是對 index() 方法的封裝涨岁。
更新數(shù)據(jù)
更新數(shù)據(jù)也非常簡單,我們同樣需要指定數(shù)據(jù)的 id 和內(nèi)容吉嚣,調(diào)用 update() 方法即可梢薪,代碼如下:
from elasticsearch import Elasticsearch
es = Elasticsearch()
data = {
'title': '美國留給伊拉克的是個爛攤子嗎',
'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm',
'date': '2020-03-26'
}
result = es.update(index='news', doc_type='politics', body=data, id=1)
print(result)
這里我們?yōu)閿?shù)據(jù)增加了一個日期字段,然后調(diào)用了 update() 方法尝哆,結(jié)果如下:
{'_index': 'news', '_type': 'politics', '_id': '1', '_version': 2, 'result': 'updated', '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 1, '_primary_term': 1}
可以看到返回結(jié)果中秉撇,result 字段為 updated,即表示更新成功秋泄,另外我們還注意到有一個字段 _version琐馆,這代表更新后的版本號數(shù),2 代表這是第二個版本恒序,因為之前已經(jīng)插入過一次數(shù)據(jù)瘦麸,所以第一次插入的數(shù)據(jù)是版本 1,可以參見上例的運行結(jié)果奸焙,這次更新之后版本號就變成了 2瞎暑,以后每更新一次,版本號都會加 1与帆。
另外更新操作其實利用 index() 方法同樣可以做到了赌,寫法如下:
es.index(index='news', doc_type='politics', body=data, id=1)
可以看到,index() 方法可以代替我們完成兩個操作玄糟,如果數(shù)據(jù)不存在勿她,那就執(zhí)行插入操作,如果已經(jīng)存在阵翎,那就執(zhí)行更新操作逢并,非常方便。
刪除數(shù)據(jù)
如果想刪除一條數(shù)據(jù)可以調(diào)用 delete() 方法郭卫,指定需要刪除的數(shù)據(jù) id 即可砍聊,寫法如下:
from elasticsearch import Elasticsearch
es = Elasticsearch()
result = es.delete(index='news', doc_type='politics', id=1)
print(result)
運行結(jié)果如下:
{'_index': 'news', '_type': 'politics', '_id': '1', '_version': 3, 'result': 'deleted', '_shards': {'total': 2, 'successful': 1, 'failed': 0}, '_seq_no': 2, '_primary_term': 1}
可以看到運行結(jié)果中 result 字段為 deleted,代表刪除成功贰军,_version 變成了 3玻蝌,又增加了 1。
查詢數(shù)據(jù)
上面的幾個操作都是非常簡單的操作,普通的數(shù)據(jù)庫如 MongoDB 都是可以完成的俯树,看起來并沒有什么了不起的帘腹,Elasticsearch 更特殊的地方在于其異常強大的檢索功能。
對于中文來說许饿,我們需要安裝一個分詞插件阳欲,這里使用的是 elasticsearch-analysis-ik,GitHub 鏈接為:https://github.com/medcl/elasticsearch-analysis-ik陋率,這里我們使用 Elasticsearch 的另一個命令行工具 elasticsearch-plugin 來安裝,這里安裝的版本是 6.8.6瓦糟,請確保和 Elasticsearch 的版本對應(yīng)起來赊窥,命令如下:
elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.8.6/elasticsearch-analysis-ik-6.8.6.zip
這里的版本號請?zhí)鎿Q成你的 Elasticsearch 的版本號。
安裝之后重新啟動 Elasticsearch 就可以了狸页,它會自動加載安裝好的插件锨能。
首先我們新建一個索引并指定需要分詞的字段,代碼如下:
from elasticsearch import Elasticsearch
es = Elasticsearch()
mapping = {
'properties': {
'title': {
'type': 'text',
'analyzer': 'ik_max_word',
'search_analyzer': 'ik_max_word'
}
}
}
es.indices.delete(index='news', ignore=[400, 404])
es.indices.create(index='news', ignore=400)
result = es.indices.put_mapping(index='news', doc_type='politics', body=mapping)
print(result)
這里我們先將之前的索引刪除了芍耘,然后新建了一個索引址遇,然后更新了它的 mapping 信息,mapping 信息中指定了分詞的字段斋竞,指定了字段的類型 type 為 text倔约,分詞器 analyzer 和 搜索分詞器 search_analyzer 為 ik_max_word,即使用我們剛才安裝的中文分詞插件坝初。如果不指定的話則使用默認的英文分詞器浸剩。
接下來我們插入幾條新的數(shù)據(jù):
datas = [
{
'title': '美國留給伊拉克的是個爛攤子嗎',
'url': 'http://view.news.qq.com/zt2011/usa_iraq/index.htm',
'date': '2020-03-26'
},
{
'title': '公安部:各地校車將享最高路權(quán)',
'url': 'http://www.chinanews.com/gn/2011/12-16/3536077.shtml',
'date': '2020-03-26'
},
{
'title': '中韓漁警沖突調(diào)查:韓警平均每天扣1艘中國漁船',
'url': 'https://news.qq.com/a/20111216/001044.htm',
'date': '2020-03-26'
},
{
'title': '中國駐洛杉磯領(lǐng)事館遭亞裔男子槍擊 嫌犯已自首',
'url': 'http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml',
'date': '2020-03-26'
}
]
for data in datas:
es.index(index='news', doc_type='politics', body=data)
這里我們指定了四條數(shù)據(jù),都帶有 title鳄袍、url绢要、date 字段,然后通過 index() 方法將其插入 Elasticsearch 中拗小,索引名稱為 news重罪,類型為 politics。
接下來我們根據(jù)關(guān)鍵詞查詢一下相關(guān)內(nèi)容:
result = es.search(index='news', doc_type='politics')
print(result)
可以看到查詢出了所有插入的四條數(shù)據(jù):
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 4,
"max_score": 1.0,
"hits": [
{
"_index": "news",
"_type": "politics",
"_id": "c05G9mQBD9BuE5fdHOUT",
"_score": 1.0,
"_source": {
"title": "美國留給伊拉克的是個爛攤子嗎",
"url": "http://view.news.qq.com/zt2011/usa_iraq/index.htm",
"date": "2020-03-26"
}
},
{
"_index": "news",
"_type": "politics",
"_id": "dk5G9mQBD9BuE5fdHOUm",
"_score": 1.0,
"_source": {
"title": "中國駐洛杉磯領(lǐng)事館遭亞裔男子槍擊哀九,嫌犯已自首",
"url": "http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml",
"date": "2020-03-26"
}
},
{
"_index": "news",
"_type": "politics",
"_id": "dU5G9mQBD9BuE5fdHOUj",
"_score": 1.0,
"_source": {
"title": "中韓漁警沖突調(diào)查:韓警平均每天扣1艘中國漁船",
"url": "https://news.qq.com/a/20111216/001044.htm",
"date": "2020-03-26"
}
},
{
"_index": "news",
"_type": "politics",
"_id": "dE5G9mQBD9BuE5fdHOUf",
"_score": 1.0,
"_source": {
"title": "公安部:各地校車將享最高路權(quán)",
"url": "http://www.chinanews.com/gn/2011/12-16/3536077.shtml",
"date": "2020-03-26"
}
}
]
}
}
可以看到返回結(jié)果會出現(xiàn)在 hits 字段里面剿配,然后其中有 total 字段標明了查詢的結(jié)果條目數(shù),還有 max_score 代表了最大匹配分數(shù)阅束。
另外我們還可以進行全文檢索呼胚,這才是體現(xiàn) Elasticsearch 搜索引擎特性的地方:
dsl = {
'query': {
'match': {
'title': '中國 領(lǐng)事館'
}
}
}
es = Elasticsearch()
result = es.search(index='news', doc_type='politics', body=dsl)
print(json.dumps(result, indent=2, ensure_ascii=False))
這里我們使用 Elasticsearch 支持的 DSL 語句來進行查詢,使用 match 指定全文檢索息裸,檢索的字段是 title蝇更,內(nèi)容是“中國領(lǐng)事館”琢融,搜索結(jié)果如下:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 2.546152,
"hits": [
{
"_index": "news",
"_type": "politics",
"_id": "dk5G9mQBD9BuE5fdHOUm",
"_score": 2.546152,
"_source": {
"title": "中國駐洛杉磯領(lǐng)事館遭亞裔男子槍擊,嫌犯已自首",
"url": "http://news.ifeng.com/world/detail_2011_12/16/11372558_0.shtml",
"date": "2020-03-26"
}
},
{
"_index": "news",
"_type": "politics",
"_id": "dU5G9mQBD9BuE5fdHOUj",
"_score": 0.2876821,
"_source": {
"title": "中韓漁警沖突調(diào)查:韓警平均每天扣1艘中國漁船",
"url": "https://news.qq.com/a/20111216/001044.htm",
"date": "2020-03-26"
}
}
]
}
}
這里我們看到匹配的結(jié)果有兩條簿寂,第一條的分數(shù)為 2.54,第二條的分數(shù)為 0.28宿亡,這是因為第一條匹配的數(shù)據(jù)中含有“中國”和“領(lǐng)事館”兩個詞常遂,第二條匹配的數(shù)據(jù)中不包含“領(lǐng)事館”,但是包含了“中國”這個詞挽荠,所以也被檢索出來了克胳,但是分數(shù)比較低。
因此可以看出圈匆,檢索時會對對應(yīng)的字段全文檢索漠另,結(jié)果還會按照檢索關(guān)鍵詞的相關(guān)性進行排序,這就是一個基本的搜索引擎雛形跃赚。
另外 Elasticsearch 還支持非常多的查詢方式笆搓,詳情可以參考官方文檔。
以上便是對 Elasticsearch 的基本介紹以及 Python 操作 Elasticsearch 的基本用法纬傲。