flamingo.me/flamingo-commerce/v3@v3.11.0/product/infrastructure/fake/searchservice.go (about) 1 package fake 2 3 import ( 4 "context" 5 "encoding/json" 6 "os" 7 "strconv" 8 9 searchDomain "flamingo.me/flamingo-commerce/v3/search/domain" 10 11 "flamingo.me/flamingo-commerce/v3/product/domain" 12 ) 13 14 type ( 15 // SearchService is just mocking stuff 16 SearchService struct { 17 productService *ProductService 18 liveSearchJSON string 19 categoryFacetItemsJSON string 20 } 21 liveSearchData struct { 22 Marketplacecodes []string `json:"marketplacecodes"` 23 Sugestions []searchDomain.Suggestion `json:"sugestions"` 24 Promotions []searchDomain.Promotion `json:"promotions"` 25 Actions []searchDomain.Action `json:"actions"` 26 } 27 ) 28 29 // Inject dependencies 30 func (s *SearchService) Inject( 31 productService *ProductService, 32 cfg *struct { 33 LiveSearchJSON string `inject:"config:commerce.product.fakeservice.jsonTestDataLiveSearch,optional"` 34 CategoryFacetItemsJSON string `inject:"config:commerce.product.fakeservice.jsonTestDataCategoryFacetItems,optional"` 35 }, 36 ) *SearchService { 37 s.productService = productService 38 if cfg != nil { 39 s.liveSearchJSON = cfg.LiveSearchJSON 40 s.categoryFacetItemsJSON = cfg.CategoryFacetItemsJSON 41 } 42 43 return s 44 } 45 46 // Search returns Products based on given Filters 47 func (s *SearchService) Search(ctx context.Context, filters ...searchDomain.Filter) (*domain.SearchResult, error) { 48 hits := s.findProducts(ctx, filters, s.productService.GetMarketPlaceCodes()) 49 currentPage := s.findCurrentPage(filters) 50 facets, selectedFacets := s.createFacets(filters) 51 52 documents := make([]searchDomain.Document, len(hits)) 53 for i, hit := range hits { 54 documents[i] = hit 55 } 56 57 return &domain.SearchResult{ 58 Result: searchDomain.Result{ 59 SearchMeta: searchDomain.SearchMeta{ 60 Query: "", 61 OriginalQuery: "", 62 Page: currentPage, 63 NumPages: 10, 64 NumResults: len(hits), 65 SelectedFacets: selectedFacets, 66 SortOptions: s.findSorts(filters), 67 }, 68 Hits: documents, 69 Suggestion: []searchDomain.Suggestion{}, 70 Facets: facets, 71 }, 72 Hits: hits, 73 }, nil 74 } 75 76 func (s *SearchService) livesearch(ctx context.Context, query string) (*domain.SearchResult, error) { 77 data := make(map[string]liveSearchData) 78 79 fileContent, err := os.ReadFile(s.liveSearchJSON) 80 if err != nil { 81 return nil, err 82 } 83 84 err = json.Unmarshal(fileContent, &data) 85 if err != nil { 86 return nil, err 87 } 88 89 liveSearchData := data[query] 90 91 hits := s.findProducts(ctx, nil, liveSearchData.Marketplacecodes) 92 documents := make([]searchDomain.Document, len(hits)) 93 for i, hit := range hits { 94 documents[i] = hit 95 } 96 97 return &domain.SearchResult{ 98 Result: searchDomain.Result{ 99 SearchMeta: searchDomain.SearchMeta{ 100 Page: 1, 101 NumPages: 1, 102 NumResults: len(hits), 103 }, 104 Hits: documents, 105 Suggestion: liveSearchData.Sugestions, 106 Promotions: liveSearchData.Promotions, 107 Actions: liveSearchData.Actions, 108 }, 109 Hits: hits, 110 }, nil 111 } 112 113 // SearchBy returns Products prefiltered by the given attribute (also based on additional given Filters) 114 func (s *SearchService) SearchBy(ctx context.Context, attribute string, _ []string, filters ...searchDomain.Filter) (*domain.SearchResult, error) { 115 if attribute == "livesearch" && s.liveSearchJSON != "" { 116 var query string 117 for _, f := range filters { 118 if qf, ok := f.(*searchDomain.QueryFilter); ok { 119 _, q := qf.Value() 120 query = q[0] 121 break 122 } 123 } 124 return s.livesearch(ctx, query) 125 } 126 127 return s.Search(ctx, filters...) 128 } 129 130 func (s *SearchService) findProducts(ctx context.Context, filters []searchDomain.Filter, marketPlaceCodes []string) []domain.BasicProduct { 131 products := make([]domain.BasicProduct, 0) 132 133 // - try finding product by marketPlaceCode given in query or return nothing if query is no-results 134 if query, found := s.filterValue(filters, "q"); found { 135 if len(query) > 0 { 136 if query[0] == "no-results" { 137 return products 138 } 139 product, _ := s.productService.Get(ctx, query[0]) 140 if product != nil { 141 products = append(products, product) 142 } 143 } 144 } 145 146 // - get default products 147 if len(products) == 0 { 148 for _, marketPlaceCode := range marketPlaceCodes { 149 product, _ := s.productService.Get(ctx, marketPlaceCode) 150 if product != nil { 151 products = append(products, product) 152 } 153 } 154 } 155 156 return products 157 } 158 159 func (s *SearchService) findCurrentPage(filters []searchDomain.Filter) int { 160 currentPage := 1 161 162 if page, found := s.filterValue(filters, "page"); found { 163 if page, err := strconv.Atoi(page[0]); err == nil { 164 currentPage = page 165 } 166 } 167 168 return currentPage 169 } 170 171 func (s *SearchService) findSorts(filters []searchDomain.Filter) []searchDomain.SortOption { 172 result := make([]searchDomain.SortOption, 0) 173 // first check if the searchDomain.SortFilter is given 174 for _, v := range filters { 175 if filter, ok := v.(*searchDomain.SortFilter); ok { 176 result = append(result, searchDomain.SortOption{ 177 Label: filter.Field(), 178 Field: filter.Field(), 179 SelectedAsc: !filter.Descending(), 180 SelectedDesc: filter.Descending(), 181 }) 182 } 183 } 184 185 return result 186 } 187 188 func (s *SearchService) createFacets(filters []searchDomain.Filter) (map[string]searchDomain.Facet, []searchDomain.Facet) { 189 selectedFacets := make([]searchDomain.Facet, 0) 190 191 categoryFilterValue := s.categoryFilterValue(filters) 192 193 facets := map[string]searchDomain.Facet{ 194 "brandCode": { 195 Type: searchDomain.ListFacet, 196 Name: "brandCode", 197 Label: "Brand", 198 Items: []*searchDomain.FacetItem{{ 199 Label: "Apple", 200 Value: "apple", 201 Active: false, 202 Selected: false, 203 Count: 2, 204 }}, 205 Position: 0, 206 }, 207 208 "retailerCode": { 209 Type: searchDomain.ListFacet, 210 Name: "retailerCode", 211 Label: "Retailer", 212 Items: []*searchDomain.FacetItem{{ 213 Label: "Test Retailer", 214 Value: "retailer", 215 Active: false, 216 Selected: false, 217 Count: 2, 218 }}, 219 Position: 0, 220 }, 221 222 "categoryCodes": s.createCategoryFacet(categoryFilterValue), 223 } 224 225 if s.hasFilterWithValue(filters, "brandCode", "apple") { 226 facets["brandCode"].Items[0].Active = true 227 facets["brandCode"].Items[0].Selected = true 228 selectedFacets = append(selectedFacets, facets["brandCode"]) 229 } 230 231 if s.hasFilterWithValue(filters, "retailerCode", "retailer") { 232 facets["retailerCode"].Items[0].Active = true 233 facets["retailerCode"].Items[0].Selected = true 234 selectedFacets = append(selectedFacets, facets["retailerCode"]) 235 } 236 237 if categoryFilterValue != "" { 238 selectedFacets = append(selectedFacets, facets["categoryCodes"]) 239 } 240 241 return facets, selectedFacets 242 } 243 244 func (s *SearchService) filterValue(filters []searchDomain.Filter, key string) ([]string, bool) { 245 for _, filter := range filters { 246 filterKey, filterValues := filter.Value() 247 if filterKey == key { 248 return filterValues, true 249 } 250 } 251 252 return []string{}, false 253 } 254 255 func (s *SearchService) hasFilterWithValue(filters []searchDomain.Filter, key string, value string) bool { 256 if filterValues, found := s.filterValue(filters, key); found { 257 for _, filterValue := range filterValues { 258 if value == filterValue { 259 return true 260 } 261 } 262 } 263 264 return false 265 } 266 267 func (s *SearchService) categoryFilterValue(filters []searchDomain.Filter) string { 268 for _, filter := range filters { 269 filterKey, filterValues := filter.Value() 270 switch filterKey { 271 case "category", "categoryCodes": 272 if len(filterValues) > 0 { 273 return filterValues[0] 274 } 275 } 276 } 277 278 return "" 279 } 280 281 func (s *SearchService) createCategoryFacet(selectedCategory string) searchDomain.Facet { 282 return searchDomain.Facet{ 283 Type: searchDomain.TreeFacet, 284 Name: "categoryCodes", 285 Label: "Category", 286 Items: s.createCategoryFacetItems(selectedCategory), 287 Position: 0, 288 } 289 } 290 291 func (s *SearchService) createCategoryFacetItems(selectedCategory string) []*searchDomain.FacetItem { 292 items, err := loadCategoryFacetItems(s.categoryFacetItemsJSON) 293 294 if err != nil { 295 return nil 296 } 297 298 selectFacetItems(selectedCategory, items) 299 return items 300 } 301 302 func selectFacetItems(selectedCategory string, items []*searchDomain.FacetItem) bool { 303 for _, item := range items { 304 if item.Value == selectedCategory { 305 item.Active = true 306 item.Selected = true 307 return true 308 } 309 childSelectedOrActive := selectFacetItems(selectedCategory, item.Items) 310 if childSelectedOrActive { 311 item.Active = true 312 return true 313 } 314 } 315 return false 316 }