FindMyRecipe / server / database / recipes.go
recipes.go
Raw
package database

import (
	"database/sql"
	m "main/server/models"
	"main/server/util"
	"math/rand"
	"time"
  "errors"
  "fmt"
)

func GetRecipes() []m.Recipe {
  var recipes []m.Recipe
  db.Raw("select * from recipes limit 10").Scan(&recipes)
  return recipes
}

func GetRecipesPage(page int) []m.Recipe {
  if page < 0 {
    return nil
  }
  var recipes []m.Recipe
  db.Raw("select * from recipes order by id limit 6 offset @page_offset", sql.Named("page_offset", (page - 1) * 6)).Scan(&recipes)
  return recipes
}

func GetRecipeById(id int) (m.Recipe, error) {
  var recipe m.Recipe
  db.Raw("select * from recipes where id = @id limit 1", sql.Named("id", id)).Scan(&recipe)
  if (recipe.Id == 0) {
    return recipe, errors.New("Recipe not found")
  }
  return recipe, nil
}

func GetRecipesByTags(tagId int) []m.Recipe {
  var recipesByTags []m.Recipe
  q := util.ReadQueryFile("FilterByTags")

  db.Raw(q, sql.Named("tag_id", tagId)).Scan(&recipesByTags)
  if len(recipesByTags) == 0 {
    return nil
  }
  return recipesByTags
}

func GetRecipesByManyTags(tagIds []int) []m.Recipe {
  var recipesByTags []m.Recipe
  q := util.ReadQueryFile("FilterByManyTags")

  prettyQuery := "{" + util.ArrayToString(tagIds, ",") + "}"

  db.Raw(q, sql.Named("tag_ids", prettyQuery), sql.Named("tag_count", len(tagIds))).Scan(&recipesByTags)
  if len(recipesByTags) == 0 {
    return nil
  }
  return recipesByTags
}

func GetMatchingRecipesByIngredients(ingredientIds []int) []m.Recipe {
  var recipes []m.Recipe
  q := util.ReadQueryFile("MatchRecipesByIngredients")

  prettyQuery := "{" + util.ArrayToString(ingredientIds, ",") + "}"

  db.Raw(q, sql.Named("ingredient_ids", prettyQuery)).Scan(&recipes)
  if len(recipes) == 0 {
    return nil
  }
  return recipes
}

func GetMatchingRecipes(ingredientIds []int, tagIds []int) []m.Recipe {
  var recipes []m.Recipe
  q := util.ReadQueryFile("MatchRecipes")

  ingredients := "{" + util.ArrayToString(ingredientIds, ",") + "}"
  tags := "{" + util.ArrayToString(tagIds, ",")+ "}"

  db.Raw(q, sql.Named("ingredient_ids", ingredients), sql.Named("tag_ids", tags),
    sql.Named("tag_count", len(tagIds))).Scan(&recipes)
  if len(recipes) == 0 {
    return nil
  }
  return recipes
}

func GetRecipesByAuthor(author int) []m.Recipe {
  var recipes []m.Recipe

  db.Raw("SELECT * FROM recipes WHERE author = @id", sql.Named("id", author)).Scan(&recipes)
  if len(recipes) == 0 {
    return nil
  }
  return recipes
}

func WeeklyFavorites() []m.Recipe {
  var recipes []m.Recipe
  var q = util.ReadQueryFile("WeeklyFavorites")

  current := time.Now()
  offset := (int(time.Monday) - int(current.Weekday()) - 7) % 7
  startWeek := current.Add(time.Duration(offset*24) * time.Hour).Add(time.Duration(-current.Hour()) * time.Hour)

  db.Raw(q, sql.Named("week_timestamp", startWeek)).Scan(&recipes)
  if len(recipes) == 0 {
    return nil
  }
  return recipes
}

func GetRecommendation(userId int) []m.Recipe {
  var recipes []m.Recipe
  var q = util.ReadQueryFile("RecommendRecipes")

  db.Raw(q, sql.Named("user_id", userId)).Scan(&recipes)
  return recipes
}

func GetSimilarRecipes(ingredientIds []int) []m.Recipe {
  var recipes []m.Recipe
  q := util.ReadQueryFile("SimilarRecipes")

  ingredients := "{" + util.ArrayToString(ingredientIds, ",") + "}"
  staples := []int{6557,14832,9454,8189,8223,2237,4621,8792,13436,10905,409,7206,1616,13150,14809,1097,3282,14020,12149,9604}
  randomIndex := rand.Intn(len(staples))
  appendedIngredientsId := append(ingredientIds, staples[randomIndex])
  appendedIngrdients := "{" + util.ArrayToString(appendedIngredientsId, ",") + "}"

  db.Raw(q, sql.Named("fridge1", appendedIngrdients), sql.Named("fridge2", ingredients)).Scan(&recipes)
  if len(recipes) == 0 {
    r := rand.Intn(200000)
    db.Raw("SELECT * FROM recipes WHERE id = @id", sql.Named("id", r)).Scan(&recipes)
    return recipes
  }
  return recipes
}


  // TODO:
  // insert into recipes values () returning id
  // get the recipe id
  // get ingredient id by name
  // insert into recipe_ingredient
  // get tag id by name
  // insert into recipe_tag
  // make sure all is all or nothing
func AddNewRecipe(recipe m.RecipeRequest) bool {
    // name text,
    // description text,
    // prep_minutes int,
    // author int,
    // steps text[],
    // nutrition float8[],

  // UserId int `json:"userId"`
  // RecipeId int `json:"Id"`
  // RecipeName string `json:"Name"`
  // PrepMinutes int `json:"PrepMinutes"`
  // Description string `json:"Description"`
  // Ingredients []string `json:"Ingredients"`
  // Nutrition []int `json:"Nutrition"`
  // Tags []string `json:"Tags"`
  // Steps []string `json:"Steps"`
  insertRecipe := "INSERT INTO recipes(name, description, prep_minutes, author, steps, nutrition) VALUES(@1, @2, @3, @4, cast(@5 as text[]), cast(@6 as float8[])) RETURNING id;"
  recipeId := 0
  recipeSteps := "{" + util.ArrayStringToString(recipe.Steps, ",") + "}"
  recipeNutrition := "{" + util.ArrayToString(recipe.Nutrition, ",") + "}"
  r1 := db.Raw(insertRecipe, sql.Named("1", recipe.RecipeName), sql.Named("2", recipe.Description), 
  sql.Named("3", recipe.PrepMinutes), sql.Named("4", recipe.UserId), sql.Named("5", recipeSteps), 
  sql.Named("6", recipeNutrition)).Scan(&recipeId)
    //  (cast(@tag_ids as int[]))
  if (r1.Error != nil || recipeId == 0) {
    return false
  }

  // insert recipe_ingredient
  ingredientIds := make([]int, 0)
  for _, ingredientName := range recipe.Ingredients {
    ingredientId := 0
    db.Raw("SELECT id FROM ingredients WHERE name = @ingredientName", sql.Named("ingredientName", ingredientName)).Scan(&ingredientId)
    ingredientIds = append(ingredientIds, ingredientId)
  }
  insertRecipeIngredient := "INSERT INTO recipe_ingredient(recipe_id, ingredient_id) VALUES @values;"
  riValues := make([][]int, 0)
  for _, ingredientId := range ingredientIds {
    value := []int{recipeId, ingredientId}
    riValues = append(riValues, [][]int{value}...)
  }
  stmt2, valueArgs2 := util.PrepareBulkInsert(insertRecipeIngredient, "@values", 2, riValues)
  r2 := db.Exec(stmt2, valueArgs2...)
  if r2.Error != nil {
    // rollback insert recipe
    rerr := db.Exec("DELETE FROM recipes WHERE id = @recipeId", sql.Named("recipeId", recipeId))
    if (rerr.Error != nil) {
      fmt.Println("Failed to rollback delete from recipes where id ", recipeId)
      return false
    }
    return false
  }

  // insert recipe_tag
  tagIds := make([]int, 0)
  for _, tagName := range recipe.Tags {
    tagId := 0
    db.Raw("SELECT id FROM tags WHERE name = @tagName", sql.Named("tagName", tagName)).Scan(&tagId)
    tagIds = append(tagIds, tagId)
  }
  insertRecipeTag := "INSERT INTO recipe_tag(recipe_id, tag_id) VALUES @values;"
  rtValues := make([][]int, 0)
  for _, tagId:= range tagIds {
    value := []int{recipeId, tagId}
    rtValues = append(rtValues, [][]int{value}...)
  }
  stmt3, valueArgs3 := util.PrepareBulkInsert(insertRecipeTag, "@values", 2, rtValues)
  r3 := db.Exec(stmt3, valueArgs3...)
  if r3.Error != nil {
    // rollback insert recipe and recipe_tag (delete recipe auto cascades recipe_tag by table def)
    rerr := db.Exec("DELETE FROM recipes WHERE id = @recipeId", sql.Named("recipeId", recipeId))
    if (rerr.Error != nil) {
      fmt.Println("Failed to rollback delete from recipes where id ", recipeId)
      return false
    }
    return false
  }
  return true
}