feat(rebuild_guard): only build index on init if empty index (#30)

* feat(persistence): added build-flag and repo funcs

* feat(init_handler): note count check to avoid unnecessary rebuilds

* ref(build_flag): remove build flag is redundant

* build
This commit is contained in:
Kristian 2026-04-23 20:38:37 +02:00 committed by GitHub
parent 478f309108
commit c1feac0812
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 62 additions and 30 deletions

View file

@ -20,16 +20,18 @@ func main() {
server := server.NewServer() server := server.NewServer()
uow := repositories.NewUnitOfWork(); uow := repositories.NewUnitOfWork();
indexRepo := repositories.NewIndexRepository()
indexRepo := repositories.NewIndexRepository(*persistence.NewReadContext())
linkRepo := repositories.NewLinkRepository(*persistence.NewReadContext()) linkRepo := repositories.NewLinkRepository(*persistence.NewReadContext())
tagRepo := repositories.NewTagRepository() tagRepo := repositories.NewTagRepository()
noteRepo := repositories.NewNoteRepository(*persistence.NewReadContext()) noteRepo := repositories.NewNoteRepository(*persistence.NewReadContext())
tagService := services.NewTagService(tagRepo) tagService := services.NewTagService(tagRepo)
linkService := services.NewLinkService(linkRepo) linkService := services.NewLinkService(linkRepo)
noteService := services.NewNoteService(tagRepo, linkRepo, noteRepo) noteService := services.NewNoteService(tagRepo, linkRepo, noteRepo)
idxr := services.NewIndexRebuilder(uow, noteRepo, linkRepo, tagRepo, indexRepo ) idxr := services.NewIndexRebuilder(uow, noteRepo, linkRepo, tagRepo, indexRepo )
server.RegisterHandler("vault/init", vault.NewInitializeHandler(idxr)) server.RegisterHandler("vault/init", vault.NewInitializeHandler(idxr, noteService))
server.RegisterHandler("note/create", note.NewCreateNoteHandler(uow, tagService, noteRepo)) server.RegisterHandler("note/create", note.NewCreateNoteHandler(uow, tagService, noteRepo))
server.RegisterHandler("note/save", note.NewSaveNoteHandler(uow, noteRepo, tagService, noteService, linkService)) server.RegisterHandler("note/save", note.NewSaveNoteHandler(uow, noteRepo, tagService, noteService, linkService))
server.RegisterHandler("note/goto", note.NewGotoNoteHandler(noteRepo)) server.RegisterHandler("note/goto", note.NewGotoNoteHandler(noteRepo))

View file

@ -3,6 +3,7 @@ package vault
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"log/slog"
"github.com/KristianJBorgwarth/dendrite.daemon/core/services" "github.com/KristianJBorgwarth/dendrite.daemon/core/services"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence" "github.com/KristianJBorgwarth/dendrite.daemon/persistence"
@ -17,10 +18,11 @@ type initializeCommand struct {
type InitializeHandler struct { type InitializeHandler struct {
idxRebuilder services.IIndexRebuilder idxRebuilder services.IIndexRebuilder
noteService services.INoteService
} }
func NewInitializeHandler(idxR services.IIndexRebuilder) *InitializeHandler { func NewInitializeHandler(idxR services.IIndexRebuilder, nsv services.INoteService) *InitializeHandler {
return &InitializeHandler{idxRebuilder: idxR} return &InitializeHandler{idxRebuilder: idxR, noteService: nsv}
} }
func (h InitializeHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) { func (h InitializeHandler) Handle(ctx context.Context, raw json.RawMessage) (any, error) {
@ -37,7 +39,18 @@ func (h InitializeHandler) Handle(ctx context.Context, raw json.RawMessage) (any
return nil, err return nil, err
} }
if err = h.idxRebuilder.RebuildIndex(ctx, cmd.VaultPath); err != nil { noteCount, err := h.noteService.GetNoteCount(ctx)
if err != nil {
return nil, err
}
if noteCount > 0 {
slog.Debug("Vault already initialized, skipping index rebuild")
return nil, nil
}
err = h.idxRebuilder.RebuildIndex(ctx, cmd.VaultPath)
if err != nil {
return nil, err return nil, err
} }

View file

@ -43,30 +43,27 @@ func NewIndexRebuilder(
} }
func (r *indexRebuilder) RebuildIndex(ctx context.Context, vaultRoot string) error { func (r *indexRebuilder) RebuildIndex(ctx context.Context, vaultRoot string) error {
files, err := r.readFiles(vaultRoot)
if err != nil {
slog.Debug("Failed to read files from vault", "vaultRoot", vaultRoot, "error", err)
return err
}
slog.Debug("Successfully read files from vault", "vaultRoot", vaultRoot, "fileCount", len(files))
notes, links, tags, noteTags := r.buildDBModels(files)
dbctx, err := r.uow.Begin() dbctx, err := r.uow.Begin()
if err != nil { if err != nil {
return err return err
} }
files, err := r.readFiles(vaultRoot)
if err != nil {
return err
}
notes, links, tags, noteTags := r.buildDBModels(files)
if err = r.indexRepo.WipeIndex(ctx, dbctx); err != nil { if err = r.indexRepo.WipeIndex(ctx, dbctx); err != nil {
r.uow.Rollback()
slog.Debug("Failed to wipe index, rolling back transaction", "error", err)
return err return err
} }
if err = r.buildIndex(ctx, dbctx, notes, links, tags, noteTags); err != nil { if err = r.buildIndex(ctx, dbctx, notes, links, tags, noteTags); err != nil {
r.uow.Rollback() return err
slog.Debug("Failed to build index, rolling back transaction", "error", err) }
if err = r.uow.Commit(); err != nil {
return err return err
} }
@ -110,7 +107,7 @@ func (r *indexRebuilder) readFiles(vault string) ([]*filehandling.File, error) {
slog.Debug("Processing file during index rebuild", "path", path) slog.Debug("Processing file during index rebuild", "path", path)
if d.IsDir() { if d.IsDir() {
if !r.shouldIndexDirectory(path) { if !r.IsValidDirectory(path) {
slog.Debug("Skipping directory during index rebuild", "path", path) slog.Debug("Skipping directory during index rebuild", "path", path)
return filepath.SkipDir return filepath.SkipDir
} }
@ -138,7 +135,7 @@ func (r *indexRebuilder) readFiles(vault string) ([]*filehandling.File, error) {
return files, nil return files, nil
} }
func (r *indexRebuilder) shouldIndexDirectory(path string) bool { func (r *indexRebuilder) IsValidDirectory(path string) bool {
ignoredDirs := []string{".git", ".templates", "temp", "issues"} ignoredDirs := []string{".git", ".templates", "temp", "issues"}
for part := range strings.SplitSeq(path, string(filepath.Separator)) { for part := range strings.SplitSeq(path, string(filepath.Separator)) {
if slices.Contains(ignoredDirs, part) { if slices.Contains(ignoredDirs, part) {

View file

@ -12,6 +12,7 @@ type INoteService interface {
CreateNote(ctx context.Context, dbCtx persistence.IDbContext, path, title, slug string) (*models.Note, error) CreateNote(ctx context.Context, dbCtx persistence.IDbContext, path, title, slug string) (*models.Note, error)
DeleteNoteMetaData(ctx context.Context, dbCtx persistence.IDbContext, noteID string) error DeleteNoteMetaData(ctx context.Context, dbCtx persistence.IDbContext, noteID string) error
UpdateNote(ctx context.Context, dbCtx persistence.IDbContext, noteID, path, title, slug string) error UpdateNote(ctx context.Context, dbCtx persistence.IDbContext, noteID, path, title, slug string) error
GetNoteCount(ctx context.Context) (int, error)
} }
type noteService struct { type noteService struct {
@ -55,3 +56,7 @@ func (s *noteService) DeleteNoteMetaData(ctx context.Context, dbCtx persistence.
return nil return nil
} }
func (s *noteService) GetNoteCount(ctx context.Context) (int, error) {
return s.noteRepo.GetNoteCount(ctx)
}

BIN
dendrite

Binary file not shown.

View file

@ -10,11 +10,12 @@ type IIndexRepository interface {
WipeIndex(ctx context.Context, dbContext persistence.IDbContext) error WipeIndex(ctx context.Context, dbContext persistence.IDbContext) error
} }
type indexRepository struct{ type indexRepository struct {
readDBContext persistence.ReadContext
} }
func NewIndexRepository() IIndexRepository { func NewIndexRepository(rdb persistence.ReadContext) IIndexRepository {
return &indexRepository{} return &indexRepository{readDBContext: rdb}
} }
func (r *indexRepository) WipeIndex(ctx context.Context, dbContext persistence.IDbContext) error { func (r *indexRepository) WipeIndex(ctx context.Context, dbContext persistence.IDbContext) error {

View file

@ -14,14 +14,15 @@ type INoteRepository interface {
InsertRange(ctx context.Context, dbContext persistence.IDbContext, note []*models.Note) error InsertRange(ctx context.Context, dbContext persistence.IDbContext, note []*models.Note) error
Update(ctx context.Context, dbContext persistence.IDbContext, noteID, path, title, slug string) error Update(ctx context.Context, dbContext persistence.IDbContext, noteID, path, title, slug string) error
GetBySlug(ctx context.Context, slug string) (*models.Note, error) GetBySlug(ctx context.Context, slug string) (*models.Note, error)
GetNoteCount(ctx context.Context) (int, error)
} }
type noteRepository struct { type noteRepository struct {
readDBContext persistence.ReadContext readDBContext persistence.ReadContext
} }
func NewNoteRepository(persistence.ReadContext) INoteRepository { func NewNoteRepository(rdb persistence.ReadContext) INoteRepository {
return &noteRepository{} return &noteRepository{readDBContext: rdb}
} }
func (r *noteRepository) Insert(ctx context.Context, dbContext persistence.IDbContext, note *models.Note) error { func (r *noteRepository) Insert(ctx context.Context, dbContext persistence.IDbContext, note *models.Note) error {
@ -82,3 +83,16 @@ func (r *noteRepository) GetBySlug(ctx context.Context, slug string) (*models.No
return models.NewNote(id, path, title, slug, createdAt, updatedAt), nil return models.NewNote(id, path, title, slug, createdAt, updatedAt), nil
} }
func (r *noteRepository) GetNoteCount(ctx context.Context) (int, error) {
query := `SELECT COUNT(*) FROM note`
row := r.readDBContext.QueryRowContext(ctx, query)
var count int
err := row.Scan(&count)
if err != nil {
return 0, err
}
return count, nil
}