用Rust制作一個(gè)小型搜索引擎
用Rust創(chuàng)建搜索引擎是探索該語(yǔ)言在性能和安全性方面具有優(yōu)勢(shì)的絕佳方式。
這個(gè)項(xiàng)目將索引和搜索概念轉(zhuǎn)移到Rust的生態(tài)系統(tǒng)中,由于Rust獨(dú)特的語(yǔ)法和范式,這是一個(gè)挑戰(zhàn),但也是有益的。
構(gòu)建搜索引擎
步驟1,創(chuàng)建項(xiàng)目
使用如下命令創(chuàng)建一個(gè)Rust新項(xiàng)目:
cargo new shrimp_engine
cd shrimp_engine
code .
步驟2,加入依賴項(xiàng)
需要一些crate來(lái)幫助解析和數(shù)據(jù)處理。例如:
- tantivy:用于索引和搜索文本(類似于Java世界中的Lucene)
- serde和serde_json:用于JSON解析
將這兩個(gè)庫(kù)加入到Cargo.toml文件中:
[dependencies]
tantivy = "0.17"
serde = "1.0"
serde_json = "1.0"
步驟3,定義數(shù)據(jù)結(jié)構(gòu)
定義我們要索引的文檔的結(jié)構(gòu)。定義一個(gè)簡(jiǎn)單的結(jié)構(gòu)體,它表示帶有標(biāo)題和正文的文檔。
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, Debug)]
struct Document {
title: String,
body: String,
}
步驟4,創(chuàng)建索引
使用tantivy,根據(jù)數(shù)據(jù)結(jié)構(gòu)創(chuàng)建索引模式,然后將文檔添加到索引中。
use tantivy::{doc, schema::*, Index};
fn create_index() -> Index {
// 定義Schema
let mut schema_builder = Schema::builder();
schema_builder.add_text_field("title", TEXT | STORED);
schema_builder.add_text_field("body", TEXT);
let schema = schema_builder.build(); // 在目錄中創(chuàng)建索引
let index = Index::create_in_ram(schema.clone()); // 獲取索引寫入器
let mut index_writer = index.writer(50_000_000).unwrap(); // 添加文檔
let title = schema.get_field("title").unwrap();
let body = schema.get_field("body").unwrap();
let doc = doc!(title => "Example Title", body => "This is the body of the document.");
let _ = index_writer.add_document(doc); // 將文檔提交到索引
let _ = index_writer.commit();
index
}
Schema定義索引的結(jié)構(gòu),指定應(yīng)該索引哪些字段(這里是標(biāo)題和正文)以及如何(例如,存儲(chǔ)、文本分析)創(chuàng)建一個(gè)內(nèi)存索引,并將文檔添加到該索引中。添加的每個(gè)文檔都由Document結(jié)構(gòu)體定義,然后對(duì)其進(jìn)行序列化以進(jìn)行索引。將更改提交到索引中,使其可搜索。
步驟5,搜索
實(shí)現(xiàn)一個(gè)搜索索引的函數(shù)。我們需要?jiǎng)?chuàng)建一個(gè)搜索器和查詢解析器。
use tantivy::query::QueryParser;
use tantivy::collector::TopDocs;
fn search_index(index: &Index, query_str: &str) -> tantivy::Result<()> {
let reader = index.reader()?;
let searcher = reader.searcher();
let schema = index.schema();
let title = schema.get_field("title").unwrap();
let body = schema.get_field("body").unwrap();
let query_parser = QueryParser::for_index(index, vec![title, body]);
let query = query_parser.parse_query(query_str)?;
let top_docs = searcher.search(&query, &TopDocs::with_limit(10))?;
for (_, doc_address) in top_docs {
let retrieved_doc = searcher.doc(doc_address)?;
println!("{:?}", retrieved_doc);
}
Ok(())
}
步驟6,測(cè)試搜索引擎,修改main函數(shù)
fn main() -> Result<(), TantivyError> {
println!("Hello, Shrimp!");
// 創(chuàng)建索引并存儲(chǔ)它
let index = create_index();
// 在創(chuàng)建的索引中搜索
search_index(&index, "Example")?;
Ok(())
}
運(yùn)行結(jié)果如下:
Hello, Shrimp!
Document { field_values: [FieldValue { field: Field(0), value: Str("Example Title") }] }
tantivy crate中的組件用于構(gòu)建搜索引擎的核心功能,從創(chuàng)建索引到查詢索引。
搜索引擎的核心機(jī)制
1,索引讀取器和搜索器:
為了搜索索引,將實(shí)例化一個(gè)索引讀取器,創(chuàng)建一個(gè)能夠?qū)λ饕龍?zhí)行查詢的搜索器。
2,查詢解析和執(zhí)行
查詢解析器解釋查詢的字符串,并根據(jù)已定義的Schema將其轉(zhuǎn)換為查詢對(duì)象。然后,搜索者使用該查詢來(lái)查找相關(guān)文檔并對(duì)其進(jìn)行排序。
3,檢索和顯示結(jié)果
檢索并顯示最匹配的文檔(在一定范圍內(nèi))?;谒阉鞑樵?、提取和審查索引內(nèi)容。
總結(jié)
本示例旨在為你提供構(gòu)建搜索引擎的起點(diǎn)。Rust的所有權(quán)和并發(fā)模型,以及它的類型系統(tǒng),為構(gòu)建更復(fù)雜和高性能的搜索引擎提供了堅(jiān)實(shí)的基礎(chǔ)。
我們可以通過(guò)添加實(shí)時(shí)索引、高級(jí)文本處理和自定義評(píng)分算法等特性來(lái)擴(kuò)展這個(gè)項(xiàng)目。