From 9398e06943b953b6d37c670453b925c1a7e80eb0 Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Tue, 3 Feb 2026 22:58:07 -0700 Subject: [PATCH] (FIX): Removed legacy code from search API. --- .../database/repository/recipe_repository.go | 199 +----------------- 1 file changed, 3 insertions(+), 196 deletions(-) diff --git a/internal/infrastructure/database/repository/recipe_repository.go b/internal/infrastructure/database/repository/recipe_repository.go index 7a6f46d..ed76710 100644 --- a/internal/infrastructure/database/repository/recipe_repository.go +++ b/internal/infrastructure/database/repository/recipe_repository.go @@ -340,13 +340,14 @@ func isBitActive(bits, pos int) bool { // 12/28/25: This function has changed, now longer returns the recipes, but their IDs for fetching // elsewhere. // -// 2/3/26: Refactored this large function to use Squirrel for simpler generation. +// 2/3/26: Refactored this large function to use Squirrel for simpler generation. Reduced line count by 50, +// but this is still insane. We need to clean this up. // // This function will only return recipes that are not deleted. Any recipes marked deleted will be ignored // and the standard "not-found" error will be returned. func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters, userId *int, favorites bool) ([]int, error) { + // Begin creating the query psql := sq.StatementBuilder.PlaceholderFormat(sq.Dollar) - query := psql.Select("r.id").From("recipes r") // Only select fields where the recipe ID can be found in the favorites table (mapped to user ID) @@ -504,200 +505,6 @@ func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters, userId *i } return ids, nil - - // LEGACY CODE - // Compute meals type filters (there are 7 bits) - // var mealConditions []string - // for i := range 7 { - // if isBitActive(filters.MealType, i) { - // mealConditions = append(mealConditions, fmt.Sprintf("category = '%s'", domain.ParseMeal(i))) - // } - // } - // - // // Compute time filters (there are 5 bits) - // var timeConditions []string - // for i := range 5 { - // var cond string - // if isBitActive(filters.Time, i) { - // switch i { - // case 0: - // cond = "(duration->>'total')::int < 15" - // case 1: - // cond = "(duration->>'total')::int BETWEEN 15 AND 30" - // case 2: - // cond = "(duration->>'total')::int BETWEEN 30 AND 60" - // case 3: - // cond = "(duration->>'total')::int BETWEEN 60 AND 120" - // case 4: - // cond = "(duration->>'total')::int > 120" - // } - // timeConditions = append(timeConditions, cond) - // } - // } - // - // // Compute difficulty filters (there are 5 bits) - // var difficultyConditions []string - // for i := range 5 { - // if isBitActive(filters.Difficulty, i) { - // cond := fmt.Sprintf("difficulty = '%d'", i+1) - // difficultyConditions = append(difficultyConditions, cond) - // } - // } - // - // // Compute serving size filters (there are 5 bits) - // var servingConditions []string - // for i := range 5 { - // var cond string - // if isBitActive(filters.ServingSize, i) { - // switch i { - // case 0: - // cond = "serves BETWEEN 1 AND 2" - // case 1: - // cond = "serves BETWEEN 2 AND 4" - // case 2: - // cond = "serves BETWEEN 4 AND 6" - // case 3: - // cond = "serves BETWEEN 6 AND 8" - // case 4: - // cond = "serves > 8" - // } - // servingConditions = append(servingConditions, cond) - // } - // } - // - // // Merge condition strings - // mealString := fmt.Sprintf("(%s)", strings.Join(mealConditions, " OR ")) - // timeString := fmt.Sprintf("(%s)", strings.Join(timeConditions, " OR ")) - // difficultyString := fmt.Sprintf("(%s)", strings.Join(difficultyConditions, " OR ")) - // servingString := fmt.Sprintf("(%s)", strings.Join(servingConditions, " OR ")) - // - // // Combine condition strings - // var conditions []string - // if len(mealConditions) > 0 { - // conditions = append(conditions, mealString) - // } - // if len(timeConditions) > 0 { - // conditions = append(conditions, timeString) - // } - // if len(difficultyConditions) > 0 { - // conditions = append(conditions, difficultyString) - // } - // if len(servingConditions) > 0 { - // conditions = append(conditions, servingString) - // } - // - // // Define columns to select - // columns := []string{ - // "r.id", - // } - // - // // Create search vector query with SAFE parameterization - // var orderBy string = "" - // var searchQuery string = "" - // - // if filters.Search != "" { - // spl := strings.Split(filters.Search, " ") - // var cleaned []string - // - // // Use a string replacer for safety - // replacer := strings.NewReplacer( - // "'", "", - // "-", "", - // "&", "", - // "|", "", - // "!", "", - // ":", "", // Remove colons to prevent tsquery syntax injection - // "(", "", - // ")", "", - // ) - // - // for i := range len(spl) { - // q := strings.TrimSpace(replacer.Replace(spl[i])) - // if q != "" { - // // Add :* suffix for prefix matching - // cleaned = append(cleaned, q+":*") - // } - // } - // - // // Join with OR operator for full-text search - // vector_query := strings.Join(cleaned, " | ") - // searchQuery = vector_query - // - // // Full-text search with prefix matching - // searchCondition := fmt.Sprintf("r.search_vector @@ to_tsquery('english', '%s')", vector_query) - // - // // Add fallback ILIKE for true substring matching - // // This catches cases where "pan" is inside "pancake" but not at word boundaries - // var ilikeConditions []string - // for _, term := range spl { - // cleanTerm := strings.TrimSpace(replacer.Replace(term)) - // if cleanTerm != "" { - // ilikeConditions = append(ilikeConditions, fmt.Sprintf("(r.title ILIKE '%%%s%%' OR r.description ILIKE '%%%s%%')", cleanTerm, cleanTerm)) - // } - // } - // - // if len(ilikeConditions) > 0 { - // searchCondition = fmt.Sprintf("(%s OR %s)", searchCondition, strings.Join(ilikeConditions, " OR ")) - // } - // - // conditions = append(conditions, searchCondition) - // - // // Ranking with preference for full-text matches - // orderBy = fmt.Sprintf(` - // ORDER BY - // CASE - // WHEN r.search_vector @@ to_tsquery('english', '%s') THEN 1 - // ELSE 2 - // END, - // ts_rank(r.search_vector, to_tsquery('english', '%s')) DESC, - // ts_rank_cd(r.search_vector, to_tsquery('english', '%s')) DESC - // `, searchQuery, searchQuery, searchQuery) - // } - // - // // Generate the query - // var query string - // if favorites && userId != nil { - // query = fmt.Sprintf( - // "SELECT %s FROM recipes r JOIN favorites f ON f.recipeId = r.id", - // strings.Join(columns, ","), - // ) - // conditions = append(conditions, fmt.Sprintf("f.userid = %d", *userId)) - // } else { - // query = fmt.Sprintf("SELECT %s FROM recipes r", strings.Join(columns, ",")) - // } - // - // // Convert and append conditions if provided - // conditions = append(conditions, "deleted = false") - // if len(conditions) > 0 { - // conditionsString := fmt.Sprintf("WHERE %s", strings.Join(conditions, " AND ")) - // query = fmt.Sprintf("%s %s", query, conditionsString) - // } - // - // // Append sorting order if exists - // if len(orderBy) > 0 { - // query = fmt.Sprintf("%s %s", query, orderBy) - // } - // - // // Finish it off with a semicolon! - // query += ";" - // - // // Execute the query - // rows, err := r.db.Query(query) - // if err != nil { - // return []int{}, fmt.Errorf("failed to query recipes: %w", err) - // } - // defer rows.Close() - // - // var ids []int - // for rows.Next() { - // var id int - // if err := rows.Scan(&id); err != nil { - // return []int{}, fmt.Errorf("failed to extract ID: %s\n", err.Error()) - // } - // ids = append(ids, id) - // } - // - // return ids, nil } // CreateRecipeTags accepts a list of tags (names) and a recipe (already created by the DB) and