Elasticsearch 使用誤區(qū)—富文本內(nèi)容寫入前不清洗
0、引言
在很多應(yīng)用場景中,我們會將富文本內(nèi)容(如 HTML 格式的網(wǎng)頁內(nèi)容)存儲到 Elasticsearch 中,以實現(xiàn)全文檢索。
然而,在實際使用過程中,我們可能會遇到一些問題,比如在檢索時,HTML 標(biāo)簽被截斷,或者檢索結(jié)果中會顯示部分 HTML 標(biāo)簽內(nèi)容。這種現(xiàn)象不僅影響了檢索體驗,還可能對頁面展示造成困擾。
本文將詳細(xì)介紹如何解決這種富文本處理中的常見問題,幫助大家在使用 Elasticsearch 時更加高效地處理和展示富文本內(nèi)容。
1、具體問題描述
Elasticsearch 所存儲的數(shù)據(jù)包含但不限于:
- 業(yè)務(wù)數(shù)據(jù)
- 日志數(shù)據(jù)
- 互聯(lián)網(wǎng)采集數(shù)據(jù)
- 其他數(shù)據(jù) 如果遇到互聯(lián)網(wǎng)采集數(shù)據(jù)場景,Elasticsearch 的 content 字段極大可能需要存儲的是富文本內(nèi)容(HTML)。
遇到這種情況,咱們得非常的慎重。
不信,你看!如下 QQ 群群友出現(xiàn)的問題可見一斑。
圖片
圖片
什么問題呢?
在輸入關(guān)鍵詞進行檢索時,可能會出現(xiàn) HTML 標(biāo)簽被截斷的情況,導(dǎo)致返回的結(jié)果中顯示部分 HTML 標(biāo)簽內(nèi)容。
這種情況對用戶展示極為不友好。
那么,我們應(yīng)該如何處理這種問題呢?
2、原因分析
Elasticsearch 本質(zhì)上是一個文本檢索引擎,它會對輸入的文本進行分析、分詞、索引,并對用戶的搜索關(guān)鍵詞進行相應(yīng)匹配。
富文本(HTML)的特殊性在于,它不僅包含可見的文字,還包含大量的標(biāo)記語言(如 <div>、<p>、<span> 等標(biāo)簽),這些標(biāo)簽在檢索過程中可能會被 Elasticsearch 解析和處理,進而影響檢索結(jié)果。
3、解決方案
針對這種情況,我們可以通過以下幾個步驟來解決問題,確保檢索結(jié)果更加干凈、準(zhǔn)確。
3.1. 清洗 HTML 內(nèi)容
在將 HTML 內(nèi)容寫入 Elasticsearch 之前,最好先對其進行清洗。
清洗操作的目的是去除不必要的 HTML 標(biāo)簽,提取其中的有用文本信息。這可以通過編寫自定義算法或使用 HTML 解析庫來實現(xiàn)。
常用的 HTML 解析庫包括 Jsoup、BeautifulSoup 等,其實采集團隊或許已經(jīng)做過處理。
如果公司采集團隊和存儲不是一個團隊,建議深入交流一下字段細(xì)節(jié)內(nèi)容。
3.2. 利用 Elasticsearch 的 HTML Strip 字符過濾器
Elasticsearch 提供了 html_strip 字符過濾器,可以用來自動去除 HTML 標(biāo)簽。
詳細(xì)參見: https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-htmlstrip-charfilter.html
通過該過濾器,我們可以確保在索引過程中,HTML 標(biāo)簽不會被保留下來,只有純文本內(nèi)容會被處理和檢索。
示例如下:
PUT my-index
{
"settings": {
"analysis": {
"char_filter": {
"html_strip": {
"type": "html_strip"
}
},
"analyzer": {
"html_analyzer": {
"type": "custom",
"char_filter": [
"html_strip"
],
"tokenizer": "standard"
}
}
}
},
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "html_analyzer"
}
}
}
解釋:
- char_filter 部分定義了 HTML 過濾器,用于去除 HTML 標(biāo)簽。
- html_analyzer 是自定義的分析器,首先使用 html_strip 過濾器去除 HTML 標(biāo)簽,隨后通過標(biāo)準(zhǔn)分詞器 standard 進行分詞和處理。
使用這個分析器進行索引和搜索時,HTML 標(biāo)簽會自動被過濾掉,確保只有純文本內(nèi)容參與搜索,進而避免了 HTML 標(biāo)簽截斷的問題。
3.3 存儲多版本的內(nèi)容
為了兼顧搜索和展示,推薦在存儲時將富文本內(nèi)容存為兩個字段:
- 原始 HTML 內(nèi)容:用于在前端完整展示。
- 純文本版本:用于索引和檢索。
并且負(fù)責(zé)任告訴大家,真實互聯(lián)網(wǎng)采集數(shù)據(jù)存儲與展示場景,就得這么干!
例如,我們可以在 Elasticsearch 中定義兩種字段:
"mappings": {
"properties": {
"html_content": {
"type": "text",
"index": false // 不參與索引,僅用于展示
},
"cleaned_content": {
"type": "text",
"analyzer": "html_analyzer" // 純文本版本,參與檢索
}
}
}
這樣,html_content 字段保留原始 HTML,用于頁面回顯;
而 cleaned_content 字段則經(jīng)過清洗處理,專用于檢索操作。
3.4. 通過前端處理回顯內(nèi)容
為了避免搜索結(jié)果中顯示部分 HTML 標(biāo)簽的情況,我們可以在前端對回顯的內(nèi)容進行進一步處理:
- 使用前端的 HTML 渲染器,比如 Vue.js 或 React.js 的 v-html 和 dangerouslySetInnerHTML,確保內(nèi)容正確渲染。
- 結(jié)合高亮功能,確保用戶能夠清晰地看到與查詢關(guān)鍵詞匹配的文本部分,而非 HTML 標(biāo)簽。
3.5. 關(guān)鍵詞高亮功能
為了增強用戶體驗,Elasticsearch 提供了高亮顯示功能,可以在返回結(jié)果時高亮顯示與關(guān)鍵詞匹配的部分。
通過以下 DSL 語句,我們可以實現(xiàn)對匹配部分的高亮顯示:
POST my-index/_search
{
"query": {
"match": {
"cleaned_content": "搜索關(guān)鍵詞"
}
},
"highlight": {
"fields": {
"cleaned_content": {}
}
}
}
這樣,在展示時,只會高亮純文本部分,HTML 標(biāo)簽將不會出現(xiàn)在檢索結(jié)果中。
4、完整示例,徹底解決富文本存儲
Elasticsearch 處理富文本內(nèi)容(HTML)的方案如下:
4.1 創(chuàng)建索引并配置 HTML 過濾器
如前方案探討所述,我們首先創(chuàng)建一個新的索引,使用 Elasticsearch 的 html_strip 過濾器來去除 HTML 標(biāo)簽,并配置兩個字段:一個存儲清洗后的純文本內(nèi)容用于檢索,另一個存儲原始 HTML 內(nèi)容用于展示。
PUT html_content_index
{
"settings": {
"analysis": {
"char_filter": {
"html_strip": {
"type": "html_strip"
}
},
"analyzer": {
"html_analyzer": {
"type": "custom",
"char_filter": [
"html_strip"
],
"tokenizer": "standard"
}
}
}
},
"mappings": {
"properties": {
"html_content": {
"type": "text",
"index": false // 原始 HTML 內(nèi)容不參與索引,僅用于展示
},
"cleaned_content": {
"type": "text",
"analyzer": "html_analyzer" // 清洗后用于檢索的純文本
}
}
}
}
4.2 向索引中寫入數(shù)據(jù)
我們將兩種不同的內(nèi)容分別寫入到 html_content 和 cleaned_content 字段。
html_content 保存的是原始 HTML,而 cleaned_content 保存的是清洗后的純文本。
POST /html_content_index/_doc
{
"html_content": "<p>這是一段關(guān)于<strong>Elasticsearch</strong>的文章。</p>",
"cleaned_content": "這是一段關(guān)于 Elasticsearch 的文章。"
}
在這個例子中,html_content 保留了 HTML 標(biāo)簽,而 cleaned_content 則去除了 HTML 標(biāo)簽,只保留了純文本內(nèi)容。
4.3 進行搜索并返回高亮結(jié)果
接下來,我們對 cleaned_content 字段進行全文檢索,同時啟用高亮功能,確保關(guān)鍵詞匹配部分能夠在前端得到突出顯示。
GET /html_content_index/_search
{
"query": {
"match": {
"cleaned_content": "Elasticsearch"
}
},
"highlight": {
"fields": {
"cleaned_content": {}
}
}
}
返回結(jié)果示例如下:
圖片
{
"took": 34,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.2876821,
"hits": [
{
"_index": "html_content_index",
"_id": "INbUKZIBSx0oX-FAHL8S",
"_score": 0.2876821,
"_source": {
"html_content": "<p>這是一段關(guān)于<strong>Elasticsearch</strong>的文章。</p>",
"cleaned_content": "這是一段關(guān)于 Elasticsearch 的文章。"
},
"highlight": {
"cleaned_content": [
"這是一段關(guān)于 <em>Elasticsearch</em> 的文章。"
]
}
}
]
}
}
4.4 前端展示:結(jié)合原始 HTML 和高亮結(jié)果
在前端,我們可以使用 html_content 字段來展示完整的 HTML 內(nèi)容,而使用 highlight 字段來突出顯示用戶搜索的關(guān)鍵詞。這樣,用戶既可以看到完整的富文本格式的內(nèi)容,又能清楚地識別出匹配的關(guān)鍵詞。
偽代碼:
<!-- 顯示原始 HTML 內(nèi)容 -->
<div v-html="html_content"></div>
<!-- 高亮顯示匹配的關(guān)鍵詞 -->
<div v-html="highlighted_cleaned_content"></div>
4.5 預(yù)處理 HTML 清洗
除了在 Elasticsearch 中使用 html_strip 過濾器,我們還可以在存入 Elasticsearch 之前預(yù)先清洗 HTML 內(nèi)容。
以下是使用 Java 的 Jsoup 庫來清洗 HTML 的示例:
import org.jsoup.Jsoup;
public class HTMLCleaner {
public static String cleanHTML(String html) {
return Jsoup.parse(html).text(); // 將 HTML 內(nèi)容轉(zhuǎn)換為純文本
}
}
在保存數(shù)據(jù)到 Elasticsearch 之前,先調(diào)用 cleanHTML 方法去除不必要的 HTML 標(biāo)簽,然后將純文本存入 cleaned_content 字段。
5、小結(jié)
在處理富文本內(nèi)容時,合理的清洗和過濾是避免 HTML 標(biāo)簽影響檢索結(jié)果的關(guān)鍵。
處理 HTML 內(nèi)容的關(guān)鍵在于:清洗 + 分離存儲 + 高亮展示。
通過預(yù)處理富文本內(nèi)容,使用 html_strip 過濾器,以及前端渲染和高亮展示,可以有效地避免 HTML 標(biāo)簽截斷問題,從而確保用戶獲得干凈、準(zhǔn)確的搜索體驗。
富文本不做處理直接寫入是 Elasticsearch 使用中的常見誤區(qū)之一,希望本文提供的解決方案能夠幫助大家在日常工作中更好地應(yīng)對類似問題。