-
Notifications
You must be signed in to change notification settings - Fork 0
/
response.go
243 lines (216 loc) · 7.21 KB
/
response.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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
package solr
import (
"encoding/json"
"time"
)
// Response represents the response from the solr server. It usually contains
// Header information, the response data or an error in case of erroneous
// response. Also it can contain Debug information when requested, a
// single document (in the case of realtimeGet) or just a status
// (in the case of the Ping request)
type Response struct {
Header *ResponseHeader `json:"responseHeader"`
Data *ResponseData `json:"response"`
Error *ResponseError `json:"error"`
Debug *map[string]interface{} `json:"debug"`
Doc *Doc `json:"doc"`
Status *string `json:"status"`
Expanded map[string]*ResponseData `json:"expanded"`
FacetCounts *FacetCounts `json:"facet_counts"`
Grouped *Grouped `json:"grouped"`
Schema *ResponseSchema `json:"schema"`
}
// ResponseHeader is populated on every response from the solr server
// unless explicitly omitted. It contains the request status code
// the time it took as well as the params for the search query
// when applicable
type ResponseHeader struct {
Status int64 `json:"status"`
QTime int64 `json:"QTime"`
Params *map[string]interface{} `json:"params"`
}
// ResponseData is populated on a successful response from the solr
// server. It contains the number of documents found, the starting
// index (in case of a search) as well as the documents found
type ResponseData struct {
NumFound int64 `json:"numFound"`
Start int64 `json:"start"`
Docs Docs `json:"docs"`
MaxScore MaxScore `json:"maxScore"`
}
// MaxScore is used as a struct due to the fact that solr
// may return it as a float or as a string indicating
// "NaN"
type MaxScore struct {
Valid bool
Score float64
}
// UnmarshalJSON implements the unmarshaler interface
func (m *MaxScore) UnmarshalJSON(b []byte) error {
var i interface{}
err := json.Unmarshal(b, &i)
if err != nil {
return err
}
switch v := i.(type) {
case float64:
m.Score = v
m.Valid = true
default:
m.Valid = false
}
return nil
}
// Docs represents an array of doc
type Docs []*Doc
// Doc is essentialy a map[string]interface{}
type Doc map[string]interface{}
// ToBytes returs a byte slice to simplify unmarshaling to JSON
func (d Docs) ToBytes() ([]byte, error) {
return interfaceToBytes(d)
}
// ToBytes returs a byte slice to simplify unmarshaling to JSON
func (d *Doc) ToBytes() ([]byte, error) {
return interfaceToBytes(d)
}
// FacetCounts is populated whenever the query to solr includes facets.
// Each of the following attributes get populated depending on the
// actual facet query. 'Fields' attribute includes a helper to
// retrieve the facets in a string: float format.
type FacetCounts struct {
Queries map[string]int `json:"facet_queries"`
Fields *FacetFields `json:"facet_fields"`
Dates map[string]interface{} `json:"facet_dates"`
Ranges map[string]*Range `json:"facet_ranges"`
Intervals map[string]interface{} `json:"facet_intervals"`
Heatmaps map[string]interface{} `json:"facet_heatmaps"`
Pivot map[string][]*Pivot `json:"facet_pivot"`
}
// FacetFields is the facet_field parameter which in Solr contains an array
// that alternates between string and numbers. In order to make this
// more Go-friendly it's using a custom unmarshaler and a getter
// that helps format the results in a map[string]float64.
type FacetFields struct {
m map[string]map[string]float64
}
// Get returns the facets on the given field in a Go-friendly way.
func (f *FacetFields) Get(s string) map[string]float64 {
return f.m[s]
}
// UnmarshalJSON implements the unmarshaler interface.
func (f *FacetFields) UnmarshalJSON(b []byte) error {
f.m = make(map[string]map[string]float64)
var temp map[string][]interface{}
err := json.Unmarshal(b, &temp)
if err != nil {
return err
}
for k, v := range temp {
values := map[string]float64{}
for i := 0; i < len(v); i += 2 {
s, ok := v[i].(string)
n, ok2 := v[i+1].(float64)
if ok && ok2 {
values[s] = n
}
}
f.m[k] = values
}
return nil
}
// Pivot contains pivot faceting results.
// More info:
// https://lucene.apache.org/solr/guide/8_5/faceting.html#pivot-decision-tree-faceting
type Pivot struct {
Field string `json:"field"`
Value interface{} `json:"value"`
Count int `json:"count"`
Pivot []*Pivot `json:"pivot"`
Stats *Stats `json:"stats"`
Queries map[string]int `json:"queries"`
Ranges map[string]*Range `json:"ranges"`
}
// Range contains range faceting results.
// More info:
// https://lucene.apache.org/solr/guide/8_5/faceting.html#range-faceting
type Range struct {
Counts *FacetFields `json:"counts"`
Gap string `json:"gap"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
}
// Stats containts the results of stats when requested during pivot faceting.
type Stats struct {
Fields map[string]interface{} `json:"stats_fields"`
}
// Grouped contains the groups that are returned when result grouping is on.
// Solr is sending a different type of response under the same attribute
// depending on whether the groups where created by a field or query 0r
// func. Therefore the Grouped stuct separates them to facilitate
// unmarshaling and ease of use.
type Grouped struct {
ByFieldOrFunc map[string]*GroupField
ByQuery map[string]*Group
}
// UnmarshalJSON implements the unmarshaler interface.
func (g *Grouped) UnmarshalJSON(b []byte) error {
g.ByFieldOrFunc = make(map[string]*GroupField)
g.ByQuery = make(map[string]*Group)
var m map[string]interface{}
err := json.Unmarshal(b, &m)
if err != nil {
return err
}
for key, val := range m {
valBytes, err := interfaceToBytes(val)
if err != nil {
return err
}
tempMap, ok := val.(map[string]interface{})
if ok {
_, ok := tempMap["groups"]
if ok {
var gf GroupField
err = json.Unmarshal(valBytes, &gf)
if err != nil {
return err
}
g.ByFieldOrFunc[key] = &gf
} else {
var gq Group
err = json.Unmarshal(valBytes, &gq)
if err != nil {
return err
}
g.ByQuery[key] = &gq
}
}
}
return nil
}
// GroupField is populated whenever the query to solr includes grouping.
// The response contains the total matches (of docs), the number of
// groups (if requested) and the groups.
type GroupField struct {
Matches int `json:"matches"`
NumberOfGroups int `json:"ngroups"`
Groups []*Group `json:"groups"`
}
// Group contains a value and the list of documents that belong
// to the specific group.
type Group struct {
Value interface{} `json:"groupValue"`
Matches int `json:"matches"`
DocList *ResponseData `json:"doclist"`
}
// ResponseSchema is populated when using the Schema API to retrive
// schema information.
type ResponseSchema struct {
Name string `json:"name"`
Version float64 `json:"version"`
UniqueKey string `json:"uniqueKey"`
FieldTypes []*FieldType `json:"fieldTypes"`
Fields []*Field `json:"fields"`
CopyFields []*CopyField `json:"copyFields"`
DynamicFields []*DynamicField `json:"dynamicFields"`
}