Python3 操作 elasticsearch

一、安裝

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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章