搜尋
首頁後端開發GolangBleve:如何建立一個快速的搜尋引擎?

Bleve: How to build a rocket-fast search engine?

Go/Golang 是我最喜歡的語言之一;我喜歡極簡主義和它的干淨,它的語法非常緊湊,並且努力讓事情變得簡單(我是KISS 原則的忠實粉絲)。

我最近面臨的主要挑戰之一是建立一個快速的搜尋引擎。當然還有 SOLR 和 ElasticSearch 等選項;兩者都工作得很好並且具有高度可擴展性,但是,我需要簡化搜索,使其更快、更容易部署,幾乎沒有依賴項。

我需要進行足夠的最佳化才能快速返回結果,以便可以對它們進行重新排名。雖然 C/Rust 可能很適合這個,但我更重視開發速度和生產力。我認為 Golang 是兩全其美的。

在本文中,我將透過一個簡單的範例來介紹如何使用 Go 建立自己的搜尋引擎,你會驚訝地發現:它並沒有您想像的那麼複雜。

Golang:Python 的增強版

我不知道為什麼,但 Golang 在某種程度上感覺像 Python。文法很容易掌握,也許是因為到處都沒有分號和括號,或是沒有醜陋的 try-catch 語句。也許這是很棒的 Go 格式化程序,我不知道。

無論如何,由於 Golang 產生一個獨立的二進位文件,因此部署到任何生產伺服器都非常容易。您只需“建置”並交換可執行檔即可。

這正是我所需要的。

你有布萊夫嗎?

不,這不是打字錯誤嗎? Bleve 是一個功能強大、易於使用且非常靈活的 Golang 搜尋庫。

身為 Go 開發人員,您通常會像躲避瘟疫一樣避免使用 3rd 方包;有時使用第三方軟體包是有意義的。 Bleve 速度快、設計精良,並提供足夠的價值來證明使用它的合理性。

此外,這就是我「Bleve」的原因:

  • 獨立,Golang 的一大優點是單一二進位文件,所以我想保持這種感覺,不需要外部資料庫或服務來儲存和查詢文件。 Bleve 與 Sqlite 類似,在記憶體中運行並寫入磁碟。

  • 易於擴充。由於它只是 Go 程式碼,因此我可以根據需要輕鬆調整庫或在我的程式碼庫中擴展它。

  • 快速:1000 萬個文件的搜尋結果只需 50-100 毫秒,其中包括過濾。

  • 分面:如果沒有一定程度的分面支持,您就無法建立現代搜尋引擎。 Bleve 完全支援常見的構面類型:例如範圍或簡單類別計數。

  • 快速索引:Bleve 比 SOLR 稍慢。 SOLR 可以在 30 分鐘內索引 1000 萬個文檔,而 Bleve 需要一個多小時,但一個小時左右仍然相當不錯,速度足以滿足我的需求。

  • 良好的品質結果。 Bleve 在關鍵字結果方面表現出色,而有些語意類型搜尋在 Bleve 中也非常有效。

  • 快速啟動:如果您需要重新啟動或部署更新,只需幾毫秒即可重新啟動 Bleve。在記憶體中重建索引時不會阻塞讀取,因此重新啟動後幾毫秒內即可搜尋索引,不會出現中斷。

設定索引?

在 Bleve 中,「索引」可以被視為資料庫表或集合(NoSQL)。與常規 SQL 表不同,您不需要指定每一列,基本上可以在大多數用例中使用預設架構。

要初始化 Bleve 索引,您可以執行下列操作:

mappings := bleve.NewIndexMapping()
index, err = bleve.NewUsing("/some/path/index.bleve", mappings, "scorch", "scorch", nil)
if err != nil {
    log.Fatal(err)
}

Bleve 支援幾種不同的索引類型,但經過多次擺弄後我發現「scorch」索引類型可以為您提供最佳效能。如果您不傳入最後 3 個參數,Bleve 將預設為 BoltDB。

新增文件

為 Bleve 新增文件非常簡單。您基本上可以在索引中儲存任何類型的結構:

type Book struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Genre string `json:"genre"`
}

b := Book{
    ID:    1234,
    Name:  "Some creative title",
    Genre: "Young Adult",
}
idStr := fmt.Sprintf("%d", b.ID)
// index(string, interface{})
index.index(idStr, b)

如果您要索引大量文檔,最好使用批次:

// You would also want to check if the batch exists already
// - so that you don't recreate it.
batch := index.NewBatch()
if batch.Size() >= 1000 {
    err := index.Batch(batch)
    if err != nil {
        // failed, try again or log etc...
    }
    batch = index.NewBatch()
} else {
    batch.index(idStr, b)
}

正如您所注意到的,使用「index.NewBatch」可以簡化諸如批次記錄並將其寫入索引之類的複雜任務,該任務會建立一個容器來暫時索引文件。

此後,您只需在循環時檢查大小,並在達到批量大小限制後刷新索引。

搜尋索引

Bleve 公開了多個不同的搜尋查詢解析器,您可以根據您的搜尋需求進行選擇。為了使本文簡短而有趣,我將使用標準查詢字串解析器。

searchParser := bleve.NewQueryStringQuery("chicken reciepe books")
maxPerPage := 50
ofsset := 0
searchRequest := bleve.NewSearchRequestOptions(searchParser, maxPerPage, offset, false)
// By default bleve returns just the ID, here we specify
// - all the other fields we would like to return.
searchRequest.Fields = []string{"id", "name", "genre"}
searchResults, err := index.Search(searchResult)

只需這幾行,您現在就擁有了一個強大的搜尋引擎,可以以較低的記憶體和資源佔用提供良好的結果。

這是搜尋結果的 JSON 表示,「hits」將包含符合的文件:

{
    "status": {
        "total": 5,
        "failed": 0,
        "successful": 5
    },
    "request": {},
    "hits": [],
    "total_hits": 19749,
    "max_score": 2.221337297308545,
    "took": 99039137,
    "facets": null
}

刻面

如前所述,Bleve 提供開箱即用的全面分面支持,而無需在您的架構中進行設定。以《流派》一書為例,您可以執行以下操作:

//... build searchRequest -- see previous section.
// Add facets
genreFacet := bleve.NewFacetRequest("genre", 50)
searchRequest.AddFacet("genre", genreFacet)
searchResults, err := index.Search(searchResult)

我們只用 2 行程式碼擴充了之前的 searchRequest。 「NewFacetRequest」接受 2 個參數:

  • 欄位:索引中要分面的欄位(字串)。

  • 大小:要計數的條目數(整數)。因此,在我們的範例中,它只會計算前 50 個流派。

以上內容現在將填入我們搜尋結果中的「面向」。

接下來,我們只需將我們的方面添加到搜尋請求中即可。它接受“方面名稱”和實際方面。 「Facet name」是您將在我們的搜尋結果中找到此結果集的「鍵」。

進階查詢和過濾

雖然“QueryStringQuery”解析器可以為您帶來相當多的幫助;有時您需要更複雜的查詢,例如“一個必須匹配”,您希望將搜尋字詞與多個欄位進行匹配,並傳回結果,只要至少有一個字段匹配即可。

您可以使用「Disjunction」和「Conjunction」查詢類型來完成此操作。

  • 聯合查詢:基本上,它允許您將多個查詢連結在一起形成一個巨大的查詢。所有子查詢必須至少符合一個文件。

  • 析取查詢:這將允許您執行上面提到的「一個必須匹配」查詢。您可以傳入 x 數量的查詢,並設定必須符合至少一個文件的子查詢數量。

析取查詢範例:

mappings := bleve.NewIndexMapping()
index, err = bleve.NewUsing("/some/path/index.bleve", mappings, "scorch", "scorch", nil)
if err != nil {
    log.Fatal(err)
}

與我們之前使用「searchParser」的方式類似,我們現在可以將「析取查詢」傳遞到「searchRequest」的建構子中。

雖然不完全相同,但類似以下 SQL:

type Book struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Genre string `json:"genre"`
}

b := Book{
    ID:    1234,
    Name:  "Some creative title",
    Genre: "Young Adult",
}
idStr := fmt.Sprintf("%d", b.ID)
// index(string, interface{})
index.index(idStr, b)

您也可以透過設定「query.Fuzziness=[0 or 1 or 2]」來調整搜尋的模糊程度

連線查詢範例:

// You would also want to check if the batch exists already
// - so that you don't recreate it.
batch := index.NewBatch()
if batch.Size() >= 1000 {
    err := index.Batch(batch)
    if err != nil {
        // failed, try again or log etc...
    }
    batch = index.NewBatch()
} else {
    batch.index(idStr, b)
}

您會注意到語法非常相似,您基本上可以互換使用“Conjunction”和“Disjunction”查詢。

這將類似於 SQL 中的以下內容:

searchParser := bleve.NewQueryStringQuery("chicken reciepe books")
maxPerPage := 50
ofsset := 0
searchRequest := bleve.NewSearchRequestOptions(searchParser, maxPerPage, offset, false)
// By default bleve returns just the ID, here we specify
// - all the other fields we would like to return.
searchRequest.Fields = []string{"id", "name", "genre"}
searchResults, err := index.Search(searchResult)

總結一下;當您希望所有子查詢符合至少一個文件時,請使用「聯合查詢」;當您希望符合至少一個子查詢但不一定符合所有子查詢時,請使用「析取查詢」。

分片

如果您遇到速度問題,Bleve 也可以將資料分佈在多個索引分片上,然後在一個請求中查詢這些分片,例如:

{
    "status": {
        "total": 5,
        "failed": 0,
        "successful": 5
    },
    "request": {},
    "hits": [],
    "total_hits": 19749,
    "max_score": 2.221337297308545,
    "took": 99039137,
    "facets": null
}

分片可能會變得相當複雜,但正如您在上面看到的,Bleve 消除了很多痛苦,因為它會自動「合併」所有索引並在它們之間進行搜索,然後在一個結果集中返回結果,就像您搜尋一樣單一索引。

我一直在使用分片來搜尋 100 個分片。整個搜尋過程平均只需 100-200 毫秒即可完成。

您可以如下建立分片:

//... build searchRequest -- see previous section.
// Add facets
genreFacet := bleve.NewFacetRequest("genre", 50)
searchRequest.AddFacet("genre", genreFacet)
searchResults, err := index.Search(searchResult)

只要確保為每個文檔建立唯一的 ID,或採用某種可預測的方式新增和更新文檔,而不會弄亂索引。

執行此操作的簡單方法是將包含分片名稱的前綴儲存在來源資料庫中或從您取得文件的任何位置。這樣,每次您嘗試插入或更新時,您都會尋找“前綴”,它會告訴您在哪個分片上呼叫“.index”。

說到更新,只需呼叫「index.index(idstr, struct)」即可更新現有文件。

結論

僅使用上面的這種基本搜尋技術並將其置於 GIN 或標準 Go HTTP 伺服器後面,您就可以建立非常強大的搜尋 API 並服務數百萬個請求,而無需推出複雜的基礎設施。

但有一點需要注意;但是,Bleve 不支援複製,因為您可以將其包裝在 API 中。只需有一個 cron 作業,從您的來源讀取數據,然後使用 goroutine 將更新「爆炸」到您的所有 Bleve 伺服器。

或者,您可以將寫入磁碟鎖定幾秒鐘,然後將資料「rsync」到從屬索引,儘管我不建議這樣做,因為您可能還需要每次重新啟動 go 二進位檔案.

以上是Bleve:如何建立一個快速的搜尋引擎?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Golang和Python:了解差異Golang和Python:了解差異Apr 18, 2025 am 12:21 AM

Golang和Python的主要區別在於並發模型、類型系統、性能和執行速度。 1.Golang使用CSP模型,適用於高並發任務;Python依賴多線程和GIL,適合I/O密集型任務。 2.Golang是靜態類型,Python是動態類型。 3.Golang編譯型語言執行速度快,Python解釋型語言開發速度快。

Golang vs.C:評估速度差Golang vs.C:評估速度差Apr 18, 2025 am 12:20 AM

Golang通常比C 慢,但Golang在並發編程和開發效率上更具優勢:1)Golang的垃圾回收和並發模型使其在高並發場景下表現出色;2)C 通過手動內存管理和硬件優化獲得更高性能,但開發複雜度較高。

Golang:雲計算和DevOps的關鍵語言Golang:雲計算和DevOps的關鍵語言Apr 18, 2025 am 12:18 AM

Golang在雲計算和DevOps中的應用廣泛,其優勢在於簡單性、高效性和並發編程能力。 1)在雲計算中,Golang通過goroutine和channel機制高效處理並發請求。 2)在DevOps中,Golang的快速編譯和跨平台特性使其成為自動化工具的首選。

Golang和C:了解執行效率Golang和C:了解執行效率Apr 18, 2025 am 12:16 AM

Golang和C 在執行效率上的表現各有優勢。 1)Golang通過goroutine和垃圾回收提高效率,但可能引入暫停時間。 2)C 通過手動內存管理和優化實現高性能,但開發者需處理內存洩漏等問題。選擇時需考慮項目需求和團隊技術棧。

Golang vs. Python:並發和多線程Golang vs. Python:並發和多線程Apr 17, 2025 am 12:20 AM

Golang更適合高並發任務,而Python在靈活性上更有優勢。 1.Golang通過goroutine和channel高效處理並發。 2.Python依賴threading和asyncio,受GIL影響,但提供多種並發方式。選擇應基於具體需求。

Golang和C:性能的權衡Golang和C:性能的權衡Apr 17, 2025 am 12:18 AM

Golang和C 在性能上的差異主要體現在內存管理、編譯優化和運行時效率等方面。 1)Golang的垃圾回收機制方便但可能影響性能,2)C 的手動內存管理和編譯器優化在遞歸計算中表現更為高效。

Golang vs. Python:申請和用例Golang vs. Python:申請和用例Apr 17, 2025 am 12:17 AM

selectgolangforhighpperformanceandcorrency,ifealforBackendServicesSandNetwork程序; selectpypypythonforrapiddevelopment,dataScience和machinelearningDuetoitsverserverserverserversator versator anderticality andextility andextentensivelibraries。

Golang vs. Python:主要差異和相似之處Golang vs. Python:主要差異和相似之處Apr 17, 2025 am 12:15 AM

Golang和Python各有优势:Golang适合高性能和并发编程,Python适用于数据科学和Web开发。Golang以其并发模型和高效性能著称,Python则以简洁语法和丰富库生态系统著称。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境