Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

クイックスタート

このチュートリアルでは、5 つのステップで完全な検索エンジンを構築する方法を説明します。最後には、ドキュメントをインデックスしてキーワード検索ができるようになります。

ステップ 1 — Storage の作成

Storage は Laurus がインデックスデータを保存する場所を決定します。開発やテストには MemoryStorage を使用します:

#![allow(unused)]
fn main() {
use std::sync::Arc;
use laurus::storage::memory::MemoryStorage;
use laurus::Storage;

let storage: Arc<dyn Storage> = Arc::new(
    MemoryStorage::new(Default::default())
);
}

ヒント: 本番環境では FileStorage(オプションで use_mmap によるメモリマップド I/O)の使用を検討してください。詳細はストレージを参照してください。

ステップ 2 — Schema の定義

Schema はドキュメント内のフィールドと、各フィールドのインデックス方法を宣言します:

#![allow(unused)]
fn main() {
use laurus::Schema;
use laurus::lexical::TextOption;

let schema = Schema::builder()
    .add_text_field("title", TextOption::default())
    .add_text_field("body", TextOption::default())
    .add_default_field("body")  // used when no field is specified in a query
    .build();
}

各フィールドには型があります。主な型は以下の通りです:

メソッドフィールド型値の例
add_text_fieldText(全文検索可能)"Hello world"
add_integer_field64 ビット整数42
add_float_field64 ビット浮動小数点数3.14
add_boolean_fieldブール値true / false
add_datetime_fieldUTC 日時2024-01-15T10:30:00Z
add_hnsw_fieldVector(HNSW インデックス)[0.1, 0.2, ...]
add_flat_fieldVector(Flat インデックス)[0.1, 0.2, ...]

全一覧はスキーマとフィールドを参照してください。

ステップ 3 — Engine の構築

Engine は Storage、Schema、ランタイムコンポーネントを統合します:

#![allow(unused)]
fn main() {
use laurus::Engine;

let engine = Engine::builder(storage, schema)
    .build()
    .await?;
}

テキストフィールドのみを使用する場合、デフォルトの StandardAnalyzer が自動的に適用されます。解析のカスタマイズや Vector エンベディングの追加については、アーキテクチャを参照してください。

ステップ 4 — ドキュメントのインデックス

DocumentBuilder でドキュメントを作成し、Engine に追加します:

#![allow(unused)]
fn main() {
use laurus::Document;

// Each document needs a unique external ID (string)
let doc = Document::builder()
    .add_text("title", "Introduction to Rust")
    .add_text("body", "Rust is a systems programming language focused on safety and performance.")
    .build();
engine.add_document("doc-1", doc).await?;

let doc = Document::builder()
    .add_text("title", "Python for Data Science")
    .add_text("body", "Python is widely used in machine learning and data analysis.")
    .build();
engine.add_document("doc-2", doc).await?;

let doc = Document::builder()
    .add_text("title", "Web Development with JavaScript")
    .add_text("body", "JavaScript powers interactive web applications and server-side code with Node.js.")
    .build();
engine.add_document("doc-3", doc).await?;

// Commit to make documents searchable
engine.commit().await?;
}

重要: ドキュメントは commit() が呼ばれるまで検索可能になりません。

ステップ 5 — 検索

SearchRequestBuilder とクエリを使ってインデックスを検索します:

#![allow(unused)]
fn main() {
use laurus::{SearchRequestBuilder, LexicalSearchRequest};
use laurus::lexical::TermQuery;

// Search for "rust" in the "body" field
let request = SearchRequestBuilder::new()
    .lexical_search_request(
        LexicalSearchRequest::new(
            Box::new(TermQuery::new("body", "rust"))
        )
    )
    .limit(10)
    .build();

let results = engine.search(request).await?;

for result in &results {
    println!("ID: {}, Score: {:.4}", result.id, result.score);
    if let Some(doc) = &result.document {
        if let Some(title) = doc.get("title") {
            println!("  Title: {:?}", title);
        }
    }
}
}

完全なサンプル

以下は、コピー・ペーストしてそのまま実行できる完全なプログラムです:

use std::sync::Arc;
use laurus::{
    Document, Engine, LexicalSearchRequest,
    Result, Schema, SearchRequestBuilder,
};
use laurus::lexical::{TextOption, TermQuery};
use laurus::storage::memory::MemoryStorage;

#[tokio::main]
async fn main() -> Result<()> {
    // 1. Storage
    let storage = Arc::new(MemoryStorage::new(Default::default()));

    // 2. Schema
    let schema = Schema::builder()
        .add_text_field("title", TextOption::default())
        .add_text_field("body", TextOption::default())
        .add_default_field("body")
        .build();

    // 3. Engine
    let engine = Engine::builder(storage, schema).build().await?;

    // 4. Index documents
    for (id, title, body) in [
        ("doc-1", "Introduction to Rust", "Rust is a systems programming language focused on safety."),
        ("doc-2", "Python for Data Science", "Python is widely used in machine learning."),
        ("doc-3", "Web Development", "JavaScript powers interactive web applications."),
    ] {
        let doc = Document::builder()
            .add_text("title", title)
            .add_text("body", body)
            .build();
        engine.add_document(id, doc).await?;
    }
    engine.commit().await?;

    // 5. Search
    let request = SearchRequestBuilder::new()
        .lexical_search_request(
            LexicalSearchRequest::new(
                Box::new(TermQuery::new("body", "rust"))
            )
        )
        .limit(10)
        .build();

    let results = engine.search(request).await?;
    for r in &results {
        println!("{}: score={:.4}", r.id, r.score);
    }

    Ok(())
}

次のステップ