193 lines
6.1 KiB
Go
193 lines
6.1 KiB
Go
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/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 SearchProxyResponse struct {
|
|
StatusCode int
|
|
Body []byte
|
|
}
|
|
|
|
type UISearchRepo interface {
|
|
Search(index string, body []byte) (*SearchProxyResponse, error)
|
|
GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, 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.MeiliSearch.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.MeiliSearch.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.MeiliSearch.ApiKey != "" {
|
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", r.cfg.MeiliSearch.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
|
|
}
|
|
|
|
// GetMeiliProductsProducts returns a batch of products with LIMIT/OFFSET pagination
|
|
// The scanning is done inside the repo to keep the service layer cleaner
|
|
func (r *SearchRepo) GetMeiliProducts(id_lang uint, offset, limit int) ([]model.MeiliSearchProduct, error) {
|
|
|
|
var products []model.MeiliSearchProduct
|
|
|
|
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, err
|
|
}
|