Merge branch 'main' of ssh://git.ma-al.com:8822/goc_daniel/b2b into test
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package restricted
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/meiliService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
@@ -23,8 +25,12 @@ func NewMeiliSearchHandler() *MeiliSearchHandler {
|
||||
func MeiliSearchHandlerRoutes(r fiber.Router) fiber.Router {
|
||||
handler := NewMeiliSearchHandler()
|
||||
|
||||
r.Get("/test", handler.Test)
|
||||
// for superadmin only
|
||||
r.Get("/create-index", handler.CreateIndex)
|
||||
r.Get("/test", handler.Test)
|
||||
|
||||
// for all users
|
||||
r.Get("/search", handler.Search)
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -61,3 +67,49 @@ func (h *MeiliSearchHandler) Test(c fiber.Ctx) error {
|
||||
|
||||
return c.JSON(response.Make(&test, 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
|
||||
func (h *MeiliSearchHandler) Search(c fiber.Ctx) error {
|
||||
id_lang, ok := c.Locals("langID").(uint)
|
||||
if !ok {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
query := c.Query("query")
|
||||
|
||||
limit_attribute := c.Query("limit")
|
||||
limit, err := strconv.Atoi(limit_attribute)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
id_category_attribute := c.Query("id_category")
|
||||
id_category, err := strconv.Atoi(id_category_attribute)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
price_lower_bound_attribute := c.Query("price_lower_bound")
|
||||
price_lower_bound, err := strconv.ParseFloat(price_lower_bound_attribute, 64)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
price_upper_bound_attribute := c.Query("price_upper_bound")
|
||||
price_upper_bound, err := strconv.ParseFloat(price_upper_bound_attribute, 64)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
test, err := h.meiliService.Search(id_lang, query, uint(limit), uint(id_category), price_lower_bound, price_upper_bound)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
}
|
||||
|
||||
return c.JSON(response.Make(&test, 0, i18n.T_(c, response.Message_OK)))
|
||||
}
|
||||
|
||||
@@ -34,11 +34,14 @@ type MeiliSearchProduct struct {
|
||||
Price float64 `gorm:"column:price"`
|
||||
Description string `gorm:"column:description"`
|
||||
DescriptionShort string `gorm:"column:description_short"`
|
||||
Usage string `gorm:"column:usage"`
|
||||
Usage string `gorm:"column:used_for"`
|
||||
EAN13 string `gorm:"column:ean13"`
|
||||
Reference string `gorm:"column:reference"`
|
||||
Width float64 `gorm:"column:width"`
|
||||
Height float64 `gorm:"column:height"`
|
||||
Depth float64 `gorm:"column:depth"`
|
||||
Weight float64 `gorm:"column:weight"`
|
||||
CategoryID uint `gorm:"column:id_category"`
|
||||
CategoryName string `gorm:"column:category_name"`
|
||||
Variations uint `gorm:"column:variations"`
|
||||
}
|
||||
|
||||
@@ -19,9 +19,10 @@ func New() UICategoriesRepo {
|
||||
func (repo *CategoriesRepo) GetAllCategories(id_lang uint) ([]model.ScannedCategory, error) {
|
||||
var allCategories []model.ScannedCategory
|
||||
|
||||
err := db.DB.Raw(`
|
||||
SELECT
|
||||
ps_category.id_category AS ID,
|
||||
err := db.DB.
|
||||
Table("ps_category").
|
||||
Select(`
|
||||
ps_category.id_category AS id,
|
||||
ps_category_lang.name AS name,
|
||||
ps_category.active AS active,
|
||||
ps_category_shop.position AS position,
|
||||
@@ -29,12 +30,22 @@ func (repo *CategoriesRepo) GetAllCategories(id_lang uint) ([]model.ScannedCateg
|
||||
ps_category.is_root_category AS is_root_category,
|
||||
ps_category_lang.link_rewrite AS link_rewrite,
|
||||
ps_lang.iso_code AS iso_code
|
||||
FROM ps_category
|
||||
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 = ?
|
||||
LEFT JOIN ps_category_shop ON ps_category_shop.id_category = ps_category.id_category AND ps_category_shop.id_shop = ?
|
||||
JOIN ps_lang ON ps_lang.id_lang = ps_category_lang.id_lang
|
||||
`,
|
||||
constdata.SHOP_ID, id_lang, constdata.SHOP_ID).
|
||||
`).
|
||||
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
|
||||
`).
|
||||
Scan(&allCategories).Error
|
||||
|
||||
return allCategories, err
|
||||
|
||||
@@ -36,41 +36,43 @@ func (repo *ListProductsRepo) GetListing(id_lang uint, p find.Paging, filt *filt
|
||||
// Limit(p.Limit()).
|
||||
// Offset(p.Offset())
|
||||
|
||||
err := db.DB.Raw(`
|
||||
SELECT
|
||||
ps_product.id_product AS ID,
|
||||
subQuery := db.DB.
|
||||
Table("ps_image").
|
||||
Select("id_product, MIN(id_image) AS id_image").
|
||||
Group("id_product")
|
||||
|
||||
err := db.DB.
|
||||
Table("ps_product").
|
||||
Select(`
|
||||
ps_product.id_product AS id,
|
||||
ps_product_lang.name AS name,
|
||||
ps_product.active AS active,
|
||||
ps_product_lang.link_rewrite AS link_rewrite,
|
||||
COALESCE (
|
||||
ps_image_shop.id_image, any_image.id_image
|
||||
) AS id_image
|
||||
FROM ps_product
|
||||
COALESCE(ps_image_shop.id_image, any_image.id_image) AS id_image
|
||||
`).
|
||||
Joins(`
|
||||
LEFT JOIN ps_product_lang
|
||||
ON ps_product_lang.id_product = ps_product.id_product
|
||||
AND ps_product_lang.id_shop = ?
|
||||
ON ps_product_lang.id_product = ps_product.id_product
|
||||
AND ps_product_lang.id_shop = ?
|
||||
AND ps_product_lang.id_lang = ?
|
||||
`, constdata.SHOP_ID, id_lang).
|
||||
Joins(`
|
||||
LEFT JOIN ps_image_shop
|
||||
ON ps_image_shop.id_product = ps_product.id_product
|
||||
AND ps_image_shop.id_shop = ?
|
||||
ON ps_image_shop.id_product = ps_product.id_product
|
||||
AND ps_image_shop.id_shop = ?
|
||||
AND ps_image_shop.cover = 1
|
||||
LEFT JOIN (
|
||||
SELECT id_product, MIN(id_image) AS id_image
|
||||
FROM ps_image
|
||||
GROUP BY id_product
|
||||
) any_image
|
||||
ON ps_product.id_product = any_image.id_product
|
||||
LIMIT ? OFFSET ?`,
|
||||
constdata.SHOP_ID, id_lang, constdata.SHOP_ID, p.Limit(), p.Offset()).
|
||||
`, constdata.SHOP_ID).
|
||||
Joins("LEFT JOIN (?) AS any_image ON ps_product.id_product = any_image.id_product", subQuery).
|
||||
Limit(p.Limit()).
|
||||
Offset(p.Offset()).
|
||||
Scan(&listing).Error
|
||||
if err != nil {
|
||||
return find.Found[model.ProductInList]{}, err
|
||||
}
|
||||
|
||||
err = db.DB.Raw(`
|
||||
SELECT COUNT(*)
|
||||
FROM ps_product`).
|
||||
Scan(&total).Error
|
||||
err = db.DB.
|
||||
Table("ps_product").
|
||||
Count(&total).Error
|
||||
if err != nil {
|
||||
return find.Found[model.ProductInList]{}, err
|
||||
}
|
||||
|
||||
@@ -80,7 +80,6 @@ func (r *ProductDescriptionRepo) GetMeiliProducts(id_lang uint) ([]model.MeiliSe
|
||||
var products []model.MeiliSearchProduct
|
||||
|
||||
err := db.DB.
|
||||
Select("pl.`usage` AS `usage`").
|
||||
Select(`
|
||||
ps.id_product AS id_product,
|
||||
pl.name AS name,
|
||||
@@ -88,17 +87,24 @@ func (r *ProductDescriptionRepo) GetMeiliProducts(id_lang uint) ([]model.MeiliSe
|
||||
ps.price AS price,
|
||||
pl.description AS description,
|
||||
pl.description_short AS description_short,
|
||||
pl.usage AS used_for,
|
||||
p.ean13 AS ean13,
|
||||
p.reference AS reference,
|
||||
p.width AS width,
|
||||
p.height AS height,
|
||||
p.depth AS depth,
|
||||
p.weight AS weight
|
||||
p.weight AS weight,
|
||||
ps.id_category_default AS id_category,
|
||||
cl.name AS category_name,
|
||||
COUNT(DISTINCT pas.id_product_attribute) AS variations
|
||||
`).
|
||||
Table("ps_product_shop AS ps").
|
||||
Joins("LEFT JOIN ps_product_lang AS pl ON ps.id_product = pl.id_product AND pl.id_shop = ? AND pl.id_lang = ?", constdata.SHOP_ID, id_lang).
|
||||
Joins("LEFT JOIN ps_product AS p ON p.id_product = ps.id_product").
|
||||
Joins("LEFT JOIN ps_category_lang AS 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 ps_product_attribute_shop AS pas ON pas.id_product = ps.id_product AND pas.id_shop = ?", constdata.SHOP_ID).
|
||||
Where("ps.id_shop = ?", constdata.SHOP_ID).
|
||||
Group("ps.id_product").
|
||||
Scan(&products).Error
|
||||
if err != nil {
|
||||
return products, fmt.Errorf("database error: %w", err)
|
||||
|
||||
@@ -36,8 +36,9 @@ func New() *MeiliService {
|
||||
|
||||
// ==================================== FOR SUPERADMIN ONLY ====================================
|
||||
func (s *MeiliService) CreateIndex(id_lang uint) error {
|
||||
products, err := s.productDescriptionRepo.GetMeiliProducts(id_lang)
|
||||
indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10)
|
||||
|
||||
products, err := s.productDescriptionRepo.GetMeiliProducts(id_lang)
|
||||
for i := 0; i < len(products); i++ {
|
||||
products[i].Description, err = cleanHTML(products[i].Description)
|
||||
if err != nil {
|
||||
@@ -67,22 +68,65 @@ func (s *MeiliService) CreateIndex(id_lang uint) error {
|
||||
}
|
||||
}
|
||||
|
||||
indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10)
|
||||
primaryKey := "ProductID"
|
||||
docOptions := &meilisearch.DocumentOptions{
|
||||
PrimaryKey: &primaryKey,
|
||||
SkipCreation: false,
|
||||
}
|
||||
|
||||
task, err := s.meiliClient.Index(indexName).AddDocuments(products, docOptions)
|
||||
if err != nil {
|
||||
return fmt.Errorf("meili AddDocuments error: %w", err)
|
||||
}
|
||||
|
||||
finishedTask, err := s.meiliClient.WaitForTask(task.TaskUID, 500*time.Millisecond)
|
||||
fmt.Printf("Task status: %s\n", finishedTask.Status)
|
||||
fmt.Printf("Task error: %s\n", finishedTask.Error)
|
||||
|
||||
filterableAttributes := []interface{}{
|
||||
"CategoryID",
|
||||
"Price",
|
||||
}
|
||||
task, err = s.meiliClient.Index(indexName).UpdateFilterableAttributes(&filterableAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("meili AddDocuments error: %w", err)
|
||||
}
|
||||
finishedTask, err = s.meiliClient.WaitForTask(task.TaskUID, 500*time.Millisecond)
|
||||
fmt.Printf("Task status: %s\n", finishedTask.Status)
|
||||
fmt.Printf("Task error: %s\n", finishedTask.Error)
|
||||
|
||||
displayedAttributes := []string{
|
||||
"ProductID",
|
||||
"Name",
|
||||
"Active",
|
||||
"Price",
|
||||
"EAN13",
|
||||
"Reference",
|
||||
"Variations",
|
||||
}
|
||||
task, err = s.meiliClient.Index(indexName).UpdateDisplayedAttributes(&displayedAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("meili AddDocuments error: %w", err)
|
||||
}
|
||||
finishedTask, err = s.meiliClient.WaitForTask(task.TaskUID, 500*time.Millisecond)
|
||||
fmt.Printf("Task status: %s\n", finishedTask.Status)
|
||||
fmt.Printf("Task error: %s\n", finishedTask.Error)
|
||||
|
||||
searchableAttributes := []string{
|
||||
"Name",
|
||||
"DescriptionShort",
|
||||
"Reference",
|
||||
"EAN13",
|
||||
"CategoryName",
|
||||
"Description",
|
||||
"Usage",
|
||||
}
|
||||
task, err = s.meiliClient.Index(indexName).UpdateSearchableAttributes(&searchableAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("meili AddDocuments error: %w", err)
|
||||
}
|
||||
finishedTask, err = s.meiliClient.WaitForTask(task.TaskUID, 500*time.Millisecond)
|
||||
fmt.Printf("Task status: %s\n", finishedTask.Status)
|
||||
fmt.Printf("Task error: %s\n", finishedTask.Error)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -91,25 +135,51 @@ func (s *MeiliService) Test(id_lang uint) (meilisearch.SearchResponse, error) {
|
||||
indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10)
|
||||
|
||||
searchReq := &meilisearch.SearchRequest{
|
||||
Limit: 3,
|
||||
Limit: 4,
|
||||
Facets: []string{
|
||||
"CategoryID",
|
||||
},
|
||||
}
|
||||
|
||||
// Perform search
|
||||
results, err := s.meiliClient.Index(indexName).Search("walek", searchReq)
|
||||
results, err := s.meiliClient.Index(indexName).Search("mat", searchReq)
|
||||
if err != nil {
|
||||
fmt.Printf("Meilisearch error: %v\n", err)
|
||||
return meilisearch.SearchResponse{}, err
|
||||
}
|
||||
|
||||
fmt.Printf("Search results for query 'walek' in %s: %d hits\n", indexName, len(results.Hits))
|
||||
fmt.Printf("Search results for query 'mat' in %s: %d hits\n", indexName, len(results.Hits))
|
||||
|
||||
return *results, nil
|
||||
}
|
||||
|
||||
// Search performs a full-text search on the specified index
|
||||
func (s *MeiliService) Search(indexName string, query string, limit int) (meilisearch.SearchResponse, error) {
|
||||
func (s *MeiliService) Search(id_lang uint, query string, limit uint, id_category uint, price_lower_bound float64, price_upper_bound float64) (meilisearch.SearchResponse, error) {
|
||||
indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10)
|
||||
|
||||
filter_query := ""
|
||||
if id_category != 0 {
|
||||
filter_query = "CategoryID = " + strconv.FormatUint(uint64(id_category), 10)
|
||||
}
|
||||
if price_lower_bound > 0.0 {
|
||||
if filter_query != "" {
|
||||
filter_query += " AND "
|
||||
}
|
||||
filter_query += "Price >= " + strconv.FormatFloat(price_lower_bound, 'f', -1, 64)
|
||||
}
|
||||
if price_upper_bound > 0.0 {
|
||||
if filter_query != "" {
|
||||
filter_query += " AND "
|
||||
}
|
||||
filter_query += "Price <= " + strconv.FormatFloat(price_upper_bound, 'f', -1, 64)
|
||||
}
|
||||
|
||||
searchReq := &meilisearch.SearchRequest{
|
||||
Limit: int64(limit),
|
||||
Facets: []string{
|
||||
"CategoryID",
|
||||
},
|
||||
Filter: filter_query,
|
||||
}
|
||||
|
||||
results, err := s.meiliClient.Index(indexName).Search(query, searchReq)
|
||||
|
||||
Reference in New Issue
Block a user