來自釘釘群的問題—Elasticsearch 如何實(shí)現(xiàn)文件名自定義排序?
1、背景
在數(shù)字時(shí)代,圖像數(shù)據(jù)的管理已成為數(shù)據(jù)架構(gòu)的一部分。然而,隨之而來的挑戰(zhàn)是如何有效地索引和檢索這些圖像文件。
這不僅涉及存儲,更重要的是如何根據(jù)特定的屬性(如文件名中的數(shù)字)進(jìn)行排序,以便用戶可以按照預(yù)期的順序查看圖像。
如下問題來自Elastic 釘釘技術(shù)交流群:
圖片
2、解決方案探討
在Elasticsearch中,我們經(jīng)常面對需要對數(shù)據(jù)進(jìn)行排序的需求。單就排序,咱們之前有過幾篇文章分析不同業(yè)務(wù)場景的排序?qū)崿F(xiàn)。
僅就上圖中的文件名進(jìn)行排序,會怎么樣呢?我們構(gòu)造一下數(shù)據(jù),執(zhí)行一下看。
用默認(rèn)動態(tài)Mapping 結(jié)構(gòu),批量寫入數(shù)據(jù)。
POST /my_photos/_bulk
{ "index" : { "_id" : "1" } }
{ "photo_id" : "photo1.jpg", "upload_date" : "2024-02-01T10:00:00" }
{ "index" : { "_id" : "2" } }
{ "photo_id" : "photo2.jpg", "upload_date" : "2024-02-01T10:05:00" }
{ "index" : { "_id" : "3" } }
{ "photo_id" : "photo12.jpg", "upload_date" : "2024-02-01T10:10:00" }
{ "index" : { "_id" : "4" } }
{ "photo_id" : "photo111.jpg", "upload_date" : "2024-02-01T10:15:00" }
### 執(zhí)行檢索
GET /my_photos/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"photo_id.keyword": {
"order": "asc"
}
}
]
}
召回結(jié)果,同圖中后半部分結(jié)果一致。
圖片
結(jié)果并沒有達(dá)到預(yù)期。
而可行的解決方案,還得從文件名入手才可以。圖像文件名包含數(shù)字,需要根據(jù)這些數(shù)字進(jìn)行排序,這才是根本!
3、解決方案實(shí)現(xiàn)
我們采用兩種不同的解決方案來嘗試解決這個(gè)問題。
第一種:基于腳本排序。
第二種:復(fù)雜問題簡單化,預(yù)處理管道拆分出數(shù)值字段,基于數(shù)值排序。
3.1 方案1:腳本排序?qū)崿F(xiàn)
使用 _script 進(jìn)行排序是一種靈活的方法,它允許我們編寫自定義腳本來解析文件名并提取排序依據(jù)的數(shù)字。
GET /my_photos/_search
{
"query": {
"match_all": {}
},
"sort": {
"_script": {
"type": "number",
"script": {
"lang": "painless",
"source": """
String photoId = doc['photo_id.keyword'].value;
if (photoId == null) return 0;
Matcher m = /[0-9]+/.matcher(photoId);
if (m.find()) {
return Integer.parseInt(m.group(0));
} else {
return 0;
}
"""
},
"order": "asc"
}
}
}
執(zhí)行結(jié)果已經(jīng)有序:
圖片
上述腳本基于正則表達(dá)式從photo_id字段中查找并提取出數(shù)字,如果找到就返回這個(gè)數(shù)字,如果找不到就返回0。
這樣的操作對于根據(jù)數(shù)字對文檔進(jìn)行排序非常有用。
雖然這種方法非常強(qiáng)大,但它可能會因?yàn)槟_本的執(zhí)行而影響查詢性能,數(shù)據(jù)量巨大的時(shí)候,咱們要慎用!
3.2 方案2:預(yù)處理解決方案實(shí)現(xiàn)
除了上面的方案,另一種方法是在索引數(shù)據(jù)時(shí)使用Ingest管道預(yù)處理圖像文件名。
這樣可以在數(shù)據(jù)索引時(shí)就提取出文件名中的數(shù)字并存儲在一個(gè)專門的字段中。
這種方法的好處是可以顯著提高排序的效率,因?yàn)閿?shù)字已經(jīng)被預(yù)處理并作為數(shù)值類型存儲,使得排序操作更加快速。
就是開頭咱們提到的復(fù)雜問題簡單化。
創(chuàng)建預(yù)處理管道,基于 grok 提取數(shù)值字段
PUT _ingest/pipeline/extract_photo_number
{
"description": "Extracts numbers from photo_id and stores it in photo_number",
"processors": [
{
"grok": {
"field": "photo_id",
"patterns": ["%{NUMBER:photo_number:int}"]
}
}
]
}
DELETE my_photos_20240201
### 創(chuàng)建索引的時(shí)候,記得指定上面創(chuàng)建好的預(yù)處理管道。
### 新增的字段photo_number,和上面的預(yù)處理管道獲取的字段一一對應(yīng)。
PUT my_photos_20240201
{
"settings": {
"default_pipeline":"extract_photo_number"
},
"mappings": {
"properties": {
"photo_id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"photo_number": {
"type": "long"
},
"upload_date": {
"type": "date"
}
}
}
}
### 批量寫入數(shù)據(jù)
POST /my_photos_20240201/_bulk
{ "index" : { "_id" : "1" } }
{ "photo_id" : "photo1.jpg", "upload_date" : "2024-02-01T10:00:00" }
{ "index" : { "_id" : "2" } }
{ "photo_id" : "photo2.jpg", "upload_date" : "2024-02-01T10:05:00" }
{ "index" : { "_id" : "3" } }
{ "photo_id" : "photo12.jpg", "upload_date" : "2024-02-01T10:10:00" }
{ "index" : { "_id" : "4" } }
{ "photo_id" : "photo111.jpg", "upload_date" : "2024-02-01T10:15:00" }
### 執(zhí)行檢索和排序
POST my_photos_20240201/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"photo_number": {
"order": "asc"
}
}
]
}
官方文檔參考:
https://www.elastic.co/guide/en/elasticsearch/reference/current/grok-processor.html
執(zhí)行結(jié)果如下:
圖片
與腳本排序?qū)Ρ瓤梢钥闯觯?/p>
- 預(yù)處理方案數(shù)據(jù)的處理邏輯前移,發(fā)生在數(shù)據(jù)索引的階段,而非查詢階段;
- 查詢時(shí)可以直接依據(jù)數(shù)值類型的 photo_number字段進(jìn)行快速排序,無需在查詢時(shí)動態(tài)解析文本字段,從而提高了查詢性能,并減少了對資源的消耗。
- 還提升了數(shù)據(jù)結(jié)構(gòu)的清晰度和索引的整體效率。
4、小結(jié)
本文探討了在Elasticsearch中對包含數(shù)字的圖像文件名進(jìn)行排序的挑戰(zhàn)及其解決方案。
在選擇哪種方案時(shí),我們需要考慮實(shí)際需求和系統(tǒng)資源。
如果對性能有較高要求,預(yù)處理方案更為合適。但如果需求復(fù)雜多變,可能需要腳本排序的靈活性。
我更想跟大家探討的是:未來的數(shù)據(jù)建模應(yīng)考慮到數(shù)據(jù)的索引和查詢模式。
例如,如果我們知道將來需要按照文件名中的數(shù)字排序,那么在設(shè)計(jì)數(shù)據(jù)模型時(shí)就應(yīng)該考慮到這一點(diǎn),以便于實(shí)現(xiàn)高效的查詢。
前置考慮得越充分,后面就越省事!