A MoonBit-native embedded full-text search kernel
Dependencies
[!IMPORTANT] MoonSearch source metadata is currently at 0.9.0 and is still evolving. Read Current limitations before adopting it for production data or a long-lived persistence contract.
moon add Lucius646/MoonSearch@0.9.0///|
import {
"Lucius646/MoonSearch",
}Schema → Documents → SegmentWriter → Segment → QueryParser → Searcher → Top-K///|
fn main {
let tokenizers = @MoonSearch.TokenizerManager::with_defaults()
let builder = @MoonSearch.SchemaBuilder::new()
let title = builder.add_text_field(
"title",
@MoonSearch.TextOptions::new(true, true).with_tokenizer("default"),
)
let body = builder.add_text_field(
"body",
@MoonSearch.TextOptions::new(true, true).with_tokenizer("en_stem"),
)
let schema = builder.build()
let writer = @MoonSearch.SegmentWriter::with_schema_and_tokenizers(
schema, tokenizers,
) catch {
error => abort(error.to_string())
}
let first = @MoonSearch.Document::new()
first.add_text(title, "MoonSearch")
first.add_text(body, "A MoonBit full-text search engine")
ignore(writer.add_document(first))
let second = @MoonSearch.Document::new()
second.add_text(title, "Language guide")
second.add_text(body, "MoonBit language documentation")
ignore(writer.add_document(second))
let searcher = @MoonSearch.Searcher::new(writer.finish())
let parser = @MoonSearch.QueryParser::new(schema, tokenizers)
let query = parser.parse_query(body, "SEARCHES") catch {
error => abort(error.to_string())
}
let hits = searcher.search(query, @MoonSearch.TopKCollector::new(10))
for hit in hits {
match searcher.doc(hit.address) {
Some(document) => {
let titles = document.texts_for(title)
println("score=\{hit.score} title=\{titles[0]}")
}
None => ()
}
}
}moon run cmd/main --frozenmoonsearch index append UTF-8 JSONL documents as one immutable Segment
moonsearch search search one indexed field
moonsearch inspect show snapshot and Schema metadatamoon run cmd/moonsearch --frozen -- --help{"title":"MoonSearch","body":"MoonBit full text search engine"}
{"title":"MoonBit Guide","body":["language documentation","MoonBit basics"]}
{"title":"Search Notes","body":"UTF-8 indexing and highlighting"}moon run cmd/moonsearch --frozen -- index \
--index .moonsearch-index \
--input cmd/moonsearch/testdata/documents.jsonl \
--field title \
--field body \
--tokenizer defaultmoon run cmd/moonsearch --frozen -- search \
--index .moonsearch-index \
--field body \
--query "moonbit search" \
--operator and \
--limit 10moon run cmd/moonsearch --frozen -- search \
--index .moonsearch-index \
--field body \
--query "full text" \
--phrase \
--highlight \
--fragment-size 160 \
--jsonmoon run cmd/moonsearch --frozen -- search \
--index .moonsearch-index \
--field body \
--query 'title:MoonSearch OR body:"full text"' \
--query-string \
--jsonmoon run cmd/moonsearch --frozen -- inspect \
--index .moonsearch-index \
--jsonnpm run demo:build
npm run demo:servenpm install
npm run test:data
npm run test:wasm
npm run test:browser
npm run benchmark:browserDocument
│
▼
Schema ──→ TokenizerManager ──→ TokenStream / TokenFilter
│ │
└──────────────────────────────────┘
│
▼
SegmentWriter
│
▼
immutable Segment + stored fields
│
▼
Directory + generation manifest
│
▼
IndexReader snapshot → Searcher
│ │
│ ▼
│ Query → Weight → Scorer
│ │
└──────────────┴─→ TopKCollector
│
▼
StoredDocument + Highlightercore ──→ schema ──→ analysis ──→ index ──→ query ──→ store
│
└─→ analysis/chinese
internal/codec shared binary primitives used by index and store
benchmarks deterministic public-API performance fixtures
cmd/main embedded lifecycle example
cmd/moonsearch JSONL command-line application
tests/integration root-facade cross-package tests| Name | Pipeline | Typical use |
|---|---|---|
| raw | Entire input as one token | Identifiers or exact whole-value matching |
| whitespace | Unicode whitespace splitting | Pre-tokenized or case-sensitive text |
| default | SimpleTokenizer → remove tokens over 40 UTF-8 bytes → lowercase | General Unicode text |
| en_stem | Simple tokenization → length filter → lowercase → Porter2 stemming | English full-text search |
///|
let lexicon = @MoonSearch.ChineseLexicon::from_dictionary_text(
(
#|月兔 100
#|全文 80
#|搜索 120
#|搜索引擎 200
#|中文 100
#|分词 90
#|
),
) catch {
error => abort(error.to_string())
}///|
let index_tokenizers = @MoonSearch.TokenizerManager::with_defaults()
index_tokenizers.register(
"zh_search",
@MoonSearch.chinese_dag_search_index_analyzer(lexicon),
) catch {
error => abort(error.to_string())
}
///|
let query_tokenizers = @MoonSearch.TokenizerManager::with_defaults()
query_tokenizers.register(
"zh_search",
@MoonSearch.chinese_dag_analyzer(lexicon),
) catch {
error => abort(error.to_string())
}| Component | Behavior |
|---|---|
| TermQuery | Matches one already analyzed field term |
| BooleanQuery | Combines Must, Should, and MustNot clauses |
| BoostQuery | Multiplies a child query's score |
| PhraseQuery / PhrasePrefixQuery | Positional phrase, configurable slop, and prefix completion |
| PrefixQuery / WildcardQuery / RegexQuery / FuzzyQuery | Bounded multi-term expansion |
| RangeQuery / TermRangeQuery | Typed and lexical ranges |
| MatchAllQuery / ExistsQuery / TermSetQuery | Match-all, field existence, and constant-score term sets |
| DisjunctionMaxQuery / ConstantScoreQuery / FunctionScoreQuery | Compound scoring |
| QueryParser | Analyzes raw text and builds Term/Boolean/Phrase queries |
| QueryStringParser | Strict or lenient field-aware parsing, binding, and highlighting |
| Bm25Scorer | Scores postings with snapshot-wide BM25 statistics |
| TopKCollector | Selects one globally ordered Top-K across Segments |
///|
let parser = @MoonSearch.QueryStringParser::new(schema, tokenizers, [
title, body,
])
///|
let ast = parser.parse_ast("title:MoonBit AND body:\"full text\"") catch {
error => abort(error.to_string())
}
///|
let query = parser.build_query_from_ast(ast) catch {
error => abort(error.to_string())
}///|
let query = @MoonSearch.BooleanQuery::new([
@MoonSearch.BooleanClause::new(
@MoonSearch.Occur::Should,
@MoonSearch.BoostQuery::new(title_query, 3.0),
),
@MoonSearch.BooleanClause::new(
@MoonSearch.Occur::Should,
@MoonSearch.BoostQuery::new(body_query, 1.0),
),
])///|
let highlighter = @MoonSearch.Highlighter::new(schema, tokenizers)
///|
let fragment = highlighter.highlight(
body,
"MOONBIT SEARCH",
stored_body,
@MoonSearch.HighlightOptions::new()
.with_mode(@MoonSearch.HighlightMode::Phrase)
.with_max_chars(160),
) catch {
error => abort(error.to_string())
}
///|
match fragment {
Some(fragment) => println(fragment.render("<mark>", "</mark>"))
None => ()
}SegmentWriter.finish()
│
▼
IndexWriter.commit(segment)
│
▼
published generation ──→ IndexReader snapshot ──→ Searchermoon bench benchmarks \
--release \
--target native \
--deny-warn \
--frozen \
--no-parallelizemoon bench benchmarks --build-only --target all --deny-warn --frozen| Path | Responsibility |
|---|---|
| core/ | IDs, Documents, stored fields, Terms, shared errors |
| schema/ | field options, Schema, SchemaBuilder |
| analysis/ | Tokenizers, TokenStreams, filters, Token Graph, registry, English stemming |
| analysis/chinese/ | dictionary, DAG, HMM, and search-mode Chinese analysis |
| index/ | Segment writing, postings, positions, codec, snapshots, Merge |
| query/ | queries, weights, scorers, BM25, collector, parser, search, highlighting |
| store/ | Directory abstraction, manifests, IndexWriter, IndexReader |
| internal/codec/ | shared binary encoding and validation primitives |
| cmd/main/ | embedded API lifecycle example |
| cmd/moonsearch/ | JSONL CLI application |
| demo/browser/ | WasmGC bridge, static browser UI, sample JSONL, and contract tests |
| benchmarks/ | deterministic benchmark fixtures and scenarios |
| tests/integration/ | public behavior through the root facade |
# Format MoonBit sources
moon fmt
# Refresh generated public interfaces
moon info
# Check every supported backend and reject warnings
moon check --target all --deny-warn --frozen
# Run the full test suite on every backend
moon test --target all --deny-warn --frozen
# Run the embedded example
moon run cmd/main --frozen
# Build benchmarks on every backend without measuring
moon bench benchmarks --build-only --target all --deny-warn --frozen
# Measure the native release build
moon bench benchmarks --release --target native --deny-warn --frozen --no-parallelize
# Build the WasmGC demo artifact and inspect its ABI
npm run test:wasm
# Execute browser query and load-failure smoke tests
npm run test:browserfn[D : DirectoryV2 + Directory] check_index(directory : D) -> IndexCheckReport raise PersistenceErrorfn chinese_dag_hmm_analyzer(dictionary : &ChineseDictionary, model : &ChineseHmmModel) -> TextAnalyzerfn chinese_dag_hmm_search_analyzer(dictionary : &ChineseDictionary, model : &ChineseHmmModel) -> TextAnalyzerfn chinese_dag_hmm_search_index_analyzer(dictionary : &ChineseDictionary, model : &ChineseHmmModel) -> TextAnalyzerA MoonBit-native embedded full-text search kernel
Dependencies