'routing, searching, fixes'
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"products-openai": {
|
||||
"source": "openAi",
|
||||
"model": "text-embedding-3-small",
|
||||
"apiKey": "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",
|
||||
"documentTemplate": "{{doc.Name}} is equipment used for {{doc.Description | truncatewords: 20}}"
|
||||
}
|
||||
}
|
||||
230
app/service/meiliService/indexation_params.go
Normal file
230
app/service/meiliService/indexation_params.go
Normal file
@@ -0,0 +1,230 @@
|
||||
package meiliService
|
||||
|
||||
import "github.com/meilisearch/meilisearch-go"
|
||||
|
||||
type IndexationParams struct {
|
||||
LocalizedAttributes []*meilisearch.LocalizedAttributes `json:"localizedAttributes"`
|
||||
StopWords []string `json:"stopWords"`
|
||||
Synonyms map[string][]string `json:"synonyms"`
|
||||
}
|
||||
|
||||
var indexatio_params = map[uint]IndexationParams{
|
||||
1: {
|
||||
LocalizedAttributes: []*meilisearch.LocalizedAttributes{
|
||||
{AttributePatterns: []string{"*"}, Locales: []string{"pol"}},
|
||||
},
|
||||
StopWords: []string{
|
||||
"i", "w", "z", "na", "do", "się", "nie", "to", "że",
|
||||
"a", "o", "jak", "ale", "po", "za", "przez", "przy",
|
||||
"dla", "czy", "lub", "oraz", "ich", "jej", "jego",
|
||||
"ten", "ta", "te", "tego", "tej", "tym", "tych",
|
||||
"jest", "są", "być", "był", "była", "było", "będzie",
|
||||
"już", "jeszcze", "też", "tylko", "więc", "jednak",
|
||||
"co", "kto", "który", "która", "które", "ze", "by",
|
||||
"ze", "im", "go", "je", "tu", "tam", "tak",
|
||||
},
|
||||
Synonyms: map[string][]string{
|
||||
"plecak": {"plecaki", "plecaku", "plecaków", "tornister", "torba szkolna"},
|
||||
"tornister": {"plecak", "torba szkolna", "plecaki"},
|
||||
"piórnik": {"piórniki", "piórnika", "etui na przybory"},
|
||||
"zeszyt": {"zeszyty", "zeszytu", "zeszytów", "notatnik", "notes"},
|
||||
"kredka": {"kredki", "kredek", "kredkami", "ołówek kolorowy"},
|
||||
"farba": {"farby", "farb", "farbami", "farba plakatowa", "tempera"},
|
||||
"nożyczki": {"nożyczki dla dzieci", "nożyczki bezpieczne"},
|
||||
"linijka": {"linijki", "linijka plastikowa", "linijka drewniana"},
|
||||
"kalkulator": {"kalkulatory", "kalkulator szkolny", "kalkulator naukowy"},
|
||||
"długopis": {"długopisy", "długopisu", "długopisów", "pisak", "pióro"},
|
||||
"ołówek": {"ołówki", "ołówka", "ołówków", "grafitowy"},
|
||||
"gumka": {"gumki", "gumka do mazania", "korektor"},
|
||||
"temperówka": {"temperówki", "temperówka elektryczna"},
|
||||
"plastelina": {"plasteliny", "masa plastyczna", "modelina", "glinka"},
|
||||
"bibuła": {"bibułki", "bibuła marszczona", "krepa"},
|
||||
"brystol": {"brystole", "brystolu", "karton", "tektura"},
|
||||
"blok": {"bloki", "blok rysunkowy", "blok techniczny", "blok A4"},
|
||||
"klej": {"kleje", "kleju", "klej w sztyfcie", "klej PVA", "klej UHU"},
|
||||
"taśma": {"taśmy", "taśma klejąca", "taśma scotch"},
|
||||
"marker": {"markery", "mazak", "mazaki", "flamaster", "flamastry"},
|
||||
"pędzel": {"pędzle", "pędzla", "pędzli", "pędzel do farb"},
|
||||
"plakat": {"plakaty", "plansza edukacyjna", "poster"},
|
||||
"atlas": {"atlasy", "atlas geograficzny", "mapa"},
|
||||
"słownik": {"słowniki", "słownika", "słownik angielski"},
|
||||
"czytanka": {"czytanki", "lektura", "książka dla dzieci", "bajka"},
|
||||
"zabawka": {"zabawki", "zabawek", "gra edukacyjna", "puzzle"},
|
||||
"układanka": {"układanki", "puzzle", "mozaika"},
|
||||
"klocki": {"klocek", "klocków", "lego", "duplo"},
|
||||
"fartuch": {"fartuchy", "fartuch malarski", "fartuch plastyczny"},
|
||||
"teczka": {"teczki", "teczka na dokumenty", "segregator", "portfolio"},
|
||||
"koszulka": {"koszulki", "koszulka na dokumenty", "foliówka"},
|
||||
"ekierka": {"ekierki", "kątownik"},
|
||||
"cyrkiel": {"cyrkle", "kompas rysunkowy"},
|
||||
"globus": {"globusy", "kula ziemska"},
|
||||
"tablica": {"tablice", "tablica magnetyczna", "whiteboard", "flipchart"},
|
||||
"magnez": {"magnesy", "magnes tablicowy"},
|
||||
"kreda": {"kredy", "kreda tablicowa", "kreda kolorowa"},
|
||||
"przedszkole": {"żłobek", "edukacja przedszkolna", "wiek przedszkolny"},
|
||||
"szkoła": {"szkolny", "szkolna", "szkolne", "podstawówka"},
|
||||
},
|
||||
},
|
||||
2: {
|
||||
LocalizedAttributes: []*meilisearch.LocalizedAttributes{
|
||||
{AttributePatterns: []string{"*"}, Locales: []string{"eng"}},
|
||||
},
|
||||
StopWords: []string{
|
||||
"a", "an", "the", "and", "or", "but", "in", "on", "at",
|
||||
"to", "for", "of", "with", "by", "from", "is", "are",
|
||||
"was", "were", "be", "been", "being", "have", "has",
|
||||
"had", "do", "does", "did", "will", "would", "could",
|
||||
"should", "may", "might", "it", "its", "this", "that",
|
||||
"these", "those", "as", "if", "so", "than", "up",
|
||||
},
|
||||
Synonyms: map[string][]string{
|
||||
"backpack": {"rucksack", "school bag", "schoolbag", "knapsack"},
|
||||
"pencil case": {"pencil pouch", "pencil box", "stationery case"},
|
||||
"notebook": {"exercise book", "jotter", "notepad", "copybook"},
|
||||
"colored pencil": {"colour pencil", "colouring pencil", "crayon pencil"},
|
||||
"crayon": {"wax crayon", "colouring crayon", "wax pencil"},
|
||||
"paint": {"poster paint", "tempera", "watercolour", "finger paint"},
|
||||
"scissors": {"safety scissors", "kids scissors", "craft scissors"},
|
||||
"ruler": {"measuring ruler", "plastic ruler", "wooden ruler"},
|
||||
"calculator": {"scientific calculator", "school calculator"},
|
||||
"pen": {"ballpoint", "ballpoint pen", "biro", "rollerball"},
|
||||
"pencil": {"graphite pencil", "HB pencil", "drawing pencil"},
|
||||
"eraser": {"rubber", "correction eraser", "white eraser"},
|
||||
"sharpener": {"pencil sharpener", "electric sharpener"},
|
||||
"plasticine": {"modelling clay", "play-doh", "clay", "dough"},
|
||||
"tissue paper": {"crepe paper", "craft paper"},
|
||||
"cardboard": {"card", "art board", "bristol board"},
|
||||
"drawing pad": {"sketch pad", "art pad", "drawing block", "sketchbook"},
|
||||
"glue": {"glue stick", "PVA glue", "craft glue", "adhesive"},
|
||||
"tape": {"sticky tape", "sellotape", "scotch tape", "masking tape"},
|
||||
"marker": {"felt tip", "felt-tip pen", "highlighter", "marker pen"},
|
||||
"paintbrush": {"brush", "art brush", "watercolour brush"},
|
||||
"poster": {"educational poster", "wall chart", "learning poster"},
|
||||
"dictionary": {"word book", "vocabulary book", "language dictionary"},
|
||||
"book": {"reading book", "children book", "picture book", "reader"},
|
||||
"toy": {"educational toy", "learning toy", "kids toy"},
|
||||
"puzzle": {"jigsaw", "jigsaw puzzle", "floor puzzle"},
|
||||
"blocks": {"building blocks", "lego", "wooden blocks", "duplo"},
|
||||
"apron": {"art apron", "painting apron", "smock"},
|
||||
"folder": {"document folder", "ring binder", "portfolio"},
|
||||
"sleeve": {"document sleeve", "plastic sleeve", "page protector"},
|
||||
"compass": {"drawing compass", "geometry compass"},
|
||||
"set square": {"triangle ruler", "geometry set"},
|
||||
"globe": {"world globe", "earth globe"},
|
||||
"whiteboard": {"dry erase board", "magnetic board", "notice board"},
|
||||
"chalk": {"blackboard chalk", "coloured chalk", "sidewalk chalk"},
|
||||
"kindergarten": {"preschool", "nursery", "early years", "pre-k"},
|
||||
"school": {"primary school", "elementary school", "educational"},
|
||||
},
|
||||
},
|
||||
3: {
|
||||
LocalizedAttributes: []*meilisearch.LocalizedAttributes{
|
||||
{AttributePatterns: []string{"*"}, Locales: []string{"deu"}},
|
||||
},
|
||||
StopWords: []string{
|
||||
"und", "oder", "aber", "in", "an", "auf", "zu", "für",
|
||||
"von", "mit", "durch", "bei", "nach", "über", "unter",
|
||||
"ist", "sind", "war", "waren", "sein", "haben", "hat",
|
||||
"der", "die", "das", "den", "dem", "des", "ein", "eine",
|
||||
"einer", "einem", "einen", "eines", "sich", "auch",
|
||||
"nicht", "noch", "schon", "nur", "so", "wie", "wenn",
|
||||
"dann", "da", "hier", "dort", "ich", "du", "er", "sie",
|
||||
"es", "wir", "ihr", "als", "am", "im", "ins",
|
||||
},
|
||||
Synonyms: map[string][]string{
|
||||
"rucksack": {"schulrucksack", "schulranzen", "ranzen", "tornister"},
|
||||
"schulranzen": {"rucksack", "tornister", "schultasche"},
|
||||
"federmäppchen": {"mäppchen", "federmappe", "stiftemäppchen", "etui"},
|
||||
"heft": {"schulheft", "schreibheft", "lineatur", "notizheft"},
|
||||
"buntstift": {"buntstifte", "farbstift", "farbstifte", "malstift"},
|
||||
"wachsmalstift": {"wachsmalstifte", "wachskreide", "malwachs"},
|
||||
"farbe": {"schulfarbe", "plakatfarbe", "wasserfarbe", "fingerfarbe", "tempera"},
|
||||
"schere": {"kinderschere", "bastelschere", "sicherheitsschere"},
|
||||
"lineal": {"schullineal", "kunststofflineal", "holzlineal"},
|
||||
"taschenrechner": {"schulrechner", "wissenschaftlicher rechner", "rechner"},
|
||||
"kugelschreiber": {"kuli", "stift", "tintenroller", "füller"},
|
||||
"bleistift": {"zeichenbleistift", "graphitstift", "schulbleistift"},
|
||||
"radiergummi": {"radierer", "radiergummi", "tipp-ex"},
|
||||
"anspitzer": {"bleistiftspitzer", "elektrischer anspitzer"},
|
||||
"knete": {"knetmasse", "plastilin", "modellierton", "play-doh"},
|
||||
"seidenpapier": {"krepppapier", "krepp", "bastelpapier"},
|
||||
"karton": {"pappe", "zeichenkarton", "bristol"},
|
||||
"zeichenblock": {"malblock", "skizzenblock", "zeichenpapier"},
|
||||
"kleber": {"klebestift", "bastelkleber", "PVA-kleber", "UHU"},
|
||||
"klebeband": {"tesa", "scotch", "klebeband transparent", "kreativband"},
|
||||
"marker": {"filzstift", "faserstift", "textmarker", "edding"},
|
||||
"pinsel": {"malpinsel", "aquarellpinsel", "zeichenpinsel"},
|
||||
"poster": {"lernposter", "bildungsposter", "schulposter", "wandkarte"},
|
||||
"wörterbuch": {"schulwörterbuch", "vokabelbuch", "lexikon"},
|
||||
"buch": {"kinderbuch", "schulbuch", "lesebuch", "bilderbuch"},
|
||||
"spielzeug": {"lernspielzeug", "pädagogisches spielzeug", "kinderspielzeug"},
|
||||
"puzzle": {"legepuzzle", "bodenpuzzle", "steckpuzzle"},
|
||||
"baustein": {"bausteine", "lego", "holzbausteine", "duplo"},
|
||||
"schürze": {"malschürze", "bastelschürze", "kittel"},
|
||||
"mappe": {"schulmappe", "hefter", "ringbuch", "ordner"},
|
||||
"klarsichthülle": {"prospekthülle", "dokumentenhülle", "sichthülle"},
|
||||
"zirkel": {"zeichenzirkel", "geometriezirkel"},
|
||||
"geodreieck": {"winkelmesser", "zeichendreieck", "geometriedreieck"},
|
||||
"globus": {"erdkugel", "weltkugel", "schulglob"},
|
||||
"whiteboard": {"magnettafel", "schreibtafel", "flipchart"},
|
||||
"kreide": {"tafelkreide", "schulkreide", "bunkreide"},
|
||||
"kindergarten": {"kita", "krippe", "vorschule", "kiga"},
|
||||
"schule": {"grundschule", "volksschule", "schulbedarf"},
|
||||
},
|
||||
},
|
||||
4: {
|
||||
LocalizedAttributes: []*meilisearch.LocalizedAttributes{
|
||||
{AttributePatterns: []string{"*"}, Locales: []string{"ces"}},
|
||||
},
|
||||
StopWords: []string{
|
||||
"a", "i", "v", "na", "do", "se", "ne", "to", "že",
|
||||
"o", "jak", "ale", "po", "za", "pro", "při", "pro",
|
||||
"nebo", "či", "anebo", "jejich", "jeho", "její",
|
||||
"ten", "ta", "to", "toho", "té", "tom", "těch",
|
||||
"je", "jsou", "být", "byl", "byla", "bylo", "bude",
|
||||
"už", "ještě", "také", "jen", "tedy", "však",
|
||||
"co", "kdo", "který", "která", "které", "ze",
|
||||
"by", "mu", "ho", "ji", "tu", "tam", "tak",
|
||||
},
|
||||
Synonyms: map[string][]string{
|
||||
"batoh": {"batohy", "batohu", "batohů", "školní batoh", "školní taška", "aktovka"},
|
||||
"aktovka": {"aktovky", "školní aktovka", "batoh", "taška do školy"},
|
||||
"penál": {"penály", "penálu", "pouzdro na tužky", "pouzdro na psací potřeby"},
|
||||
"sešit": {"sešity", "sešitu", "sešitů", "poznámkový blok", "zápisník"},
|
||||
"pastelka": {"pastelky", "pastelku", "pastelkách", "barevná tužka", "barevné tužky"},
|
||||
"voskovka": {"voskovky", "vosková pastelka", "voskové pastelky"},
|
||||
"barva": {"barvy", "barev", "školní barva", "plakátová barva", "temperová barva", "vodová barva", "prstová barva"},
|
||||
"nůžky": {"dětské nůžky", "bezpečné nůžky", "školní nůžky"},
|
||||
"pravítko": {"pravítka", "plastové pravítko", "dřevěné pravítko"},
|
||||
"kalkulačka": {"kalkulačky", "školní kalkulačka", "vědecká kalkulačka"},
|
||||
"propiska": {"propiska", "propisky", "kuličkové pero", "pero", "roller"},
|
||||
"tužka": {"tužky", "grafitová tužka", "školní tužka", "kreslicí tužka"},
|
||||
"guma": {"gumy", "guma na mazání", "mazací guma", "korektor"},
|
||||
"ořezávátko": {"ořezávátka", "elektrické ořezávátko"},
|
||||
"plastelína": {"plastelíny", "modelovací hmota", "modelína", "play-doh", "hlína"},
|
||||
"hedvábný papír": {"krepový papír", "krepp", "krepa"},
|
||||
"karton": {"kartony", "výkres", "výkresy", "bristol", "čtvrtka"},
|
||||
"skicák": {"skicáky", "blok na kreslení", "kreslicí blok", "náčrtník"},
|
||||
"lepidlo": {"lepidla", "lepicí tyčinka", "PVA lepidlo", "UHU", "lepidlo v tyčince"},
|
||||
"lepicí páska": {"scotch", "průhledná páska", "izolepa"},
|
||||
"fix": {"fixy", "fixů", "fixů", "popisovač", "popisovače", "zvýrazňovač"},
|
||||
"štětec": {"štětce", "malířský štětec", "akvarelový štětec"},
|
||||
"plakát": {"plakáty", "vzdělávací plakát", "výuková tabule", "nástěnná mapa"},
|
||||
"slovník": {"slovníky", "školní slovník", "výkladový slovník"},
|
||||
"kniha": {"knihy", "dětská kniha", "čítanka", "učebnice", "obrázkové knihy"},
|
||||
"hračka": {"hračky", "vzdělávací hračka", "didaktická hračka"},
|
||||
"puzzle": {"skládačka", "skládačky", "mozaika", "jigsaw"},
|
||||
"kostky": {"stavebnice", "lego", "dřevěné kostky", "duplo"},
|
||||
"zástěra": {"zástěry", "malířská zástěra", "ochranný plášť"},
|
||||
"desky": {"složka", "složky", "pořadač", "kroužkový pořadač", "portfolio"},
|
||||
"fólie": {"průhledná fólie", "eurodesky", "plastová fólie"},
|
||||
"kružítko": {"kružítka", "rýsovací kružítko"},
|
||||
"úhloměr": {"pravítko s úhloměrem", "trojúhelník", "rýsovací trojúhelník"},
|
||||
"glóbus": {"glóbusy", "zeměkoule", "školní glóbus"},
|
||||
"tabule": {"magnetická tabule", "whiteboard", "flipchart", "školní tabule"},
|
||||
"křída": {"křídy", "barevná křída", "školní křída"},
|
||||
"školka": {"mateřská škola", "MŠ", "předškolní věk", "jesle"},
|
||||
"škola": {"školní", "základní škola", "ZŠ", "školní potřeby"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,31 +1,34 @@
|
||||
package meiliService
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/productDescriptionRepo"
|
||||
constdata "git.ma-al.com/goc_daniel/b2b/app/utils/const_data"
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
)
|
||||
|
||||
// MeiliIndexSettings holds the configurable index settings
|
||||
type MeiliIndexSettings struct {
|
||||
SearchableAttributes []string `json:"searchableAttributes"`
|
||||
DisplayedAttributes []string `json:"displayedAttributes"`
|
||||
FilterableAttributes []string `json:"filterableAttributes"`
|
||||
SortableAttributes []string `json:"sortableAttributes"`
|
||||
}
|
||||
|
||||
type MeiliService struct {
|
||||
productDescriptionRepo productDescriptionRepo.UIProductDescriptionRepo
|
||||
meiliClient meilisearch.ServiceManager
|
||||
}
|
||||
|
||||
func New() *MeiliService {
|
||||
meiliURL := os.Getenv("MEILISEARCH_URL")
|
||||
meiliAPIKey := os.Getenv("MEILISEARCH_API_KEY")
|
||||
|
||||
client := meilisearch.New(
|
||||
meiliURL,
|
||||
meilisearch.WithAPIKey(meiliAPIKey),
|
||||
config.Get().MailiSearch.ServerURL,
|
||||
meilisearch.WithAPIKey(config.Get().MailiSearch.ApiKey),
|
||||
)
|
||||
|
||||
return &MeiliService{
|
||||
@@ -34,148 +37,181 @@ func New() *MeiliService {
|
||||
}
|
||||
}
|
||||
|
||||
func GetIndexName(id_lang uint) string {
|
||||
return fmt.Sprintf("shop_%d_lang_%d", constdata.SHOP_ID, id_lang)
|
||||
}
|
||||
|
||||
// ==================================== FOR TESTING ONLY ====================================
|
||||
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)
|
||||
indexName := GetIndexName(id_lang)
|
||||
|
||||
products, err := s.productDescriptionRepo.GetMeiliProducts(id_lang)
|
||||
for i := 0; i < len(products); i++ {
|
||||
products[i].Description, err = cleanHTML(products[i].Description)
|
||||
const batchSize = 500
|
||||
offset := 0
|
||||
|
||||
for {
|
||||
// Get batch of products from repo (includes scanning)
|
||||
products, err := s.productDescriptionRepo.GetMeiliProducts(id_lang, offset, batchSize)
|
||||
if err != nil {
|
||||
fmt.Printf("products[i].Description: %v\n", products[i].Description)
|
||||
fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID)
|
||||
fmt.Println("failed at description")
|
||||
fmt.Printf("err: %v\n", err)
|
||||
return err
|
||||
return fmt.Errorf("failed to get products batch at offset %d: %w", offset, err)
|
||||
}
|
||||
|
||||
products[i].DescriptionShort, err = cleanHTML(products[i].DescriptionShort)
|
||||
if err != nil {
|
||||
fmt.Printf("products[i].DescriptionShort: %v\n", products[i].DescriptionShort)
|
||||
fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID)
|
||||
fmt.Println("failed at description short")
|
||||
fmt.Printf("err: %v\n", err)
|
||||
return err
|
||||
// If no products returned, we're done
|
||||
if len(products) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
products[i].Usage, err = cleanHTML(products[i].Usage)
|
||||
if err != nil {
|
||||
fmt.Printf("products[i].Usage: %v\n", products[i].Usage)
|
||||
fmt.Printf("products[i].ProductID: %v\n", products[i].ProductID)
|
||||
fmt.Println("failed at usage")
|
||||
fmt.Printf("err: %v\n", err)
|
||||
return err
|
||||
// Add batch to index
|
||||
if err := s.addBatchToIndex(indexName, products); err != nil {
|
||||
return fmt.Errorf("failed to add batch to index: %w", err)
|
||||
}
|
||||
|
||||
// Update offset for next batch
|
||||
offset += batchSize
|
||||
|
||||
fmt.Printf("Indexed %d products (offset: %d)\n", len(products), offset)
|
||||
}
|
||||
|
||||
primaryKey := "ProductID"
|
||||
// Configure filterable attributes
|
||||
filterableAttributes := []interface{}{
|
||||
"product_id",
|
||||
"category_id",
|
||||
"category_ids",
|
||||
"attr",
|
||||
"feat",
|
||||
"variations",
|
||||
"price",
|
||||
}
|
||||
task, err := s.meiliClient.Index(indexName).UpdateFilterableAttributes(&filterableAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update filterable attributes: %w", err)
|
||||
}
|
||||
_, err = s.meiliClient.WaitForTask(task.TaskUID, 500*time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for filterable task: %w", err)
|
||||
}
|
||||
|
||||
// Configure sortable attributes
|
||||
sortableAttributes := []string{
|
||||
"price",
|
||||
"name",
|
||||
"product_id",
|
||||
"name",
|
||||
"category_ids",
|
||||
}
|
||||
task, err = s.meiliClient.Index(indexName).UpdateSortableAttributes(&sortableAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update sortable attributes: %w", err)
|
||||
}
|
||||
|
||||
task, err = s.meiliClient.Index(indexName).UpdateSettings(&meilisearch.Settings{
|
||||
LocalizedAttributes: indexatio_params[id_lang].LocalizedAttributes,
|
||||
Synonyms: indexatio_params[id_lang].Synonyms,
|
||||
StopWords: indexatio_params[id_lang].StopWords,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update ranking rules: %w", err)
|
||||
}
|
||||
|
||||
_, err = s.meiliClient.WaitForTask(task.TaskUID, 500*time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for sortable task: %w", err)
|
||||
}
|
||||
|
||||
// Configure displayed attributes
|
||||
displayedAttributes := []string{
|
||||
"product_id",
|
||||
"name",
|
||||
"ean13",
|
||||
"reference",
|
||||
"variations",
|
||||
"id_image",
|
||||
"price",
|
||||
"category_name",
|
||||
}
|
||||
task, err = s.meiliClient.Index(indexName).UpdateDisplayedAttributes(&displayedAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update displayed attributes: %w", err)
|
||||
}
|
||||
_, err = s.meiliClient.WaitForTask(task.TaskUID, 500*time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for displayed task: %w", err)
|
||||
}
|
||||
|
||||
// Configure searchable attributes
|
||||
searchableAttributes := []string{
|
||||
"name",
|
||||
"description",
|
||||
"ean13",
|
||||
"category_name",
|
||||
"reference",
|
||||
}
|
||||
task, err = s.meiliClient.Index(indexName).UpdateSearchableAttributes(&searchableAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update searchable attributes: %w", err)
|
||||
}
|
||||
|
||||
task, err = s.meiliClient.Index(indexName).UpdateFaceting(&meilisearch.Faceting{MaxValuesPerFacet: 5})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update ranking rules: %w", err)
|
||||
}
|
||||
|
||||
// keyRequest := &meilisearch.Key{
|
||||
// Key: "my secret key", // ✅ provide your own token
|
||||
// Description: "Search-only key for frontend",
|
||||
// Actions: []string{"search", "settings.get"},
|
||||
// Indexes: []string{indexName}, // restrict to your index
|
||||
// }
|
||||
|
||||
// createdKey, err := s.meiliClient.CreateKey(keyRequest)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// fmt.Println("Custom search key created:", createdKey.Key)
|
||||
|
||||
_, err = s.meiliClient.WaitForTask(task.TaskUID, 500*time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for searchable task: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addBatchToIndex adds a batch of products to the Meilisearch index
|
||||
func (s *MeiliService) addBatchToIndex(indexName string, products []model.MeiliSearchProduct) error {
|
||||
primaryKey := "product_id"
|
||||
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)
|
||||
return fmt.Errorf("failed to add documents: %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)
|
||||
finishedTask, err := s.meiliClient.WaitForTask(task.TaskUID, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("meili AddDocuments error: %w", err)
|
||||
return fmt.Errorf("failed to wait for task: %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
|
||||
}
|
||||
|
||||
// ==================================== FOR TESTING 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: 4,
|
||||
Facets: []string{
|
||||
"CategoryID",
|
||||
},
|
||||
if finishedTask.Status == "failed" {
|
||||
return fmt.Errorf("task failed: %v", finishedTask.Error)
|
||||
}
|
||||
|
||||
// Perform search
|
||||
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 'mat' in %s: %d hits\n", indexName, len(results.Hits))
|
||||
|
||||
return *results, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Search performs a full-text search on the specified index
|
||||
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)
|
||||
func (s *MeiliService) Search(id_lang uint, query string, id_category uint) (meilisearch.SearchResponse, error) {
|
||||
indexName := GetIndexName(id_lang)
|
||||
|
||||
filter_query := ""
|
||||
filter_query := "Active = 1"
|
||||
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)
|
||||
// Use CategoryIDs to include products from child categories
|
||||
filter_query += fmt.Sprintf(" AND CategoryIDs = %d", id_category)
|
||||
}
|
||||
|
||||
searchReq := &meilisearch.SearchRequest{
|
||||
Limit: int64(limit),
|
||||
Limit: 30,
|
||||
Facets: []string{
|
||||
"CategoryID",
|
||||
},
|
||||
@@ -201,40 +237,109 @@ func (s *MeiliService) HealthCheck() (*meilisearch.Health, error) {
|
||||
return health, nil
|
||||
}
|
||||
|
||||
// remove all tags from HTML text
|
||||
func cleanHTML(s string) (string, error) {
|
||||
r := strings.NewReader(s)
|
||||
d := xml.NewDecoder(r)
|
||||
// GetIndexSettings retrieves the current settings for a specific index
|
||||
func (s *MeiliService) GetIndexSettings(id_lang uint) (map[string]interface{}, error) {
|
||||
indexName := GetIndexName(id_lang)
|
||||
|
||||
text := ""
|
||||
index := s.meiliClient.Index(indexName)
|
||||
|
||||
// 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
|
||||
} else if err != nil {
|
||||
return text, err
|
||||
result := make(map[string]interface{})
|
||||
|
||||
// Get searchable attributes
|
||||
searchable, err := index.GetSearchableAttributes()
|
||||
if err == nil {
|
||||
result["searchableAttributes"] = searchable
|
||||
}
|
||||
|
||||
// Get filterable attributes
|
||||
filterable, err := index.GetFilterableAttributes()
|
||||
if err == nil {
|
||||
result["filterableAttributes"] = filterable
|
||||
}
|
||||
|
||||
// Get displayed attributes
|
||||
displayed, err := index.GetDisplayedAttributes()
|
||||
if err == nil {
|
||||
result["displayedAttributes"] = displayed
|
||||
}
|
||||
|
||||
// Get ranking rules
|
||||
ranking, err := index.GetRankingRules()
|
||||
if err == nil {
|
||||
result["rankingRules"] = ranking
|
||||
}
|
||||
|
||||
// Get distinct attribute
|
||||
distinct, err := index.GetDistinctAttribute()
|
||||
if err == nil && distinct != nil {
|
||||
result["distinctAttribute"] = *distinct
|
||||
}
|
||||
|
||||
// Get typo tolerance
|
||||
typo, err := index.GetTypoTolerance()
|
||||
if err == nil {
|
||||
result["typoTolerance"] = typo
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UpdateIndexSettings updates the index settings (searchable, displayed, filterable, sortable attributes)
|
||||
func (s *MeiliService) UpdateIndexSettings(id_lang uint, settings MeiliIndexSettings) error {
|
||||
indexName := GetIndexName(id_lang)
|
||||
index := s.meiliClient.Index(indexName)
|
||||
|
||||
// Update searchable attributes
|
||||
if len(settings.SearchableAttributes) > 0 {
|
||||
task, err := index.UpdateSearchableAttributes(&settings.SearchableAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update searchable attributes: %w", err)
|
||||
}
|
||||
|
||||
switch v := token.(type) {
|
||||
case xml.StartElement:
|
||||
if len(text) > 0 && text[len(text)-1] != '\n' {
|
||||
text += " \n "
|
||||
}
|
||||
case xml.EndElement:
|
||||
case xml.CharData:
|
||||
if strings.TrimSpace(string(v)) != "" {
|
||||
text += string(v)
|
||||
}
|
||||
case xml.Comment:
|
||||
case xml.ProcInst:
|
||||
case xml.Directive:
|
||||
_, err = s.meiliClient.WaitForTask(task.TaskUID, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for searchable attributes task: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return text, nil
|
||||
// Update displayed attributes
|
||||
if len(settings.DisplayedAttributes) > 0 {
|
||||
task, err := index.UpdateDisplayedAttributes(&settings.DisplayedAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update displayed attributes: %w", err)
|
||||
}
|
||||
_, err = s.meiliClient.WaitForTask(task.TaskUID, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for displayed attributes task: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Update filterable attributes
|
||||
if len(settings.FilterableAttributes) > 0 {
|
||||
var filterable []interface{}
|
||||
for _, attr := range settings.FilterableAttributes {
|
||||
filterable = append(filterable, attr)
|
||||
}
|
||||
task, err := index.UpdateFilterableAttributes(&filterable)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update filterable attributes: %w", err)
|
||||
}
|
||||
_, err = s.meiliClient.WaitForTask(task.TaskUID, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for filterable attributes task: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Update sortable attributes
|
||||
if len(settings.SortableAttributes) > 0 {
|
||||
task, err := index.UpdateSortableAttributes(&settings.SortableAttributes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update sortable attributes: %w", err)
|
||||
}
|
||||
_, err = s.meiliClient.WaitForTask(task.TaskUID, 1000*time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to wait for sortable attributes task: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user