Documentation
¶
Overview ¶
Package roaringrange turns a roaringsearch (FTSR) index into a range-fetchable static index (RRS, see FORMAT.md) that a browser can query over HTTP Range requests with no backend.
The layout puts the whole n-gram dictionary in one contiguous, key-sorted block so a reader loads a sparse view in a single ranged GET, then fetches each posting by absolute byte offset. Postings are byte-identical portable RoaringBitmaps, so transcoding copies them verbatim and the Rust/WASM reader deserializes them with the `roaring` crate. Optional faceting is layered on via a companion sidecar (see FACETS.md).
Index ¶
- Constants
- Variables
- func FacetKey(field, category string) uint64
- func FacetKeyWith(field, category string, caseFold bool) uint64
- func NgramKeys(query string, gramSize int) []uint64
- func NgramKeysWith(query string, gramSize int, caseFold bool) []uint64
- func QuantizeImpact(tf uint32, dl, avgdl, k1, b float32) byte
- func TermTokenize(text string) []string
- func TermTokenizeWith(text string, caseFold bool) []string
- func Transcode(src io.Reader, dst io.Writer) error
- func TranscodeStride(src io.Reader, dst io.Writer, stride int) error
- func WriteFacets(dst io.Writer, fields []FacetField) error
- func WriteFacetsWith(dst io.Writer, fields []FacetField, caseNormalization bool) error
- func WriteHotcache(dst io.Writer, members []MemberSpec, inlineThreshold uint32) error
- func WriteImpacts(dst io.Writer, dict []DictEntry, acc *ImpactsAccumulator, k1, b float32) error
- func WriteIndex(dst io.Writer, gramSize uint16, stride int, entries []IndexEntry) error
- func WriteIndexWith(dst io.Writer, gramSize uint16, stride int, entries []IndexEntry, ...) error
- func WriteLookup(dst io.Writer, entries []LookupEntry) error
- func WritePerm(dst io.Writer, primaryOfSecondary []uint32) error
- func WriteRRVI(dst io.Writer, m *ivfpq.Model) error
- func WriteRecords(bin, idx io.Writer, records [][]byte) error
- func WriteRecordsZstd(bin, idx io.Writer, records [][]byte, dict []byte) error
- func WriteRerank(dst io.Writer, dim int, vectors [][]float32, l2Normalize bool) error
- func WriteSortcols(dst io.Writer, cols []SortColumn) error
- func WriteSplitSet(w io.Writer, splits []SplitSpec, config SplitSetConfig) error
- func WriteTermIndex(dst io.Writer, postings map[string]*roaring.Bitmap, headBoundary uint32, ...) error
- func WriteTermIndexFull(dst io.Writer, postings map[string]*roaring.Bitmap, headBoundary uint32, ...) error
- func WriteTermIndexWith(dst io.Writer, postings map[string]*roaring.Bitmap, headBoundary uint32, ...) error
- type BuiltSplitSet
- type ColumnValues
- type DictEntry
- type F32Column
- type FacetCategory
- type FacetField
- type I32Column
- type ImpactsAccumulator
- type Index
- type IndexEntry
- type LookupEntry
- type MemberSpec
- type MemberTag
- type NamedSplit
- type NgramKeyer
- type RecordStore
- type RecordWriter
- type SortColSpec
- type SortColumn
- type SplitBuildConfig
- type SplitSetBuilder
- func (b *SplitSetBuilder) AddFaceted(text string, facets map[string][]string) (uint32, error)
- func (b *SplitSetBuilder) AddKeys(keys []uint64) (uint32, error)
- func (b *SplitSetBuilder) AddText(text string) (uint32, error)
- func (b *SplitSetBuilder) DocCount() uint32
- func (b *SplitSetBuilder) Finish() (*BuiltSplitSet, error)
- type SplitSetConfig
- type SplitSpec
- type TermLanguage
- type TermSplitBuildConfig
- type TermSplitSetBuilder
- type TermTokenizer
- type TrigramMonolithBuilder
- type U16Column
- type U32Column
Constants ¶
const ( // DefaultK1 / DefaultB are the BM25 parameters (match the Rust DEFAULT_K1/_B). DefaultK1 float32 = 1.2 DefaultB float32 = 0.75 )
const ( // MagicFacet is the RRSF facet sidecar magic. MagicFacet = "RRSF" // VersionFacet is the RRSF format version number. VersionFacet = 1 )
const ( // MagicRecord is the RRSR record-store index magic. MagicRecord = "RRSR" // VersionRecord is the RRSR format version this package's writer emits // (untagged raw records). VersionRecord = 1 )
const ( // MagicSplitSet is the RRSS split-set manifest magic. MagicSplitSet = "RRSS" // VersionSplitSet is the RRSS format version number. VersionSplitSet = 1 // PolicyTiered assigns docs to splits by rank (top-cited first). PolicyTiered = 0 // PolicyStableKey assigns docs by ingest order; rank is a query-time RRSC column. PolicyStableKey = 1 // BodyKindTrigram marks split data files as trigram RRS indexes (header byte 9; the // default, so older manifests read back as trigram). Go only builds trigram bodies. BodyKindTrigram = 0 // BodyKindTerm marks split data files as term RRTI (FST) indexes (header byte 9). BodyKindTerm = 1 // SplitSetFlagBloom marks per-split term Bloom-filter summaries present (header flag). SplitSetFlagBloom = 1 << 0 // SplitSetFlagFacet marks per-split facet-presence summaries present (header flag). SplitSetFlagFacet = 1 << 1 // SplitSetFlagTime marks per-split time min/max summaries present (header flag). SplitSetFlagTime = 1 << 2 // SplitSetFlagTombstones marks per-split tombstone postings present (header flag). SplitSetFlagTombstones = 1 << 3 // SplitSetFlagCaseSensitive marks a case-sensitive split set: n-gram and facet keys were // not lowercased, so a query derives keys without folding. Unset (the default) keeps every // manifest byte-identical. Mirrors the Rust splitset::FLAG_CASE_SENSITIVE. SplitSetFlagCaseSensitive = 1 << 4 // SplitFlagHasTombstone marks a split that carries a tombstone posting (per-split flag). SplitFlagHasTombstone = 1 << 0 // SplitFlagAbsoluteIDs marks a split that stores absolute global doc IDs (per-split flag). SplitFlagAbsoluteIDs = 1 << 1 // SortColFlagDescending marks a descending rank sort-column (higher value = better rank). SortColFlagDescending = 1 << 0 )
const ( // Magic is the RRS index magic. Magic = "RRSI" // Version is the RRS format version number. Version = 3 // VersionV4 is the RRS format version emitted only for a case-sensitive index: identical // to v3 plus a trailing flags u16 at offset 16. Default (case-folding) builds stay v3. VersionV4 = 4 // DefaultStride is the default sparse-index stride. DefaultStride = 512 )
Variables ¶
var ( // ErrSrcMagic is returned when the source is not a roaringsearch (FTSR) index. ErrSrcMagic = errors.New("source is not a roaringsearch (FTSR) index") // ErrMagic is returned when an index does not start with the RRS magic. ErrMagic = errors.New("bad RRS magic") // ErrVersion is returned when an index's format version is unsupported. The RRS // reader is v3-only, matching the Rust reference reader; a v2 file has a different // header and dictionary layout and would otherwise misparse silently. ErrVersion = errors.New("unsupported RRS format version") // ErrTruncated is returned when an index ends before its declared structure. ErrTruncated = errors.New("truncated index") // ErrCompressedRecord is returned when a version-2 record store holds a // zstd-compressed frame (tag 1) but the store was opened without a dictionary // decoder — open it with OpenRecordStoreWithDict to inflate compressed records. ErrCompressedRecord = errors.New("compressed record (zstd frame) requires opening the store with OpenRecordStoreWithDict") )
Functions ¶
func FacetKey ¶
FacetKey derives the category key as FNV-1a 64-bit over lower(field), a 0x1f separator, and lower(category). See FACETS.md. Equivalent to FacetKeyWith with case folding on (the default).
func FacetKeyWith ¶ added in v0.25.0
FacetKeyWith is FacetKey with an explicit case-fold flag. When caseFold is false the field/category are hashed verbatim (a case-sensitive index keeps "Smith" and "smith" distinct). Build and pruning sides must pass the same mode. Mirrors the Rust facet_key.
func NgramKeys ¶
NgramKeys derives the deduplicated n-gram keys for a query. The query is split on whitespace and each word is keyed independently (normalize: keep Unicode letters/digits, lowercase; then key each gramSize-rune window), unioning keys in first-seen order. Per-word keying avoids cross-word boundary trigrams — a query like "legends travis" must not require "dst" from legend·s·t·ravis. Mirrors roaringsearch's per-word query matching. See FORMAT.md.
The key derivation here must stay byte-compatible with roaringsearch's builder (and the Rust reader's port); the cross-library test in ./conformance is the guard that enforces it.
func NgramKeysWith ¶ added in v0.25.0
NgramKeysWith is NgramKeys with an explicit case-fold flag. When caseFold is false the kept letters/digits are not lowercased, so a case-sensitive trigram index keys on the original case. The builder and reader must agree; the choice is recorded in the RRSI header (a v4 flags field) so the reader derives the same caseFold. See FORMAT.md.
func QuantizeImpact ¶ added in v0.24.0
QuantizeImpact quantizes one (term, doc) BM25 impact to a byte (1–255, never 0 — presence in the posting implies a nonzero contribution): the tf component with the document-length norm folded in, scaled by k1+1. The arithmetic is float32 throughout and rounds half-away-from-zero, matching the Rust quantize_impact byte-for-byte (f32→f64 widening is exact, and math.Round uses the same rounding rule as f32::round). The saturating float→int + clamp mirrors Rust's `as i64`.
func TermTokenize ¶ added in v0.24.0
TermTokenize is the base RRTI tokenizer (no stemming / stop words): maximal runs of Rust-alphanumeric runes, lowercased with the full per-rune mapping. Mirrors the Rust terms::tokenize. Equivalent to TermTokenizeWith with case folding on (the default).
func TermTokenizeWith ¶ added in v0.25.0
TermTokenizeWith is the base tokenizer with an explicit case-fold flag. When caseFold is false the token runes are kept verbatim (a case-sensitive index); the boundary rule (maximal Rust-alphanumeric runs) is unchanged. Mirrors the Rust terms::tokenize_with.
func Transcode ¶
Transcode reads a roaringsearch FTSR index from src and writes the v3 RRS range-fetchable layout to dst using the default stride. Each term is written as one portable RoaringBitmap posting. Entries are sorted by key ascending. See FORMAT.md.
func TranscodeStride ¶
TranscodeStride is Transcode with an explicit sparse-index stride. A stride of zero or less is replaced by DefaultStride.
func WriteFacets ¶
func WriteFacets(dst io.Writer, fields []FacetField) error
WriteFacets writes the RRSF facet sidecar for the given fields to dst. Each category posting is split into head (docs [0,65536)) and tail (docs [65536, ∞)) portable RoaringBitmaps, mirroring the text index. Categories are grouped by field and sorted by key within a field. See FACETS.md.
func WriteFacetsWith ¶ added in v0.25.0
func WriteFacetsWith(dst io.Writer, fields []FacetField, caseNormalization bool) error
WriteFacetsWith is WriteFacets with an explicit caseNormalization flag. true (the default) lowercases field/category for the facet-key hash and writes a byte-identical v1 sidecar; false keys on the raw bytes (a case-sensitive index) and sets the reserved-field flag so a split-set's facet pruning recomputes keys identically. Category display names are stored verbatim either way. Mirrors the Rust write_facets_with.
func WriteHotcache ¶ added in v0.24.0
func WriteHotcache(dst io.Writer, members []MemberSpec, inlineThreshold uint32) error
WriteHotcache writes the RRHC bundle for members to dst — byte-for-byte with the Rust write_hotcache. A member whose boot fits inlineThreshold is copied into the inlined-boot blob (returned free with the single GET); larger boots are referenced by (BootOff, BootLen) in the member's own data file.
func WriteImpacts ¶ added in v0.24.0
WriteImpacts writes the RRSB sidecar to dst for a finished .rrt whose dictionary is dict over the stats in acc — byte-for-byte with the Rust write_impacts. Every dict term must have accumulated stats (else error — a tokenizer-config mismatch would otherwise mis-address every later term). k1/b are the BM25 parameters used.
PERFORMANCE: this emits many small (~20-byte) writes to dst; pass a buffered writer (e.g. bufio.Writer) when dst is a file or socket and flush it after. The library does not buffer internally.
func WriteIndex ¶ added in v0.24.0
WriteIndex writes a v3 RRS (RRSI) trigram index over key->posting entries to dst — the byte-for-byte Go mirror of the Rust build::write_index. Entries are sorted by key here (so the caller need not pre-sort), and a stride of zero or less becomes DefaultStride. Each posting must be one portable RoaringBitmap (see roaring.Bitmap.ToBytes). See FORMAT.md.
func WriteIndexWith ¶ added in v0.25.0
func WriteIndexWith(dst io.Writer, gramSize uint16, stride int, entries []IndexEntry, caseNormalization bool) error
WriteIndexWith is WriteIndex with an explicit caseNormalization flag. true (the default) emits a v3 index byte-identical to before; false emits a case-sensitive v4 index (the caller must have keyed entries with NgramKeysWith(.., false)). Mirrors the Rust build::write_index_with.
func WriteLookup ¶ added in v0.24.0
func WriteLookup(dst io.Writer, entries []LookupEntry) error
WriteLookup writes the RRIL index over entries to dst — byte-for-byte with the Rust write_lookup. Identifiers are normalized and double-hashed, empties dropped, and the records sorted by (hash, doc) with a stable sort (matching Rust's stable sort_by on equal-hash, equal-doc ties).
PERFORMANCE: this emits many small (~16-byte) writes to dst; pass a buffered writer (e.g. bufio.Writer) when dst is a file or socket and flush it after. The library does not buffer internally.
func WritePerm ¶ added in v0.24.0
WritePerm writes a one-column u32 RRSC store named "primary" mapping a secondary doc-ID space back to the primary one (primaryOfSecondary[secondary] = primary) — byte-for-byte with the Rust write_perm.
func WriteRRVI ¶ added in v0.24.0
WriteRRVI serializes a trained IVFPQ model to dst in the RRVI byte layout the Rust VectorIndex (and the wasm reader) read over HTTP Range — the byte-for-byte mirror of the Rust Ivfpq::write. Layout (all little-endian): a 48-byte header, an optional OPQ rotation, the coarse centroids, the PQ codebooks, an nlist×12 cluster directory (absolute list offset + count), then each cluster's [ids][codes]. See VECTORS.md.
func WriteRecords ¶ added in v0.24.0
WriteRecords writes a record store from an in-memory slice of records, in doc-ID order — a convenience over RecordWriter for callers that already hold every record. See RECORDS.md.
func WriteRecordsZstd ¶ added in v0.24.0
WriteRecordsZstd writes a version-2 (framed) RRSR record store that compresses each record against the shared dict, in doc-ID order — the klauspost counterpart of WriteRecords. Each record is framed [tag][payload]: the smaller of a zstd frame (tag 1) and the raw bytes (tag 0), so a record never grows; a zero-length record stays zero-length (no tag), matching the version-1 convention. The same dict must be shipped to the reader as the *.dict sidecar and passed to OpenRecordStoreWithDict. The frame bytes differ from the Rust libzstd builder but read back identically through either reader.
func WriteRerank ¶ added in v0.24.0
WriteRerank writes the RRVR bf16 re-rank sidecar (read by the Rust RerankStore): a 20-byte header then a dense bf16 array of vectors keyed by doc ID (slice index == doc ID). Every vector must have length dim. Set l2Normalize for an inner-product index so the stored vectors match the unit-sphere space the index was built in. Byte-for-byte with the Rust write_rerank.
func WriteSortcols ¶ added in v0.24.0
func WriteSortcols(dst io.Writer, cols []SortColumn) error
WriteSortcols writes the RRSC store for cols to dst — byte-for-byte with the Rust write_sortcols. Every column must hold the same number of values (one per doc).
func WriteSplitSet ¶ added in v0.24.0
func WriteSplitSet(w io.Writer, splits []SplitSpec, config SplitSetConfig) error
WriteSplitSet writes the RRSS manifest for splits to w, in the order given (base splits first, then delta splits — BaseCount marks the boundary). The output is byte-for-byte identical to the Rust writer for the same inputs. See SPLITSET.md.
PERFORMANCE: this emits many small (~56-byte) writes to w; pass a buffered writer (e.g. bufio.Writer) when w is a file or socket and flush it after. The library does not buffer internally.
func WriteTermIndex ¶ added in v0.24.0
func WriteTermIndex(dst io.Writer, postings map[string]*roaring.Bitmap, headBoundary uint32, lang TermLanguage, stopwords bool, blockCap int) error
WriteTermIndex writes an RRTI v2 term index over postings (term → bitmap of the shared rank-order doc IDs) to dst — byte-for-byte the Rust write_term_index_from_postings. headBoundary is the head/tail doc-ID split (a multiple of 65536); language/stopwords are recorded in the header so the reader tokenizes queries identically; blockCap of 0 takes the default.
func WriteTermIndexFull ¶ added in v0.26.0
func WriteTermIndexFull(dst io.Writer, postings map[string]*roaring.Bitmap, headBoundary uint32, lang TermLanguage, stem, stopwords, caseNormalization bool, blockCap int) error
WriteTermIndexFull writes an RRTI v2 term index with independent stem and stopwords filters over a shared language (mirrors the Rust write_term_index_from_postings). stem controls the stemmed header flag; the language byte is recorded when either filter is on, and both filters require a language (a filter on with none set is an error). blockCap of 0 takes the default. It is a thin error-only wrapper over WriteTermIndexFullDict for source compatibility.
func WriteTermIndexWith ¶ added in v0.25.0
func WriteTermIndexWith(dst io.Writer, postings map[string]*roaring.Bitmap, headBoundary uint32, lang TermLanguage, stopwords, caseNormalization bool, blockCap int) error
WriteTermIndexWith is WriteTermIndex with an explicit caseNormalization flag. true (the default) lowercases nothing differently and writes a byte-identical header; false records the case-sensitive flag (termFlagCaseSensitive) so the reader skips query-side folding. Back-compat: a non-None language implies stemming — use WriteTermIndexFull to control stemming independently (e.g. stop words without stemming).
Types ¶
type BuiltSplitSet ¶ added in v0.24.0
type BuiltSplitSet struct {
Manifest []byte
Splits []NamedSplit
Facets []NamedSplit
}
BuiltSplitSet is the output of SplitSetBuilder.Finish: the manifest, each split's (filename, RRS bytes), and — for a faceted build — each split's (filename, RRSF bytes) facet sidecar. The caller writes them out; nothing is written here.
type ColumnValues ¶ added in v0.24.0
type ColumnValues interface {
Len() int
// contains filtered or unexported methods
}
ColumnValues is one sort column's dense values in doc-ID order. The concrete type (U16Column / U32Column / I32Column / F32Column) selects the on-disk value type.
type DictEntry ¶ added in v0.24.0
DictEntry is one (term, posting head_off) pair from the paired .rrt dictionary, in dictionary order (ascending head_off).
func WriteTermIndexFullDict ¶ added in v0.28.0
func WriteTermIndexFullDict(dst io.Writer, postings map[string]*roaring.Bitmap, headBoundary uint32, lang TermLanguage, stem, stopwords, caseNormalization bool, blockCap int) ([]DictEntry, error)
WriteTermIndexFullDict is WriteTermIndexFull that additionally returns the dictionary it wrote: the (term, posting head_off) pairs in byte-lexicographic dictionary order, carrying the *real* head offsets front-coded into the .rrt. Feed the result straight to WriteImpacts to build a BM25 .rrb sidecar that addresses this exact .rrt--the offsets are the ones the reader recovers on lookup, not fabricated. The bytes written to dst are identical to WriteTermIndexFull; only the extra return value differs.
type F32Column ¶ added in v0.24.0
type F32Column []float32
U16Column / U32Column / I32Column / F32Column are the four on-disk value types.
type FacetCategory ¶
FacetCategory is one category value within a field and the doc-ID posting of the documents carrying it.
type FacetField ¶
type FacetField struct {
Name string
Categories []FacetCategory
}
FacetField is a named facet field with its categories. Mirrors one field of a roaringsearch BitmapFilter.
type I32Column ¶ added in v0.24.0
type I32Column []int32
U16Column / U32Column / I32Column / F32Column are the four on-disk value types.
type ImpactsAccumulator ¶ added in v0.24.0
type ImpactsAccumulator struct {
// contains filtered or unexported fields
}
ImpactsAccumulator gathers per-term (doc, tf) plus per-doc lengths over a corpus added in ascending doc-ID order (the shared rank order), for WriteImpacts. Mirrors the Rust bm25::build::ImpactsAccumulator: the per-term lists are ascending by doc by construction, matching posting iteration order.
func NewImpactsAccumulator ¶ added in v0.24.0
func NewImpactsAccumulator(tok *TermTokenizer) *ImpactsAccumulator
NewImpactsAccumulator builds an accumulator over tok, which MUST match the .rrt build's tokenizer (same language / stop-word config) or the vocabularies diverge.
func (*ImpactsAccumulator) AddDoc ¶ added in v0.24.0
func (a *ImpactsAccumulator) AddDoc(text string) uint32
AddDoc tokenizes text as the next sequential doc ID and returns that ID.
func (*ImpactsAccumulator) DocCount ¶ added in v0.24.0
func (a *ImpactsAccumulator) DocCount() uint32
DocCount is the number of documents accumulated so far.
type Index ¶
type Index struct {
// GramSize is the n-gram window the index was built with.
GramSize int
// CaseFold reports whether queries should lowercase their n-grams before keying
// (false for a v4 case-sensitive index). Callers deriving keys should use
// NgramKeysWith(query, GramSize, CaseFold) so they key exactly as the index was built.
CaseFold bool
// contains filtered or unexported fields
}
Index is a reference reader over a v3 RRS index accessed by byte range. It reads only the 16-byte header and the sparse index up front (mirroring the browser reader's boot ranged GETs); the dictionary and postings are fetched lazily, one ranged ReadAt per block, never in their entirety. See FORMAT.md.
func Open ¶
Open reads and parses the RRS header and sparse index via ranged reads. It does not read the dictionary or postings; those are fetched per lookup.
func (*Index) NgramCount ¶
NgramCount returns the number of n-grams in the dictionary.
func (*Index) Posting ¶ added in v0.24.0
Posting returns the full posting bytes for key via one ranged dictionary read and one ranged posting read, or ok=false if key is absent.
func (*Index) Postings ¶ added in v0.27.0
Postings returns the posting bytes for each present key, keyed by key. The dict blocks are read once per distinct sparse block (deduping keys that share a block), then one ranged read per present posting -- so an n-key query costs (distinct dict blocks + present postings) reads, not up to 2n. Absent keys are omitted from the map.
type IndexEntry ¶ added in v0.24.0
IndexEntry is one v3 RRS dictionary entry for WriteIndex: a trigram key and its single serialized portable-RoaringBitmap posting. The public mirror of the internal indexEntry.
type LookupEntry ¶ added in v0.24.0
LookupEntry is one (identifier, doc-ID) pair fed to WriteLookup.
type MemberSpec ¶ added in v0.24.0
type MemberSpec struct {
Tag MemberTag
DataFile string
BootOff uint64
BootLen uint32
BootBytes []byte
}
MemberSpec describes one hotcache member: its format tag, the data-file name its per-query reads go to, the boot byte-range within that file, and the boot bytes themselves (so the writer can inline or measure them). BootLen must equal len(BootBytes).
type MemberTag ¶ added in v0.24.0
type MemberTag uint16
MemberTag is a hotcache member's format type (the on-disk u16 tag) — values match the Rust hotcache::MemberTag::to_u16.
type NamedSplit ¶ added in v0.24.0
NamedSplit is one emitted split: its filename and RRS bytes.
type NgramKeyer ¶ added in v0.27.0
type NgramKeyer struct {
// contains filtered or unexported fields
}
NgramKeyer derives n-gram keys while reusing its scratch buffers across calls, for the per-document ingest hot path where NgramKeysWith's fresh map + rune slice per call cost hundreds of millions of allocations over a full 484M-doc build. Not safe for concurrent use; hold one per builder.
func (*NgramKeyer) Keys ¶ added in v0.27.0
func (k *NgramKeyer) Keys(query string, gramSize int, caseFold bool) []uint64
Keys returns the deduplicated n-gram keys for query, byte-identical to NgramKeysWith but reusing internal buffers. The returned slice aliases the keyer's buffer and is valid only until the next Keys call -- copy it if it must outlive that.
type RecordStore ¶ added in v0.24.0
type RecordStore struct {
// contains filtered or unexported fields
}
RecordStore is a reference reader over an RRSR record store accessed by byte range: an offset index (idx) over a record blob (bin). It mirrors the browser reader's access pattern — one ranged read of the 16-byte offset pair in the index, one ranged read of the record slice in the blob. See RECORDS.md.
func OpenRecordStore ¶ added in v0.24.0
func OpenRecordStore(idx, bin io.ReaderAt) (*RecordStore, error)
OpenRecordStore boots the store: reads the 16-byte index header and validates magic and version. idx addresses the offset index, bin the record blob. Accepts version 1 (untagged raw records) and version 2 ([tag][payload]-framed records, matching the Rust reader); a version-2 zstd-compressed frame (tag 1) errors at Get — open with OpenRecordStoreWithDict (records_zstd.go) to attach a dictionary-backed zstd decoder.
func OpenRecordStoreWithDict ¶ added in v0.24.0
func OpenRecordStoreWithDict(idx, bin io.ReaderAt, dict []byte) (*RecordStore, error)
OpenRecordStoreWithDict boots an RRSR store like OpenRecordStore but attaches a dictionary-backed zstd decoder, so version-2 zstd frames (tag 1) inflate at Get — the mirror of the Rust RecordStore::open_with_dict. A version-1 (raw) store ignores the dictionary. The dict must be the *.dict sidecar the store was built against (Rust libzstd or the Go WriteRecordsZstd builder — either reads here).
func (*RecordStore) Get ¶ added in v0.24.0
func (s *RecordStore) Get(id uint32) (data []byte, ok bool, err error)
Get returns the raw record bytes for doc id via one ranged index read (the 16-byte offset pair) and one ranged blob read, or ok=false if id is out of range. A zero-length record (a doc with no stored fields) returns ok=true with empty bytes.
func (*RecordStore) GetMany ¶ added in v0.27.0
func (s *RecordStore) GetMany(ids []uint32) (map[uint32][]byte, error)
GetMany returns the decoded record bytes for each in-range id, keyed by id. Doc IDs are rank-ordered by construction, so a top-k page's ids are frequently near-contiguous: GetMany reads the offset-table entries in coalesced waves and then the record blobs in coalesced waves (bridging gaps <= recordCoalesceGap), so ~20 near-contiguous records cost a handful of ranged reads rather than ~2 per id. Out-of-range ids are omitted; a zero-length record maps to empty bytes. Results are identical to calling Get on each id.
func (*RecordStore) Len ¶ added in v0.24.0
func (s *RecordStore) Len() uint32
Len returns the number of records (doc IDs 0..Len).
type RecordWriter ¶ added in v0.24.0
type RecordWriter struct {
// contains filtered or unexported fields
}
RecordWriter streams the RRSR record store: record bytes are pushed one at a time in doc-ID order, so a builder that produces records incrementally never has to hold them all in memory. The concatenated record bytes go to bin and a range-fetchable offset index to idx. Records are opaque to the library — the caller chooses the encoding (JSON, msgpack, …); the store only frames them for O(1) Range lookup by doc ID. Mirrors the Rust RecordWriter. See RECORDS.md.
count is written into the header up front, so the caller must know the record total in advance and call Write exactly that many times (the offset table is sized for count+1 entries).
func NewRecordWriter ¶ added in v0.24.0
func NewRecordWriter(bin, idx io.Writer, count uint32) (*RecordWriter, error)
NewRecordWriter opens a streaming record store for count records, writing the 16-byte RRSR header and the leading off[0]=0 to idx. Push each record with Write in ascending doc-ID order.
PERFORMANCE: Write issues one bin write plus one 8-byte idx write per record, so pass BUFFERED writers (e.g. bufio.Writer) for bin and idx when they are files or sockets — otherwise a full-corpus build pays ~2 syscalls per record. The library does not buffer internally (so it never double-buffers a caller that already does); flush your buffers after the final Write / Finish.
func (*RecordWriter) Finish ¶ added in v0.27.0
func (w *RecordWriter) Finish() error
Finish verifies the writer produced exactly the record count declared in the header. The header commits to count up front and the offset table is sized for count+1 entries, so an under- or over-count leaves the store internally inconsistent — a reader Get of a high id would read past the written offsets. Call it after the last Write; it writes nothing (the output stays byte-identical for a correct writer) and only reports the mismatch. Streaming callers that cannot know the total in advance should size the writer to the count they will actually emit.
func (*RecordWriter) Write ¶ added in v0.24.0
func (w *RecordWriter) Write(rec []byte) error
Write appends one record's bytes to the blob and its cumulative end offset to the index. A zero-length record (a doc with no stored fields) stays addressable.
func (*RecordWriter) Written ¶ added in v0.24.0
func (w *RecordWriter) Written() uint32
Written returns the number of records written so far.
type SortColSpec ¶ added in v0.24.0
type SortColSpec struct {
Name string // the RRSC data-file name holding the rank column
Column uint16 // column index within that RRSC
Descending bool // whether a higher value ranks better
}
SortColSpec is the stable-key rank source recorded in the manifest header.
type SortColumn ¶ added in v0.24.0
type SortColumn struct {
Name string
Values ColumnValues
}
SortColumn is one named sort column: a display name plus its dense values.
type SplitBuildConfig ¶ added in v0.24.0
type SplitBuildConfig struct {
Policy int // PolicyTiered | PolicyStableKey
ByteCap uint64 // per-split seal target (the FIRST tier's cap when CapMax > 0)
CapMax uint64 // geometric tiering: tier i's cap = min(ByteCap << i, CapMax); 0 = flat
GramSize uint16 // n-gram window (e.g. 3)
HeadBoundary uint32 // split head/tail boundary; 0 -> 65536
Stride uint32 // sparse-index stride; 0 -> DefaultStride
NamePrefix string // split filenames: ‹prefix›-s00000.rrs, ...
SortCol *SortColSpec // stable-key rank source, or nil
BloomBitsPerKey uint32 // per-split term Bloom bits/key (0 disables)
// CaseSensitive builds a case-sensitive split set: n-gram and facet keys are NOT
// lowercased (v4 RRS splits, case-sensitive RRSF keys, the manifest's case-sensitive
// flag). The zero value (false) is the default case-folding behavior, byte-identical to
// before. (This is the inverse of the Rust SplitBuildConfig.case_normalization field; Go
// uses the zero-value-safe inverse so existing configs are unaffected.)
CaseSensitive bool
}
SplitBuildConfig is the build-time configuration for a SplitSetBuilder.
type SplitSetBuilder ¶ added in v0.24.0
type SplitSetBuilder struct {
// contains filtered or unexported fields
}
SplitSetBuilder accumulates documents and seals them into byte-capped RRS splits.
func NewSplitSetBuilder ¶ added in v0.24.0
func NewSplitSetBuilder(cfg SplitBuildConfig) *SplitSetBuilder
NewSplitSetBuilder creates a builder. A HeadBoundary/Stride of 0 take the RRS defaults.
func (*SplitSetBuilder) AddFaceted ¶ added in v0.24.0
AddFaceted tokenizes text and appends it as one document, recording its facet memberships (each field mapped to the categories this document belongs to). Each sealed split then gets its own RRSF facet sidecar plus a facet-presence summary, so a facet-filtered query can skip a split lacking a selected category. Returns the global id.
func (*SplitSetBuilder) AddKeys ¶ added in v0.24.0
func (b *SplitSetBuilder) AddKeys(keys []uint64) (uint32, error)
AddKeys appends one document by its (deduplicated) n-gram keys, returning its global doc id. A keyword-less document still consumes an id (dense id space).
func (*SplitSetBuilder) AddText ¶ added in v0.24.0
func (b *SplitSetBuilder) AddText(text string) (uint32, error)
AddText tokenizes text into n-gram keys and appends it as one document, returning its global doc id.
func (*SplitSetBuilder) DocCount ¶ added in v0.24.0
func (b *SplitSetBuilder) DocCount() uint32
DocCount is the number of documents added so far.
func (*SplitSetBuilder) Finish ¶ added in v0.24.0
func (b *SplitSetBuilder) Finish() (*BuiltSplitSet, error)
Finish seals the final open split and serializes the manifest, returning the manifest bytes and every split's (filename, RRS bytes). Errors if a single document's postings alone exceed the byte cap (a degenerate corpus).
type SplitSetConfig ¶ added in v0.24.0
type SplitSetConfig struct {
Policy int // PolicyTiered | PolicyStableKey
BodyKind uint8 // BodyKindTrigram (RRS) | BodyKindTerm (RRTI); 0 keeps older manifests byte-identical
TierCount uint16 // number of rank tiers (tiered); 0 for stable-key
BaseCount uint32 // splits [0, BaseCount) are base, the rest delta
ByteCap uint64 // the per-split byte cap (informational)
GramSize uint16 // n-gram window the splits were built with (for Bloom pruning); 0 for a term-bodied set
SortCol *SortColSpec // stable-key rank source, or nil
Flags uint16 // header summary-presence flags
}
SplitSetConfig is the manifest-level configuration.
type SplitSpec ¶ added in v0.24.0
type SplitSpec struct {
DataFile string // the split's RRS data-file name
Tier uint16 // rank tier (tiered policy; 0 for stable-key / delta)
DocCount uint32 // docs in the split
DocIDLo uint32 // min global doc id present (inclusive); the local-id base
DocIDHi uint32 // max global doc id present (inclusive)
Epoch uint64 // flush/build epoch (supersession ordering)
ByteSize uint64 // the split RRS file size in bytes
Flags uint16 // per-split flags (SplitFlagHasTombstone | SplitFlagAbsoluteIDs)
Summary []byte // opaque summary TLV bytes (Bloom / facet / time / tombstone)
}
SplitSpec is one split recorded in the manifest. The split's RRS bytes live in its own DataFile and are not passed here.
type TermLanguage ¶ added in v0.24.0
type TermLanguage uint8
TermLanguage selects the Snowball stemmer recorded in the RRTI header (the on-disk language byte; values match the Rust Language::to_u8).
const ( // TermLanguageNone builds an unstemmed index with no stop-word list. TermLanguageNone TermLanguage = 0 // The Snowball languages, byte values matching the Rust Language::to_u8. All are wired // for stemming on the Go build side (see stemAlgorithm) and every language has a // stop-word list (stopwordFile), so a stemmed and/or stop-words index can be built in // any of them. TermLanguageEnglish TermLanguage = 1 TermLanguageSpanish TermLanguage = 2 TermLanguageArabic TermLanguage = 3 TermLanguageDanish TermLanguage = 4 TermLanguageDutch TermLanguage = 5 TermLanguageFinnish TermLanguage = 6 TermLanguageFrench TermLanguage = 7 TermLanguageGerman TermLanguage = 8 TermLanguageGreek TermLanguage = 9 TermLanguageHungarian TermLanguage = 10 TermLanguageItalian TermLanguage = 11 TermLanguageNorwegian TermLanguage = 12 TermLanguagePortuguese TermLanguage = 13 TermLanguageRomanian TermLanguage = 14 TermLanguageRussian TermLanguage = 15 TermLanguageSwedish TermLanguage = 16 TermLanguageTamil TermLanguage = 17 TermLanguageTurkish TermLanguage = 18 )
type TermSplitBuildConfig ¶ added in v0.24.0
type TermSplitBuildConfig struct {
Policy int // PolicyTiered | PolicyStableKey
ByteCap uint64 // per-split seal target (the FIRST tier's cap when CapMax > 0)
CapMax uint64 // geometric tiering: tier i's cap = min(ByteCap << i, CapMax); 0 = flat
HeadBoundary uint32 // head/tail doc-ID split; 0 -> 65536
NamePrefix string // split filenames: ‹prefix›-s00000.rrt, …
SortCol *SortColSpec // stable-key rank source, or nil
Language TermLanguage // index language, shared by Stem and Stopwords; required when either is set
Stem bool // apply Snowball stemming in Language (independent of Stopwords)
Stopwords bool // remove the language's stop words (and from queries); requires Language
// CaseSensitive builds a case-sensitive term split set: terms and facet keys are NOT
// lowercased (each split's RRTI carries the case-sensitive flag, the RRSF keys are
// case-sensitive, and the manifest sets its case-sensitive flag). The zero value (false)
// is the default case-folding behavior, byte-identical to before. Inverse of the Rust
// TermSplitBuildConfig.case_normalization (Go uses the zero-value-safe inverse).
CaseSensitive bool
}
TermSplitBuildConfig configures a TermSplitSetBuilder.
type TermSplitSetBuilder ¶ added in v0.24.0
type TermSplitSetBuilder struct {
// contains filtered or unexported fields
}
TermSplitSetBuilder accumulates documents and seals them into byte-capped RRTI splits with per-split facet sidecars.
func NewTermSplitSetBuilder ¶ added in v0.24.0
func NewTermSplitSetBuilder(cfg TermSplitBuildConfig) *TermSplitSetBuilder
NewTermSplitSetBuilder creates a builder. A HeadBoundary of 0 takes the RRS default.
func (*TermSplitSetBuilder) AddFaceted ¶ added in v0.24.0
AddFaceted is AddText plus the document's facet memberships, recorded into the open split's RRSF sidecar and facet-presence summary.
func (*TermSplitSetBuilder) AddText ¶ added in v0.24.0
func (b *TermSplitSetBuilder) AddText(text string) (uint32, error)
AddText tokenizes text and appends it as one document, returning its global doc id. A token-less document still consumes an id (dense id space).
func (*TermSplitSetBuilder) DocCount ¶ added in v0.24.0
func (b *TermSplitSetBuilder) DocCount() uint32
DocCount is the number of documents added so far.
func (*TermSplitSetBuilder) Finish ¶ added in v0.24.0
func (b *TermSplitSetBuilder) Finish() (*BuiltSplitSet, error)
Finish seals the final open split and serializes the manifest (BodyKindTerm, gramSize 0), returning the manifest and every split's bytes. Errors if any single document's postings alone exceed the byte cap.
type TermTokenizer ¶ added in v0.24.0
type TermTokenizer struct {
// contains filtered or unexported fields
}
TermTokenizer is the configured token-filter chain (base tokenize → optional stop-word removal → optional Snowball stemming), fixed at build time and recorded in the header so queries tokenize identically. Mirrors the Rust terms::Tokenizer.
func NewTermTokenizer ¶ added in v0.24.0
func NewTermTokenizer(lang TermLanguage, stopwords bool) *TermTokenizer
NewTermTokenizer builds the chain for the given language / stop-word setting, with case folding on (the default). Mirrors the Rust Tokenizer::new(.., true).
func NewTermTokenizerFull ¶ added in v0.26.0
func NewTermTokenizerFull(lang TermLanguage, stem, stopwords, caseFold bool) *TermTokenizer
NewTermTokenizerFull builds the chain with independent stem and stopwords filters over a shared language (mirrors the Rust Tokenizer::with). The stemmer is created only when stem is set, so an index can strip a language's stop words without stemming. Every go-stemmers language is wired (see stemAlgorithm); a stem request for TermLanguageNone or an unsupported byte leaves the stemmer nil.
func NewTermTokenizerWith ¶ added in v0.25.0
func NewTermTokenizerWith(lang TermLanguage, stopwords, caseFold bool) *TermTokenizer
NewTermTokenizerWith builds the chain with an explicit case-fold flag. Back-compat: a non-None language implies stemming (the historical coupling). Prefer NewTermTokenizerFull to strip a language's stop words without stemming.
func (*TermTokenizer) Tokenize ¶ added in v0.24.0
func (t *TermTokenizer) Tokenize(text string) []string
Tokenize applies the full chain to text.
type TrigramMonolithBuilder ¶ added in v0.24.0
type TrigramMonolithBuilder struct {
// contains filtered or unexported fields
}
TrigramMonolithBuilder accumulates trigram postings over a whole corpus into a single v3 RRS index. Docs are added in ascending doc-ID order — each Add returns the doc's id, and an empty doc (no trigrams) still consumes an id so the doc-ID space stays dense and aligned with the records / facet / lookup sidecars. Write seals the accumulated postings to one RRSI index, byte-identical to a split set's single split over the same docs. All postings are held in memory; for a 100+ GB corpus use the chunked Rust builder instead.
func NewTrigramMonolithBuilder ¶ added in v0.24.0
func NewTrigramMonolithBuilder(gramSize uint16, stride int) *TrigramMonolithBuilder
NewTrigramMonolithBuilder opens a monolith builder for the given trigram size and sparse stride, with case folding on (the default). A gramSize of zero defaults to 3; a stride of zero or less to DefaultStride.
func NewTrigramMonolithBuilderWith ¶ added in v0.25.0
func NewTrigramMonolithBuilderWith(gramSize uint16, stride int, caseSensitive bool) *TrigramMonolithBuilder
NewTrigramMonolithBuilderWith opens a monolith builder; when caseSensitive is true the trigram keys are not lowercased and Write emits a case-sensitive v4 index. caseSensitive false reproduces the default (case-folding) behavior byte-for-byte.
func (*TrigramMonolithBuilder) AddKeys ¶ added in v0.24.0
func (b *TrigramMonolithBuilder) AddKeys(keys []uint64) uint32
AddKeys indexes the given trigram keys under the next doc ID and returns that ID. An empty keys slice still advances the doc-ID space (the doc is indexed as having no trigrams).
func (*TrigramMonolithBuilder) AddText ¶ added in v0.24.0
func (b *TrigramMonolithBuilder) AddText(text string) uint32
AddText tokenizes text into gramSize-gram trigram keys and indexes them under the next doc ID, returning that ID. Mirrors SplitSetBuilder.AddText.
func (*TrigramMonolithBuilder) DocCount ¶ added in v0.24.0
func (b *TrigramMonolithBuilder) DocCount() uint32
DocCount returns the number of docs added so far (the next doc ID).