Documentation
¶
Overview ¶
Package imdb provides a CGO-free SQLite catalog of IMDb titles and facts. Update creates or refreshes the catalog atomically from streaming gzip datasets. Catalog.Resolve returns a Resolution only for unique exact or conservatively normalized names; AmbiguousError reports bounded conflicts without a winner. Catalog.Candidates returns heuristic Candidate suggestions, never identities. Catalog.View scopes multi-call reads to one committed generation. Invalid input returns ValidationError matching ErrInvalidInput. Readers and refreshes require schema v2. IMDb data is subject to separate personal, non-commercial licensing terms.
Index ¶
- Variables
- type AmbiguousError
- type Candidate
- type Catalog
- func (c *Catalog) Candidates(ctx context.Context, q Query, opts SearchOptions) (SearchResult, error)
- func (c *Catalog) Close() error
- func (c *Catalog) Info(ctx context.Context) (Info, error)
- func (c *Catalog) Lookup(ctx context.Context, id string) (Title, error)
- func (c *Catalog) LookupMany(ctx context.Context, ids []string) (map[string]Title, error)
- func (c *Catalog) Names(ctx context.Context, id string) ([]Name, error)
- func (c *Catalog) Resolve(ctx context.Context, q Query) (Resolution, error)
- func (c *Catalog) View(ctx context.Context, fn func(*View) error) error
- type Counts
- type Dataset
- type DatasetReport
- type HTTPError
- type HTTPSource
- type Info
- type Kind
- type MatchMethod
- type Name
- type NameSource
- type Progress
- type ProgressPhase
- type Query
- type Resolution
- type SearchOptions
- type SearchResult
- type Source
- type SourceFunc
- type SourceMetadata
- type Title
- type UpdateOptions
- type ValidationError
- type View
- func (v *View) Candidates(ctx context.Context, q Query, opts SearchOptions) (SearchResult, error)
- func (v *View) Info(ctx context.Context) (Info, error)
- func (v *View) Lookup(ctx context.Context, id string) (Title, error)
- func (v *View) LookupMany(ctx context.Context, ids []string) (map[string]Title, error)
- func (v *View) Names(ctx context.Context, id string) ([]Name, error)
- func (v *View) Resolve(ctx context.Context, q Query) (Resolution, error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotFound means no retained title satisfies the lookup or resolution. ErrNotFound = errors.New("imdb: title not found") // ErrAmbiguous is matched by AmbiguousError via errors.Is. ErrAmbiguous = errors.New("imdb: ambiguous title") // ErrInvalidInput is matched by ValidationError via errors.Is. ErrInvalidInput = errors.New("imdb: invalid input") // ErrSchema means the database is foreign, uninitialized, or incompatible. ErrSchema = errors.New("imdb: unsupported or uninitialized catalog") // ErrViewClosed means a View was used outside its callback or is uninitialized. ErrViewClosed = errors.New("imdb: read view closed") )
Functions ¶
This section is empty.
Types ¶
type AmbiguousError ¶
type AmbiguousError struct {
Titles []Title `json:"titles"`
Truncated bool `json:"truncated"`
Generation string `json:"generation"`
}
AmbiguousError reports conflicting titles sharing a conservatively normalized name under the query's filters. Titles contains at most 20 distinct titles, ordered by IMDb ID, not popularity. Truncated means more conflicts exist. All titles and Generation belong to the same snapshot. None is selected.
func (*AmbiguousError) Error ¶
func (e *AmbiguousError) Error() string
func (*AmbiguousError) Unwrap ¶
func (e *AmbiguousError) Unwrap() error
type Candidate ¶
type Candidate struct {
Title Title `json:"title"`
Name Name `json:"matched_name"`
Method MatchMethod `json:"method"`
Score float64 `json:"score"`
}
Candidate is a suggestion, never proof of identity, even for Exact matches. Score is ranking similarity, not a confidence probability. Use Resolve for automated identity selection rather than candidate count, method, or score.
type Catalog ¶
type Catalog struct {
// contains filtered or unexported fields
}
Catalog is a concurrency-safe read-only handle. Each operation reads one transaction snapshot; later operations automatically observe committed updates. Close releases its resources. Do not share the database over a network filesystem.
func Open ¶
Open opens an existing catalog read-only and validates its schema. It never creates a missing file. Path must be a filesystem path, not a SQLite URI.
func (*Catalog) Candidates ¶
func (c *Catalog) Candidates(ctx context.Context, q Query, opts SearchOptions) (SearchResult, error)
Candidates returns bounded exact, normalized, prefix, substring and whole-title typo suggestions. Substrings require three folded Unicode characters; typos require four and run only when direct matches do not fill Limit. Retrieval is heuristic: clipping flags do not measure recall. Invalid queries or options return *ValidationError. No suggestions returns an empty, non-nil Matches. Use Resolve, never candidate rank or count, for authoritative identity.
Example ¶
package main
import (
"context"
"fmt"
imdb "github.com/odwrtw/libimdb"
)
func main() {
catalog, err := imdb.Open(context.Background(), "imdb.sqlite")
if err != nil {
return
}
defer func() { _ = catalog.Close() }()
result, err := catalog.Candidates(context.Background(), imdb.Query{
Title: "matrix", Kind: imdb.Movie,
}, imdb.SearchOptions{Limit: 20})
if err != nil {
return
}
for _, match := range result.Matches {
fmt.Println(match.Title.ID, match.Title.PrimaryTitle, match.Method)
}
// These are suggestions, never proof of a unique identity.
fmt.Println("limit clipped:", result.LimitTruncated, "budget exhausted:", result.BudgetExhausted)
}
Output:
func (*Catalog) Close ¶
Close releases the catalog's resources and prevents new operations. Finish View callbacks before closing the Catalog.
func (*Catalog) Lookup ¶
Lookup returns the retained title with id, or ErrNotFound. IDs must be "tt" followed by one or more ASCII digits; invalid IDs return ValidationError. Valid IDs for unsupported title types are treated as not found.
func (*Catalog) LookupMany ¶
LookupMany returns a non-nil map of found titles, omitting absent IDs and collapsing duplicates. Empty input returns an empty map. All results belong to one snapshot. At most 10000 IDs are allowed; any invalid ID rejects the entire request with ValidationError. ID syntax is the same as for Lookup.
func (*Catalog) Names ¶
Names returns primary/original/alias names without deduplication. Ordering is dataset import order within this snapshot, not a preference ranking or stable across generations. An original name identical to the primary is not repeated as an OriginalName. It returns ErrNotFound for an absent title and ValidationError for invalid ID syntax (see Lookup).
func (*Catalog) Resolve ¶
Resolve accepts only a unique identity after Unicode NFC, case and whitespace normalization. It never uses fuzzy or popularity ranking. All matching names participate in ambiguity detection, even when a primary name is an exact spelling match. StartYear never falls back to another or unknown year. No match returns ErrNotFound; multiple identities return *AmbiguousError, matching ErrAmbiguous. Invalid queries return *ValidationError. On any error the returned Resolution is zero; ambiguity never selects a winner.
Example ¶
package main
import (
"context"
"errors"
"fmt"
imdb "github.com/odwrtw/libimdb"
)
func main() {
// Populate this catalog with imdb.Update before opening it.
catalog, err := imdb.Open(context.Background(), "imdb.sqlite")
if err != nil {
return
}
defer func() { _ = catalog.Close() }()
match, err := catalog.Resolve(context.Background(), imdb.Query{
Title: "The Matrix", Kind: imdb.Movie, StartYear: 1999,
})
var ambiguity *imdb.AmbiguousError
switch {
case errors.As(err, &ambiguity):
for _, title := range ambiguity.Titles {
fmt.Println(title.ID, title.PrimaryTitle)
}
fmt.Println("more conflicts:", ambiguity.Truncated)
case errors.Is(err, imdb.ErrNotFound):
fmt.Println("no safe identity match")
case err != nil:
fmt.Println("catalog failure:", err)
default:
fmt.Println(match.Title.ID, match.Generation)
}
}
Output:
func (*Catalog) View ¶
View runs fn synchronously against one committed generation, releasing the snapshot even on callback error or panic. It returns fn's error, or the context error if fn succeeds after cancellation. A nil fn is invalid input. Use the supplied View for all reads inside fn, not the Catalog. Keep callbacks short: long-lived snapshots can prevent SQLite from reclaiming WAL space.
Example ¶
package main
import (
"context"
"fmt"
imdb "github.com/odwrtw/libimdb"
)
func main() {
ctx := context.Background()
catalog, err := imdb.Open(ctx, "imdb.sqlite")
if err != nil {
return
}
defer func() { _ = catalog.Close() }()
err = catalog.View(ctx, func(view *imdb.View) error {
resolved, err := view.Resolve(ctx, imdb.Query{Title: "The Matrix"})
if err != nil {
return err
}
names, err := view.Names(ctx, resolved.Title.ID)
if err != nil {
return err
}
// Both results come from one generation, even across a refresh.
fmt.Println(resolved.Generation, names)
return nil
})
if err != nil {
fmt.Println(err)
}
}
Output:
type Counts ¶
type Counts struct {
Movies int64 `json:"movies"`
Shows int64 `json:"shows"`
Names int64 `json:"names"`
Ratings int64 `json:"ratings"`
}
Counts reports retained catalog rows. Names includes repeated raw aliases.
type Dataset ¶
type Dataset string
Dataset identifies one of the three required gzip-compressed IMDb TSV files.
type DatasetReport ¶
type DatasetReport struct {
SourceMetadata
Dataset Dataset `json:"dataset"`
SHA256 string `json:"sha256"`
Rows int64 `json:"rows"`
}
DatasetReport combines source metadata with facts computed by the importer. SHA256 hashes the consumed compressed input; it is not compared to an external expected digest. Rows excludes the header and includes filtered records.
type HTTPSource ¶
HTTPSource streams official datasets. BaseURL and Client allow test servers or caller-managed HTTP policy. An empty BaseURL uses https://datasets.imdbws.com/; a custom BaseURL must be absolute HTTP(S), without query or fragment. A nil Client uses a two-hour per-request timeout; callers should also supply an overall context deadline.
func (HTTPSource) Open ¶
func (s HTTPSource) Open(ctx context.Context, d Dataset) (io.ReadCloser, SourceMetadata, error)
Open downloads d without transparent HTTP decompression. The caller owns the returned body on success. Invalid datasets or BaseURL return ValidationError; non-200 responses return HTTPError after closing the response body.
type Info ¶
type Info struct {
SchemaVersion int `json:"schema_version"`
NormalizationVersion int `json:"normalization_version"`
Generation string `json:"generation"`
UpdatedAt time.Time `json:"updated_at"`
Counts Counts `json:"counts"`
Sources []DatasetReport `json:"sources"`
}
Info describes one committed catalog generation and its dataset provenance. UpdatedAt is the UTC time recorded immediately before commit.
func Update ¶
Update creates or refreshes a catalog in one transaction, without dataset temporary files or whole-download buffers. The catalog must be owned by this library using the current schema or empty. Foreign, unversioned and incompatible-schema databases are never overwritten. WAL is used for concurrent readers; SQLite's own transaction files are required. A failed update preserves previous committed data; a failed first creation may leave an empty, uninitialized database file.
type MatchMethod ¶
type MatchMethod string
MatchMethod describes how a name matched, not whether its identity is unique.
const ( Exact MatchMethod = "exact" Normalized MatchMethod = "normalized" Prefix MatchMethod = "prefix" Substring MatchMethod = "substring" Fuzzy MatchMethod = "fuzzy" )
type Name ¶
type Name struct {
Title string `json:"title"`
Source NameSource `json:"source"`
Region string `json:"region,omitempty"`
Language string `json:"language,omitempty"`
Types []string `json:"types,omitempty"`
Attributes []string `json:"attributes,omitempty"`
IsOriginal *bool `json:"is_original"`
}
Name is a primary title, distinct original title, or raw alias. Empty Region and Language and nil Types and Attributes mean unavailable or not applicable. IsOriginal is IMDb's alias isOriginalTitle field; it is nil for primary and original names and is not a general test for whether a name is original.
type NameSource ¶
type NameSource string
NameSource identifies the dataset role of a name, not its language or region.
const ( PrimaryName NameSource = "primary" OriginalName NameSource = "original" AliasName NameSource = "alias" )
type Progress ¶
type Progress struct {
Dataset Dataset
Rows int64 // successfully processed records, excluding header, including filtered records
Phase ProgressPhase
Attempt int // one-based; zero if options were rejected before any attempt
Attempts int // maximum attempts, not necessarily all used
BytesRead int64
TotalBytes int64 // source-supplied compressed size; zero means unknown, totals are advisory
Err error // non-nil only for Retrying and Failed, including cancellation
}
Progress is a snapshot of a streaming update. Counters reset for each dataset and full-update attempt. BytesRead counts compressed input, not decompressed TSV bytes; buffering means bytes and rows advance independently. Reaching TotalBytes does not imply dataset validation or catalog commit has completed.
type ProgressPhase ¶
type ProgressPhase string
ProgressPhase identifies the current operation. Only Opening, Importing, and DatasetDone carry a Dataset and per-dataset counters; other phases are update-wide.
const ( Preparing ProgressPhase = "preparing" Opening ProgressPhase = "opening" Importing ProgressPhase = "importing" DatasetDone ProgressPhase = "dataset_done" Indexing ProgressPhase = "indexing" Validating ProgressPhase = "validating" Committing ProgressPhase = "committing" Retrying ProgressPhase = "retrying" Done ProgressPhase = "done" Failed ProgressPhase = "failed" )
type Query ¶
Query takes a title, not a filename. Title must be valid UTF-8 without NUL, nonempty after normalization, at most 1024 bytes and 256 normalized Unicode code points. Empty Kind searches both kinds. StartYear zero is unspecified; a positive value is a hard constraint excluding unknown years. ExcludeAdult excludes only titles explicitly marked adult, not those with unknown flags.
type Resolution ¶
type Resolution struct {
Title Title `json:"title"`
Name Name `json:"matched_name"`
Method MatchMethod `json:"method"`
Generation string `json:"generation"`
}
Resolution is a unique identity accepted by Resolve. Method is Exact or Normalized; Name is the matching name. Generation identifies its snapshot.
type SearchOptions ¶
type SearchOptions struct {
Limit int // default 20, range 1–100
Budget int // entries/postings inspected per stage, before filters; default 1000, range 1–10000
}
SearchOptions bounds heuristic retrieval. Zero values select defaults. Budget counts distinct IDs in the exact stage, folded names in prefix and substring stages, and postings across all selected typo streams combined. It applies before year/adult and similarity filters; different names for one title can consume multiple entries. Each stream may read one extra row to detect clipping. Budgets bound these stages, not overall query time.
type SearchResult ¶
type SearchResult struct {
Matches []Candidate `json:"matches"`
LimitTruncated bool `json:"limit_truncated"` // retrieved matches were omitted to satisfy Limit
BudgetExhausted bool `json:"budget_exhausted"` // a retrieval stage was clipped by Budget
Generation string `json:"generation"`
}
SearchResult contains distinct-ID suggestions ordered by method, descending score, descending votes, ID, then matched name. Matches is non-nil on success. Neither clipping flag measures recall or promises exhaustive retrieval.
type Source ¶
type Source interface {
Open(context.Context, Dataset) (io.ReadCloser, SourceMetadata, error)
}
Source supplies complete gzip streams. Open and the returned reader must honor cancellation. On success, Open returns a non-nil reader owned and closed by Update; on error, the source must release any acquired resources. Calls are sequential within an update, but retries reopen datasets and require fresh complete streams starting at byte zero. Shared sources must handle concurrent independent updates if used that way. Source metadata is advisory.
type SourceFunc ¶
type SourceFunc func(context.Context, Dataset) (io.ReadCloser, SourceMetadata, error)
SourceFunc adapts a function to Source.
func (SourceFunc) Open ¶
func (f SourceFunc) Open(ctx context.Context, d Dataset) (io.ReadCloser, SourceMetadata, error)
Open calls f with the requested dataset.
type SourceMetadata ¶
type SourceMetadata struct {
URL string `json:"url,omitempty"`
ETag string `json:"etag,omitempty"`
LastModified string `json:"last_modified,omitempty"`
SizeBytes int64 `json:"size_bytes,omitempty"`
}
SourceMetadata is supplied by a source, not computed or verified by Update. URL is a provenance location and may be a local path. Empty strings mean unavailable; SizeBytes <= 0 means unknown. SizeBytes is an advisory compressed size for progress, not an integrity assertion. LastModified is opaque metadata.
type Title ¶
type Title struct {
ID string `json:"imdb_id"`
Kind Kind `json:"kind"`
TitleType string `json:"title_type"`
PrimaryTitle string `json:"primary_title"`
OriginalTitle string `json:"original_title,omitempty"`
StartYear *int `json:"start_year"`
EndYear *int `json:"end_year"`
RuntimeMinutes *int `json:"runtime_minutes"`
Adult *bool `json:"is_adult"`
Genres []string `json:"genres"`
Rating *float64 `json:"rating"`
Votes *int64 `json:"votes"`
}
Title contains IMDb facts, not provider enrichment or application state. Nil numeric/boolean fields and nil Genres are unavailable. An empty OriginalTitle is unavailable. StartYear is the release or series start year. Only movie, tvMovie, tvSeries, and tvMiniSeries title types are retained.
type UpdateOptions ¶
type UpdateOptions struct {
Source Source // nil uses the official HTTPSource
Attempts int // default 1, maximum 5; retries restart the entire transaction
RetryDelay time.Duration // default one second
// Progress receives synchronous snapshots, including lifecycle and retry
// events. It must return promptly and must not reenter Update. Ordinary
// counters are throttled to about four events/second; boundaries are always
// reported. Nil disables reporting. Cancel the context to stop an update.
Progress func(Progress)
}
UpdateOptions controls source policy, whole-transaction retries, and reporting. Zero values select the documented defaults. Invalid options return ValidationError.
type ValidationError ¶
ValidationError identifies an invalid argument or option. Field is a Go field name (for example, Query.Title or UpdateOptions.Attempts), or an argument name. Problem describes the constraint without echoing potentially sensitive input.
Example ¶
package main
import (
"context"
"errors"
"fmt"
imdb "github.com/odwrtw/libimdb"
)
func main() {
_, err := imdb.Open(context.Background(), "")
var invalid *imdb.ValidationError
if errors.As(err, &invalid) {
fmt.Println(invalid.Field)
}
fmt.Println(errors.Is(err, imdb.ErrInvalidInput))
}
Output: path true
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
func (*ValidationError) Unwrap ¶
func (e *ValidationError) Unwrap() error
type View ¶
type View struct {
// contains filtered or unexported fields
}
View is a read-only snapshot scoped to a Catalog.View callback. Its zero value is closed. Do not copy it, retain it, or use it concurrently. Do not call the same Catalog from its callback: a Catalog serializes operations on one connection. Results remain valid after the callback; the View itself does not.
func (*View) Candidates ¶
func (v *View) Candidates(ctx context.Context, q Query, opts SearchOptions) (SearchResult, error)
Candidates is like Catalog.Candidates, using this view's snapshot.
func (*View) Info ¶
Info returns metadata for this view's generation. The returned data can be modified by the caller without changing the view's metadata.
func (*View) LookupMany ¶
LookupMany is like Catalog.LookupMany, using this view's snapshot.