-
Notifications
You must be signed in to change notification settings - Fork 0
/
schema.go
365 lines (316 loc) · 14.2 KB
/
schema.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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
package solr
import (
"context"
"errors"
"net/http"
"net/url"
)
// Valid commands for the schema API
const (
SchemaCommandAddField SchemaCommand = "add-field"
SchemaCommandDeleteField SchemaCommand = "delete-field"
SchemaCommandReplaceField SchemaCommand = "replace-field"
SchemaCommandAddDynamicField SchemaCommand = "add-dynamic-field"
SchemaCommandDeleteDynamicField SchemaCommand = "delete-dynamic-field"
SchemaCommandReplaceDynamicField SchemaCommand = "replace-dynamic-field"
SchemaCommandAddFieldType SchemaCommand = "add-field-type"
SchemaCommandDeleteFieldType SchemaCommand = "delete-field-type"
SchemaCommandReplaceFieldType SchemaCommand = "replace-field-type"
SchemaCommandAddCopyField SchemaCommand = "add-copy-field"
SchemaCommandDeleteCopyField SchemaCommand = "delete-copy-field"
)
// Various errors returned from the schema API
var (
ErrFieldNotFound = errors.New("field not found")
ErrFieldTypeNotFound = errors.New("field type not found")
ErrDynamicFieldNotFound = errors.New("dynamic field not found")
ErrCopyFieldNotFound = errors.New("copy field not found")
)
// Analyzer represents the analyzer entity. An analyzer examines the text of
// fields and generates a token stream. For more info:
// https://lucene.apache.org/solr/guide/8_5/analyzers.html
type Analyzer struct {
Tokenizer map[string]interface{} `json:"tokenizer"`
Filters []map[string]interface{} `json:"filters"`
}
// FieldType represents a solr field type. A field type defines the analysis that will occur on a field when documents
// are indexed or queries are sent to the index. Only a name and the class name are mandatory. For more info:
// https://lucene.apache.org/solr/guide/8_5/field-type-definitions-and-properties.html#general-properties
type FieldType struct {
Name string `json:"name"`
CLass string `json:"class"`
PositionIncrementGap string `json:"positionIncrementGap,omitempty"`
AutoGeneratePhraseQueries string `json:"autoGeneratePhraseQueries,omitempty"`
SynonymQueryStyle string `json:"synonymQueryStyle,omitempty"`
EnableGraphQueries bool `json:"enableGraphQueries,omitempty"`
DocValuesFormat string `json:"docValuesFormat,omitempty"`
PostingsFormat string `json:"postingsFormat,omitempty"`
Analyzer *Analyzer `json:"analyzer,omitempty"`
IndexAnalyzer *Analyzer `json:"indexAnalyzer,omitempty"`
QueryAnalyzer *Analyzer `json:"queryAnalyzer,omitempty"`
FieldDefaultProperties
}
// FieldDefaultProperties represents the defualt properties shared by field types and fields. These are propertries
// that can be specified either on the field types, or on individual fields to override the values provided
// by the field types. Built according to schema version 1.6. For more info:
//https://lucene.apache.org/solr/guide/8_5/field-type-definitions-and-properties.html#field-default-properties
type FieldDefaultProperties struct {
Indexed *bool `json:"indexed,omitempty"`
Stored *bool `json:"stored,omitempty"`
DocValues *bool `json:"docValues,omitempty"`
SortMissingFirst *bool `json:"sortMissingFirst,omitempty"`
SortMissingLast *bool `json:"sortMissingLast,omitempty"`
MultiValued *bool `json:"multiValued,omitempty"`
Uninvertible *bool `json:"uninvertible,omitempty"`
OmitNorms *bool `json:"omitNorms,omitempty"`
OmitTermFreqAndPositions *bool `json:"omitTermFreqAndPositions,omitempty"`
OmitPositions *bool `json:"omitPositions,omitempty"`
TermVectors *bool `json:"termVectors,omitempty"`
TermPositions *bool `json:"termPositions,omitempty"`
TermOffsets *bool `json:"termOffsets,omitempty"`
TermPayloads *bool `json:"termPayloads,omitempty"`
Required *bool `json:"required,omitempty"`
UseDocValuesAsStored *bool `json:"useDocValuesAsStored,omitempty"`
Large *bool `json:"large,omitempty"`
}
// Field represents a solr field. For more info:
// https://lucene.apache.org/solr/guide/8_5/defining-fields.html#field-properties
type Field struct {
Name string `json:"name"`
Type string `json:"type"`
Default interface{} `json:"default,omitempty"`
FieldDefaultProperties
}
// CopyField represents a solr copy field rule, solr's mechanism for making copies of fields
// so that you can apply several distinct field types to a single piece of incoming
// information. The name of the field you want to copy is the source, and the
// name of the copy is the destination. For more info:
// https://lucene.apache.org/solr/guide/8_5/copying-fields.html
type CopyField struct {
Source string `json:"source"`
Dest string `json:"dest"`
MaxChars int `json:"maxChars,omitempty"`
}
// DynamicField is just like a regular field except it has a name with a wildcard in it.
// For more info: https://lucene.apache.org/solr/guide/8_5/dynamic-fields.html
type DynamicField Field
// SchemaCommand is used to restrict the available update commands that can
// be included in the body of a s.conn.request to the `/update` endpoint.
type SchemaCommand string
func (c SchemaCommand) String() string {
return string(c)
}
type schemaBuilder struct {
commands map[SchemaCommand]interface{}
}
func newSchemaBuilder() *schemaBuilder {
commands := make(map[SchemaCommand]interface{})
return &schemaBuilder{commands: commands}
}
func (b *schemaBuilder) add(command SchemaCommand, item interface{}) {
b.commands[command] = item
}
func (b *schemaBuilder) del(command SchemaCommand, name string) {
b.commands[command] = map[string]string{"name": name}
}
func (b *schemaBuilder) delCopyField(source, dest string) {
b.commands[SchemaCommandDeleteCopyField] = map[string]string{"source": source, "dest": dest}
}
// SchemaAPI contains a connection to solr and the path to it.
type SchemaAPI struct {
conn *Connection
Path string
}
// NewSchemaAPI returns a new schema API, creating a connection to solr using the provided
// http client and host, core info.
func NewSchemaAPI(ctx context.Context, host, core string, client *http.Client) (*SchemaAPI, error) {
if host == "" || core == "" {
return nil, ErrInvalidConfig
}
_, err := url.ParseRequestURI(host)
if err != nil {
return nil, err
}
conn := &Connection{
Host: host,
Core: core,
httpClient: client,
}
path := formatBasePath(host, core) + "/schema"
return &SchemaAPI{conn: conn, Path: path}, nil
}
// SetBasicAuth sets the authentication credentials if needed.
func (s *SchemaAPI) SetBasicAuth(username, password string) {
s.conn.Username = username
s.conn.Password = password
}
func (s *SchemaAPI) post(ctx context.Context, body interface{}) (*Response, error) {
bodyBytes, err := interfaceToBytes(body)
if err != nil {
return nil, err
}
return s.conn.request(ctx, http.MethodPost, s.Path, bodyBytes)
}
// RetrieveSchema allows you to read how your schema has been defined. The output will
// include all fields, field types, dynamic rules and copy field rules in json.
// The schema name and version are also included.
func (s *SchemaAPI) RetrieveSchema(ctx context.Context) (*Response, error) {
return s.conn.request(ctx, http.MethodGet, s.Path, nil)
}
// AddFieldType adds a new field type to the schema. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#add-a-new-field-type
func (s *SchemaAPI) AddFieldType(ctx context.Context, ft *FieldType) (*Response, error) {
sb := newSchemaBuilder()
sb.add(SchemaCommandAddFieldType, ft)
return s.post(ctx, sb.commands)
}
// ReplaceFieldType replaces a field type in your schema. Note that you must supply the full definition
// for a field type - this command will not partially modify a field type’s definition. If the field
// type does not exist in the schema an error is thrown. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#replace-a-field-type
func (s *SchemaAPI) ReplaceFieldType(ctx context.Context, ft *FieldType) (*Response, error) {
sb := newSchemaBuilder()
sb.add(SchemaCommandReplaceFieldType, ft)
return s.post(ctx, sb.commands)
}
// DeleteFieldType removes a field type from your schema. If the field type does not
// exist in the schema, or if any field or dynamic field rule in the schema uses
// the field type, an error is thrown. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#delete-a-field-type
func (s *SchemaAPI) DeleteFieldType(ctx context.Context, name string) (*Response, error) {
sb := newSchemaBuilder()
sb.del(SchemaCommandDeleteFieldType, name)
return s.post(ctx, sb.commands)
}
// RetrieveFieldType returns the specified field type.
func (s *SchemaAPI) RetrieveFieldType(ctx context.Context, name string) (*FieldType, error) {
res, err := s.RetrieveSchema(ctx)
if err != nil {
return nil, err
}
if res.Schema != nil && len(res.Schema.FieldTypes) > 0 {
for _, ft := range res.Schema.FieldTypes {
if ft.Name == name {
return ft, nil
}
}
}
return nil, ErrFieldTypeNotFound
}
// Field methods
// AddField adds a new field definition to your schema. If a field with the same name exists
// an error is thrown. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#add-a-new-field
func (s *SchemaAPI) AddField(ctx context.Context, fl *Field) (*Response, error) {
sb := newSchemaBuilder()
sb.add(SchemaCommandAddField, fl)
return s.post(ctx, sb.commands)
}
// ReplaceField replaces a field’s definition. Note that you must supply the full definition for a
// field - this command will not partially modify a field’s definition. If the field does not
// exist in the schema an error is thrown. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#replace-a-field
func (s *SchemaAPI) ReplaceField(ctx context.Context, fl *Field) (*Response, error) {
sb := newSchemaBuilder()
sb.add(SchemaCommandReplaceField, fl)
return s.post(ctx, sb.commands)
}
// DeleteField removes a field definition from your schema. If the field does not exist in the schema,
// or if the field is the source or destination of a copy field rule, an error is thrown.
// For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#delete-a-field
func (s *SchemaAPI) DeleteField(ctx context.Context, name string) (*Response, error) {
sb := newSchemaBuilder()
sb.del(SchemaCommandDeleteField, name)
return s.post(ctx, sb.commands)
}
// RetrieveField returns the specified field.
func (s *SchemaAPI) RetrieveField(ctx context.Context, name string) (*Field, error) {
res, err := s.RetrieveSchema(ctx)
if err != nil {
return nil, err
}
if res.Schema != nil && len(res.Schema.Fields) > 0 {
for _, fl := range res.Schema.Fields {
if fl.Name == name {
return fl, nil
}
}
}
return nil, ErrFieldNotFound
}
// Dynamic Field Methods
// AddDynamicField adds a new dynamic field rule to your schema. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#add-a-dynamic-field-rule
func (s *SchemaAPI) AddDynamicField(ctx context.Context, df *DynamicField) (*Response, error) {
sb := newSchemaBuilder()
sb.add(SchemaCommandAddDynamicField, df)
return s.post(ctx, sb.commands)
}
// ReplaceDynamicField replaces a dynamic field rule in your schema. Note that you must supply the full definition
// for a dynamic field rule - this command will not partially modify a dynamic field rule’s definition. If the
// dynamic field rule does not exist in the schema an error is thrown. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#replace-a-dynamic-field-rule
func (s *SchemaAPI) ReplaceDynamicField(ctx context.Context, df *DynamicField) (*Response, error) {
sb := newSchemaBuilder()
sb.add(SchemaCommandReplaceDynamicField, df)
return s.post(ctx, sb.commands)
}
// DeleteDynamicField deletes a dynamic field rule from your schema. If the dynamic field rule does not exist
// in the schema, or if the schema contains a copy field rule with a target or destination that matches
// only this dynamic field rule, an error is thrown. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#delete-a-dynamic-field-rule
func (s *SchemaAPI) DeleteDynamicField(ctx context.Context, name string) (*Response, error) {
sb := newSchemaBuilder()
sb.del(SchemaCommandDeleteDynamicField, name)
return s.post(ctx, sb.commands)
}
// RetrieveDynamicField returns the specified dynamic field.
func (s *SchemaAPI) RetrieveDynamicField(ctx context.Context, name string) (*DynamicField, error) {
res, err := s.RetrieveSchema(ctx)
if err != nil {
return nil, err
}
if res.Schema != nil && len(res.Schema.DynamicFields) > 0 {
for _, df := range res.Schema.DynamicFields {
if df.Name == name {
return df, nil
}
}
}
return nil, ErrDynamicFieldNotFound
}
// Copy Field Methods
// AddCopyField adds a new copy field rule to your schema. Source and Destination are required.
// Destination is always a string so for ease of use, unlike with the json API it is not
// possible to copy a field to multiple destinations. A different copy field rule must
// be made for each. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#add-a-new-copy-field-rule
func (s *SchemaAPI) AddCopyField(ctx context.Context, cf *CopyField) (*Response, error) {
sb := newSchemaBuilder()
sb.add(SchemaCommandAddCopyField, cf)
return s.post(ctx, sb.commands)
}
// DeleteCopyField deletes a copy field rule from your schema. If the copy field rule does not exist in
// the schema an error is thrown. For more info:
// https://lucene.apache.org/solr/guide/8_5/schema-api.html#delete-a-copy-field-rule
func (s *SchemaAPI) DeleteCopyField(ctx context.Context, source, dest string) (*Response, error) {
sb := newSchemaBuilder()
sb.delCopyField(source, dest)
return s.post(ctx, sb.commands)
}
// RetrieveCopyField returns the specified copy field rule.
func (s *SchemaAPI) RetrieveCopyField(ctx context.Context, source, dest string) (*CopyField, error) {
res, err := s.RetrieveSchema(ctx)
if err != nil {
return nil, err
}
if res.Schema != nil && len(res.Schema.CopyFields) > 0 {
for _, cf := range res.Schema.CopyFields {
if cf.Source == source && cf.Dest == dest {
return cf, nil
}
}
}
return nil, ErrCopyFieldNotFound
}