一、安裝
pip install elasticsearch
二、連接集羣節點
1、指定連接
es = Elasticsearch(
['172.16.153.129:9200'],
# 認證信息
# http_auth=('elastic', 'changeme')
)
2、動態連接
es = Elasticsearch(
['esnode1:port', 'esnode2:port'],
# 在做任何操作之前,先進行嗅探
sniff_on_start=True,
# 節點沒有響應時,進行刷新,重新連接
sniff_on_connection_fail=True,
# 每 60 秒刷新一次
sniffer_timeout=60
)
3、對不同的節點,賦予不同的參數
es = Elasticsearch([
{'host': 'localhost'},
{'host': 'othernode', 'port': 443, 'url_prefix': 'es', 'use_ssl': True},
])
#假如使用了 ssl
es = Elasticsearch(
['localhost:443', 'other_host:443'],
#打開SSL
use_ssl=True,
#確保我們驗證了SSL證書(默認關閉)
verify_certs=True,
#提供CA證書的路徑
ca_certs='/path/to/CA_certs',
#PEM格式的SSL客戶端證書
client_cert='/path/to/clientcert.pem',
#PEM格式的SSL客戶端密鑰
client_key='/path/to/clientkey.pem'
)
三、獲取相關信息
1、測試集羣是否啓動
In [40]: es.ping()
Out[40]: True
2、獲取集羣基本信息
In [39]: es.info()
Out[39]:
{'cluster_name': 'sharkyun',
'cluster_uuid': 'rIt2U-unRuG0hJBt6BXxqw',
'name': 'master',
'tagline': 'You Know, for Search',
'version': {'build_date': '2017-10-06T20:33:39.012Z',
'build_hash': '1a2f265',
'build_snapshot': False,
'lucene_version': '6.6.1',
'number': '5.6.3'}}
3、獲取集羣的健康狀態信息
In [41]: es.cluster.health()
Out[41]:
{'active_primary_shards': 6,
'active_shards': 6,
'active_shards_percent_as_number': 50.0,
'cluster_name': 'sharkyun',
'delayed_unassigned_shards': 0,
'initializing_shards': 0,
'number_of_data_nodes': 1,
'number_of_in_flight_fetch': 0,
'number_of_nodes': 1,
'number_of_pending_tasks': 0,
'relocating_shards': 0,
'status': 'yellow',
'task_max_waiting_in_queue_millis': 0,
'timed_out': False,
'unassigned_shards': 6}
4、獲取當前連接的集羣節點信息
In [43]: es.cluster.client.info()
5、獲取集羣目前所有的索引
In [55]: print(es.cat.indices())
yellow open logstash-2017.11.04 Zt2K7k0yRZaIwmEsZ9H3DA 5 1 301000 0 162.3mb 162.3mb
yellow open .kibana 1Epb3nPFRimFJoRwKHtXIg 1 1 2 0 13.4kb 13.4kb
6、獲取集羣的更多信息
es.cluster.stats()
7、利用實例的 cat 屬性得到更簡單易讀的信息
In [85]: es.cat.health()
Out[85]: '1510431262 04:14:22 sharkyun yellow 1 1 6 6 0 0 6 0 - 50.0%\n'
In [86]: es.cat.master()
Out[86]: 'VXgFbKAaTtGO5a1QAfdcLw 172.16.153.129 172.16.153.129 master\n'
In [87]: es.cat.nodes()
Out[87]: '172.16.153.129 27 49 0 0.02 0.01 0.00 mdi * master\n'
In [88]: es.cat.indices()
Out[88]: 'yellow open logstash-2017.11.04 Zt2K7k0yRZaIwmEsZ9H3DA 5 1 301000 0 162.3mb 162.3mb\nyellow open .kibana 1Epb3nPFRimFJoRwKHtXIg 1 1 2 0 13.4kb 13.4kb\n'
In [89]: es.cat.count()
Out[89]: '1510431323 04:15:23 301002\n'
In [90]: es.cat.plugins()
Out[90]: ''
In [91]: es.cat.templates()
Out[91]: 'logstash logstash-* 0 50001\nfilebeat filebeat-* 0 \n'
8、任務
es.tasks.get()
es.tasks.list()
四、查詢
1、發送查詢請求
es = Elasticsearch(
['172.16.153.129:9200']
)
response = es.search(
index="logstash-2017.11.14", # 索引名
body={ # 請求體
"query": { # 關鍵字,把查詢語句給 query
"bool": { # 關鍵字,表示使用 filter 查詢,沒有匹配度
"must": [ # 表示裏面的條件必須匹配,多個匹配元素可以放在列表裏
{
"match": { # 關鍵字,表示需要匹配的元素
"TransId": '06100021650016153' # TransId 是字段名, 06100021650016153 是此字段需要匹配到的值
}
},
{
"match": {
"Ds": '2017-05-06'
}
},
{
"match": {
"Gy": '2012020235'
}
}, ],
"must_not": { # 關鍵字,表示查詢的結果裏必須不匹配裏面的元素
"match": { # 關鍵字
"message": "M(" # message 字段名,這個字段的值一般是查詢到的結果內容體。這裏的意思是,返回的結果裏不能包含特殊字符 'M('
}
}
}
},
# 下面是對返回的結果繼續排序
"sort": [{"@timestamp": {"order": "desc"}}],
"from": start, # 從匹配到的結果中的第幾條數據開始返回,值是匹配到的數據的下標,從 0 開始
"size": size # 返回多少條數據
}
)
2、得到返回結果的總條數
total = res['hits']['total']
3、循環返回的結果,得到想要的內容
res_dict={}
for hit in res['hits']['hits']:
log_time = "%s|%s" % (hit['_source']['Ds'], hit['_source']['Us'])
res_dict[log_time] = "%s|%s|%s|%s" % (hit['_source']['beat']['hostname'],hit['_source']['FileName'], hit['_source']['FileNum'],hit['_source']['Messager'])
4、實例查詢7天之內的流水號爲:06100021650016153 的日誌信息
query_body={
'bool': {
'must_not': {'match': {'message': 'M('}},
'must': [
{'match': {'TransId': '06100021650016153'}},
{'range': {'@timestamp': {'gte': u'now-7d', 'lte': 'now'}}}
]
}
}
res = es.search(
index='logstash-2017.11.14',
body={
"query": query_body,
"sort":[{"@timestamp": {"order": "desc"}}]})
}
)
五、更高級的 elasticsearch_dsl 模塊
官網:https://elasticsearch-dsl.readthedocs.io/en/latest/
1、小試牛刀
①單一字段查詢
es = Elasticsearch(
['172.16.153.129:9200']
)
s = Search(using=es,
index="logstash-2017.11.14").filter("match",Gy='20160521491').query("match", TransId='06100021650016153').exclude("match", message="M(")
response = s.execute()
using:指明用那個已經連接的對象
query:接收的是查詢體語句
exclude:接收的是不匹配的字段 就像 must_not
filter:接收的是過濾語句 ,過濾的條件意思是在返回結果中有這些條件的信息
②統計結果總數
s.count()
response = sexecute()
response.hits.total
③獲取結果
res_dict={}
for hit in s:
log_time = "%s|%s" % (hit.Ds, hit.Us')
res_dict[log_time] = "%s|%s|%s|%s" % (hit.beat['hostname'],hit.FileName, hit.FileNum,hit.Messager)
2、設置連接
最簡單的選擇,也是最有用的,就是定義一個默認連接,每次調用API時都會使用這個連接,而不需要顯式傳遞其他連接。
除非要從應用程序訪問多個羣集,否則強烈建議您使用該 create_connection 方法創建一個默認連接,所有操作都將自動使用該連接。
3、顯式傳遞一個連接
如果你不想提供全局配置(也就是默認連接),你可以傳入你自己的連接(實例elasticsearch.Elasticsearch)作爲參數, 使用 using 接受它:
s = Search(using=Elasticsearch('localhost'))
甚至你可以下面的方式來覆蓋一個對象已經關聯的任何連接
s = s.using(Elasticsearch('otherhost:9200'))
4、默認連接
要定義全局使用的默認連接,請使用 connections 模塊和create_connection方法:
from elasticsearch_dsl.connections import connections
client = connections.create_connection(hosts=['172.16.153.129:9200'],
http_auth=('elastic', 'changeme'), timeout=20)
執行搜索就不必再傳連接對象了
s = Search(index="logstash-2017.11.14").filter("match",Gy='20160521491').query("match", TransId='06100021650016153').exclude("match", message="M(")
六、多集羣環境的連接
1、多個集羣
配置同時連接多個羣集
from elasticsearch_dsl.connections import connections
clients = connections.configure(
default={'hosts': 'localhost'},
dev={
'hosts': ['esdev1.example.com:9200'],
'sniff_on_start': True
}
)
備註:上面的情況是適用於第一次連接時的情況
2、運行中設置連接
# if you have configuration to be passed to Elasticsearch.__init__
# 直接傳遞一個配置信息給 Elasticsearch
connections.create_connection('qa', hosts=['esqa1.example.com'], sniff_on_start=True)
# if you already have an Elasticsearch instance ready
# 追加一個已經準備好的連接對象
connections.add_connection('qa', my_client)
3、使用連接對象的別名
當使用多個連接時,您可以使用您在下面註冊的字符串別名來引用它們:
s = Search(using='qa')
#如果在該別名下沒有註冊的連接,KeyError 異常將會被拋出
KeyError: "There is no connection with alias 'qa'."
七、Search DSL
1、該Search對象
該Search對象代表整個搜索請求:
查詢(queries)
過濾器(filters)
聚合(aggregations)
排序(sort)
分頁(pagination)
附加的參數(additional parameters)
關聯客戶端(associated client)
API 被設計爲可鏈接的。除了聚合功能以外,這意味着Search對象是不可變的(對對象的所有更改都將導致創建(拷貝)一個包含更改的副本)。這意味着您可以安全地將Search對象傳遞給外部代碼,而不必擔心這個對象會被修改。
實例化對象時,您可以傳遞低級別的elasticsearch客戶端實例Search:
from elasticsearch import Elasticsearch
from elasticsearch_dsl import Search
client = Elasticsearch()
s = Search(using=client)
所有的方法都會返回一個對象的副本,從而安全地傳遞給外部代碼。
API 是可連接的,允許你在一個語句中調用多個方法:
s = Search().using(client).query("match", title="python")
要將請求發送到Elasticsearch:
s.execute()
如果您只是想遍歷搜索返回的匹配,則可以遍歷該Search對象:
for hit in s:
print(log_time = "%s|%s" % (hit.Ds, hit.Us'))
print(hit.beat['hostname'],hit.FileName, hit.FileNum,hit.Messager)
DS、US、beat、FileName等都是映射好的字段名,就是用 elasticsearch 模塊的 search 方法得到的結果裏的 hit[’_source’] 裏面的內容。
搜索結果將被緩存。隨後調用execute或試圖遍歷已經執行的Search對象將不會觸發額外的請求發送到Elasticsearch。強制請求時指定 ignore_cache=True調用execute。
這個 Search 的對象也可以轉換爲之前的 Query DSL 格式
s.to_dict()
八、查詢
Elasticsearch_dsl 的 query 類爲所有Elasticsearch查詢類型提供類。 傳遞所有參數作爲關鍵字參數。 這些類接受任何關鍵字參數,然後dsl將傳遞給構造函數的所有參數作爲結果字典中的頂級關鍵字序列化(因此生成的json被髮送到elasticsearch)。 這意味着在DSL中原始查詢和其等價物之間存在明確的一對一映射:
from elasticsearch_dsl.query import MultiMatch, Match
# {"multi_match": {"query": "python django", "fields": ["title", "body"]}}
MultiMatch(query='python django', fields=['title', 'body'])
# {"match": {"title": {"query": "web framework", "type": "phrase"}}}
Match(title={"query": "web framework", "type": "phrase"})
在某些情況下,由於python對標識符的限制,這種方法不支持字段中含有特殊字符的情況,比如:@timestamp。
在這種情況下,你必須史使用原來的字典形式:Range(** {’@timestamp’: {‘lt’: ‘now’}})
九、強大的 Q
1、使用Q快捷方式可以把帶參數的名稱或原始數據的dict構建成 Search 對應類的實例:
from elasticsearch_dsl import Q
Q("multi_match", query='python django', fields=['title', 'body'])
Q({"multi_match": {"query": "python django", "fields": ["title", "body"]}})
# 這兩種方式最後轉換的結果是一致的
MultiMatch(fields=['title', 'body'], query='python django')
2、將查詢添加到Search對象,請使用以下.query()方法
q = Q("multi_match", query='python django', fields=['title', 'body'])
s = s.query(q)
3、Q 接收的參數,,query 方法都支持
s = s.query("multi_match", query='python django', fields=['title', 'body'])
4、用 Q 實現組合查詢
Q 對象可以使用邏輯運算符進行組合:
Q("match", title='python') | Q("match", title='django')
# {"bool": {"should": [...]}}
# 匹配到任意條件即可
Q("match", title='python') & Q("match", title='django')
# {"bool": {"must": [...]}}
# 列表裏的條件必須同時匹配
~Q("match", title="python")
# {"bool": {"must_not": [...]}}
# 非
5、實現組合查詢的另一種方法
query 方法可以被連續調用
In [193]: sa = Search().query().query('match',title='python').query('match',body='django')
In [194]: sa.to_dict()
Out[194]:
{
"query": {
"bool": {
"must": [
{"match": {
"title": "python"}
},
{"match": {
"body": "django"}
}
]
}
}
}
假如希望對查詢的條件進行精確的控制,請使用 Q 構造組合查詢:
q = Q('bool',
must=[Q('match', title='python')],
should=[Q(...), Q(...)],
minimum_should_match=1
)
s = Search().query(q)
十、過濾器
1、過濾使用 filter 方法
s = Search()
s = s.filter('terms', tags=['search', 'python'])
# {'query': {'bool': {'filter': [{'terms': {'tags': ['search', 'python']}}]}}}
在幕後,這將產生一個Bool查詢並將指定的 terms查詢放入其filter分支,使其等價於:
s = Search()
s = s.query('bool', filter=[Q('terms', tags=['search', 'python'])])
# {'query': {'bool': {'filter': [{'terms': {'tags': ['search', 'python']}}]}}}
如果想使用post_filter元素進行分面導航,請使用該 .post_filter()方法。也可以用 exclude() 排除查詢項目:
s = Search()
s = s.exclude('terms', tags=['search', 'python'])
官網上說下面是簡寫
s = s.query('bool', filter=[~Q('terms', tags=['search', 'python'])])
十一、聚合
1、定義一個聚合,請使用 A
A('terms', field='tags')
# {"terms": {"field": "tags"}}
2、嵌套聚合,可以使用.bucket(),.metric()和 .pipeline()方法
a = A('terms', field='category')
# {'terms': {'field': 'category'}}
a.metric('clicks_per_category', 'sum', field='clicks')\
.bucket('tags_per_category', 'terms', field='tags')
# {
# 'terms': {'field': 'category'},
# 'aggs': {
# 'clicks_per_category': {'sum': {'field': 'clicks'}},
# 'tags_per_category': {'terms': {'field': 'tags'}}
# }
# }
3、將聚合添加到Search對象,請使用.aggs充當頂級聚合的屬性
s = Search()
a = A('terms', field='category')
s.aggs.bucket('category_terms', a)
# {
# 'aggs': {
# 'category_terms': {
# 'terms': {
# 'field': 'category'
# }
# }
# }
# }
或者下面這樣的
s = Search()
s.aggs.bucket('articles_per_day', 'date_histogram', field='publish_date', interval='day')\
.metric('clicks_per_day', 'sum', field='clicks')\
.pipeline('moving_click_average', 'moving_avg', buckets_path='clicks_per_day')\
.bucket('tags_per_day', 'terms', field='tags')
s.to_dict()
# {
# "aggs": {
# "articles_per_day": {
# "date_histogram": { "interval": "day", "field": "publish_date" },
# "aggs": {
# "clicks_per_day": { "sum": { "field": "clicks" } },
# "moving_click_average": { "moving_avg": { "buckets_path": "clicks_per_day" } },
# "tags_per_day": { "terms": { "field": "tags" } }
# }
# }
# }
# }
4、通過名稱訪問現有存儲桶
s = Search()
s.aggs.bucket('per_category', 'terms', field='category')
s.aggs['per_category'].metric('clicks_per_category', 'sum', field='clicks')
s.aggs['per_category'].bucket('tags_per_category', 'terms', field='tags')
當鏈接多個聚合時,什麼.bucket()和.metric()方法返回之間是有區別的
.bucket()返回新定義的存儲區,同時.metric()返回其父容器以允許進一步鏈接。
與Search對象上的其他方法相反,定義聚合是在原地完成的(不返回副本)。
十二、排序
1、要指定排序順序,請使用 .sort() 方法:
s = Search().sort(
'category',
'-title',
{"lines" : {"order" : "asc", "mode" : "avg"}}
)
它接受可以是字符串或字典的位置參數。字符串值是一個字段名稱,可以用-符號前綴來指定降序。
2、恢復排序使用無參的 sort() 方法
s = s.sort()
十三、分頁
1、要指定from / size參數,請使用Python切片AP
s = s[10:20]
# {"from": 10, "size": 10}
2、如果要訪問與查詢匹配的所有文檔,可以使用 scan使用掃描/滾動彈性搜索API的方法
for hit in s.scan():
print(hit.title)
備註:在這種情況下,結果將不會被排序。
十四、突出高亮
1、設置突出顯示的常用屬性,使用highlight_options方法
s = s.highlight_options(order='score')
2、爲各個字段啓用突出顯示使用以下highlight方法完成
s = s.highlight('title')
# or, including parameters:
s = s.highlight('title', fragment_size=50)
返回的結果將在被賦值到一個對象(變量)上可用,.meta.highlight.FIELD 將包含結果的列表:
response = s.execute()
for hit in response:
for fragment in hit.meta.highlight.title:
print(fragment)
十五、Suggestions 建議
要在Search對象上指定建議請求,請使用以下suggest方法:
s = s.suggest('my_suggestion', 'pyhton', term={'field': 'title'})
第一個參數是建議名稱(它將返回的名稱),第二個是你希望建議者處理的實際文本,關鍵字參數將被添加到建議的json中,這意味着它應該成爲其中一個term,phrase或者completion指出應該使用哪種類型的建議者。
如果您只希望運行搜索的建議部分(通過_suggest 端點),您可以通過execute_suggest以下方式進行:
s = s.suggest('my_suggestion', 'pyhton', term={'field': 'title'})
suggestions = s.execute_suggest()
print(suggestions.my_suggestion)
十六、額外的屬性和參數
1、要設置搜索請求的額外屬性,請使用該.extra()方法。這可以被用於定義在不能經由像特定的API方法來限定所述主體的鍵explain或search_after:
s = s.extra(explain=True)
2、要設置查詢參數,請使用以下.params()方法:
s = s.params(search_type="count")
3、如果您需要限制elasticsearch返回的字段,請使用以下 source()方法:
#只返回選定的字段
s = s.source(['title', 'body'])
#不返回任何字段,只是元數據
s = s.source(False)
#明確包含/排除字段
s = s.source(include=["title"], exclude=["user.*"])
#重置字段選擇
s = s.source(None)
十七、序列化和反序列化
1、Search 的對象可以使用 .to_dict() 方法序列化爲一個字典
您也可以使用 Search 類的 .from_dict() 方法創建一個Search對象。
這將創建一個新的對象,並使用字典中的數據填充這個新的對象
s = Search.from_dict({"query": {"match": {"title": "python"}}})
如果要修改現有的Search對象,並重寫它的屬性,可以使用這個實例的 update_from_dict() 方法,改變是實時生效的:
In [2]: s = Search()
In [4]: s.to_dict()
Out[4]: {'query': {'match_all': {}}}
In [5]: s.update_from_dict({"query": {"match": {"title": "python"}}, "size": 42}
...: )
Out[5]: <elasticsearch_dsl.search.Search at 0x10a7c8550>
In [6]: s.to_dict()
Out[6]: {'query': {'match': {'title': 'python'}}, 'size': 42}
2、返回結果
您可以通過調用 Search 對象的 .execute() 方法來執行搜索,之後將返回一個 Response 對象。
你可以將這個返回的對象(結果)賦值給一個對象。
該 Response 對象允許您通過屬性訪問的方式(也就是 . )來訪問 Response 對象字典中的任何鍵。
它還提供了一些方便的幫手:
response = s.execute()
print(response.success())
# 是否成功
# True
print(response.took)
# 命中數
# 12
print(response.hits.total)
print(response.suggest.my_suggestions)
如果要檢查response對象的內容,只需使用其 to_dict方法訪問原始數據即可打印。
十八、Hits
要訪問搜索返回的匹配對象,請訪問該hits屬性或只是遍歷該Response對象:
response = s.execute()
print('Total %d hits found.' % response.hits.total)
for h in response:
print(h.title, h.body)
十九、結果
單獨的命中包裝在一個便利的類,允許屬性訪問返回的字典中的鍵。結果的所有元數據都可以通過meta(不帶下劃線開頭 _ )訪問:
response = s.execute()
h = response.hits[0]
print('/%s/%s/%s returned with score %f' % (
h.meta.index, h.meta.doc_type, h.meta.id, h.meta.score))
響應 = s.執行()
h = 響應.命中[ 0 ]
打印('/ %s / %s / %s 與得分返回%F ' %(ħ.元.指數, ħ.元.DOC_TYPE , ħ.元.ID, ħ.元.得分))
注意:如果剛好文檔中有一個字段叫 meta ,可以使用字典的鍵方法來訪問它:hit[‘meta’]
二十、聚合
聚合可通過aggregations屬性獲得
for tag in response.aggregations.per_tag.buckets:
print(tag.key, tag.max_lines.value)
二十一、MultiSearch
如果您需要同時執行多個搜索,則可以使用 MultiSearch 類,這將使用該類的 _msearch API
from elasticsearch_dsl import MultiSearch, Search
ms = MultiSearch(index='blogs')
ms = ms.add(Search().filter('term', tags='python'))
ms = ms.add(Search().filter('term', tags='elasticsearch'))
responses = ms.execute()
for response in responses:
print("Results for query %r." % response.search.query)
for hit in response:
print(hit.title)
二十二、持久化
1、映射
您可以使用dsl庫爲應用程序定義映射和基本的持久層。
映射定義遵循與查詢dsl類似的模式:
from elasticsearch_dsl import Keyword, Mapping, Nested, Text
# name your type
m = Mapping('my-type')
# add fields
m.field('title', 'text')
# you can use multi-fields easily
m.field('category', 'text', fields={'raw': Keyword()})
# you can also create a field manually
comment = Nested()
comment.field('author', Text())
comment.field('created_at', Date())
# and attach it to the mapping
m.field('comments', comment)
# you can also define mappings for the meta fields
m.meta('_all', enabled=False)
# save the mapping into index 'my-index'
m.save('my-index')
備註:
默認情況下,所有的字段(除了Nested)都會有單個值。
您可以在創建/定義字段期間,通過向構造函數傳入 multi=True(m.field(‘tags’, Keyword(multi=True)))來始終覆蓋此期望值。
那麼,即使字段沒有被設置,字段的值也將是一個空的列表,使您能夠寫入。
doc.tags.append('search')
特別是如果您使用動態映射,則可以根據Elasticsearch中的現有類型更新映射,或直接從現有類型創建映射:
# get the mapping from our production cluster
m = Mapping.from_es('my-index', 'my-type', using='prod')
# update based on data in QA cluster
m.update_from_es('my-index', using='qa')
# update the mapping on production
# 在生產上更新映射
m.save('my-index', using='prod')
常用字段選項:
multi:如果設置True爲該字段的值將被設置爲[]第一次訪問。
required:指示字段是否需要文檔的有效值。
二十三、Analysis
要指定字段的analyzer值,Text您可以使用分析儀的名稱(作爲字符串),並依靠定義的分析儀(如內置分析儀)或手動定義分析儀。
或者,您可以創建自己的分析器並讓持久層處理其創建:
from elasticsearch_dsl import analyzer, tokenizer
my_analyzer = analyzer('my_analyzer',
tokenizer=tokenizer('trigram', 'nGram', min_gram=3, max_gram=3),
filter=['lowercase']
)
每個分析對象需要有一個名字(my_analyzer和trigram在我們的例子)和斷詞,令牌過濾器和過濾器炭還需要指定類型(nGram在我們的例子)。
在創建依賴於自定義分析器的映射時,索引必須不存在或被關閉。要創建多個DocType定義的映射,您可以使用Index對象
二十四、DocType
在文檔中創建一個類似於模型的包裝,請使用 DocType類:
from datetime import datetime
from elasticsearch_dsl import DocType, Date, Nested, Boolean, \
analyzer, InnerObjectWrapper, Completion, Keyword, Text
html_strip = analyzer('html_strip',
tokenizer="standard",
filter=["standard", "lowercase", "stop", "snowball"],
char_filter=["html_strip"]
)
class Comment(InnerObjectWrapper):
def age(self):
return datetime.now() - self.created_at
class Post(DocType):
title = Text()
title_suggest = Completion()
created_at = Date()
published = Boolean()
category = Text(
analyzer=html_strip,
fields={'raw': Keyword()}
)
comments = Nested(
doc_class=Comment,
properties={
'author': Text(fields={'raw': Keyword()}),
'content': Text(analyzer='snowball'),
'created_at': Date()
}
)
class Meta:
index = 'blog'
def add_comment(self, author, content):
self.comments.append(
{'author': author, 'content': content})
def save(self, ** kwargs):
self.created_at = datetime.now()
return super().save(** kwargs)
二十五、文檔生命週期 (Document life cycle)
在首次使用Post文檔類型之前,您需要在Elasticsearch中創建映射。爲此,您可以使用Index對象或通過調用init類方法直接創建映射:
# create the mappings in Elasticsearch
Post.init()
要創建一個新Post文檔只需實例化這個類並傳入你想要設置的任何字段,就可以使用標準屬性設置來改變/添加更多的字段。請注意,您不限於顯式定義的字段:
# instantiate the document
first = Post(title='My First Blog Post, yay!', published=True)
# assign some field values, can be values or lists of values
first.category = ['everything', 'nothing']
# every document has an id in meta
first.meta.id = 47
# save the document into the cluster
first.save()
所有的元數據字段(id,parent,routing,index等等)可以被訪問(和設置)。
通過meta屬性或直接用下劃線變體訪問它們:
post = Post(meta={'id': 42})
# prints 42, same as post._id
print(post.meta.id)
# override default index, same as post._index
post.meta.index = 'my-blog'
To retrieve(檢索,得到) an existing document use the get class method:
# retrieve the document
first = Post.get(id=42)
# now we can call methods, change fields, ...
first.add_comment('me', 'This is nice!')
# and save the changes into the cluster again
first.save()
# you can also(也) update just individual fields which will call the update API
# and also(並且) update the document in place(首先)
first.update(published=True, published_by='me')
If the document is not found in elasticsearch an exception (elasticsearch.NotFoundError) will be raised. If you wish to return None instead just pass in ignore=404 to suppress the exception:
p = Post.get(id='not-in-es', ignore=404)
p is None
當您想要同時檢索多個文檔時,id 您可以使用以下mget方法:
posts = Post.mget([42, 47, 256])
mgetNotFoundError如果有任何文件沒有找到,並且RequestError文件中有任何內容導致錯誤,將會默認提出。您可以通過設置參數來控制此行爲:
raise_on_error
如果True(默認),那麼任何錯誤都會引發異常。否則,包含錯誤的所有文檔將被視爲丟失。
missing
可以有三個可能的值:('none'默認)'raise'和 'skip'。如果文檔丟失或出錯,將被替換爲None,將引發異常或文檔將完全跳過。
所有有關的信息DocType,包括它的信息Mapping都可以通過_doc_type類的屬性來訪問:
# name of the type and index in elasticsearch
Post._doc_type.name
Post._doc_type.index
# the raw Mapping object
# 原始映射對象
Post._doc_type.mapping
# the optional name of the parent type (if defined)
# 父類型的可選名稱(如果已定義)
Post._doc_type.parent
The _doc_type attribute is also home to the refresh method which will update the mapping on the DocType from elasticsearch.
二十六、_doc_type 屬性也是可以通過 refresh 方法來更新elasticsearch的DocType上的映射
假如你使用的是動態映射,並希望類知道這些字段,(比如,你希望日期字段能被正確的序列化)你這樣將會是很有用的:
Post._doc_type.refresh()
##To delete a document just call its delete method:
first = Post.get(id=42)
first.delete()
二十七、Search
To search for this document type, use the search class method:
# by calling .search we get back a standard Search object
# 通過調用 .search(), 我們得到一個標準的搜索對象
s = Post.search()
# the search is already limited to the index and doc_type of our document
s = s.filter('term', published=True).query('match', title='first')
results = s.execute()
# when you execute the search the results are wrapped in your document class (Post)
for post in results:
print(post.meta.score, post.title)
或者,您可以只取一個Search對象,並限制它返回我們的文檔類型,用正確的類包裝:
s = Search()
s = s.doc_type(Post)
您也可以將文檔類與標準文檔類型(只是字符串)結合起來,這將像以前一樣處理。您也可以傳入多個DocType 子類,響應中的每個文檔將被包裝在它的類中。
If you want to run suggestions, just use the suggest method on the Search object:
s = Post.search()
s = s.suggest('title_suggestions', 'pyth', completion={'field': 'title_suggest'})
# you can even execute just the suggestions via the _suggest API
suggestions = s.execute_suggest()
for result in suggestions.title_suggestions:
print('Suggestions for %s:' % result.text)
for option in result.options:
print(' %s (%r)' % (option.text, option.payload))
二十八、class Meta 選項
在Meta文檔定義的類中,您可以爲文檔定義各種元數據:
doc_type:elasticsearch中的doc_type的名稱。默認情況下,它將從類名(MyDocument - > my_document)
index:文檔的默認索引,默認情況下它是空的,並且每個操作(比如get或save需要一個明確的index參數)
using:默認使用的連接別名,默認爲 ‘default’
mapping:Mapping類的可選實例,用作從文檔類本身上的字段創建的映射的基礎。
在任何屬性Meta是的實例類MetaField將被用於控制元字段(的映射_all,_parent等等)。只需將參數(不帶前導下劃線)命名爲要映射的字段並將任何參數傳遞給MetaField類:
class Post(DocType):
title = Text()
class Meta:
all = MetaField(enabled=False)
parent = MetaField(type='blog')
dynamic = MetaField('strict')
1、索引(Index)
Index是一個類,負責在elasticsearch映射和設置中保存與索引有關的所有元數據。
在定義 index 時最爲有用,因爲它允許同時輕鬆創建多個 index。在遷移中設置彈性搜索對象時,這非常有用:
from elasticsearch_dsl import Index, DocType, Text, analyzer
blogs = Index('blogs')
# define custom settings
blogs.settings(
number_of_shards=1,
number_of_replicas=0
)
# define aliases
blogs.aliases(
old_blogs={}
)
# register a doc_type with the index
blogs.doc_type(Post)
# can also be used as class decorator when defining the DocType
@blogs.doc_type
class Post(DocType):
title = Text()
# You can attach custom analyzers to the index
html_strip = analyzer('html_strip',
tokenizer="standard",
filter=["standard", "lowercase", "stop", "snowball"],
char_filter=["html_strip"]
)
blogs.analyzer(html_strip)
# delete the index, ignore if it doesn't exist
blogs.delete(ignore=404)
# create the index in elasticsearch
blogs.create()
您還可以爲您的索引設置模板,並使用該clone方法創建特定的副本:
blogs = Index('blogs', using='production')
blogs.settings(number_of_shards=2)
blogs.doc_type(Post)
# create a copy of the index with different name
company_blogs = blogs.clone('company-blogs')
# create a different copy on different cluster
dev_blogs = blogs.clone('blogs', using='dev')
# and change its settings
dev_blogs.setting(number_of_shards=1)