package db import ( "context" "time" "github.com/dtoro/oikos/internal/core/ports" "github.com/google/uuid" ) // KnowledgeRepo implements ports.KnowledgeRepository. type KnowledgeRepo struct { pool *Pool } var _ ports.KnowledgeRepository = (*KnowledgeRepo)(nil) func NewKnowledgeRepo(pool *Pool) *KnowledgeRepo { return &KnowledgeRepo{pool: pool} } func (r *KnowledgeRepo) Search(ctx context.Context, query string, limit int) ([]ports.KnowledgeEntry, error) { rows, err := r.pool.Query(ctx, ` SELECT k.slug, k.title, k.kind, k.updated_at FROM knowledge_entities k WHERE k.deleted_at IS NULL AND (k.slug ILIKE '%'||$1||'%' OR k.title ILIKE '%'||$1||'%' OR k.content ILIKE '%'||$1||'%') ORDER BY k.updated_at DESC LIMIT $2`, query, limit) if err != nil { return nil, err } defer rows.Close() var items []ports.KnowledgeEntry for rows.Next() { entry, err := scanKnowledgeEntry(rows) if err != nil { return nil, err } items = append(items, entry) } return items, rows.Err() } func (r *KnowledgeRepo) GetContent(ctx context.Context, slug string) (ports.KnowledgeEntry, error) { return scanKnowledgeEntry(r.pool.QueryRow(ctx, ` SELECT slug, title, kind, content, updated_at FROM knowledge_entities WHERE slug = $1 AND deleted_at IS NULL`, slug)) } func (r *KnowledgeRepo) Upsert(ctx context.Context, input ports.KnowledgeUpsertInput) (ports.KnowledgeEntry, error) { id, _ := uuid.NewV7() _, err := r.pool.Exec(ctx, ` INSERT INTO knowledge_entities (slug, title, kind, content, updated_at) VALUES ($1, $2, $3, $4, now()) ON CONFLICT (slug) DO UPDATE SET title = $2, kind = $3, content = $4, updated_at = now()`, input.Entry.Slug, input.Entry.Title, input.Entry.Kind, input.Entry.Content) if err != nil { return ports.KnowledgeEntry{}, err } _ = id return input.Entry, nil } func (r *KnowledgeRepo) Tags(ctx context.Context) (map[string]int, error) { return nil, nil } func (r *KnowledgeRepo) SoftDelete(ctx context.Context, slug string) error { _, err := r.pool.Exec(ctx, `UPDATE knowledge_entities SET deleted_at = now() WHERE slug = $1`, slug) return err } func (r *KnowledgeRepo) Restore(ctx context.Context, slug string) error { _, err := r.pool.Exec(ctx, `UPDATE knowledge_entities SET deleted_at = NULL WHERE slug = $1`, slug) return err } func (r *KnowledgeRepo) Revisions(ctx context.Context, slug string, limit int) ([]ports.KnowledgeEntry, error) { return nil, nil } func (r *KnowledgeRepo) Orphans(ctx context.Context, staleDays int) ([]ports.KnowledgeEntry, error) { return nil, nil } func (r *KnowledgeRepo) Duplicates(ctx context.Context, threshold float64) ([]ports.KnowledgeEntry, error) { return nil, nil } func (r *KnowledgeRepo) Merge(ctx context.Context, targetSlug string, sourceSlugs []string) error { return nil } func scanKnowledgeEntry(row interface{ Scan(dest ...any) error }) (ports.KnowledgeEntry, error) { var slug, title, kind, content string var updatedAt time.Time if err := row.Scan(&slug, &title, &kind, &content, &updatedAt); err != nil { return ports.KnowledgeEntry{}, err } return ports.KnowledgeEntry{ Slug: slug, Title: title, Kind: kind, Content: content, UpdatedAt: updatedAt, }, nil }