A simple, type-safe, full-text document search library for Go powered by SQLite FTS5.
- Type-safe document storage and retrieval using Go generics
- Full-text search capabilities using SQLite FTS5
- Automatic UUID generation for documents without IDs
- Support for in-memory and file-based databases
- Simple and intuitive API
- Relatively fast. hlx can index thousands of documents per second in a modern laptop and perform sub-second searches, thanks to the blazing fast SQLite FTS5 engine.
go get github.com/rubiojr/hlx
Important
When building examples and running tests, `--tags fts5 needs to be passed to the go command, to enable FTS5 support in some SQLite drivers like mattn/go-sqlite3.
package main
import (
"fmt"
"github.com/rubiojr/hlx"
_ "github.com/mattn/go-sqlite3"
)
// Define your document structure
type Document struct {
Id string // Id field needs to be defined but not set
Title string
Content string
}
func main() {
// Create a new index (in-memory database)
idx, err := hlx.NewIndex[Document](":memory:")
if err != nil {
panic(err)
}
// Insert documents
err = idx.Insert(
Document{
Title: "Hello World",
Content: "This is my first document",
},
Document{
Title: "Second Post",
Content: "Another example document",
},
)
if err != nil {
panic(err)
}
// Search documents
results, err := idx.Search("first OR example")
if err != nil {
panic(err)
}
// Print results
for _, doc := range results {
fmt.Printf("Found document: %s - %s\n", doc.Title, doc.Content)
}
}
// Create an in-memory index with default settings
idx, err := hlx.NewIndex[Document](":memory:")
// Create a persistent database
idx, err := hlx.NewIndex[Document]("./documents.db")
import (
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)
// Use your own database connection
db, err := sqlx.Open("sqlite3", ":memory:")
if err != nil {
panic(err)
}
idx, err := hlx.NewIndex[Document]("", hlx.WithDB(db))
// Use custom SQLite pragmas for performance tuning
customPragmas := []string{
"PRAGMA journal_mode=WAL",
"PRAGMA synchronous=NORMAL",
"PRAGMA cache_size=20000",
"PRAGMA temp_store=memory",
"PRAGMA busy_timeout=5000",
}
idx, err := hlx.NewIndex[Document]("./documents.db", hlx.WithPragmas(customPragmas))
// Combine multiple options
db, err := sqlx.Open("sqlite3", "./documents.db")
if err != nil {
panic(err)
}
customPragmas := []string{
"PRAGMA journal_mode=WAL",
"PRAGMA cache_size=50000",
}
idx, err := hlx.NewIndex[Document]("",
hlx.WithDB(db),
hlx.WithPragmas(customPragmas),
)
// Use a specific SQLite driver (default is "sqlite3")
idx, err := hlx.NewIndex[Document]("./documents.db",
hlx.WithSQLiteDriver("modernc.org/sqlite"),
)
The search syntax follows SQLite FTS5 query syntax. Here are some examples:
// Simple word search
results, _ := idx.Search("hello")
// Phrase search
results, _ := idx.Search(`"hello world"`)
// Boolean operators
results, _ := idx.Search("hello OR world")
results, _ := idx.Search("hello AND world")
// Column-specific search
results, _ := idx.Search("title:hello")
// Complex queries
results, _ := idx.Search(`(title:"hello world") AND content:example`)
// Exclude columns from search
results, _ := idx.Search(`- { title content } : "hello"`)
// Get document by ID
doc, err := idx.Get("some-id")
// Delete document by ID
err := idx.Delete("some-id")
// Get available fields
fields := idx.Fields()
See performance.txt.
- The document struct must have an
Id
field (case-sensitive) - If no ID is provided when inserting a document, a UUID will be automatically generated
- All struct fields will be indexed and searchable
- Field names are case-insensitive in searches
See LICENSE.