dendrite.nvim/core/services/index_rebuilder.go
Kristian 478f309108
chore(idx_rebuilder): mv to core and use repos (#29)
* ref(index_rebuilder): mv to core

* ref(idx_rebuilder): moved db acc code to repos

* feat(idx_rebuilder): mv idx rebuilder to core and implemented build
2026-04-18 15:57:30 +02:00

173 lines
4.3 KiB
Go

package services
import (
"context"
"io/fs"
"log/slog"
"path/filepath"
"slices"
"strings"
filehandling "github.com/KristianJBorgwarth/dendrite.daemon/core/file_handling"
"github.com/KristianJBorgwarth/dendrite.daemon/core/models"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence"
"github.com/KristianJBorgwarth/dendrite.daemon/persistence/repositories"
)
type IIndexRebuilder interface {
RebuildIndex(ctx context.Context, vaultRoot string) error
}
type indexRebuilder struct {
uow *repositories.UnitOfWork
noteRepo repositories.INoteRepository
linkRepo repositories.ILinkRepository
tagRepo repositories.ITagRepository
indexRepo repositories.IIndexRepository
}
func NewIndexRebuilder(
uow *repositories.UnitOfWork,
noteRepo repositories.INoteRepository,
linkRepo repositories.ILinkRepository,
tagRepo repositories.ITagRepository,
indexRepo repositories.IIndexRepository,
) *indexRebuilder {
return &indexRebuilder{
uow: uow,
noteRepo: noteRepo,
linkRepo: linkRepo,
tagRepo: tagRepo,
indexRepo: indexRepo,
}
}
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()
if err != nil {
return err
}
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
}
if err = r.buildIndex(ctx, dbctx, notes, links, tags, noteTags); err != nil {
r.uow.Rollback()
slog.Debug("Failed to build index, rolling back transaction", "error", err)
return err
}
return nil
}
func (r *indexRebuilder) buildIndex(
ctx context.Context,
dbctx persistence.IDbContext,
notes []*models.Note,
links []*models.Link,
tags []*models.Tag,
noteTags []*models.NoteTag,
) error {
if err := r.noteRepo.InsertRange(ctx, dbctx, notes); err != nil {
return err
}
if err := r.linkRepo.InsertRange(ctx, dbctx, links); err != nil {
return err
}
if err := r.tagRepo.InsertRange(ctx, dbctx, tags); err != nil {
return err
}
if err := r.tagRepo.InsertNoteTags(ctx, dbctx, noteTags); err != nil {
return err
}
return nil
}
func (r *indexRebuilder) readFiles(vault string) ([]*filehandling.File, error) {
var files []*filehandling.File
err := filepath.WalkDir(vault, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
slog.Debug("Processing file during index rebuild", "path", path)
if d.IsDir() {
if !r.shouldIndexDirectory(path) {
slog.Debug("Skipping directory during index rebuild", "path", path)
return filepath.SkipDir
}
return nil
}
if filepath.Ext(path) != ".md" {
slog.Debug("Skipping non-markdown file during index rebuild", "path", path)
return nil
}
pendingFile, err := filehandling.ReadFile(path)
if err != nil {
return err
}
files = append(files, pendingFile)
return nil
})
if err != nil {
return nil, err
}
return files, nil
}
func (r *indexRebuilder) shouldIndexDirectory(path string) bool {
ignoredDirs := []string{".git", ".templates", "temp", "issues"}
for part := range strings.SplitSeq(path, string(filepath.Separator)) {
if slices.Contains(ignoredDirs, part) {
return false
}
}
return true
}
func (r *indexRebuilder) buildDBModels(files []*filehandling.File) ([]*models.Note, []*models.Link, []*models.Tag, []*models.NoteTag) {
var notes []*models.Note
var links []*models.Link
tagMap := make(map[string]*models.Tag)
var noteTags []*models.NoteTag
for _, file := range files {
note := models.CreateNote(file.Path, file.Title, file.Slug)
notes = append(notes, note)
for _, t := range models.CreateTags(file.FrontMatter.Tags) {
tagMap[t.Name()] = t
}
noteTags = append(noteTags, models.CreateNoteTags(note.ID(), file.FrontMatter.Tags)...)
links = append(links, models.MapToLinkModel(note.ID(), file.ExtractedLinks)...)
}
tags := make([]*models.Tag, 0, len(tagMap))
for _, tag := range tagMap {
tags = append(tags, tag)
}
return notes, links, tags, noteTags
}