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