package meiliService import ( "encoding/xml" "fmt" "io" "os" "strconv" "strings" "time" "git.ma-al.com/goc_daniel/b2b/app/db" "git.ma-al.com/goc_daniel/b2b/app/model" constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data" "github.com/meilisearch/meilisearch-go" ) type MeiliService struct { meiliClient meilisearch.ServiceManager } func New() *MeiliService { meiliURL := os.Getenv("MEILISEARCH_URL") meiliAPIKey := os.Getenv("MEILISEARCH_API_KEY") client := meilisearch.New( meiliURL, meilisearch.WithAPIKey(meiliAPIKey), ) return &MeiliService{ meiliClient: client, } } // ==================================== FOR SUPERADMIN ONLY ==================================== func (s *MeiliService) CreateIndex(id_lang uint) error { var products []model.ProductDescription err := db.DB. Table("ps_product_lang"). Where("id_shop = ? AND id_lang = ?", constdata.SHOP_ID, id_lang). Scan(&products).Error if err != nil { return fmt.Errorf("database error: %w", err) } var meiliProducts []model.MeiliSearchProduct for i := 0; i < len(products); i++ { var nextMeiliProduct model.MeiliSearchProduct nextMeiliProduct.ProductID = products[i].ProductID nextMeiliProduct.Name = products[i].Name nextMeiliProduct.Description = cleanHTML(products[i].Description) nextMeiliProduct.DescriptionShort = cleanHTML(products[i].DescriptionShort) nextMeiliProduct.Usage = cleanHTML(products[i].Usage) meiliProducts = append(meiliProducts, nextMeiliProduct) } indexName := "meili_products_shop" + strconv.FormatInt(constdata.SHOP_ID, 10) + "_lang" + strconv.FormatInt(int64(id_lang), 10) primaryKey := "product_id" docOptions := &meilisearch.DocumentOptions{ PrimaryKey: &primaryKey, SkipCreation: false, } task, err := s.meiliClient.Index(indexName).AddDocuments(meiliProducts, 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) return err } // ==================================== FOR DEBUG ONLY ==================================== 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, } // Perform search results, err := s.meiliClient.Index(indexName).Search("walek", 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)) 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) { searchReq := &meilisearch.SearchRequest{ Limit: int64(limit), } results, err := s.meiliClient.Index(indexName).Search(query, searchReq) if err != nil { fmt.Printf("Meilisearch search error: %v\n", err) return meilisearch.SearchResponse{}, err } return *results, nil } // HealthCheck checks if Meilisearch is healthy and accessible func (s *MeiliService) HealthCheck() (*meilisearch.Health, error) { health, err := s.meiliClient.Health() if err != nil { return nil, fmt.Errorf("meilisearch health check failed: %w", err) } return health, nil } // remove all tags from HTML text func cleanHTML(s string) string { r := strings.NewReader(s) d := xml.NewDecoder(r) text := "" // Configure the decoder for HTML; leave off strict and autoclose for XHTML d.Strict = true d.AutoClose = xml.HTMLAutoClose d.Entity = xml.HTMLEntity for { token, err := d.Token() if err == io.EOF { break } switch v := token.(type) { case xml.StartElement: text += "\n" case xml.EndElement: case xml.CharData: text += string(v) case xml.Comment: case xml.ProcInst: case xml.Directive: } } return text }