# Demo — Text Search Library

Complete working example covering all search modes, utilities, and semantic search.

```gcl
@library("std", "7.8.4-dev");
@library("explorer", "7.8.0-dev");
@library("ai", "7.8.9-dev");
@library("text_search", "0.0.0-dev");

@format_indent(4);
@format_line_width(280);
@format_eol_last(true);

// Embedding function for semantic search — retrieves the model loaded with id "gemma_embed"
fn gemma_embed(text: String): Tensor {
    var m = Model::get("gemma_embed")!!;
    return m.embed(text, TensorType::f32, ContextParams { n_ctx: 512, n_batch: 512 });
}

fn main() {
    // =========================================================================
    // 1. Create index with config
    // =========================================================================
    var idx = TextIndex<String> {
        config: TextIndexConfig {
            usePhonetic: true,
            stopWords: StopWordOptions {
                mode: StopWordMode::default,
                language: TextSearchLanguage::en,
            },
            tokenization: TokenizationOptions { stemming: true },
        },
    };

    // =========================================================================
    // 2. Add documents (text → associated value)
    // =========================================================================
    idx.add("The quick brown fox jumps over the lazy dog", "doc1");
    idx.add("A fast brown fox leaps over a sleepy hound", "doc2");
    idx.add("The dog barked loudly at the passing car", "doc3");
    idx.add("Foxes are clever animals that live in forests", "doc4");
    idx.add("Machine learning and artificial intelligence are transforming technology", "doc5");
    idx.add("Deep learning neural networks process natural language", "doc6");
    idx.add("The restaurant serves excellent pasta and fresh salads", "doc7");
    idx.add("Climate change affects global weather patterns significantly", "doc8");
    idx.add("Quantum computing promises exponential speedup for certain problems", "doc9");
    idx.add("The photographer captured stunning landscapes at sunrise", "doc10");

    // =========================================================================
    // 3. Build index (computes IDF, phonetic index, etc.)
    // =========================================================================
    idx.build();

    // Print stats
    var s = idx.stats();
    info("=== Index Stats ===");
    info("Documents: ${s.totalEntries}, Terms: ${s.totalTerms}, Avg length: ${s.avgTokenCount}");

    var k = 5; // max results

    // =========================================================================
    // 4. BM25 — probabilistic relevance ranking (default mode)
    // =========================================================================
    println("");
    info("=== BM25 Search: 'brown fox' ===");
    var bm25_results = idx.search_bm25("brown fox", k);
    print_results(bm25_results);

    // =========================================================================
    // 5. Exact — normalized substring matching
    // =========================================================================
    println("");
    info("=== Exact Search: 'lazy dog' ===");
    var exact_results = idx.search_exact("lazy dog", k);
    print_results(exact_results);

    // =========================================================================
    // 6. Fuzzy — tolerates typos (Levenshtein distance)
    // =========================================================================
    println("");
    info("=== Fuzzy Search: 'languge' (typo for 'language') ===");
    var fuzzy_results = idx.search_fuzzy("languge", k, FuzzyOptions { maxEdits: 2 });
    print_results(fuzzy_results);

    // =========================================================================
    // 7. Phrase — exact phrase with positional verification
    // =========================================================================
    println("");
    info("=== Phrase Search: 'brown fox' (exact phrase) ===");
    var phrase_results = idx.search_phrase("brown fox", k, null);
    print_results(phrase_results);

    // =========================================================================
    // 8. Boolean — AND / OR / NOT operators
    // =========================================================================
    println("");
    info("=== Boolean Search: 'fox AND brown' ===");
    var bool_results = idx.search_boolean("fox AND brown", k);
    print_results(bool_results);

    println("");
    info("=== Boolean Search: 'dog NOT fox' ===");
    var bool_results2 = idx.search_boolean("dog NOT fox", k);
    print_results(bool_results2);

    // =========================================================================
    // 9. Prefix — autocomplete style
    // =========================================================================
    println("");
    info("=== Prefix Search: 'comp' ===");
    var prefix_results = idx.search_prefix("comp", k);
    print_results(prefix_results);

    // =========================================================================
    // 10. Wildcard — pattern matching with * and ?
    // =========================================================================
    println("");
    info("=== Wildcard Search: 'learn*' ===");
    var wildcard_results = idx.search_wildcard("learn*", k);
    print_results(wildcard_results);

    // =========================================================================
    // 11. Proximity — two terms within N positions
    // =========================================================================
    println("");
    info("=== Proximity Search: 'fox' near 'dog' (distance 5) ===");
    var prox_results = idx.search_proximity("fox", "dog", k, ProximityOptions { distance: 5 });
    print_results(prox_results);

    // =========================================================================
    // 12. Phonetic — sound-alike matching (Double Metaphone)
    // =========================================================================
    println("");
    info("=== Phonetic Search: 'focks' (sounds like 'fox') ===");
    var phonetic_results = idx.search_phonetic("focks", k);
    print_results(phonetic_results);

    // =========================================================================
    // 13. Quorum — minimum N of M terms must match
    // =========================================================================
    println("");
    info("=== Quorum Search: 'quick brown lazy fox' (min 2 match) ===");
    var quorum_results = idx.search_quorum("quick brown lazy fox", k, 2);
    print_results(quorum_results);

    // =========================================================================
    // 14. Hybrid — fuses multiple modes (BM25 + exact + fuzzy)
    // =========================================================================
    println("");
    info("=== Hybrid Search: 'fox jumps' ===");
    var hybrid_results = idx.search("fox jumps", k, null);
    print_results(hybrid_results);

    // =========================================================================
    // 15. Hybrid with custom SearchOptions
    // =========================================================================
    println("");
    info("=== Hybrid (BM25 + Phrase + Fuzzy) with custom weights ===");
    var w = Map<SearchMode, float> {};
    w.set(SearchMode::bm25, 0.5);
    w.set(SearchMode::phrase, 0.3);
    w.set(SearchMode::fuzzy, 0.2);

    var modes = Array<SearchMode> {};
    modes.add(SearchMode::bm25);
    modes.add(SearchMode::phrase);
    modes.add(SearchMode::fuzzy);

    var opts = SearchOptions {
        modes: modes,
        weights: w,
        fusionMethod: FusionMethod::rrf,
    };
    var custom_results = idx.search("brown fox jumps", k, opts);
    print_results(custom_results);

    // =========================================================================
    // 16. Utilities: snippet, suggest, did_you_mean
    // =========================================================================
    println("");
    info("=== Snippet for doc matching 'artificial intelligence' ===");
    var snip = idx.snippet("Machine learning and artificial intelligence are transforming technology", "artificial intelligence", SnippetOptions { maxLength: 80 });
    if (snip != null) {
        info("  Snippet: ${snip!!.text}");
        info("  Highlighted: ${snip!!.highlighted}");
    }

    println("");
    info("=== Suggest completions for 'lea' ===");
    var suggestions = idx.suggest("lea", 5);
    for (_, sg in suggestions) {
        info("  ${sg.term} (score: ${sg.score}, df: ${sg.df})");
    }

    println("");
    info("=== Did you mean? 'compueting' ===");
    var dym = idx.did_you_mean("compueting");
    info("  Original: ${dym.originalQuery} -> Corrected: ${dym.correctedQuery}");

    // =========================================================================
    // 17. BM25 Score Explanation
    // =========================================================================
    println("");
    info("=== BM25 Explanation for 'brown fox' on doc1 ===");
    var explanation = idx.explain("brown fox", "The quick brown fox jumps over the lazy dog");
    if (explanation != null) {
        info("  Total score: ${explanation.totalScore}");
        for (_, te in explanation.terms) {
            info("  Term '${te.term}': tf=${te.tf}, idf=${te.idf}, score=${te.score}");
        }
    }

    // =========================================================================
    // 18. Semantic Search — embedding-based similarity (embeddinggemma GGUF)
    // =========================================================================
    println("");
    info("=== Semantic Search (embeddinggemma-300M) ===");
    semantic_search_example();
}

fn semantic_search_example() {
    // Check if the GGUF model file exists
    var model_path = "./embeddinggemma-300M-F32.gguf";
    var f = File::open(model_path);
    if (f == null) {
        info("ERROR: Embedding model not found at '${model_path}'");
        info("Please download it from:");
        info("  https://huggingface.co/unsloth/embeddinggemma-300m-GGUF/resolve/main/embeddinggemma-300M-F32.gguf?download=true");
        info("");
        info("Example:");
        info("  wget -O embeddinggemma-300M-F32.gguf 'https://huggingface.co/unsloth/embeddinggemma-300m-GGUF/resolve/main/embeddinggemma-300M-F32.gguf?download=true'");
        return;
    }

    // Load the embedding model
    var model = Model::load("gemma_embed", model_path, ModelParams {
        n_gpu_layers: -1,
    });
    var model_info = model.info();
    info("Loaded: ${model_info.description} (${model_info.n_params} params, embedding dim: ${model_info.n_embd})");

    // -------------------------------------------------------------------------
    // 18a. Pure semantic search using TextIndex with embed function
    // -------------------------------------------------------------------------
    info("");
    info("--- 18a. Pure Semantic Search (search_semantic) ---");

    // Create a TextIndex with an embed function pointing to our model
    var sem_idx = TextIndex<String> {
        config: TextIndexConfig {
            embed: gemma_embed,
            stopWords: StopWordOptions { mode: StopWordMode::none },
        },
    };

    // Add the same documents — each will be automatically embedded
    sem_idx.add("The quick brown fox jumps over the lazy dog", "doc1");
    sem_idx.add("A fast brown fox leaps over a sleepy hound", "doc2");
    sem_idx.add("The dog barked loudly at the passing car", "doc3");
    sem_idx.add("Foxes are clever animals that live in forests", "doc4");
    sem_idx.add("Machine learning and artificial intelligence are transforming technology", "doc5");
    sem_idx.add("Deep learning neural networks process natural language", "doc6");
    sem_idx.add("The restaurant serves excellent pasta and fresh salads", "doc7");
    sem_idx.add("Climate change affects global weather patterns significantly", "doc8");
    sem_idx.add("Quantum computing promises exponential speedup for certain problems", "doc9");
    sem_idx.add("The photographer captured stunning landscapes at sunrise", "doc10");
    sem_idx.build();

    // Semantic queries — find meaning, not just keywords
    var queries = [
        "animals in the wild",
        "AI and deep learning",
        "food and dining",
        "environmental issues",
        "fast computation",
    ];

    for (_, query in queries) {
        println("");
        info("  Query: '${query}'");
        var results = sem_idx.search_semantic(query, 3);
        print_results(results);
    }

    // -------------------------------------------------------------------------
    // 18b. Hybrid BM25 + Semantic search (fused ranking)
    // -------------------------------------------------------------------------
    println("");
    info("--- 18b. Hybrid BM25 + Semantic Search ---");

    var hybrid_w = Map<SearchMode, float> {};
    hybrid_w.set(SearchMode::bm25, 0.4);
    hybrid_w.set(SearchMode::semantic, 0.6);

    var hybrid_idx = TextIndex<String> {
        config: TextIndexConfig {
            embed: gemma_embed,
            stopWords: StopWordOptions {
                mode: StopWordMode::default,
                language: TextSearchLanguage::en,
            },
            tokenization: TokenizationOptions { stemming: true },
            fusion: FusionOptions {
                method: FusionMethod::rrf,
                weights: hybrid_w,
            },
        },
    };

    hybrid_idx.add("The quick brown fox jumps over the lazy dog", "doc1");
    hybrid_idx.add("A fast brown fox leaps over a sleepy hound", "doc2");
    hybrid_idx.add("The dog barked loudly at the passing car", "doc3");
    hybrid_idx.add("Foxes are clever animals that live in forests", "doc4");
    hybrid_idx.add("Machine learning and artificial intelligence are transforming technology", "doc5");
    hybrid_idx.add("Deep learning neural networks process natural language", "doc6");
    hybrid_idx.add("The restaurant serves excellent pasta and fresh salads", "doc7");
    hybrid_idx.add("Climate change affects global weather patterns significantly", "doc8");
    hybrid_idx.add("Quantum computing promises exponential speedup for certain problems", "doc9");
    hybrid_idx.add("The photographer captured stunning landscapes at sunrise", "doc10");
    hybrid_idx.build();

    // Hybrid queries — BM25 handles keyword matching, semantic handles meaning
    var hybrid_modes = Array<SearchMode> {};
    hybrid_modes.add(SearchMode::bm25);
    hybrid_modes.add(SearchMode::semantic);

    var hybrid_query_w = Map<SearchMode, float> {};
    hybrid_query_w.set(SearchMode::bm25, 0.4);
    hybrid_query_w.set(SearchMode::semantic, 0.6);

    var hybrid_opts = SearchOptions {
        modes: hybrid_modes,
        weights: hybrid_query_w,
        fusionMethod: FusionMethod::rrf,
    };

    var hybrid_queries = [
        "clever wild animals",
        "neural network language processing",
        "outdoor photography nature",
    ];

    for (_, query in hybrid_queries) {
        println("");
        info("  Hybrid query: '${query}'");
        var results = hybrid_idx.search(query, 3, hybrid_opts);
        print_results(results);
    }

    // Clean up
    model.free();
}

fn print_results(results: Array<TextResult>) {
    if (results.size() == 0) {
        info("  (no results)");
        return;
    }
    for (_, r in results) {
        info("  [${r.score}] ${r.value} — ${r.key}");
    }
}
```
