lucisearch 0.8.0

Embeddable, in-process search engine — the SQLite/DuckDB of Elasticsearch
Documentation
use luci::index::Index;
use luci::mapping::{FieldType, Mapping};
use luci::search::expression::parse_search;
use serde_json::json;

fn main() {
    // 1. Define a schema
    let schema = Mapping::builder()
        .field("title", FieldType::Text)
        .field("body", FieldType::Text)
        .field("tag", FieldType::Keyword)
        .build();

    // 2. Create an index
    let path = std::env::temp_dir().join("luci_quickstart");
    let _ = std::fs::remove_dir_all(&path);

    let mut index = Index::create_with_mapping(&path, schema).expect("failed to create index");

    // 3. Add documents
    let docs = vec![
        json!({"title": "The Rust Programming Language", "body": "Rust is a systems programming language focused on safety and performance.", "tag": "programming"}),
        json!({"title": "Search Engine Design", "body": "Building a search engine requires inverted indexes, scoring algorithms, and query parsing.", "tag": "search"}),
        json!({"title": "Database Internals", "body": "Understanding storage engines, B-trees, and write-ahead logs.", "tag": "databases"}),
        json!({"title": "Information Retrieval", "body": "The science of searching for information in documents, including BM25 scoring and relevance ranking.", "tag": "search"}),
        json!({"title": "Systems Programming in Rust", "body": "Using Rust to build high-performance systems like databases and search engines.", "tag": "programming"}),
    ];

    index.bulk(docs).expect("failed to add documents");

    println!("Indexed 5 documents.\n");

    // 4. Search — term query on keyword field
    println!("=== Term query: tag = \"search\" ===");
    let expr = parse_search(json!({"term": {"tag": "search"}}), 10).unwrap();
    let results = index.search(&expr).expect("search failed");
    println!("Hits: {}", results.total_hits().value);
    for i in 0..results.len() {
        let hit = results.hit(i).unwrap();
        let src = hit.source().unwrap();
        println!("  [{:.3}] {}", hit.score(), src["title"]);
    }

    // 5. Search — full-text match query
    println!("\n=== Match query: body = \"search engine\" ===");
    let expr = parse_search(json!({"match": {"body": "search engine"}}), 10).unwrap();
    let results = index.search(&expr).expect("search failed");
    println!("Hits: {}", results.total_hits().value);
    for i in 0..results.len() {
        let hit = results.hit(i).unwrap();
        let src = hit.source().unwrap();
        println!("  [{:.3}] {}", hit.score(), src["title"]);
    }

    // 6. Search — phrase query
    println!("\n=== Phrase query: body = \"search engine\" ===");
    let expr = parse_search(json!({"match_phrase": {"body": "search engine"}}), 10).unwrap();
    let results = index.search(&expr).expect("search failed");
    println!("Hits: {}", results.total_hits().value);
    for i in 0..results.len() {
        let hit = results.hit(i).unwrap();
        let src = hit.source().unwrap();
        println!("  [{:.3}] {}", hit.score(), src["title"]);
    }

    // 7. Search — bool query: must match "rust" AND tag = "programming"
    println!("\n=== Bool query: match \"rust\" AND tag = \"programming\" ===");
    let expr = parse_search(
        json!({
            "bool": {
                "must": [{"match": {"body": "rust"}}],
                "filter": [{"term": {"tag": "programming"}}]
            }
        }),
        10,
    )
    .unwrap();
    let results = index.search(&expr).expect("search failed");
    println!("Hits: {}", results.total_hits().value);
    for i in 0..results.len() {
        let hit = results.hit(i).unwrap();
        let src = hit.source().unwrap();
        println!("  [{:.3}] {}", hit.score(), src["title"]);
    }

    // 8. Search — match_all
    println!("\n=== Match all ===");
    let expr = parse_search(json!({"match_all": {}}), 10).unwrap();
    let results = index.search(&expr).expect("search failed");
    println!("Hits: {}", results.total_hits().value);

    // Cleanup
    let _ = std::fs::remove_dir_all(&path);
    println!("\nDone.");
}