Documentation
¶
Overview ¶
Package gotoken implements SmartToken, a deterministic multilingual tokenization algorithm for hashed full-text search.
SmartToken splits each whitespace-delimited token at Unicode character-class and script boundaries into segments, then emits every contiguous run of at most D segments as an overlapping sub-token, where the depth D is chosen by a pluggable Policy. The whole token is always emitted, so exact-match lookups keep full recall. Each sub-token carries a TokenInfo with the detected language index and the byte range of the language-relevant portion.
Three policies are provided: FixedPolicy returns a constant depth, LinearPolicy interpolates the depth from the segment count (the default), and CountPolicy picks the largest depth whose output stays within a hard per-token budget, which is the recommended choice for capacity-planned indexes.
A Tokenizer is immutable once constructed and safe for concurrent use, provided its Policy is too; all policies in this package are.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CountPolicy ¶
type CountPolicy struct {
// contains filtered or unexported fields
}
CountPolicy limits the total number of subtokens generated per input token. It calculates the optimal depth to stay within the limit while:
- Always including the whole token
- Adding complete levels from smallest to largest subtokens
- Never partially filling a level
The whole token is charged against maxCount, so a token never expands into more than maxCount map entries.
func NewCountPolicy ¶
func NewCountPolicy(maxCount int) *CountPolicy
NewCountPolicy creates a policy that limits total subtoken count.
The algorithm prioritizes:
- Whole token (always included)
- Single-segment subtokens (depth 1)
- Two-segment subtokens (depth 2)
- And so on, until adding another level would exceed maxCount
func (*CountPolicy) Depth ¶
func (p *CountPolicy) Depth(segments int) int
Depth calculates the maximum depth that keeps total subtokens ≤ maxCount.
For N segments at depth D, the sliding window emits D*N - D*(D-1)/2 subtokens. Tokenize always emits the whole token on top of those whenever the window is too narrow to span it, so one slot is reserved for it and the largest D satisfying D*N - D*(D-1)/2 ≤ maxCount-1 is returned.
Returns 0 if the remaining budget is too small for even single-segment subtokens, meaning only the whole token should be emitted.
type FixedPolicy ¶
type FixedPolicy struct {
// contains filtered or unexported fields
}
FixedPolicy returns a constant depth regardless of segment count.
func NewFixedPolicy ¶
func NewFixedPolicy(depth int) *FixedPolicy
NewFixedPolicy creates a policy with constant depth. A negative depth is clamped to 0 (whole token only).
func (*FixedPolicy) Depth ¶
func (p *FixedPolicy) Depth(segments int) int
Depth returns the configured constant depth.
type LinearPolicy ¶
type LinearPolicy struct {
// contains filtered or unexported fields
}
LinearPolicy interpolates depth linearly between two points. For tokens with few segments (≤ shortLen), it returns shortDepth. For tokens with many segments (≥ longLen), it returns longDepth. For tokens in between, it linearly interpolates.
func NewLinearPolicy ¶
func NewLinearPolicy(shortLen, shortDepth, longLen, longDepth int) *LinearPolicy
NewLinearPolicy creates a policy that linearly interpolates depth.
Parameters:
- shortLen: tokens with this many segments or fewer get shortDepth
- shortDepth: depth for short tokens (typically higher)
- longLen: tokens with this many segments or more get longDepth
- longDepth: depth for long tokens (typically lower)
func (*LinearPolicy) Depth ¶
func (p *LinearPolicy) Depth(segments int) int
Depth returns the interpolated depth for the given segment count.
type Option ¶
type Option func(*Tokenizer)
Option configures a Tokenizer.
func WithLanguage ¶
func WithLanguage(rt *unicode.RangeTable) Option
WithLanguage adds a Unicode range table for language detection. The order of added languages determines their index (0, 1, 2, ...). A nil range table is ignored.
func WithLanguages ¶
func WithLanguages(tables ...*unicode.RangeTable) Option
WithLanguages adds multiple Unicode range tables for language detection. Nil range tables are ignored.
func WithPolicy ¶
WithPolicy sets the tokenization depth policy.
A nil Policy selects the default. That covers a nil interface only — an interface holding a nil pointer, as in WithPolicy(p) for a nil *FixedPolicy p, is itself non-nil and will fault on the first Depth call.
type Policy ¶
type Policy interface {
// Depth returns the maximum tokenization depth for a token with given segments.
Depth(segments int) int
}
Policy determines the tokenization depth based on segment count. A segment is a contiguous run of runes sharing a character class and, for letters, a detected language. Depth controls how many levels of sub-tokens are generated.
A Tokenizer calls Depth once per token, so an implementation shared by concurrent Tokenize calls must be safe for concurrent use. The policies in this package are immutable and therefore safe.
type RuneClass ¶
type RuneClass int
RuneClass categorizes Unicode runes into character classes.
Classes are numbered from 1. Zero is reserved as "no class assigned", so a default-initialized RuneClass never reads as a real classification.
const ( // ClassLetter covers Unicode letters (unicode.IsLetter). ClassLetter RuneClass // ClassDigit covers decimal digits only (unicode.IsDigit); other // numerals fall into ClassOther. ClassDigit // ClassPunct covers Unicode punctuation (unicode.IsPunct). ClassPunct // ClassOther covers every rune outside the classes above, including // symbols, marks, and non-decimal numerics. ClassOther )
type TokenInfo ¶
type TokenInfo struct {
// Language is the detected language index, or -1 if unknown or
// undetermined.
Language int
// Base contains half-open byte offsets [start, end) of the
// language-relevant portion, relative to the start of the (sub-)token, so
// subtoken[Base[0]:Base[1]] slices it directly.
//
// The zero value [0, 0] is a sentinel meaning "undetermined": it is used
// for non-letter tokens and for sub-tokens spanning more than
// maxAnalyzedSegments segments, where no single language span is
// identified. This holds on every path, including the whole-token-only one
// taken when a policy reports a depth of zero.
Base [2]int
}
TokenInfo contains metadata about a detected token.
type Tokenizer ¶
type Tokenizer struct {
// contains filtered or unexported fields
}
Tokenizer implements the SmartToken tokenization algorithm. It generates sub-tokens with language detection for multilingual text.
A Tokenizer is immutable once constructed, and each call to Tokenize uses its own scanning state, so it is safe for concurrent use by multiple goroutines provided the configured Policy is too. All policies in this package are.
The map returned by Tokenize is not shared and carries no such restriction.
func (*Tokenizer) Tokenize ¶
Tokenize processes the input string and returns all detected tokens with their info.
The result is keyed by sub-token text, so identical sub-token strings are deduplicated and the last occurrence wins. Map keys are sub-slices of source, so the returned map keeps source's backing storage alive.