Reputation: 6209
I'm playing around with building an API in Go, and it allows the user to search on 3 main points to find a "Job".
LIKE
on certain fields eg. Title, Descriptionjob_location
job_skill
If a user doesn't provide any of these it defaults to returning ALL jobs in the table. My issue I feel is that the approach I am taking isn't scalable. There are 3 paramters, therefore ~9 possible combinations of what the user can provide. If I were to add a fourth, I would need to cover 16! searh combinations.
Here's what I have in my controller:
func (j *Jobs) List(c *client.Client, q string, l string, s string) error {
// Return all jobs
if q == "" && l == "" && s == "" {
err := c.Database.Debug().Preload("Locations").Preload("Skills").Find(&j).Error //
if err != nil {
return err
}
}
// Return based on query
if q != "" && l == "" && s == "" {
c.Database.Where("LOWER(title) like LOWER(?) OR LOWER(description) like LOWER(?)", "%"+q+"%", "%"+q+"%").Preload("Locations").Preload("Skills").Find(&j)
}
// return based on location
if q == "" && l != "" && s == "" {
c.Database.Preload("Skills").Preload("Locations").Joins("INNER JOIN job_location jl ON jl.job_id = jobs.id").Where("jl.location_name = ?", l).Find(&j)
}
// return based on skill
if q == "" && l == "" && s != "" {
c.Database.Preload("Skills").Preload("Locations").Joins("INNER JOIN job_skill js ON js.job_id = jobs.id").Where("js.skill_name = ?", s).Find(&j)
}
// return based on query + location
if q != "" && l != "" && s == "" {
c.Database.Preload("Locations").Preload("Skills").Joins("INNER JOIN job_location jl ON jl.job_id = jobs.id").Where("jl.location_name = ?", l).Where("LOWER(title) like LOWER(?) OR LOWER(description) like LOWER(?)", "%"+q+"%", "%"+q+"%").Find(&j)
}
// return based on query + skill
if q != "" && l == "" && s != "" {
c.Database.Preload("Locations").Preload("Skills").Joins("INNER JOIN job_skill js ON js.job_id = jobs.id").Where("js.skill_name = ?", s).Where("LOWER(title) like LOWER(?) OR LOWER(description) like LOWER(?)", "%"+q+"%", "%"+q+"%").Find(&j)
}
// return based on location + skill
if q == "" && l != "" && s != "" {
c.Database.Preload("Skills").Preload("Locations").Joins("INNER JOIN job_location jl ON jl.job_id = jobs.id").Joins("INNER JOIN job_skill js ON js.job_id = jobs.id").Where("jl.location_name = ?", l).Where("js.skill_name = ?", s).Find(&j)
}
// return based on query + skill + location
if q != "" && l != "" && s != "" {
c.Database.Preload("Locations").Preload("Skills").Joins("INNER JOIN job_location jl ON jl.job_id = jobs.id").Joins("INNER JOIN job_skill js ON js.job_id = jobs.id").Where("js.skill_name = ?", s).Where("jl.location_name = ?", l).Where("LOWER(title) like LOWER(?) OR LOWER(description) like LOWER(?)", "%"+q+"%", "%"+q+"%").Find(&j)
}
return nil
}
As you can see, with just 3 parameters it's a monsterous block of code. What are some ways I could improve this and make it more manageable as fields are added.
Upvotes: 0
Views: 233
Reputation: 813
You can build up your query by creating a common part and later add the optional part to it, like this:
func (j *Jobs) List(c *client.Client, q string, l string, s string) error {
query := c.Database.Debug().Preload("Locations").Preload("Skills")
// return based on query
if q != "" {
query = query.Where("LOWER(title) like LOWER(?) OR LOWER(description) like LOWER(?)", "%"+q+"%", "%"+q+"%")
}
// return based on location
if l != "" {
query = query.Joins("INNER JOIN job_location jl ON jl.job_id = jobs.id").Where("jl.location_name = ?", l)
}
// return based on skill
if s != "" {
query = query.Joins("INNER JOIN job_skill js ON js.job_id = jobs.id").Where("js.skill_name = ?", s)
}
// Return all jobs
return query.Find(&j).Error
}
Upvotes: 6