fix products listing
This commit is contained in:
109
app/repos/attributeRepo/attributeRepo.go
Normal file
109
app/repos/attributeRepo/attributeRepo.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package attributerepo
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||
)
|
||||
|
||||
type AttributeWithColor struct {
|
||||
ID uint
|
||||
Name string
|
||||
Color string
|
||||
}
|
||||
|
||||
type AttributeGroupWithAttrs struct {
|
||||
GroupName string
|
||||
Attrs map[uint]AttributeWithColor
|
||||
}
|
||||
|
||||
type UIAttributeRepo interface {
|
||||
GetAttributeGroupsWithAttributes(groupIDs []uint, attrIDs []uint, idLang uint) (map[uint]AttributeGroupWithAttrs, error)
|
||||
}
|
||||
|
||||
type AttributeRepo struct{}
|
||||
|
||||
func New() UIAttributeRepo {
|
||||
return &AttributeRepo{}
|
||||
}
|
||||
|
||||
func (r *AttributeRepo) GetAttributeGroupsWithAttributes(groupIDs []uint, attrIDs []uint, idLang uint) (map[uint]AttributeGroupWithAttrs, error) {
|
||||
result := make(map[uint]AttributeGroupWithAttrs)
|
||||
|
||||
if len(groupIDs) == 0 && len(attrIDs) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if len(attrIDs) > 0 {
|
||||
type attrResult struct {
|
||||
IDAttribute int32
|
||||
IDAttributeGroup int32
|
||||
GroupName string
|
||||
AttrName string
|
||||
Color string
|
||||
}
|
||||
|
||||
var attrs []attrResult
|
||||
if err := db.Get().
|
||||
Model(dbmodel.PsAttribute{}).
|
||||
Select(`
|
||||
ps_attribute.id_attribute,
|
||||
ps_attribute.id_attribute_group,
|
||||
COALESCE(ps_attribute_group_lang.name, '') as group_name,
|
||||
COALESCE(ps_attribute_lang.name, '') as attr_name,
|
||||
ps_attribute.color
|
||||
`).
|
||||
Joins("LEFT JOIN ps_attribute_lang ON ps_attribute_lang.id_attribute = ps_attribute.id_attribute AND ps_attribute_lang.id_lang = ?", idLang).
|
||||
Joins("LEFT JOIN ps_attribute_group_lang ON ps_attribute_group_lang.id_attribute_group = ps_attribute.id_attribute_group AND ps_attribute_group_lang.id_lang = ?", idLang).
|
||||
Where("ps_attribute.id_attribute IN ?", attrIDs).
|
||||
Scan(&attrs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, a := range attrs {
|
||||
if _, exists := result[uint(a.IDAttributeGroup)]; !exists {
|
||||
result[uint(a.IDAttributeGroup)] = AttributeGroupWithAttrs{
|
||||
GroupName: a.GroupName,
|
||||
Attrs: make(map[uint]AttributeWithColor),
|
||||
}
|
||||
}
|
||||
result[uint(a.IDAttributeGroup)].Attrs[uint(a.IDAttribute)] = AttributeWithColor{
|
||||
ID: uint(a.IDAttribute),
|
||||
Name: a.AttrName,
|
||||
Color: a.Color,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(groupIDs) > 0 {
|
||||
type groupResult struct {
|
||||
IDAttributeGroup int32
|
||||
GroupName string
|
||||
}
|
||||
|
||||
var groups []groupResult
|
||||
if err := db.Get().
|
||||
Model(dbmodel.PsAttributeGroupLang{}).
|
||||
Select("ps_attribute_group_lang.id_attribute_group, COALESCE(ps_attribute_group_lang.name, '') as group_name").
|
||||
Where("ps_attribute_group_lang.id_attribute_group IN ?", groupIDs).
|
||||
Where("ps_attribute_group_lang.id_lang = ?", idLang).
|
||||
Scan(&groups).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, g := range groups {
|
||||
if existing, ok := result[uint(g.IDAttributeGroup)]; ok {
|
||||
if g.GroupName != "" {
|
||||
existing.GroupName = g.GroupName
|
||||
result[uint(g.IDAttributeGroup)] = existing
|
||||
}
|
||||
} else {
|
||||
result[uint(g.IDAttributeGroup)] = AttributeGroupWithAttrs{
|
||||
GroupName: g.GroupName,
|
||||
Attrs: make(map[uint]AttributeWithColor),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -3,11 +3,12 @@ package categoriesRepo
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
)
|
||||
|
||||
type UICategoriesRepo interface {
|
||||
GetAllCategories(id_lang uint) ([]model.ScannedCategory, error)
|
||||
GetAllCategories(idLang uint) ([]model.ScannedCategory, error)
|
||||
}
|
||||
|
||||
type CategoriesRepo struct{}
|
||||
@@ -16,11 +17,16 @@ func New() UICategoriesRepo {
|
||||
return &CategoriesRepo{}
|
||||
}
|
||||
|
||||
func (repo *CategoriesRepo) GetAllCategories(id_lang uint) ([]model.ScannedCategory, error) {
|
||||
func (r *CategoriesRepo) GetAllCategories(idLang uint) ([]model.ScannedCategory, error) {
|
||||
var allCategories []model.ScannedCategory
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_category").
|
||||
categoryTbl := (&dbmodel.PsCategory{}).TableName()
|
||||
categoryLangTbl := (&dbmodel.PsCategoryLang{}).TableName()
|
||||
categoryShopTbl := (&dbmodel.PsCategoryShop{}).TableName()
|
||||
langTbl := (&dbmodel.PsLang{}).TableName()
|
||||
|
||||
err := db.Get().
|
||||
Model(dbmodel.PsCategory{}).
|
||||
Select(`
|
||||
ps_category.id_category AS category_id,
|
||||
ps_category_lang.name AS name,
|
||||
@@ -31,21 +37,12 @@ func (repo *CategoriesRepo) GetAllCategories(id_lang uint) ([]model.ScannedCateg
|
||||
ps_category_lang.link_rewrite AS link_rewrite,
|
||||
ps_lang.iso_code AS iso_code
|
||||
`).
|
||||
Joins(`
|
||||
LEFT JOIN ps_category_lang
|
||||
ON ps_category_lang.id_category = ps_category.id_category
|
||||
AND ps_category_lang.id_shop = ?
|
||||
AND ps_category_lang.id_lang = ?
|
||||
`, constdata.SHOP_ID, id_lang).
|
||||
Joins(`
|
||||
LEFT JOIN ps_category_shop
|
||||
ON ps_category_shop.id_category = ps_category.id_category
|
||||
AND ps_category_shop.id_shop = ?
|
||||
`, constdata.SHOP_ID).
|
||||
Joins(`
|
||||
JOIN ps_lang
|
||||
ON ps_lang.id_lang = ps_category_lang.id_lang
|
||||
`).
|
||||
Joins(`LEFT JOIN ? ON ??.id_category = ??.id_category AND ??.id_shop = ? AND ??.id_lang = ?`,
|
||||
categoryLangTbl, categoryLangTbl, categoryTbl, categoryLangTbl, constdata.SHOP_ID, categoryLangTbl, idLang).
|
||||
Joins(`LEFT JOIN ? ON ??.id_category = ??.id_category AND ??.id_shop = ?`,
|
||||
categoryShopTbl, categoryShopTbl, categoryTbl, categoryShopTbl, constdata.SHOP_ID).
|
||||
Joins(`JOIN ? ON ??.id_lang = ??.id_lang`,
|
||||
langTbl, langTbl, categoryLangTbl).
|
||||
Scan(&allCategories).Error
|
||||
|
||||
return allCategories, err
|
||||
|
||||
44
app/repos/categoryRepo/categoryRepo.go
Normal file
44
app/repos/categoryRepo/categoryRepo.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package categoryrepo
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||
)
|
||||
|
||||
type UICategoryRepo interface {
|
||||
GetCategoryTranslations(ids []uint, idLang uint) (map[uint]string, error)
|
||||
}
|
||||
|
||||
type CategoryRepo struct{}
|
||||
|
||||
func New() UICategoryRepo {
|
||||
return &CategoryRepo{}
|
||||
}
|
||||
|
||||
func (r *CategoryRepo) GetCategoryTranslations(ids []uint, idLang uint) (map[uint]string, error) {
|
||||
if len(ids) == 0 {
|
||||
return map[uint]string{}, nil
|
||||
}
|
||||
|
||||
type result struct {
|
||||
IDCategory int32
|
||||
Name string
|
||||
}
|
||||
|
||||
var results []result
|
||||
if err := db.Get().
|
||||
Model(dbmodel.PsCategoryLang{}).
|
||||
Select("ps_category_lang.id_category, COALESCE(ps_category_lang.name, '') as name").
|
||||
Where("ps_category_lang.id_category IN ?", ids).
|
||||
Where("ps_category_lang.id_lang = ?", idLang).
|
||||
Scan(&results).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
translations := make(map[uint]string, len(results))
|
||||
for _, res := range results {
|
||||
translations[uint(res.IDCategory)] = res.Name
|
||||
}
|
||||
|
||||
return translations, nil
|
||||
}
|
||||
95
app/repos/featureRepo/featureRepo.go
Normal file
95
app/repos/featureRepo/featureRepo.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package featurerepo
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||
)
|
||||
|
||||
type FeatureGroupWithValues struct {
|
||||
FeatureName string
|
||||
Values map[uint]string
|
||||
}
|
||||
|
||||
type UIFeatureRepo interface {
|
||||
GetFeaturesWithValues(featureIDs []uint, featureValueIDs []uint, idLang uint) (map[uint]FeatureGroupWithValues, error)
|
||||
}
|
||||
|
||||
type FeatureRepo struct{}
|
||||
|
||||
func New() UIFeatureRepo {
|
||||
return &FeatureRepo{}
|
||||
}
|
||||
|
||||
func (r *FeatureRepo) GetFeaturesWithValues(featureIDs []uint, featureValueIDs []uint, idLang uint) (map[uint]FeatureGroupWithValues, error) {
|
||||
result := make(map[uint]FeatureGroupWithValues)
|
||||
|
||||
if len(featureIDs) == 0 && len(featureValueIDs) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if len(featureValueIDs) > 0 {
|
||||
type valueResult struct {
|
||||
IDFeatureValue int32
|
||||
IDFeature int32
|
||||
Value string
|
||||
}
|
||||
|
||||
var values []valueResult
|
||||
if err := db.Get().
|
||||
Model(dbmodel.PsFeatureValueLang{}).
|
||||
Select(`
|
||||
ps_feature_value_lang.id_feature_value,
|
||||
ps_feature_value.id_feature,
|
||||
COALESCE(ps_feature_value_lang.value, '') as value
|
||||
`).
|
||||
Joins("LEFT JOIN ps_feature_value ON ps_feature_value.id_feature_value = ps_feature_value_lang.id_feature_value").
|
||||
Where("ps_feature_value_lang.id_lang = ?", idLang).
|
||||
Where("ps_feature_value_lang.id_feature_value IN ?", featureValueIDs).
|
||||
Scan(&values).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, v := range values {
|
||||
if _, exists := result[uint(v.IDFeature)]; !exists {
|
||||
result[uint(v.IDFeature)] = FeatureGroupWithValues{
|
||||
FeatureName: v.Value,
|
||||
Values: make(map[uint]string),
|
||||
}
|
||||
}
|
||||
result[uint(v.IDFeature)].Values[uint(v.IDFeatureValue)] = v.Value
|
||||
}
|
||||
}
|
||||
|
||||
if len(featureIDs) > 0 {
|
||||
type featureResult struct {
|
||||
IDFeature int32
|
||||
FeatureName string
|
||||
}
|
||||
|
||||
var features []featureResult
|
||||
if err := db.Get().
|
||||
Model(dbmodel.PsFeatureLang{}).
|
||||
Select("ps_feature_lang.id_feature, COALESCE(ps_feature_lang.name, '') as feature_name").
|
||||
Where("ps_feature_lang.id_feature IN ?", featureIDs).
|
||||
Where("ps_feature_lang.id_lang = ?", idLang).
|
||||
Scan(&features).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, f := range features {
|
||||
if existing, ok := result[uint(f.IDFeature)]; ok {
|
||||
if f.FeatureName != "" {
|
||||
existing.FeatureName = f.FeatureName
|
||||
result[uint(f.IDFeature)] = existing
|
||||
}
|
||||
} else {
|
||||
result[uint(f.IDFeature)] = FeatureGroupWithValues{
|
||||
FeatureName: f.FeatureName,
|
||||
Values: make(map[uint]string),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package listProductsRepo
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/filters"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/query/find"
|
||||
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
|
||||
)
|
||||
|
||||
type UIListProductsRepo interface {
|
||||
@@ -22,26 +25,32 @@ func (repo *ListProductsRepo) GetListing(id_lang uint, p find.Paging, filt *filt
|
||||
var total int64
|
||||
|
||||
query := db.Get().
|
||||
Table("ps_product_shop AS ps").
|
||||
Table("ps_product_shop ps").
|
||||
Select(`
|
||||
ps.id_product AS product_id,
|
||||
pl.name AS name,
|
||||
pl.link_rewrite AS link_rewrite,
|
||||
CONCAT('https://www.naluconcept.com', '/', ims.id_image, '-small_default/', pl.link_rewrite, '.webp') AS image_link,
|
||||
cl.name AS category_name,
|
||||
p.reference AS reference,
|
||||
COUNT(DISTINCT pas.id_product_attribute) AS variants_number,
|
||||
sa.quantity AS quantity
|
||||
`).
|
||||
Joins("JOIN ps_product p ON p.id_product = ps.id_product").
|
||||
Joins("JOIN ps_category_product cp ON ps.id_product = cp.id_product").
|
||||
ps.id_product AS product_id,
|
||||
pl.name AS name,
|
||||
pl.link_rewrite AS link_rewrite,
|
||||
CONCAT(?, ims.id_image, '-small_default/', pl.link_rewrite, '.webp') AS image_link,
|
||||
cl.name AS category_name,
|
||||
p.reference AS reference,
|
||||
COALESCE(v.variants_number, 0) AS variants_number,
|
||||
sa.quantity AS quantity
|
||||
`, config.Get().Image.ImagePrefix).
|
||||
Joins("JOIN "+dbmodel.PsProductCols.IDProduct.Tab()+" p ON p.id_product = ps.id_product").
|
||||
Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_lang = ?", id_lang).
|
||||
Joins("JOIN ps_image_shop ims ON ims.id_product = ps.id_product AND ims.cover = 1").
|
||||
Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_lang = ?", id_lang).
|
||||
Joins("LEFT JOIN ps_product_attribute_shop pas ON pas.id_product = cp.id_product").
|
||||
Joins("LEFT JOIN ps_stock_available sa ON sa.id_product = ps.id_product").
|
||||
Joins("JOIN ps_category_product cp ON cp.id_product = ps.id_product").
|
||||
Joins("LEFT JOIN variants v ON v.id_product = ps.id_product").
|
||||
Joins("LEFT JOIN ps_stock_available sa ON sa.id_product = ps.id_product AND sa.id_product_attribute = 0").
|
||||
Where("ps.active = ?", 1).
|
||||
Group("cp.id_product")
|
||||
Group("ps.id_product").
|
||||
Clauses(exclause.With{CTEs: []exclause.CTE{
|
||||
{
|
||||
Name: "variants",
|
||||
Subquery: exclause.Subquery{DB: db.Get().Model(&dbmodel.PsProductAttributeShop{}).Select("id_product", "COUNT(*) AS variants_number").Group("id_product")},
|
||||
},
|
||||
}})
|
||||
|
||||
// Apply all filters
|
||||
if filt != nil {
|
||||
@@ -58,7 +67,7 @@ func (repo *ListProductsRepo) GetListing(id_lang uint, p find.Paging, filt *filt
|
||||
Order("ps.id_product DESC").
|
||||
Limit(p.Limit()).
|
||||
Offset(p.Offset()).
|
||||
Scan(&listing).Error
|
||||
Find(&listing).Error
|
||||
if err != nil {
|
||||
return find.Found[model.ProductInList]{}, err
|
||||
}
|
||||
|
||||
@@ -16,22 +16,16 @@ func New() UILocaleSelectorRepo {
|
||||
return &LocaleSelectorRepo{}
|
||||
}
|
||||
|
||||
func (repo *LocaleSelectorRepo) GetLanguages() ([]model.Language, error) {
|
||||
func (r *LocaleSelectorRepo) GetLanguages() ([]model.Language, error) {
|
||||
var languages []model.Language
|
||||
|
||||
err := db.DB.Table("b2b_language").Scan(&languages).Error
|
||||
|
||||
err := db.Get().Find(&languages).Error
|
||||
return languages, err
|
||||
}
|
||||
|
||||
func (repo *LocaleSelectorRepo) GetCountriesAndCurrencies() ([]model.Country, error) {
|
||||
func (r *LocaleSelectorRepo) GetCountriesAndCurrencies() ([]model.Country, error) {
|
||||
var countries []model.Country
|
||||
|
||||
err := db.DB.
|
||||
Table("b2b_countries").
|
||||
Select("b2b_countries.id, b2b_countries.name, b2b_countries.flag, ps_currency.id_currency as id_currency, ps_currency.name as currency_name, ps_currency.iso_code as currency_iso_code").
|
||||
Joins("LEFT JOIN ps_currency ON ps_currency.id_currency = b2b_countries.currency_id").
|
||||
Scan(&countries).Error
|
||||
|
||||
err := db.Get().
|
||||
Preload("PSCurrency").
|
||||
Find(&countries).Error
|
||||
return countries, err
|
||||
}
|
||||
|
||||
@@ -5,14 +5,16 @@ import (
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model/dbmodel"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
"github.com/WinterYukky/gorm-extra-clause-plugin/exclause"
|
||||
)
|
||||
|
||||
type UIProductDescriptionRepo interface {
|
||||
GetProductDescription(productID uint, productid_lang uint) (*model.ProductDescription, error)
|
||||
CreateIfDoesNotExist(productID uint, productid_lang uint) error
|
||||
UpdateFields(productID uint, productid_lang uint, updates map[string]string) error
|
||||
GetMeiliProducts(id_lang uint) ([]model.MeiliSearchProduct, error)
|
||||
GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, error)
|
||||
}
|
||||
|
||||
type ProductDescriptionRepo struct{}
|
||||
@@ -25,9 +27,12 @@ func New() UIProductDescriptionRepo {
|
||||
func (r *ProductDescriptionRepo) GetProductDescription(productID uint, productid_lang uint) (*model.ProductDescription, error) {
|
||||
var ProductDescription model.ProductDescription
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product_lang").
|
||||
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, constdata.SHOP_ID, productid_lang).
|
||||
err := db.Get().
|
||||
Where(&dbmodel.PsProductLang{
|
||||
IDProduct: int32(productID),
|
||||
IDShop: int32(constdata.SHOP_ID),
|
||||
IDLang: int32(productid_lang),
|
||||
}).
|
||||
First(&ProductDescription).Error
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database error: %w", err)
|
||||
@@ -44,9 +49,12 @@ func (r *ProductDescriptionRepo) CreateIfDoesNotExist(productID uint, productid_
|
||||
LangID: productid_lang,
|
||||
}
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product_lang").
|
||||
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, constdata.SHOP_ID, productid_lang).
|
||||
err := db.Get().
|
||||
Where(&dbmodel.PsProductLang{
|
||||
IDProduct: int32(productID),
|
||||
IDShop: int32(constdata.SHOP_ID),
|
||||
IDLang: int32(productid_lang),
|
||||
}).
|
||||
FirstOrCreate(&record).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error: %w", err)
|
||||
@@ -64,9 +72,13 @@ func (r *ProductDescriptionRepo) UpdateFields(productID uint, productid_lang uin
|
||||
updatesIface[k] = v
|
||||
}
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product_lang").
|
||||
Where("id_product = ? AND id_shop = ? AND id_lang = ?", productID, constdata.SHOP_ID, productid_lang).
|
||||
err := db.Get().
|
||||
Model(&dbmodel.PsProductLang{}).
|
||||
Where(&dbmodel.PsProductLang{
|
||||
IDProduct: int32(productID),
|
||||
IDShop: int32(constdata.SHOP_ID),
|
||||
IDLang: int32(productid_lang),
|
||||
}).
|
||||
Updates(updatesIface).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("database error: %w", err)
|
||||
@@ -75,127 +87,107 @@ func (r *ProductDescriptionRepo) UpdateFields(productID uint, productid_lang uin
|
||||
return nil
|
||||
}
|
||||
|
||||
// We assume that any user has access to all product descriptions
|
||||
func (r *ProductDescriptionRepo) GetMeiliProducts(id_lang uint) ([]model.MeiliSearchProduct, error) {
|
||||
// GetMeiliProductsBatchedScanned returns a batch of products with LIMIT/OFFSET pagination
|
||||
// The scanning is done inside the repo to keep the service layer cleaner
|
||||
func (r *ProductDescriptionRepo) GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, error) {
|
||||
|
||||
var products []model.MeiliSearchProduct
|
||||
|
||||
query := db.Get().Debug().Raw(`
|
||||
WITH products_page AS (
|
||||
SELECT ps.id_product, ps.price
|
||||
FROM ps_product_shop ps
|
||||
WHERE ps.id_shop = ? AND ps.active = 1
|
||||
),
|
||||
variation_attributes AS (
|
||||
SELECT pas.id_product, pagl.public_name AS attribute_name,
|
||||
JSON_ARRAYAGG(DISTINCT pal.name) AS attribute_values
|
||||
FROM ps_product_attribute_shop pas
|
||||
JOIN ps_product_attribute_combination ppac
|
||||
ON ppac.id_product_attribute = pas.id_product_attribute
|
||||
JOIN ps_attribute_lang pal
|
||||
ON pal.id_attribute = ppac.id_attribute AND pal.id_lang = ?
|
||||
JOIN ps_attribute pa
|
||||
ON pa.id_attribute = ppac.id_attribute
|
||||
JOIN ps_attribute_group pag
|
||||
ON pag.id_attribute_group = pa.id_attribute_group
|
||||
JOIN ps_attribute_group_lang pagl
|
||||
ON pagl.id_attribute_group = pag.id_attribute_group AND pagl.id_lang = ?
|
||||
WHERE pas.id_shop = ?
|
||||
GROUP BY pas.id_product, pagl.public_name
|
||||
),
|
||||
variations AS (
|
||||
SELECT id_product, JSON_OBJECTAGG(attribute_name, attribute_values) AS attributes
|
||||
FROM variation_attributes
|
||||
GROUP BY id_product
|
||||
),
|
||||
variation_attribute_filters AS (
|
||||
SELECT pas.id_product,
|
||||
JSON_ARRAYAGG(
|
||||
DISTINCT CONCAT(
|
||||
LOWER(REPLACE(CAST(pagl.public_name AS CHAR) COLLATE utf8mb4_unicode_ci, ' ', '_')),
|
||||
':',
|
||||
LOWER(REPLACE(CAST(pal.name AS CHAR) COLLATE utf8mb4_unicode_ci, ' ', '_'))
|
||||
)
|
||||
) AS attribute_filters
|
||||
FROM ps_product_attribute_shop pas
|
||||
JOIN ps_product_attribute_combination ppac
|
||||
ON ppac.id_product_attribute = pas.id_product_attribute
|
||||
JOIN ps_attribute_lang pal
|
||||
ON pal.id_attribute = ppac.id_attribute AND pal.id_lang = ?
|
||||
JOIN ps_attribute pa
|
||||
ON pa.id_attribute = ppac.id_attribute
|
||||
JOIN ps_attribute_group pag
|
||||
ON pag.id_attribute_group = pa.id_attribute_group
|
||||
JOIN ps_attribute_group_lang pagl
|
||||
ON pagl.id_attribute_group = pag.id_attribute_group AND pagl.id_lang = ?
|
||||
WHERE pas.id_shop = ?
|
||||
GROUP BY pas.id_product
|
||||
),
|
||||
features AS (
|
||||
SELECT pfp.id_product, JSON_OBJECTAGG(pfl.name, pfvl.value) AS features
|
||||
FROM ps_feature_product pfp
|
||||
JOIN ps_feature_lang pfl
|
||||
ON pfl.id_feature = pfp.id_feature AND pfl.id_lang = ?
|
||||
JOIN ps_feature_value_lang pfvl
|
||||
ON pfvl.id_feature_value = pfp.id_feature_value AND pfvl.id_lang = ?
|
||||
GROUP BY pfp.id_product
|
||||
),
|
||||
images AS (
|
||||
SELECT id_product, id_image
|
||||
FROM ps_image_shop
|
||||
WHERE id_shop = ? AND cover = 1
|
||||
),
|
||||
categories AS (
|
||||
SELECT id_product, JSON_ARRAYAGG(id_category) AS category_ids
|
||||
FROM ps_category_product
|
||||
GROUP BY id_product
|
||||
)
|
||||
SELECT pp.id_product,
|
||||
pl.name,
|
||||
TRIM(REGEXP_REPLACE(REGEXP_REPLACE(pl.description, '<[^>]*>', ' '), '[[:space:]]+', ' ')) AS description,
|
||||
TRIM(REGEXP_REPLACE(REGEXP_REPLACE(pl.description_short, '<[^>]*>', ' '), '[[:space:]]+', ' ')) AS description_short,
|
||||
TRIM(REGEXP_REPLACE(REGEXP_REPLACE(pl.usage, '<[^>]*>', ' '), '[[:space:]]+', ' ')) AS used_for,
|
||||
p.ean13,
|
||||
p.reference,
|
||||
pp.price,
|
||||
ps.id_category_default AS id_category,
|
||||
cl.name AS category_name,
|
||||
cl.link_rewrite,
|
||||
COALESCE(vary.attributes, JSON_OBJECT()) AS attributes,
|
||||
COALESCE(vaf.attribute_filters, JSON_ARRAY()) AS attribute_filters,
|
||||
COALESCE(feat.features, JSON_OBJECT()) AS features,
|
||||
img.id_image,
|
||||
cat.category_ids,
|
||||
(SELECT COUNT(*) FROM ps_product_attribute_shop pas2 WHERE pas2.id_product = pp.id_product AND pas2.id_shop = ?) AS variations
|
||||
FROM products_page pp
|
||||
JOIN ps_product_shop ps ON ps.id_product = pp.id_product
|
||||
JOIN ps_product_lang pl
|
||||
ON pl.id_product = ps.id_product AND pl.id_shop = ? AND pl.id_lang = ?
|
||||
JOIN ps_product p ON p.id_product = ps.id_product
|
||||
JOIN ps_category_lang cl
|
||||
ON cl.id_category = ps.id_category_default AND cl.id_shop = ? AND cl.id_lang = ?
|
||||
LEFT JOIN variations vary ON vary.id_product = ps.id_product
|
||||
LEFT JOIN variation_attribute_filters vaf ON vaf.id_product = ps.id_product
|
||||
LEFT JOIN features feat ON feat.id_product = ps.id_product
|
||||
LEFT JOIN images img ON img.id_product = ps.id_product
|
||||
LEFT JOIN categories cat ON cat.id_product = ps.id_product
|
||||
ORDER BY ps.id_product
|
||||
`,
|
||||
constdata.SHOP_ID, // products_page
|
||||
id_lang, id_lang, // variation_attributes pal.id_lang, pagl.id_lang
|
||||
constdata.SHOP_ID, // variation_attributes pas.id_shop
|
||||
id_lang, id_lang, // variation_attribute_filters pal.id_lang, pagl.id_lang
|
||||
constdata.SHOP_ID, // variation_attribute_filters pas.id_shop
|
||||
id_lang, id_lang, // features pfl.id_lang, pfvl.id_lang
|
||||
constdata.SHOP_ID, // images id_shop
|
||||
constdata.SHOP_ID, // variation count subquery
|
||||
constdata.SHOP_ID, // ps_product_lang pl.id_shop
|
||||
id_lang, // ps_product_lang pl.id_lang
|
||||
constdata.SHOP_ID, // ps_category_lang cl.id_shop
|
||||
id_lang, // ps_category_lang cl.id_lang
|
||||
)
|
||||
if err := query.Scan(&products).Error; err != nil {
|
||||
return products, fmt.Errorf("database error: %w", err)
|
||||
}
|
||||
err := db.Get().
|
||||
Table("ps_product_shop ps").
|
||||
Select(`
|
||||
ps.id_product AS id_product,
|
||||
pl.name AS name,
|
||||
TRIM(REGEXP_REPLACE(REGEXP_REPLACE(pl.description_short, '<[^>]*>', ' '), '[[:space:]]+', ' ')) AS description,
|
||||
p.ean13,
|
||||
p.reference,
|
||||
ps.price,
|
||||
ps.id_category_default AS id_category,
|
||||
cl.name AS cat_name,
|
||||
cl.link_rewrite AS l_rew,
|
||||
COALESCE(vary.attributes, JSON_OBJECT()) AS attr,
|
||||
COALESCE(feat.features, JSON_OBJECT()) AS feat,
|
||||
img.id_image,
|
||||
cat.category_ids,
|
||||
(SELECT COUNT(*) FROM ps_product_attribute_shop pas2 WHERE pas2.id_product = ps.id_product AND pas2.id_shop = ?) AS variations
|
||||
`, constdata.SHOP_ID).
|
||||
Joins("JOIN ps_product p ON p.id_product = ps.id_product").
|
||||
Joins("JOIN ps_product_lang pl ON pl.id_product = ps.id_product AND pl.id_shop = ? AND pl.id_lang = ?", constdata.SHOP_ID, id_lang).
|
||||
Joins("JOIN ps_category_lang cl ON cl.id_category = ps.id_category_default AND cl.id_shop = ? AND cl.id_lang = ?", constdata.SHOP_ID, id_lang).
|
||||
Joins("LEFT JOIN variations vary ON vary.id_product = ps.id_product").
|
||||
Joins("LEFT JOIN features feat ON feat.id_product = ps.id_product").
|
||||
Joins("LEFT JOIN images img ON img.id_product = ps.id_product").
|
||||
Joins("LEFT JOIN categories cat ON cat.id_product = ps.id_product").
|
||||
Joins("JOIN products_page pp ON pp.id_product = ps.id_product").
|
||||
Where("ps.active = ?", 1).
|
||||
Order("ps.id_product").
|
||||
Clauses(exclause.With{CTEs: []exclause.CTE{
|
||||
{
|
||||
Name: "products_page",
|
||||
Subquery: exclause.Subquery{
|
||||
DB: db.Get().
|
||||
Model(&dbmodel.PsProductShop{}).
|
||||
Select("id_product, price").
|
||||
Where("id_shop = ? AND active = 1", constdata.SHOP_ID).
|
||||
Order("id_product").
|
||||
Limit(limit).
|
||||
Offset(offset),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "variation_attributes",
|
||||
Subquery: exclause.Subquery{
|
||||
DB: db.Get().
|
||||
Table("ps_product_attribute_shop pas"). // <- explicit alias here
|
||||
Select(`
|
||||
pas.id_product,
|
||||
pag.id_attribute_group AS attribute_name,
|
||||
JSON_ARRAYAGG(DISTINCT pa.id_attribute) AS attribute_values
|
||||
`).
|
||||
Joins("JOIN ps_product_attribute_combination ppac ON ppac.id_product_attribute = pas.id_product_attribute").
|
||||
Joins("JOIN ps_attribute pa ON pa.id_attribute = ppac.id_attribute").
|
||||
Joins("JOIN ps_attribute_group pag ON pag.id_attribute_group = pa.id_attribute_group").
|
||||
Where("pas.id_shop = ?", constdata.SHOP_ID).
|
||||
Group("pas.id_product, pag.id_attribute_group"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "variations",
|
||||
Subquery: exclause.Subquery{
|
||||
DB: db.Get().
|
||||
Table("variation_attributes").
|
||||
Select("id_product, JSON_OBJECTAGG(attribute_name, attribute_values) AS attributes").
|
||||
Group("id_product"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "features",
|
||||
Subquery: exclause.Subquery{
|
||||
DB: db.Get().
|
||||
Table("ps_feature_product pfp"). // <- explicit alias
|
||||
Select("pfp.id_product, JSON_OBJECTAGG(pfp.id_feature, pfp.id_feature_value) AS features").
|
||||
Group("pfp.id_product"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "images",
|
||||
Subquery: exclause.Subquery{
|
||||
DB: db.Get().
|
||||
Model(&dbmodel.PsImageShop{}).
|
||||
Select("id_product, id_image").
|
||||
Where("id_shop = ? AND cover = 1", constdata.SHOP_ID),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "categories",
|
||||
Subquery: exclause.Subquery{
|
||||
DB: db.Get().
|
||||
Model(&dbmodel.PsCategoryProduct{}).
|
||||
Select("id_product, JSON_ARRAYAGG(id_category) AS category_ids").
|
||||
Group("id_product"),
|
||||
},
|
||||
},
|
||||
}}).Find(&products).Error
|
||||
|
||||
return products, nil
|
||||
return products, err
|
||||
}
|
||||
|
||||
82
app/repos/searchRepo/searchRepo.go
Normal file
82
app/repos/searchRepo/searchRepo.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package searchrepo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
)
|
||||
|
||||
type SearchProxyResponse struct {
|
||||
StatusCode int
|
||||
Body []byte
|
||||
}
|
||||
|
||||
type UISearchRepo interface {
|
||||
Search(index string, body []byte) (*SearchProxyResponse, error)
|
||||
GetIndexSettings(index string) (*SearchProxyResponse, error)
|
||||
GetRoutes(langId uint) ([]model.Route, error)
|
||||
}
|
||||
|
||||
type SearchRepo struct {
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func New() UISearchRepo {
|
||||
return &SearchRepo{
|
||||
cfg: config.Get(),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SearchRepo) Search(index string, body []byte) (*SearchProxyResponse, error) {
|
||||
url := fmt.Sprintf("%s/indexes/%s/search", r.cfg.MailiSearch.ServerURL, index)
|
||||
return r.doRequest(http.MethodPost, url, body)
|
||||
}
|
||||
|
||||
func (r *SearchRepo) GetIndexSettings(index string) (*SearchProxyResponse, error) {
|
||||
url := fmt.Sprintf("%s/indexes/%s/settings", r.cfg.MailiSearch.ServerURL, index)
|
||||
return r.doRequest(http.MethodGet, url, nil)
|
||||
}
|
||||
|
||||
func (r *SearchRepo) doRequest(method, url string, body []byte) (*SearchProxyResponse, error) {
|
||||
var reqBody *bytes.Reader
|
||||
if body != nil {
|
||||
reqBody = bytes.NewReader(body)
|
||||
} else {
|
||||
reqBody = bytes.NewReader([]byte{})
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if r.cfg.MailiSearch.ApiKey != "" {
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.cfg.MailiSearch.ApiKey))
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to reach meilisearch: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
return &SearchProxyResponse{
|
||||
StatusCode: resp.StatusCode,
|
||||
Body: respBody,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *SearchRepo) GetRoutes(langId uint) ([]model.Route, error) {
|
||||
return nil, nil
|
||||
}
|
||||
Reference in New Issue
Block a user