struct.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // Copyright 2015 Google Inc. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package search
  5. import (
  6. "fmt"
  7. "reflect"
  8. "strings"
  9. "sync"
  10. )
  11. // ErrFieldMismatch is returned when a field is to be loaded into a different
  12. // than the one it was stored from, or when a field is missing or unexported in
  13. // the destination struct.
  14. type ErrFieldMismatch struct {
  15. FieldName string
  16. Reason string
  17. }
  18. func (e *ErrFieldMismatch) Error() string {
  19. return fmt.Sprintf("search: cannot load field %q: %s", e.FieldName, e.Reason)
  20. }
  21. // ErrFacetMismatch is returned when a facet is to be loaded into a different
  22. // type than the one it was stored from, or when a field is missing or
  23. // unexported in the destination struct. StructType is the type of the struct
  24. // pointed to by the destination argument passed to Iterator.Next.
  25. type ErrFacetMismatch struct {
  26. StructType reflect.Type
  27. FacetName string
  28. Reason string
  29. }
  30. func (e *ErrFacetMismatch) Error() string {
  31. return fmt.Sprintf("search: cannot load facet %q into a %q: %s", e.FacetName, e.StructType, e.Reason)
  32. }
  33. // structCodec defines how to convert a given struct to/from a search document.
  34. type structCodec struct {
  35. // byIndex returns the struct tag for the i'th struct field.
  36. byIndex []structTag
  37. // fieldByName returns the index of the struct field for the given field name.
  38. fieldByName map[string]int
  39. // facetByName returns the index of the struct field for the given facet name,
  40. facetByName map[string]int
  41. }
  42. // structTag holds a structured version of each struct field's parsed tag.
  43. type structTag struct {
  44. name string
  45. facet bool
  46. ignore bool
  47. }
  48. var (
  49. codecsMu sync.RWMutex
  50. codecs = map[reflect.Type]*structCodec{}
  51. )
  52. func loadCodec(t reflect.Type) (*structCodec, error) {
  53. codecsMu.RLock()
  54. codec, ok := codecs[t]
  55. codecsMu.RUnlock()
  56. if ok {
  57. return codec, nil
  58. }
  59. codecsMu.Lock()
  60. defer codecsMu.Unlock()
  61. if codec, ok := codecs[t]; ok {
  62. return codec, nil
  63. }
  64. codec = &structCodec{
  65. fieldByName: make(map[string]int),
  66. facetByName: make(map[string]int),
  67. }
  68. for i, I := 0, t.NumField(); i < I; i++ {
  69. f := t.Field(i)
  70. name, opts := f.Tag.Get("search"), ""
  71. if i := strings.Index(name, ","); i != -1 {
  72. name, opts = name[:i], name[i+1:]
  73. }
  74. ignore := false
  75. if name == "-" {
  76. ignore = true
  77. } else if name == "" {
  78. name = f.Name
  79. } else if !validFieldName(name) {
  80. return nil, fmt.Errorf("search: struct tag has invalid field name: %q", name)
  81. }
  82. facet := opts == "facet"
  83. codec.byIndex = append(codec.byIndex, structTag{name: name, facet: facet, ignore: ignore})
  84. if facet {
  85. codec.facetByName[name] = i
  86. } else {
  87. codec.fieldByName[name] = i
  88. }
  89. }
  90. codecs[t] = codec
  91. return codec, nil
  92. }
  93. // structFLS adapts a struct to be a FieldLoadSaver.
  94. type structFLS struct {
  95. v reflect.Value
  96. codec *structCodec
  97. }
  98. func (s structFLS) Load(fields []Field, meta *DocumentMetadata) error {
  99. var err error
  100. for _, field := range fields {
  101. i, ok := s.codec.fieldByName[field.Name]
  102. if !ok {
  103. // Note the error, but keep going.
  104. err = &ErrFieldMismatch{
  105. FieldName: field.Name,
  106. Reason: "no such struct field",
  107. }
  108. continue
  109. }
  110. f := s.v.Field(i)
  111. if !f.CanSet() {
  112. // Note the error, but keep going.
  113. err = &ErrFieldMismatch{
  114. FieldName: field.Name,
  115. Reason: "cannot set struct field",
  116. }
  117. continue
  118. }
  119. v := reflect.ValueOf(field.Value)
  120. if ft, vt := f.Type(), v.Type(); ft != vt {
  121. err = &ErrFieldMismatch{
  122. FieldName: field.Name,
  123. Reason: fmt.Sprintf("type mismatch: %v for %v data", ft, vt),
  124. }
  125. continue
  126. }
  127. f.Set(v)
  128. }
  129. if meta == nil {
  130. return err
  131. }
  132. for _, facet := range meta.Facets {
  133. i, ok := s.codec.facetByName[facet.Name]
  134. if !ok {
  135. // Note the error, but keep going.
  136. if err == nil {
  137. err = &ErrFacetMismatch{
  138. StructType: s.v.Type(),
  139. FacetName: facet.Name,
  140. Reason: "no matching field found",
  141. }
  142. }
  143. continue
  144. }
  145. f := s.v.Field(i)
  146. if !f.CanSet() {
  147. // Note the error, but keep going.
  148. if err == nil {
  149. err = &ErrFacetMismatch{
  150. StructType: s.v.Type(),
  151. FacetName: facet.Name,
  152. Reason: "unable to set unexported field of struct",
  153. }
  154. }
  155. continue
  156. }
  157. v := reflect.ValueOf(facet.Value)
  158. if ft, vt := f.Type(), v.Type(); ft != vt {
  159. if err == nil {
  160. err = &ErrFacetMismatch{
  161. StructType: s.v.Type(),
  162. FacetName: facet.Name,
  163. Reason: fmt.Sprintf("type mismatch: %v for %d data", ft, vt),
  164. }
  165. continue
  166. }
  167. }
  168. f.Set(v)
  169. }
  170. return err
  171. }
  172. func (s structFLS) Save() ([]Field, *DocumentMetadata, error) {
  173. fields := make([]Field, 0, len(s.codec.fieldByName))
  174. var facets []Facet
  175. for i, tag := range s.codec.byIndex {
  176. if tag.ignore {
  177. continue
  178. }
  179. f := s.v.Field(i)
  180. if !f.CanSet() {
  181. continue
  182. }
  183. if tag.facet {
  184. facets = append(facets, Facet{Name: tag.name, Value: f.Interface()})
  185. } else {
  186. fields = append(fields, Field{Name: tag.name, Value: f.Interface()})
  187. }
  188. }
  189. return fields, &DocumentMetadata{Facets: facets}, nil
  190. }
  191. // newStructFLS returns a FieldLoadSaver for the struct pointer p.
  192. func newStructFLS(p interface{}) (FieldLoadSaver, error) {
  193. v := reflect.ValueOf(p)
  194. if v.Kind() != reflect.Ptr || v.IsNil() || v.Elem().Kind() != reflect.Struct {
  195. return nil, ErrInvalidDocumentType
  196. }
  197. codec, err := loadCodec(v.Elem().Type())
  198. if err != nil {
  199. return nil, err
  200. }
  201. return structFLS{v.Elem(), codec}, nil
  202. }
  203. func loadStructWithMeta(dst interface{}, f []Field, meta *DocumentMetadata) error {
  204. x, err := newStructFLS(dst)
  205. if err != nil {
  206. return err
  207. }
  208. return x.Load(f, meta)
  209. }
  210. func saveStructWithMeta(src interface{}) ([]Field, *DocumentMetadata, error) {
  211. x, err := newStructFLS(src)
  212. if err != nil {
  213. return nil, nil, err
  214. }
  215. return x.Save()
  216. }
  217. // LoadStruct loads the fields from f to dst. dst must be a struct pointer.
  218. func LoadStruct(dst interface{}, f []Field) error {
  219. return loadStructWithMeta(dst, f, nil)
  220. }
  221. // SaveStruct returns the fields from src as a slice of Field.
  222. // src must be a struct pointer.
  223. func SaveStruct(src interface{}) ([]Field, error) {
  224. f, _, err := saveStructWithMeta(src)
  225. return f, err
  226. }