Skip to content

Commit

Permalink
Filter Product API is now only 1 database call!
Browse files Browse the repository at this point in the history
  • Loading branch information
santoshkavhar committed Oct 9, 2020
1 parent ec8f629 commit 0348281
Showing 1 changed file with 26 additions and 44 deletions.
70 changes: 26 additions & 44 deletions db/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ var (
FROM products p
INNER JOIN category c
ON p.cid = c.cid`

filterStart = `SELECT count(*) OVER() AS total,*
FROM products p
INNER JOIN category c
ON p.cid = c.cid`
)

type Filter struct {
Expand All @@ -46,90 +51,67 @@ type Filter struct {
func (s *pgStore) FilteredProducts(ctx context.Context, filter Filter, limitStr string, offsetStr string) (int, []Product, error) {

var found bool
var totalRecords int
var products []Product
var records []Record
var err error

// helper will be used in making query dynamic.
// See how it's getting concatanation added in case a flag was Filter Flag is true
sqlRegexp := ``
isFiltered := ` `
filterQuery := ` `
if filter.CategoryFlag == true {
isFiltered += ` c.cid = ` + filter.CategoryId + ` AND`
filterQuery += ` c.cid = ` + filter.CategoryId + ` AND`
sqlRegexp += filter.CategoryId
found = true
}
if filter.BrandFlag {
isFiltered += ` LOWER(p.brand) = LOWER('` + filter.Brand + `') AND`
filterQuery += ` LOWER(p.brand) = LOWER('` + filter.Brand + `') AND`
sqlRegexp += filter.Brand
found = true
}
if filter.SizeFlag {
isFiltered += ` LOWER(p.size) = LOWER('` + filter.Size + `') AND`
filterQuery += ` LOWER(p.size) = LOWER('` + filter.Size + `') AND`
sqlRegexp += filter.Size
found = true
}
if filter.ColorFlag {
isFiltered += ` LOWER(p.color) =LOWER('` + filter.Color + `') AND`
filterQuery += ` LOWER(p.color) =LOWER('` + filter.Color + `') AND`
sqlRegexp += filter.Color
found = true
}
if found {
var validParameters = regexp.MustCompile(`^[\w ]+$`)
if !validParameters.MatchString(sqlRegexp) {
err := fmt.Errorf("Possible SQL Injection Attack.")
err = fmt.Errorf("Possible SQL Injection Attack.")
logger.WithField("err", err.Error()).Error("Error In Parameters, special Characters are present.")
return 0, []Product{}, err
}
isFiltered = ` WHERE ` + isFiltered[:len(isFiltered)-3]
}

getFilterProductCount := productCount + isFiltered + `;`
resultCount, err := s.db.Query(getFilterProductCount)

if err != nil {
logger.WithField("err", err.Error()).Error("Error getting Count of Filtered Products from database")
return 0, []Product{}, err
}

for resultCount.Next() {
err = resultCount.Scan(&totalRecords)
if err != nil {
logger.WithField("err", err.Error()).Error("Error fetching count of getFilterProductCount from database")
return 0, []Product{}, err
}
break
filterQuery = ` WHERE ` + filterQuery[:len(filterQuery)-3]
}

offset, _ := strconv.Atoi(offsetStr)

if totalRecords-1 < offset {
err = fmt.Errorf("Page out of Range!")
logger.WithField("err", err.Error()).Error("Error Offset is greater than total records")
return 0, []Product{}, err

}

getFilterProduct := filterProduct + isFiltered
filterQuery = filterStart + filterQuery

if filter.PriceFlag {
getFilterProduct += ` ORDER BY p.price ` + filter.Price + `, p.id LIMIT ` + limitStr + ` OFFSET ` + offsetStr + ` ;`
filterQuery += ` ORDER BY p.price ` + filter.Price + `, p.id LIMIT ` + limitStr + ` OFFSET ` + offsetStr + ` ;`
} else {
getFilterProduct += ` ORDER BY p.id LIMIT ` + limitStr + ` OFFSET ` + offsetStr + ` ;`
filterQuery += ` ORDER BY p.id LIMIT ` + limitStr + ` OFFSET ` + offsetStr + ` ;`
}
//fmt.Println(getFilterProduct)

err = s.db.Select(&products, getFilterProduct)
err = s.db.Select(&records, filterQuery)
if err != nil {
logger.WithField("err", err.Error()).Error("Error fetching Product Ids from database")
logger.WithField("err", err.Error()).Error("Error fetching Products from database")
return 0, []Product{}, err
}
if products == nil {
err = fmt.Errorf("Desired page not found")
} else if len(records) == 0 {
err = fmt.Errorf("Desired page not found, Offset was big")
logger.WithField("err", err.Error()).Error("Products don't exist by such filters!")
return 0, []Product{}, err
}

return totalRecords, products, nil
for _, record := range records {
products = append(products, record.Product)
}

return records[0].TotalRecords, products, nil
}

// @Title SearchRecords
Expand Down

1 comment on commit 0348281

@santoshkavhar
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previous Changes made on the Product Structure have been discarded. New structure by the name Record is introduced. Record is used to hold the total records count. This updated code has served Search and Filter API for proper optimization.
TODO: We are currently copying product from Records structure, that is double work, need some alternative.

Please sign in to comment.