API Reference

Contracts and behavior for pkg/acor. Generated signatures live on pkg.go.dev.

Creating a collection

ac, err := acor.Create(&acor.AhoCorasickArgs{...})
defer ac.Close()

CreateContext is the same constructor with a context bounding the setup I/O — the schema check, the initialization write, and the initial keyword load:

setupCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
instance, err := acor.CreateContext(setupCtx, &acor.AhoCorasickArgs{
    Addr: "localhost:6379",
    Name: "default",
})
_ = instance
_ = err

That context bounds construction only. Cancelling it later neither closes the instance nor stops its invalidation listener — use Close for that and the *Context methods for per-operation cancellation. The Pub/Sub subscribe belongs to the listener, so it runs on the instance’s own context.

AhoCorasickArgs

type AhoCorasickArgs struct {
    Addr                            string            // Standalone Redis address (conflicts with Addrs)
    Addrs                           []string          // Sentinel or Cluster addresses (one entry still means cluster)
    RingAddrs                       map[string]string // Ring shard addresses
    MasterName                      string            // Sentinel master name
    Password                        string            // Redis password
    DB                              int               // Redis database number (default: 0; rejected with Addrs)
    DialTimeout                     time.Duration     // Connection timeout (zero: go-redis default)
    ReadTimeout                     time.Duration     // Socket read timeout (zero: go-redis default)
    WriteTimeout                    time.Duration     // Socket write timeout (zero: go-redis default)
    MaxRetries                      int               // Command retries (zero: go-redis default; -1: disabled)
    PoolSize                        int               // Connections per server (zero: go-redis default)
    Name                            string            // Collection name (required)
    Debug                           bool              // Send the default logger to stdout (ignored when Logger is set)
    Logger                          Logger            // Custom logger (nil disables logging)
    SchemaVersion                   int               // 0 or 2: V2 (default, optimized); 1: V1 (deprecated)
    EnableCache                     bool              // Local caching for Find/FindIndex (V2 only, not with Preset)
    SelfInvalidationCleanupInterval uint64            // Cleanup frequency for self-invalidation map (default: 128)
    CaseSensitive                   bool              // Enable case-sensitive matching (default: false)
    RollbackTimeout                 time.Duration     // V1 flush/rollback timeout, not the caller's ctx (default: 10s)
    Preset                          Preset            // Architecture preset (default: PresetNone)
    InvalidationPollInterval        time.Duration     // Preset version polling (zero: disabled)
}

Setting Preset switches reads to a local automaton — see Preset-Optimized Engine. Topology fields are covered in Redis topologies.

Writing

MethodReturnsNotes
Add(keyword)(int, error)1 if the collection changed, 0 if it was already there
Remove(keyword)(int, error)Same convention
AddMany(keywords, *BatchOptions)(*BatchResult, error)One transaction; nil options mean best-effort
RemoveMany(keywords, *BatchOptions)(*BatchResult, error)Same
Flush()errorDeletes every key in the collection
Close()errorCloses the connection and stops the invalidation listener
result, err := ac.RemoveMany([]string{"a", "b"}, nil)
// Pass options when the whole batch must land or fail together.
result, err = ac.RemoveMany([]string{"c", "d"}, &acor.BatchOptions{
    Mode: acor.BatchModeTransactional,
})
_ = result
_ = err

See Batch Operations for the modes and result shape.

Matching

MethodReturnsShape
Find(text)[]stringWhich keywords occur
FindIndex(text)map[string][]intKeyword to its rune start positions
FindSet(text)[]stringEach keyword once, in first-match order
FindMatches(text, *MatchOptions)[]MatchEvery occurrence with its rune span
Contains(text)boolStops at the first match
FindStream(reader, callback)errorScans an io.Reader without buffering it
FindMany(texts)map[string][]stringKeyed by input text
FindParallel(text, *ParallelOptions)[]stringChunked across workers
FindIndexParallel(text, *ParallelOptions)map[string][]intSame chunking, with positions

All positions are rune offsets, not byte offsets.

FindMatches

Every occurrence in scan order with its half-open rune span [Start, End). The default includes overlaps; MatchKindLeftmostLongest produces non-overlapping spans suitable for tokenizing or replacing.

matches, err := ac.FindMatches("classic class", &acor.MatchOptions{
    Kind:      acor.MatchKindLeftmostLongest,
    WholeWord: true,
})
_ = matches
_ = err
type Match struct {
    Keyword string
    Start   int // Rune offset, inclusive
    End     int // Rune offset, exclusive
}

type MatchOptions struct {
    Kind      MatchKind
    WholeWord bool
    WordRune  func(rune) bool // Optional whole-word predicate
}

const (
    MatchKindOverlapping MatchKind = iota // Default
    MatchKindLeftmostLongest
)

WholeWord treats letters, digits, combining marks, and underscores as word runes. Set WordRune for scripts those defaults do not fit — in CJK or Thai text every adjacent character counts as a word rune, so nearly every match is dropped as mid-word.

FindStream

Matches keep rune offsets across reads and arrive in scan order; returning false from the callback stops the scan.

err := ac.FindStream(strings.NewReader("sample text"), func(match acor.Match) bool {
    _ = match
    return true
})
_ = err

Streaming always overlaps: whole-word and leftmost-longest need buffering, so use FindMatches on a bounded string when you need them.

Parallel

type ParallelOptions struct {
    Workers     int           // Concurrent goroutines (default: runtime.NumCPU())
    ChunkSize   int           // Target chunk size in runes (required; no fallback)
    Boundary    ChunkBoundary // How chunks are split (default: ChunkBoundaryWord)
    Overlap     int           // Overlap runes between chunks (unset means zero)
    AutoOverlap bool          // Extend each chunk by the dictionary's longest keyword (default: false)
}

const (
    ChunkBoundaryWord     ChunkBoundary = iota // Split at whitespace (default)
    ChunkBoundarySentence                      // Split at . ! ?
    ChunkBoundaryLine                          // Split at newlines
)

DefaultParallelOptions() returns a usable starting point. Without AutoOverlap, a keyword longer than Overlap can be missed at a chunk boundary; with it, chunks extend by the dictionary’s longest keyword at no extra Redis cost. Details: Parallel Matching.

Suggest

Suggest(prefix) returns keywords starting with the prefix; SuggestIndex(prefix) returns the same keys mapped to [0], since a prefix match always starts at the beginning. Both require Redis and are unavailable in Preset mode (ErrSuggestRequiresRedis).

Batch types

type BatchOptions struct {
    Mode BatchMode // BatchModeBestEffort (default) or BatchModeTransactional
}

type BatchResult struct {
    Added   []string       // Successfully added keywords
    Removed []string       // Successfully removed keywords
    Failed  []KeywordError // Keywords that failed, with their errors
    Skipped []string       // Duplicate adds or absent removes
}

type KeywordError struct {
    Keyword string
    Error   error
}

Statistics

Info() reads Redis; CacheStats() does not, so it is cheap to scrape on a timer.

type AhoCorasickInfo struct {
    Keywords    int    // Number of keywords
    Nodes       int    // Number of trie nodes (states)
    Preset      Preset // Architecture preset (internal default sentinel in original mode)
    MemoryBytes int64  // Estimated memory usage in bytes (zero in original mode)
    TrieDepth   int    // Maximum trie depth (zero in original mode)
}
type CacheStats struct {
    PresetReloadFailures uint64        // Failed shared reload jobs, once per job (Preset only; cancellation excluded)
    PresetPollFailures   uint64        // Failed version polls (Preset with polling enabled only)
    Hits                 uint64        // Reads served without rebuilding the automaton
    Misses               uint64        // Reads that waited for a rebuild
    Rebuilds             uint64        // Automaton builds (starts at 1 in Preset mode)
    RebuildDuration      time.Duration // Cumulative build time, excluding Redis I/O
    LastInvalidationLag  time.Duration // Last peer invalidation delay (Preset/EnableCache only; carries clock skew)
}

CacheStats is returned by ACOR and never constructed by callers, so fields may be added inside v1. Counters are per instance and per process — scrape every instance in a fleet. How to read them, including why Rebuilds does not equal Misses: Monitoring.

Presets

const (
    PresetNone            Preset = iota // Zero value — original V1/V2 mode
    PresetSpeed                         // Full DFA + flat array — max speed, higher memory
    PresetBalanced                      // Double-Array Trie + Banded DFA — best speed-to-memory ratio
    PresetMemoryEfficient               // Map-based + Bloom filter — min memory, slower search
)

Context variants

Every method that may touch Redis has a *Context twin taking an explicit context.Context: AddContext, RemoveContext, FindContext, FindIndexContext, FindSetContext, FindMatchesContext, ContainsContext, FindStreamContext, FlushContext, InfoContext, SuggestContext, SuggestIndexContext, AddManyContext, RemoveManyContext, FindManyContext, FindParallelContext, and FindIndexParallelContext.

matches, err := ac.FindMatchesContext(ctx, text, nil)

Beyond V1/V2

  • OpenVersioned(ctx, *VersionedOptions) opens a separate V3 collection with leased snapshots, paginated list/diff, expected-version writes, background engine refresh, and operation receipts. See Versioned dictionaries.
  • Scan, MaskText, ReplaceText report original byte and rune spans under explicit work limits, on both AhoCorasick and VersionedCollection. See bounded text processing.