| // Copyright 2014 Google Inc. All rights reserved. |
| // Use of this source code is governed by the Apache 2.0 |
| // license that can be found in the LICENSE file. |
| |
| package search |
| |
| import ( |
| "fmt" |
| "reflect" |
| ) |
| |
| // Field is a name/value pair. A search index's document can be loaded and |
| // saved as a sequence of Fields. |
| type Field struct { |
| // Name is the field name. |
| Name string |
| // Value is the field value. The valid types are: |
| // - string, |
| // - search.Atom, |
| // - search.HTML, |
| // - time.Time (stored with millisecond precision), |
| // - float64, |
| // - GeoPoint. |
| Value interface{} |
| // Language is a two-letter ISO 693-1 code for the field's language, |
| // defaulting to "en" if nothing is specified. It may only be specified for |
| // fields of type string and search.HTML. |
| Language string |
| // Derived marks fields that were calculated as a result of a |
| // FieldExpression provided to Search. This field is ignored when saving a |
| // document. |
| Derived bool |
| } |
| |
| // DocumentMetadata is a struct containing information describing a given document. |
| type DocumentMetadata struct { |
| // Rank is an integer specifying the order the document will be returned in |
| // search results. If zero, the rank will be set to the number of seconds since |
| // 2011-01-01 00:00:00 UTC when being Put into an index. |
| Rank int |
| } |
| |
| // FieldLoadSaver can be converted from and to a slice of Fields |
| // with additional document metadata. |
| type FieldLoadSaver interface { |
| Load([]Field, *DocumentMetadata) error |
| Save() ([]Field, *DocumentMetadata, error) |
| } |
| |
| // FieldList converts a []Field to implement FieldLoadSaver. |
| type FieldList []Field |
| |
| // Load loads all of the provided fields into l. |
| // It does not first reset *l to an empty slice. |
| func (l *FieldList) Load(f []Field, _ *DocumentMetadata) error { |
| *l = append(*l, f...) |
| return nil |
| } |
| |
| // Save returns all of l's fields as a slice of Fields. |
| func (l *FieldList) Save() ([]Field, *DocumentMetadata, error) { |
| return *l, nil, nil |
| } |
| |
| var _ FieldLoadSaver = (*FieldList)(nil) |
| |
| // structFLS adapts a struct to be a FieldLoadSaver. |
| type structFLS struct { |
| reflect.Value |
| } |
| |
| func (s structFLS) Load(fields []Field, _ *DocumentMetadata) (err error) { |
| for _, field := range fields { |
| f := s.FieldByName(field.Name) |
| if !f.IsValid() { |
| err = &ErrFieldMismatch{ |
| FieldName: field.Name, |
| Reason: "no such struct field", |
| } |
| continue |
| } |
| if !f.CanSet() { |
| err = &ErrFieldMismatch{ |
| FieldName: field.Name, |
| Reason: "cannot set struct field", |
| } |
| continue |
| } |
| v := reflect.ValueOf(field.Value) |
| if ft, vt := f.Type(), v.Type(); ft != vt { |
| err = &ErrFieldMismatch{ |
| FieldName: field.Name, |
| Reason: fmt.Sprintf("type mismatch: %v for %v data", ft, vt), |
| } |
| continue |
| } |
| f.Set(v) |
| } |
| return err |
| } |
| |
| func (s structFLS) Save() ([]Field, *DocumentMetadata, error) { |
| fields := make([]Field, 0, s.NumField()) |
| for i := 0; i < s.NumField(); i++ { |
| f := s.Field(i) |
| if !f.CanSet() { |
| continue |
| } |
| fields = append(fields, Field{ |
| Name: s.Type().Field(i).Name, |
| Value: f.Interface(), |
| }) |
| } |
| return fields, nil, nil |
| } |
| |
| // newStructFLS returns a FieldLoadSaver for the struct pointer p. |
| func newStructFLS(p interface{}) (FieldLoadSaver, error) { |
| v := reflect.ValueOf(p) |
| if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct { |
| return nil, ErrInvalidDocumentType |
| } |
| return structFLS{v.Elem()}, nil |
| } |
| |
| // LoadStruct loads the fields from f to dst. dst must be a struct pointer. |
| func LoadStruct(dst interface{}, f []Field) error { |
| x, err := newStructFLS(dst) |
| if err != nil { |
| return err |
| } |
| return x.Load(f, nil) |
| } |
| |
| // SaveStruct returns the fields from src as a slice of Field. |
| // src must be a struct pointer. |
| func SaveStruct(src interface{}) ([]Field, error) { |
| x, err := newStructFLS(src) |
| if err != nil { |
| return nil, err |
| } |
| fs, _, err := x.Save() |
| return fs, err |
| } |