flamingo.me/flamingo-commerce/v3@v3.11.0/search/domain/service.go (about)

     1  package domain
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"net/url"
     9  	"sort"
    10  )
    11  
    12  const (
    13  	// SuggestionTypeProduct represents product suggestions
    14  	SuggestionTypeProduct = "product"
    15  	// SuggestionTypeCategory represents category suggestions
    16  	SuggestionTypeCategory = "category"
    17  )
    18  
    19  type (
    20  	// SearchService defines how to access search
    21  	SearchService interface {
    22  		// Types() []string
    23  		Search(ctx context.Context, filter ...Filter) (results map[string]Result, err error)
    24  		SearchFor(ctx context.Context, typ string, filter ...Filter) (result *Result, err error)
    25  	}
    26  
    27  	// Result defines a search result for one type
    28  	Result struct {
    29  		SearchMeta SearchMeta
    30  		Hits       []Document
    31  		Suggestion []Suggestion
    32  		Facets     FacetCollection
    33  		Promotions []Promotion
    34  		Actions    []Action
    35  	}
    36  
    37  	// Action might be considered on the frontend to be taken depending on search results
    38  	Action struct {
    39  		Type                 string
    40  		Content              string
    41  		AdditionalAttributes map[string]interface{}
    42  	}
    43  
    44  	// SearchMeta data
    45  	SearchMeta struct {
    46  		Query          string
    47  		OriginalQuery  string
    48  		Page           int
    49  		NumPages       int
    50  		NumResults     int
    51  		SelectedFacets []Facet
    52  		SortOptions    []SortOption
    53  	}
    54  
    55  	// SortOption defines how sorting is possible, and which of them are activated with both an asc and desc option
    56  	SortOption struct {
    57  		// Label that you normally want to show in the frontend (e.g. "Price")
    58  		Label string
    59  		// Field that you need to use in SearchRequest>SortFilter
    60  		Field string
    61  		// SelectedAsc true if sorting by this field is active
    62  		SelectedAsc bool
    63  		// SelectedDesc true if sorting by this field is active
    64  		SelectedDesc bool
    65  		// Asc - represents the field that is used to trigger ascending search.
    66  		// Deprecated: use "Field" and "SelectedAsc" instead to set which field should be sortable
    67  		Asc string
    68  		// Desc - represents the field that is used to trigger descending search.
    69  		// Deprecated: use "Field" and "SelectedDesc" instead to set which field should be sortable
    70  		Desc string
    71  	}
    72  
    73  	// FacetType for type facets
    74  	FacetType string
    75  
    76  	// FacetItem contains information about a facet item
    77  	FacetItem struct {
    78  		Label    string
    79  		Value    string
    80  		Active   bool
    81  		Selected bool
    82  
    83  		// Tree Facet
    84  		Items []*FacetItem
    85  
    86  		// Range Facet
    87  		Min, Max                 float64
    88  		SelectedMin, SelectedMax float64
    89  
    90  		Count int64
    91  	}
    92  
    93  	// Facet provided by the search backend
    94  	Facet struct {
    95  		Type     FacetType
    96  		Name     string
    97  		Label    string
    98  		Items    []*FacetItem
    99  		Position int
   100  	}
   101  
   102  	// FacetCollection for all available facets
   103  	FacetCollection map[string]Facet
   104  	facetSlice      []Facet
   105  
   106  	// Suggestion hint
   107  	Suggestion struct {
   108  		Type                 string
   109  		Text                 string
   110  		Highlight            string
   111  		AdditionalAttributes map[string]string
   112  	}
   113  
   114  	// Promotion result during search
   115  	Promotion struct {
   116  		Title                string
   117  		Content              string
   118  		URL                  string
   119  		Media                []Media
   120  		AdditionalAttributes map[string]interface{}
   121  	}
   122  
   123  	// Media contains promotion media data
   124  	Media struct {
   125  		Type      string
   126  		MimeType  string
   127  		Usage     string
   128  		Title     string
   129  		Reference string
   130  	}
   131  
   132  	// Document holds a search result document
   133  	Document interface{}
   134  
   135  	// RedirectError suggests to redirect
   136  	RedirectError struct {
   137  		To string
   138  	}
   139  
   140  	// RequestQueryHook can be used to enforce redirect errors
   141  	RequestQueryHook interface {
   142  		Hook(ctx context.Context, path string, query *url.Values) error
   143  	}
   144  )
   145  
   146  func (re *RedirectError) Error() string {
   147  	return "Error: enforced redirect to " + re.To
   148  }
   149  
   150  func (fs facetSlice) Len() int {
   151  	return len(fs)
   152  }
   153  
   154  func (fs facetSlice) Less(i, j int) bool {
   155  	return fs[i].Position < fs[j].Position
   156  }
   157  
   158  func (fs facetSlice) Swap(i, j int) {
   159  	fs[i], fs[j] = fs[j], fs[i]
   160  }
   161  
   162  // Order a facet collection
   163  func (fc FacetCollection) Order() []string {
   164  	order := make(facetSlice, len(fc))
   165  	i := 0
   166  	for _, k := range fc {
   167  		order[i] = k
   168  		i++
   169  	}
   170  
   171  	sort.Stable(order)
   172  
   173  	strings := make([]string, len(order))
   174  	for i, v := range order {
   175  		strings[i] = v.Name
   176  	}
   177  
   178  	return strings
   179  }
   180  
   181  // Facet types
   182  const (
   183  	ListFacet  FacetType = "ListFacet"
   184  	TreeFacet  FacetType = "TreeFacet"
   185  	RangeFacet FacetType = "RangeFacet"
   186  )
   187  
   188  var (
   189  	// ErrNotFound error
   190  	ErrNotFound = errors.New("search not found")
   191  )
   192  
   193  // ValidatePageSize checks if the pageSize is logical for current result
   194  func (sm *SearchMeta) ValidatePageSize(pageSize int) error {
   195  	if pageSize == 0 {
   196  		return errors.New("cannot validate - no expected pageSize given")
   197  	}
   198  	expectedNumPages := math.Ceil(float64(sm.NumResults) / float64(pageSize))
   199  	if expectedNumPages != float64(sm.NumPages) {
   200  		return fmt.Errorf("pagesize not valid expected %f / given in result: %d", expectedNumPages, sm.NumPages)
   201  	}
   202  	return nil
   203  }