Elasticsearch_dsl(python)的搜索|查詢|聚合操作實例

百度云搜索,搜各種資料:http://www.81ad.cn

1、基礎(chǔ)操作

# 相關(guān)導(dǎo)入
import time
from elasticsearch import Elasticsearch
from elasticsearch_dsl import Search
# 創(chuàng)建相關(guān)實例
es = Elasticsearch()
# using參數(shù)是指定Elasticsearch實例對象,index指定索引,可以縮小范圍,index接受一個列表作為多個索引,且也可以用正則表示符合某種規(guī)則的索引都可以被索引,如index=["bank", "banner", "country"]又如index=["b*"]后者可以同時索引所有以b開頭的索引,search中同樣可以指定具體doc-type
s = Search(using=es, index="time_appid_placementid_country")
# 添加索引,其中index和doc_type自己指定一個值即可,id可以指定,如果沒有指定elasticsearch會隨機分配,body即為索引的內(nèi)容
dict_1 = {"any": "test", "timepstamp": "ddd"}
es.index(index="bank", doc_type="account", id="qwe", body=dict_1)
# 通過id獲取特定文檔,各參數(shù)的意義同上
res = es.get(index="bank", doc_type="account", id=1)
# 根據(jù)字段查詢,可以多個查詢條件疊加,hightlight可以指定高亮,但是我的沒有出現(xiàn)高亮,對數(shù)據(jù)處理沒啥用不去深究
# res_2 = s.query("match", gender="F").query("match", age="32").highlight("age").execute()
# 用Q()對象查詢多個對象,在多個字段中,fields是一個列表,可以存放多個field,query為所要查詢的值,如果要查詢多個值可以用空格隔開(似乎查詢的時候Q對象只接受同種類型的數(shù)據(jù),如果文本和數(shù)字混雜在一塊就會報錯,建立查詢語句出錯,有待考察,如query="Amber 11"就會失敗,fields也是一樣,另外query可以接受單個數(shù)字的查詢,如果是多個同樣會報相同的錯誤)

# Q()第一個參數(shù)是查詢方法,具體用法及其他方法可以參考elasticsearch的官方文檔

q = Q("multi_match", query="Amber Hattie", fields=["firstname"])
res_3 = s.query(q).execute()
# 搜索,q是指定搜索內(nèi)容,可以看到空格對q查詢結(jié)果沒有影響,size指定個數(shù),from_指定起始位置,q用空格隔開可以多個查詢也可以限定返回結(jié)果的字段,filter_path可以指定需要顯示的數(shù)據(jù),如本例中顯示在最后的結(jié)果中的只有_id和_type

res_3 = es.search(index="bank", q="Holmes", size=1, from_=1)
res_4 = es.search(index="bank", q=" 39225    5686 ", size=1000, filter_path=['hits.hits._id', 'hits.hits._type'])
res_4 = es.search(index="bank", q=" 39225    5686 ", size=1000)
# 直接執(zhí)行Search()會默認搜索所有數(shù)據(jù)
# res_5 = s.execute()
# 排除
s.exclude()
# 刪除索引
es.indices.delete(index="test-index", ignore=[400, 404])
# 查詢,match指定操作方法,country="all",指定查詢值,country為要查詢的值所在的field,
s = s.query("match", country="all")
# execute()為執(zhí)行以上操作
res = s.execute()
# to_dict可以把結(jié)果轉(zhuǎn)化成字典
print(res.to_dict())

# 下面的過濾聚合等操作也是同樣,不再贅述
# 過濾,在此為范圍過濾,range是方法,timestamp是所要查詢的field的名字,gte意為大于等于,lt意為小于,根據(jù)需要設(shè)定即可(似乎過濾只能接受數(shù)字形式的內(nèi)容,如果是文本就會返回空)
# 關(guān)于term和match的區(qū)別,term是精確匹配,match會模糊化,會進行分詞,返回匹配度分數(shù),(term查詢字符串之接受小寫字母,如果有大寫會返回空即沒有命中,match則是不區(qū)分大小寫都可以進行查詢,返回結(jié)果也一樣)

實例1:范圍查詢
s = s.filter("range", timestamp={"gte": 0, "lt": time.time()}).query("match", country="in")
實例2:普通過濾
res_3 = s.filter("terms", balance_num=["39225", "5686"]).execute()
# 聚合,聚合可以放在查詢,過濾等操作的后面疊加,需要加aggs,bucket即為分組,其中第一個參數(shù)是分組的名字,自己指定即可,第二個參數(shù)是方法,第三個是指定的field,metric也是同樣,metric的方法有sum、avg、max、min等等,但是需要指出的是有兩個方法可以一次性返回這些值,stats和extended_stats,后者還可以返回方差等值,很方便,此過程可能會出現(xiàn)一些錯誤,具體見本文最后相關(guān)bug

# 實例1
s.aggs.bucket("per_country", "terms", field="timestamp").metric("sum_click", "stats", field="click").metric("sum_request", "stats", field="request")

# 實例2
s.aggs.bucket("per_age", "terms", field="click.keyword").metric("sum_click", "stats", field="click")

# 實例3
s.aggs.metric("sum_age", "extended_stats", field="impression")

# 實例4
s.aggs.bucket("per_age", "terms", field="country.keyword")
# 最后依然是要execute,此處注意s.aggs......的操作不能用變量接收(如res=s.aggs......的操作就是錯誤的),聚合的結(jié)果會在res中顯示

# 實例5
a = A("range", field="account_number", ranges=[{"to": 10}, {"from": 11, "to": 21}])
# 此聚合是根據(jù)區(qū)間進行聚合
res = s.execute()

2、高級設(shè)置

1.設(shè)置分片數(shù)量

s = Index("your_index", using=your_elasticsearch)
s.settings(number_of_shards=20)
s.save()
注:查到較早的資料顯示,官方建議每個node分配的shards最好不要超過3個,shards根據(jù)node來進行分配,過多的shards會帶來I/O壓力,
同樣不利于查詢速度,但是目前的elasticsearch如果沒有指定shards數(shù)量則會默認分配5個。
2.設(shè)置mapping,數(shù)據(jù)類型的映射

需要指出,elasticsearch中一旦建立好索引之后,mapping就不可以再更改,如果想更改mapping,比如原來的數(shù)據(jù)是str或text類型,
現(xiàn)在想改成int或者ip類型,此時只有一個辦法,就是刪除原來的索引,重新新建一個索引。
官方解釋如下:類似一個數(shù)據(jù)庫,顯然你不能更改一個已經(jīng)建好的數(shù)據(jù)表的字段類型,沒毛病。
3.es索引的方式

可能有時候你會發(fā)現(xiàn)一個問題,用term或terms進行查詢文本信息的時候竟然沒有返回任何數(shù)據(jù),例如:需要查詢elasticsearch-dsl,Elasticseach,
elasticsearch client這類文本信息,結(jié)果都是查不到的,原因不在于term方法,而是因為es建立索引的方式,
es默認建立索引的時候會是analyzed,這個設(shè)置會對寫入es的數(shù)據(jù)進行分詞并全部轉(zhuǎn)換成小寫,這樣做是為了方便進行全文搜索,
如elasticsearch-dsl存儲的時候是elasticsearch和dsl分開的,但有時候我們需要進行精確查詢,這個守候需要設(shè)置索引為not_analyzed,
此設(shè)置下不會進行分詞處理,但顯然不利于全文搜索,
查到有人的解決方法是,設(shè)置兩個存儲相同內(nèi)容的field,一個設(shè)置analyzed,另一個設(shè)置not_analyzed,
一個用來精確查詢,另一個用來全文搜索。但是也可以為每個字段設(shè)置兩個index,一個是text類型,一個keyword類型。
另外如果對數(shù)據(jù)內(nèi)容沒有太大要求的時候,可以再寫入es之前對數(shù)據(jù)進行處理,過濾掉“-“、空格等非單詞字符,
如Elasticsearch-dsl client直接存儲成Elasticsearchdslclient

在elasticsearch-dsl中沒有找到設(shè)置not_analyzed的接口,原因是在elasticsearch-dsl只需要設(shè)置類型為keyword即可,
在此還有一個高級功能,一個field也可以設(shè)置多個類型,代碼如下:

    es = Elasticsearch(hosts=es_hosts)
    m = Mapping(your_type)
    m.field("country", "keyword")
    m.field("province", "keyword")
    m.field("phone_brand", "keyword", field={"use_for_search": Text() **1**})
    m.field("phone_model", "keyword")
    m.save(es_index, using=es)

    i = Index(_es_index, using=es)
    print(i.get_mapping())

**1**處的類型需要從elasticsearch_dsl導(dǎo)入,其他類型可以自行查找。
多個類型怎么用呢?指定field的同時指定類型即可,
例如我想對phone_brand進行全文搜索,這個時候就不希望精確查詢了,phone_brand換成phone_brand.use_for_search即可,前面的例子中keyword就是這個原因。

3、性能優(yōu)化

1.過濾比查詢要快,因為過濾不需要計算相關(guān)性分數(shù),相關(guān)性分數(shù)的計算也會浪費很多時間;
2.不得不提一下range過濾,這個方法能不用就不要用,相當消耗時間,我去除這個方法之后時間快了一半不止;
3.對數(shù)據(jù)進行分類,分成幾個大類,分別建立其索引,這樣速度也會快很多;
4.精簡數(shù)據(jù),不需要的數(shù)據(jù)直接舍棄

4、BUG

bug1
elasticsearch.exceptions.RequestError: TransportError(400, 'search_phase_execution_exception', 'Fielddata is disabled on text fields by default. Set fielddata=true on [country] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead.')

解決:關(guān)于fielddata可以自行搜索,默認為text類型的數(shù)據(jù)不能進行聚合操作,會報出以上錯誤,有三種解決方法,方法一:如上面實例2中,在相關(guān)field中用keyword即可,簡單解決,方法二:如錯誤日志中所示,設(shè)置fielddata=true,方法三:官方文檔中所示,附上連接https://www.elastic.co/guide/en/elasticsearch/reference/current/fielddata.html#_fielddata_is_disabled_on_literal_text_literal_fields_by_default

bug2
elasticsearch.exceptions.RequestError: TransportError(400, 'search_phase_execution_exception', 'Expected numeric type on field [country.keyword], but got [keyword]')

當執(zhí)行聚合的運算操作,如求和,最大最小,平均值等的時候,你會發(fā)現(xiàn)你用了keyword仍然會報錯,錯誤日志如上,可以看到意思是,希望獲得數(shù)值,但顯然傳遞給elasticsearch的不是數(shù)值類型,原因是向elasticsearch中寫入數(shù)據(jù)的時候,錯誤對應(yīng)的field的值類型不是可以進行數(shù)學(xué)運算的類型,解決方法就是,向elasticsearch中添加索引的時候,相關(guān)field值的類型用可以參加數(shù)學(xué)運算的類型,如int,float等類型,也可以后續(xù)更改字段類型,但是不建議這樣做,elasticsearch理論上認為字段的mapping一旦被設(shè)定就不可更改,更好和更簡單的方法就是在寫入elasticsearch的時候就指定好類型,具體可以通過以下方法查看字段類型:

from elasticsearch.client import IndicesClient(elastic search_dsl 中同樣有以下兩個方法,from elastivsearch_dsl import Index)
i = IndicesClient(es)
res_1 = i.get_field_mapping(index="time_appid_placementid_country", doc_type="my-type", fields="country")
# 通過i.get_field_mapping可以查看某個具體字段的類型,text類型的不能參加聚合,但是可以通過以上的keyword解決這個問題

# 亦或通過get_mapping(index=, doc_type=)查看所有字段的類型
bug3
elasticsearch.exceptions.RequestError: TransportError(400, 'search_phase_execution_exception', 'failed to create query

按照以上過濾和查詢兩個似乎的解釋可以解決,
似乎的原因就是es索引的方式,大小寫轉(zhuǎn)換等(原因已經(jīng)找到)
轉(zhuǎn)自:https://blog.csdn.net/JunFeng666/article/details/78251788

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容