Files
backend_fiesta/models/catalogue.go
2026-09-29 17:27:48 +05:30

90 lines
2.9 KiB
Go

package models
import (
"strings"
"time"
)
// CatalogueProduct maps to the per-brand tables (brand_dabur, brand_nestle, ...)
// in the separate pgvector catalogue database. The `embedding` vector column
// is intentionally not mapped here — it is never needed in an API response.
type CatalogueProduct struct {
ID int64 `json:"id"`
Brand string `json:"brand"`
ProductName string `json:"product_name"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Category string `json:"category,omitempty"`
ImageID string `json:"image_id,omitempty"`
Images []string `json:"images,omitempty"`
Size string `json:"size,omitempty"`
VariantKey string `json:"variant_key,omitempty"`
ProductSKU string `json:"product_sku,omitempty"`
SKUSource string `json:"sku_source,omitempty"`
PriceRange string `json:"price_range,omitempty"`
Providers PGStringArray `json:"providers,omitempty"`
FSSAILicense string `json:"fssai_license,omitempty"`
Highlights PGStringArray `json:"highlights,omitempty"`
Nutrients PGStringArray `json:"nutrients,omitempty"`
// The structured panel, when the catalogue carries one. Nil falls back to
// parsing Nutrients above — see NutritionFromLines.
Nutrition *NutritionPanel `json:"nutrition,omitempty"`
SearchQuery string `json:"search_query,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
}
// CatalogueBrand describes a brand available in the catalogue DB.
type CatalogueBrand struct {
Brand string `json:"brand"`
ProductCount int64 `json:"product_count"`
}
// PGStringArray is a plain []string used for catalogue array fields
// (providers, highlights, nutrients) in JSON responses.
type PGStringArray []string
// ParsePGArray parses a Postgres text[] literal (e.g. "{Amazon,Flipkart}")
// into a Go []string. GORM's raw-scan-into-struct silently drops slice-kind
// destination fields, so array columns are queried as `col::text` and
// converted with this function after scanning, rather than scanned directly.
func ParsePGArray(s string) PGStringArray {
s = strings.TrimSpace(s)
if s == "" || s == "{}" {
return PGStringArray{}
}
if strings.HasPrefix(s, "{") && strings.HasSuffix(s, "}") {
s = s[1 : len(s)-1]
}
var result PGStringArray
var buf strings.Builder
inQuotes := false
escaped := false
for _, r := range s {
switch {
case escaped:
buf.WriteRune(r)
escaped = false
case r == '\\':
escaped = true
case r == '"':
inQuotes = !inQuotes
case r == ',' && !inQuotes:
result = append(result, buf.String())
buf.Reset()
default:
buf.WriteRune(r)
}
}
result = append(result, buf.String())
for i, v := range result {
if v == "NULL" {
result[i] = ""
}
}
return result
}