feat: add target deletion functionality with cascaded removal of associated jobs and tasks

This commit is contained in:
2026-08-22 01:15:05 -07:00
parent af1ee1c9b3
commit 367929e7d0
9 changed files with 501 additions and 21 deletions
+81 -2
View File
@@ -2,6 +2,7 @@ package db
import (
"database/sql"
"fmt"
"math"
"regexp"
"strings"
@@ -83,6 +84,86 @@ func (db *DB) UpdateTargetCrawledAt(id int64) error {
return err
}
func (db *DB) DeleteTarget(id int64) (*SearchTarget, int64, error) {
tx, err := db.Begin()
if err != nil {
return nil, 0, err
}
defer func() { _ = tx.Rollback() }()
// 1. Get target info before deleting
var target SearchTarget
var lastCrawled sql.NullTime
err = tx.QueryRow(`SELECT id, name, target_url, platform, is_active, last_crawled_at FROM search_targets WHERE id = ?`, id).Scan(
&target.ID, &target.Name, &target.TargetURL, &target.Platform, &target.IsActive, &lastCrawled,
)
if err != nil {
return nil, 0, err
}
if lastCrawled.Valid {
target.LastCrawledAt = &lastCrawled.Time
}
// 2. Find all raw_jobs IDs associated with this target
rows, err := tx.Query(`SELECT id, canonical_id FROM raw_jobs WHERE target_id = ?`, id)
if err != nil {
return nil, 0, err
}
var rawJobIDs []int64
var canonicalIDs []string
for rows.Next() {
var rID int64
var cID string
if err := rows.Scan(&rID, &cID); err != nil {
_ = rows.Close()
return nil, 0, err
}
rawJobIDs = append(rawJobIDs, rID)
canonicalIDs = append(canonicalIDs, cID)
}
_ = rows.Close()
// 3. Delete tasks referencing this target or its discovered jobs
targetIDPattern := fmt.Sprintf(`%%"target_id":%d%%`, id)
targetIDPatternStr := fmt.Sprintf(`%%"target_id": %d%%`, id)
_, _ = tx.Exec(`DELETE FROM tasks WHERE payload LIKE ? OR payload LIKE ?`, targetIDPattern, targetIDPatternStr)
for _, cid := range canonicalIDs {
cPattern := fmt.Sprintf(`%%"canonical_id":"%s"%%`, cid)
_, _ = tx.Exec(`DELETE FROM tasks WHERE payload LIKE ?`, cPattern)
}
for _, rID := range rawJobIDs {
rPattern := fmt.Sprintf(`%%"raw_job_id":%d%%`, rID)
rPatternStr := fmt.Sprintf(`%%"raw_job_id": %d%%`, rID)
_, _ = tx.Exec(`DELETE FROM tasks WHERE payload LIKE ? OR payload LIKE ?`, rPattern, rPatternStr)
}
// 4. Delete job_applications and job_analyses for these raw_jobs
for _, rID := range rawJobIDs {
_, _ = tx.Exec(`DELETE FROM job_applications WHERE raw_job_id = ?`, rID)
_, _ = tx.Exec(`DELETE FROM job_analyses WHERE raw_job_id = ?`, rID)
}
// 5. Delete raw_jobs for this target
res, err := tx.Exec(`DELETE FROM raw_jobs WHERE target_id = ?`, id)
if err != nil {
return nil, 0, err
}
deletedJobsCount, _ := res.RowsAffected()
// 6. Delete search_target
_, err = tx.Exec(`DELETE FROM search_targets WHERE id = ?`, id)
if err != nil {
return nil, 0, err
}
if err := tx.Commit(); err != nil {
return nil, 0, err
}
return &target, deletedJobsCount, nil
}
// Raw Jobs Queries & Vector Similarity Deduplication
func (db *DB) InsertRawJob(canonicalID string, targetID *int64, title, company, location, applyURL, rawContent string) (*RawJob, bool, error) {
// Deduplication Check 1 & 2: Check canonical_id exist AND vector similarity >= 99%
@@ -207,8 +288,6 @@ func tokenizeToVector(text string) map[string]int {
return freqMap
}
// Job Analyses Queries
func (db *DB) CreateJobAnalysis(rawJobID int64, score float64, isQualified bool, status string) error {
query := `