store

package
v0.7.12 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 12, 2026 License: MIT Imports: 14 Imported by: 0

README

SQLite Help System

This package implements a SQLite-backed help system for the Glazed CLI and related tools. It replaces the previous in-memory slice-based system with a performant, searchable, and extensible SQLite database.

Features

  • Performance: Fast lookups even with hundreds or thousands of help sections
  • Full-text search: Advanced search capabilities using SQLite FTS5 (optional)
  • Predicate-based querying: Composable query language for complex filtering
  • Boolean logic: Support for AND, OR, and NOT operations
  • Flexible filtering: Filter by section type, topic, flag, command, slug, etc.
  • Backward compatibility: Drop-in replacement for existing help system
  • Build tag support: Optional FTS5 features via sqlite_fts5 build tag

Architecture

The system consists of several key components:

  • Model: Data structures for help sections (model.Section, model.SectionType)
  • Store: SQLite-backed storage with CRUD operations
  • Query: Predicate-based query language with SQL compilation
  • Loader: Markdown file parsing and data synchronization
  • Compat: Compatibility layer for existing help system interface

Quick Start

Basic Usage
package main

import (
    "log"
    "github.com/go-go-golems/glazed/pkg/help/store"
    "github.com/go-go-golems/glazed/pkg/help/model"
)

func main() {
    // Create an in-memory help system
    hs, err := store.NewInMemoryHelpSystem()
    if err != nil {
        log.Fatal(err)
    }
    defer hs.Close()

    // Add a section
    section := &model.Section{
        Slug:        "getting-started",
        Title:       "Getting Started",
        SectionType: model.SectionGeneralTopic,
        Content:     "This section covers the basics...",
        Topics:      []string{"basics", "introduction"},
        IsTopLevel:  true,
    }

    if err := hs.AddSection(section); err != nil {
        log.Fatal(err)
    }

    // Query sections
    topLevel, err := hs.Find(store.IsTopLevel())
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("Found %d top-level sections", len(topLevel))
}
Predicate-based Queries

The system uses a powerful predicate-based query language:

// Simple queries
examples := hs.Find(store.IsExample())
topLevel := hs.Find(store.IsTopLevel())
withTopic := hs.Find(store.HasTopic("configuration"))

// Boolean combinations
complexQuery := hs.Find(store.And(
    store.Or(store.IsExample(), store.IsTutorial()),
    store.HasTopic("basics"),
    store.ShownByDefault(),
))

// Text search (uses FTS5 if available, LIKE fallback otherwise)
searchResults := hs.Find(store.TextSearch("hello world"))

// Ordering and pagination
ordered := hs.Find(store.And(
    store.IsExample(),
    store.OrderByOrder(),
    store.Limit(10),
    store.Offset(20),
))
Available Predicates
Type Filters
  • IsType(sectionType) - Filter by section type
  • IsExample() - Filter for examples
  • IsTutorial() - Filter for tutorials
  • IsGeneralTopic() - Filter for general topics
  • IsApplication() - Filter for applications
Content Filters
  • HasTopic(topic) - Sections with specific topic
  • HasFlag(flag) - Sections with specific flag
  • HasCommand(command) - Sections with specific command
  • SlugEquals(slug) - Exact slug match
  • SlugIn(slugs) - Slug in list
  • TitleContains(term) - Title contains term
  • ContentContains(term) - Content contains term
  • TextSearch(term) - Full-text search
Metadata Filters
  • IsTopLevel() - Top-level sections
  • ShownByDefault() / NotShownByDefault() - Default display
  • IsTemplate() - Template sections
Ordering and Pagination
  • OrderByOrder() - Sort by order field
  • OrderByTitle() - Sort by title
  • OrderByCreatedAt() - Sort by creation time
  • Limit(n) - Limit results
  • Offset(n) - Skip results
Boolean Combinators
  • And(predicates...) - All predicates must match
  • Or(predicates...) - Any predicate must match
  • Not(predicate) - Predicate must not match

FTS5 Support

Full-text search is available when building with the sqlite_fts5 tag:

# Build with FTS5 support
go build -tags sqlite_fts5

# Test with FTS5 support
go test -tags sqlite_fts5

Without the tag, TextSearch() falls back to LIKE queries.

Loading Data

From Filesystem
import "embed"

//go:embed help/**/*.md
var helpFS embed.FS

// Load all markdown files from embedded filesystem
err := hs.LoadSectionsFromFS(helpFS, "help")

// Or sync (clears existing data first)
err := hs.SyncFromFS(helpFS, "help")
Markdown Format

Sections are defined in markdown files with YAML frontmatter:

---
Slug: getting-started
Title: Getting Started Guide
SectionType: Tutorial
Topics: [basics, setup]
Commands: [init, start]
Flags: [--verbose, --help]
IsTopLevel: true
ShowPerDefault: true
Order: 1
---

# Getting Started

This is the content of the help section...

Database Schema

The system creates the following tables:

  • sections - Main sections table with all metadata
  • sections_fts - FTS5 virtual table (when enabled)

Indexes are automatically created for performance on common query patterns.

Compatibility Layer

The HelpSystem type provides backward compatibility:

// Drop-in replacement for existing help system
hs, err := store.NewInMemoryHelpSystem()

// Use existing interface methods
section, err := hs.GetSectionWithSlug("example")
sections, err := hs.GetSections()
examples, err := hs.GetExamplesForTopic("basics")

Performance

The SQLite-backed system provides significant performance improvements:

  • Fast indexed lookups for common queries
  • Full-text search with ranking
  • Efficient pagination for large result sets
  • Minimal memory usage (data stored on disk)

Testing

# Run all tests
go test ./pkg/help/store/...

# Run with FTS5
go test -tags sqlite_fts5 ./pkg/help/store/...

# Run examples
go test -run Example ./pkg/help/store/...

Migration from Existing System

The new system is designed as a drop-in replacement:

  1. Replace help.NewHelpSystem() with store.NewInMemoryHelpSystem()
  2. Use existing methods or migrate to predicate-based queries
  3. Optionally enable FTS5 for enhanced search capabilities

The predicate system is more powerful than the existing SectionQuery, but the compatibility layer ensures existing code continues to work.

Documentation

Overview

Example

Example demonstrates basic usage of the SQLite help system

package main

import (
	"fmt"
	"log"

	"github.com/go-go-golems/glazed/pkg/help/model"
	"github.com/go-go-golems/glazed/pkg/help/store"
)

func main() {
	// Create an in-memory help system for demonstration
	hs, err := store.NewInMemoryHelpSystem()
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		_ = hs.Close()
	}()

	// Add some example sections
	sections := []*model.Section{
		{
			Slug:           "getting-started",
			Title:          "Getting Started",
			SectionType:    model.SectionGeneralTopic,
			Content:        "This section covers the basics of getting started.",
			Topics:         []string{"basics", "introduction"},
			IsTopLevel:     true,
			ShowPerDefault: true,
			Order:          1,
		},
		{
			Slug:           "example-hello",
			Title:          "Hello World Example",
			SectionType:    model.SectionExample,
			Content:        "A simple hello world example.",
			Topics:         []string{"basics", "examples"},
			Commands:       []string{"hello"},
			ShowPerDefault: true,
			Order:          2,
		},
		{
			Slug:           "advanced-tutorial",
			Title:          "Advanced Tutorial",
			SectionType:    model.SectionTutorial,
			Content:        "Advanced concepts and techniques.",
			Topics:         []string{"advanced", "tutorial"},
			ShowPerDefault: false,
			Order:          3,
		},
	}

	// Add sections to the help system
	for _, section := range sections {
		if err := hs.AddSection(section); err != nil {
			log.Fatal(err)
		}
	}

	// Query examples using the predicate system

	// 1. Find all top-level sections
	topLevel, err := hs.Find(store.IsTopLevel())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Top-level sections: %d\n", len(topLevel))

	// 2. Find examples related to "basics"
	basicExamples, err := hs.Find(store.And(
		store.IsExample(),
		store.HasTopic("basics"),
	))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Basic examples: %d\n", len(basicExamples))

	// 3. Complex query: Find sections that are either examples OR tutorials AND relate to "basics"
	complexQuery, err := hs.Find(store.And(
		store.Or(store.IsExample(), store.IsTutorial()),
		store.HasTopic("basics"),
	))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Examples or tutorials about basics: %d\n", len(complexQuery))

	// 4. Text search (uses LIKE fallback when FTS5 is not available)
	searchResults, err := hs.Find(store.TextSearch("hello"))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Search results for 'hello': %d\n", len(searchResults))

	// 5. Get sections shown by default, ordered by order field
	defaultSections, err := hs.Find(store.And(
		store.ShownByDefault(),
		store.OrderByOrder(),
	))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Default sections: %d\n", len(defaultSections))

	// 6. Convenience methods
	stats, err := hs.GetStats()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Total sections: %d\n", stats["total"])

}
Output:

Top-level sections: 1
Basic examples: 1
Examples or tutorials about basics: 1
Search results for 'hello': 1
Default sections: 2
Total sections: 3
Example (AdvancedQuery)

Example_advancedQuery demonstrates more complex querying capabilities

package main

import (
	"fmt"
	"log"

	"github.com/go-go-golems/glazed/pkg/help/model"
	"github.com/go-go-golems/glazed/pkg/help/store"
)

func main() {
	hs, err := store.NewInMemoryHelpSystem()
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		_ = hs.Close()
	}()

	// Add test data
	sections := []*model.Section{
		{
			Slug:        "cmd-help",
			Title:       "Command Help",
			SectionType: model.SectionExample,
			Topics:      []string{"commands"},
			Commands:    []string{"help", "man"},
			Flags:       []string{"--verbose", "--help"},
			Order:       1,
		},
		{
			Slug:        "config-tutorial",
			Title:       "Configuration Tutorial",
			SectionType: model.SectionTutorial,
			Topics:      []string{"configuration", "setup"},
			Commands:    []string{"config"},
			Flags:       []string{"--config"},
			Order:       2,
		},
	}

	for _, section := range sections {
		if err := hs.AddSection(section); err != nil {
			log.Fatal(err)
		}
	}

	// Complex query: Find sections that have the "help" command OR the "--help" flag
	results, err := hs.Find(store.Or(
		store.HasCommand("help"),
		store.HasFlag("--help"),
	))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Sections with 'help' command or '--help' flag: %d\n", len(results))

	// Query with negation: Find all sections that are NOT tutorials
	nonTutorials, err := hs.Find(store.Not(store.IsTutorial()))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Non-tutorial sections: %d\n", len(nonTutorials))

	// Pagination example: Get first 1 result, then skip 1 and get next 1
	firstResult, err := hs.Find(store.And(
		store.OrderByOrder(),
		store.Limit(1),
	))
	if err != nil {
		log.Fatal(err)
	}

	secondResult, err := hs.Find(store.And(
		store.OrderByOrder(),
		store.Limit(1),
		store.Offset(1),
	))
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("First result: %s\n", firstResult[0].Slug)
	fmt.Printf("Second result: %s\n", secondResult[0].Slug)

}
Output:

Sections with 'help' command or '--help' flag: 1
Non-tutorial sections: 1
First result: cmd-help
Second result: config-tutorial
Example (LegacyCompatibility)

Example_legacyCompatibility shows how to use the system as a drop-in replacement

package main

import (
	"fmt"
	"log"

	"github.com/go-go-golems/glazed/pkg/help/model"
	"github.com/go-go-golems/glazed/pkg/help/store"
)

func main() {
	// Create help system (compatible with existing interface)
	hs, err := store.NewInMemoryHelpSystem()
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		_ = hs.Close()
	}()

	// Add a section using the legacy-compatible method
	section := &model.Section{
		Slug:        "example-section",
		Title:       "Example Section",
		SectionType: model.SectionExample,
		Topics:      []string{"examples"},
	}

	if err := hs.AddSection(section); err != nil {
		log.Fatal(err)
	}

	// Use legacy-compatible methods
	retrieved, err := hs.GetSectionWithSlug("example-section")
	if err != nil {
		log.Fatal(err)
	}

	count, err := hs.Count()
	if err != nil {
		log.Fatal(err)
	}

	// Get examples for a topic (legacy-compatible)
	examples, err := hs.GetExamplesForTopic("examples")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("Retrieved section: %s\n", retrieved.Title)
	fmt.Printf("Total sections: %d\n", count)
	fmt.Printf("Examples for 'examples' topic: %d\n", len(examples))

}
Output:

Retrieved section: Example Section
Total sections: 1
Examples for 'examples' topic: 1

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type HelpSystem

type HelpSystem struct {
	// contains filtered or unexported fields
}

HelpSystem provides a compatibility layer with the existing help system interface

func NewHelpSystem

func NewHelpSystem(dbPath string) (*HelpSystem, error)

NewHelpSystem creates a new HelpSystem with SQLite backing

func NewInMemoryHelpSystem

func NewInMemoryHelpSystem() (*HelpSystem, error)

NewInMemoryHelpSystem creates a new in-memory HelpSystem

func (*HelpSystem) AddSection

func (hs *HelpSystem) AddSection(section *model.Section) error

AddSection adds a single section to the help system

func (*HelpSystem) Clear

func (hs *HelpSystem) Clear() error

Clear removes all sections from the help system

func (*HelpSystem) Close

func (hs *HelpSystem) Close() error

Close closes the underlying database connection

func (*HelpSystem) Count

func (hs *HelpSystem) Count() (int64, error)

Count returns the total number of sections

func (*HelpSystem) Find

func (hs *HelpSystem) Find(predicate Predicate) ([]*model.Section, error)

Find executes a predicate-based query

func (*HelpSystem) GetApplicationsForTopic

func (hs *HelpSystem) GetApplicationsForTopic(topic string) ([]*model.Section, error)

GetApplicationsForTopic returns applications for a specific topic

func (*HelpSystem) GetDefaultExamplesForTopic

func (hs *HelpSystem) GetDefaultExamplesForTopic(topic string) ([]*model.Section, error)

GetDefaultExamplesForTopic returns default examples for a specific topic

func (*HelpSystem) GetDefaultTutorialsForTopic

func (hs *HelpSystem) GetDefaultTutorialsForTopic(topic string) ([]*model.Section, error)

GetDefaultTutorialsForTopic returns default tutorials for a specific topic

func (*HelpSystem) GetExamplesForTopic

func (hs *HelpSystem) GetExamplesForTopic(topic string) ([]*model.Section, error)

GetExamplesForTopic returns examples for a specific topic

func (*HelpSystem) GetSectionWithSlug

func (hs *HelpSystem) GetSectionWithSlug(slug string) (*model.Section, error)

GetSectionWithSlug retrieves a section by its slug

func (*HelpSystem) GetSections

func (hs *HelpSystem) GetSections() ([]*model.Section, error)

GetSections returns all sections (for compatibility with existing interface)

func (*HelpSystem) GetSectionsByType

func (hs *HelpSystem) GetSectionsByType(sectionType model.SectionType) ([]*model.Section, error)

GetSectionsByType returns sections of a specific type

func (*HelpSystem) GetSectionsForCommand

func (hs *HelpSystem) GetSectionsForCommand(command string) ([]*model.Section, error)

GetSectionsForCommand returns sections related to a specific command

func (*HelpSystem) GetSectionsForFlag

func (hs *HelpSystem) GetSectionsForFlag(flag string) ([]*model.Section, error)

GetSectionsForFlag returns sections related to a specific flag

func (*HelpSystem) GetStats

func (hs *HelpSystem) GetStats() (map[string]int64, error)

GetStats returns statistics about the help system

func (*HelpSystem) GetTopLevelSections

func (hs *HelpSystem) GetTopLevelSections() ([]*model.Section, error)

GetTopLevelSections returns all top-level sections

func (*HelpSystem) GetTutorialsForTopic

func (hs *HelpSystem) GetTutorialsForTopic(topic string) ([]*model.Section, error)

GetTutorialsForTopic returns tutorials for a specific topic

func (*HelpSystem) LoadSectionsFromFS

func (hs *HelpSystem) LoadSectionsFromFS(filesystem fs.FS, dir string) error

LoadSectionsFromFS loads sections from a filesystem, compatible with existing interface

func (*HelpSystem) Loader

func (hs *HelpSystem) Loader() *Loader

Loader returns the underlying loader (for advanced usage)

func (*HelpSystem) SearchSections

func (hs *HelpSystem) SearchSections(term string) ([]*model.Section, error)

SearchSections performs a text search across sections

func (*HelpSystem) Store

func (hs *HelpSystem) Store() *Store

Store returns the underlying store (for advanced usage)

func (*HelpSystem) SyncFromFS

func (hs *HelpSystem) SyncFromFS(filesystem fs.FS, dir string) error

SyncFromFS synchronizes the help system with markdown files from a filesystem

type Loader

type Loader struct {
	// contains filtered or unexported fields
}

Loader handles loading and syncing markdown files into the SQLite store

func NewLoader

func NewLoader(store *Store) *Loader

NewLoader creates a new loader for the given store

func (*Loader) BatchUpsert

func (l *Loader) BatchUpsert(ctx context.Context, sections []*model.Section) error

BatchUpsert performs bulk upsert of sections

func (*Loader) GetSectionStats

func (l *Loader) GetSectionStats(ctx context.Context) (map[string]int64, error)

GetSectionStats returns statistics about the loaded sections

func (*Loader) LoadFromFS

func (l *Loader) LoadFromFS(ctx context.Context, filesystem fs.FS, rootDir string) error

LoadFromFS loads all markdown files from a filesystem into the store

func (*Loader) LoadFromMarkdown

func (l *Loader) LoadFromMarkdown(markdownBytes []byte) (*model.Section, error)

LoadFromMarkdown parses a markdown file and returns a Section

func (*Loader) LoadSections

func (l *Loader) LoadSections(ctx context.Context, markdownFiles map[string][]byte) error

LoadSections loads multiple sections from markdown bytes

func (*Loader) SyncFromFS

func (l *Loader) SyncFromFS(ctx context.Context, filesystem fs.FS, rootDir string) error

SyncFromFS synchronizes the store with markdown files from a filesystem This will clear the store and reload all sections

type Predicate

type Predicate func(*QueryCompiler)

Predicate represents a query predicate that can be compiled to SQL

func And

func And(predicates ...Predicate) Predicate

And combines multiple predicates with AND logic

func ContentContains

func ContentContains(term string) Predicate

ContentContains filters sections where content contains the term

func DefaultExamplesForTopic

func DefaultExamplesForTopic(topic string) Predicate

DefaultExamplesForTopic filters for default examples related to a specific topic

func DefaultTutorialsForTopic

func DefaultTutorialsForTopic(topic string) Predicate

DefaultTutorialsForTopic filters for default tutorials related to a specific topic

func ExamplesForTopic

func ExamplesForTopic(topic string) Predicate

ExamplesForTopic filters for examples related to a specific topic

func HasCommand

func HasCommand(command string) Predicate

HasCommand filters sections that have a specific command

func HasFlag

func HasFlag(flag string) Predicate

HasFlag filters sections that have a specific flag

func HasTopic

func HasTopic(topic string) Predicate

HasTopic filters sections that have a specific topic

func IsApplication

func IsApplication() Predicate

IsApplication filters for application sections

func IsExample

func IsExample() Predicate

IsExample filters for example sections

func IsGeneralTopic

func IsGeneralTopic() Predicate

IsGeneralTopic filters for general topic sections

func IsTemplate

func IsTemplate() Predicate

IsTemplate filters for template sections

func IsTopLevel

func IsTopLevel() Predicate

IsTopLevel filters sections that are top level

func IsTutorial

func IsTutorial() Predicate

IsTutorial filters for tutorial sections

func IsType

func IsType(sectionType model.SectionType) Predicate

IsType filters sections by type

func Limit

func Limit(limit int) Predicate

Limit sets the maximum number of results

func Not

func Not(predicate Predicate) Predicate

Not negates a predicate

func NotShownByDefault

func NotShownByDefault() Predicate

NotShownByDefault filters sections that are not shown by default

func Offset

func Offset(offset int) Predicate

Offset sets the number of results to skip

func Or

func Or(predicates ...Predicate) Predicate

Or combines multiple predicates with OR logic

func OrderByCreatedAt

func OrderByCreatedAt() Predicate

OrderByCreatedAt sorts sections by creation time

func OrderByOrder

func OrderByOrder() Predicate

OrderByOrder sorts sections by their order field

func OrderByTitle

func OrderByTitle() Predicate

OrderByTitle sorts sections by title

func ShownByDefault

func ShownByDefault() Predicate

ShownByDefault filters sections that are shown by default

func SlugEquals

func SlugEquals(slug string) Predicate

SlugEquals filters sections by exact slug match

func SlugIn

func SlugIn(slugs []string) Predicate

SlugIn filters sections by slug list

func TextSearch

func TextSearch(term string) Predicate

TextSearch performs a fallback text search using LIKE when FTS5 is not available

func TitleContains

func TitleContains(term string) Predicate

TitleContains filters sections where title contains the term

func TopLevelDefaults

func TopLevelDefaults() Predicate

TopLevelDefaults filters for top-level sections shown by default

func TutorialsForTopic

func TutorialsForTopic(topic string) Predicate

TutorialsForTopic filters for tutorials related to a specific topic

type QueryCompiler

type QueryCompiler struct {
	// contains filtered or unexported fields
}

QueryCompiler builds SQL queries from predicates

func NewQueryCompiler

func NewQueryCompiler() *QueryCompiler

NewQueryCompiler creates a new query compiler

func (*QueryCompiler) AddJoin

func (qc *QueryCompiler) AddJoin(join string)

AddJoin adds a JOIN clause

func (*QueryCompiler) AddWhere

func (qc *QueryCompiler) AddWhere(condition string, args ...interface{})

AddWhere adds a WHERE clause condition

func (*QueryCompiler) BuildQuery

func (qc *QueryCompiler) BuildQuery() (string, []interface{})

BuildQuery builds the complete SQL query

func (*QueryCompiler) SetLimit

func (qc *QueryCompiler) SetLimit(limit int)

SetLimit sets the LIMIT clause

func (*QueryCompiler) SetOffset

func (qc *QueryCompiler) SetOffset(offset int)

SetOffset sets the OFFSET clause

func (*QueryCompiler) SetOrderBy

func (qc *QueryCompiler) SetOrderBy(orderBy string)

SetOrderBy sets the ORDER BY clause

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store represents the SQLite-backed help system storage

func New

func New(dbPath string) (*Store, error)

New creates a new SQLite store

func NewInMemory

func NewInMemory() (*Store, error)

NewInMemory creates a new in-memory SQLite store

func (*Store) Clear

func (s *Store) Clear(ctx context.Context) error

Clear removes all sections from the store

func (*Store) Close

func (s *Store) Close() error

Close closes the database connection

func (*Store) Count

func (s *Store) Count(ctx context.Context) (int64, error)

Count returns the total number of sections

func (*Store) Delete

func (s *Store) Delete(ctx context.Context, id int64) error

Delete removes a section from the store

func (*Store) Find

func (s *Store) Find(ctx context.Context, predicate Predicate) ([]*model.Section, error)

Find executes a query using the provided predicate

func (*Store) GetByID

func (s *Store) GetByID(ctx context.Context, id int64) (*model.Section, error)

GetByID retrieves a section by its ID

func (*Store) GetBySlug

func (s *Store) GetBySlug(ctx context.Context, slug string) (*model.Section, error)

GetBySlug retrieves a section by its slug

func (*Store) Insert

func (s *Store) Insert(ctx context.Context, section *model.Section) error

Insert adds a new section to the store

func (*Store) List

func (s *Store) List(ctx context.Context, orderBy string) ([]*model.Section, error)

List retrieves all sections with optional ordering

func (*Store) Update

func (s *Store) Update(ctx context.Context, section *model.Section) error

Update modifies an existing section in the store

func (*Store) Upsert

func (s *Store) Upsert(ctx context.Context, section *model.Section) error

Upsert inserts or updates a section based on its slug

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL