github.com/SaurabhDubey-Groww/go-cloud@v0.0.0-20221124105541-b26c29285fd8/docstore/driver/document.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package driver 16 17 import ( 18 "reflect" 19 20 "gocloud.dev/docstore/internal/fields" 21 "gocloud.dev/gcerrors" 22 "gocloud.dev/internal/gcerr" 23 ) 24 25 // A Document is a lightweight wrapper around either a map[string]interface{} or a 26 // struct pointer. It provides operations to get and set fields and field paths. 27 type Document struct { 28 Origin interface{} // the argument to NewDocument 29 m map[string]interface{} // nil if it's a *struct 30 s reflect.Value // the struct reflected 31 fields fields.List // for structs 32 } 33 34 // NewDocument creates a new document from doc, which must be a non-nil 35 // map[string]interface{} or struct pointer. 36 func NewDocument(doc interface{}) (Document, error) { 37 if doc == nil { 38 return Document{}, gcerr.Newf(gcerr.InvalidArgument, nil, "document cannot be nil") 39 } 40 if m, ok := doc.(map[string]interface{}); ok { 41 if m == nil { 42 return Document{}, gcerr.Newf(gcerr.InvalidArgument, nil, "document map cannot be nil") 43 } 44 return Document{Origin: doc, m: m}, nil 45 } 46 v := reflect.ValueOf(doc) 47 t := v.Type() 48 if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct { 49 return Document{}, gcerr.Newf(gcerr.InvalidArgument, nil, "expecting *struct or map[string]interface{}, got %s", t) 50 } 51 t = t.Elem() 52 if v.IsNil() { 53 return Document{}, gcerr.Newf(gcerr.InvalidArgument, nil, "document struct pointer cannot be nil") 54 } 55 fields, err := fieldCache.Fields(t) 56 if err != nil { 57 return Document{}, err 58 } 59 return Document{Origin: doc, s: v.Elem(), fields: fields}, nil 60 } 61 62 // GetField returns the value of the named document field. 63 func (d Document) GetField(field string) (interface{}, error) { 64 if d.m != nil { 65 x, ok := d.m[field] 66 if !ok { 67 return nil, gcerr.Newf(gcerr.NotFound, nil, "field %q not found in map", field) 68 } 69 return x, nil 70 } 71 v, err := d.structField(field) 72 if err != nil { 73 return nil, err 74 } 75 return v.Interface(), nil 76 } 77 78 // getDocument gets the value of the given field path, which must be a document. 79 // If create is true, it creates intermediate documents as needed. 80 func (d Document) getDocument(fp []string, create bool) (Document, error) { 81 if len(fp) == 0 { 82 return d, nil 83 } 84 x, err := d.GetField(fp[0]) 85 if err != nil { 86 if create && gcerrors.Code(err) == gcerrors.NotFound { 87 // TODO(jba): create the right type for the struct field. 88 x = map[string]interface{}{} 89 if err := d.SetField(fp[0], x); err != nil { 90 return Document{}, err 91 } 92 } else { 93 return Document{}, err 94 } 95 } 96 d2, err := NewDocument(x) 97 if err != nil { 98 return Document{}, err 99 } 100 return d2.getDocument(fp[1:], create) 101 } 102 103 // Get returns the value of the given field path in the document. 104 func (d Document) Get(fp []string) (interface{}, error) { 105 d2, err := d.getDocument(fp[:len(fp)-1], false) 106 if err != nil { 107 return nil, err 108 } 109 return d2.GetField(fp[len(fp)-1]) 110 } 111 112 func (d Document) structField(name string) (reflect.Value, error) { 113 // We do case-insensitive match here to cover the MongoDB's lowercaseFields 114 // option. 115 f := d.fields.MatchFold(name) 116 if f == nil { 117 return reflect.Value{}, gcerr.Newf(gcerr.NotFound, nil, "field %q not found in struct type %s", name, d.s.Type()) 118 } 119 fv, ok := fieldByIndex(d.s, f.Index) 120 if !ok { 121 return reflect.Value{}, gcerr.Newf(gcerr.InvalidArgument, nil, "nil embedded pointer; cannot get field %q from %s", 122 name, d.s.Type()) 123 } 124 return fv, nil 125 } 126 127 // Set sets the value of the field path in the document. 128 // This creates sub-maps as necessary, if possible. 129 func (d Document) Set(fp []string, val interface{}) error { 130 d2, err := d.getDocument(fp[:len(fp)-1], true) 131 if err != nil { 132 return err 133 } 134 return d2.SetField(fp[len(fp)-1], val) 135 } 136 137 // SetField sets the field to value in the document. 138 func (d Document) SetField(field string, value interface{}) error { 139 if d.m != nil { 140 d.m[field] = value 141 return nil 142 } 143 v, err := d.structField(field) 144 if err != nil { 145 return err 146 } 147 if !v.CanSet() { 148 return gcerr.Newf(gcerr.InvalidArgument, nil, "cannot set field %s in struct of type %s: not addressable", 149 field, d.s.Type()) 150 } 151 v.Set(reflect.ValueOf(value)) 152 return nil 153 } 154 155 // FieldNames returns names of the top-level fields of d. 156 func (d Document) FieldNames() []string { 157 var names []string 158 if d.m != nil { 159 for k := range d.m { 160 names = append(names, k) 161 } 162 } else { 163 for _, f := range d.fields { 164 names = append(names, f.Name) 165 } 166 } 167 return names 168 } 169 170 // Encode encodes the document using the given Encoder. 171 func (d Document) Encode(e Encoder) error { 172 if d.m != nil { 173 return encodeMap(reflect.ValueOf(d.m), e) 174 } 175 return encodeStructWithFields(d.s, d.fields, e) 176 } 177 178 // Decode decodes the document using the given Decoder. 179 func (d Document) Decode(dec Decoder) error { 180 if d.m != nil { 181 return decodeMap(reflect.ValueOf(d.m), dec) 182 } 183 return decodeStruct(d.s, dec) 184 } 185 186 // HasField returns whether or not d has a certain field. 187 func (d Document) HasField(field string) bool { 188 return d.hasField(field, true) 189 } 190 191 // HasFieldFold is like HasField but matches case-insensitively for struct 192 // field. 193 func (d Document) HasFieldFold(field string) bool { 194 return d.hasField(field, false) 195 } 196 197 func (d Document) hasField(field string, exactMatch bool) bool { 198 if d.m != nil { 199 _, ok := d.m[field] 200 return ok 201 } 202 if exactMatch { 203 return d.fields.MatchExact(field) != nil 204 } 205 return d.fields.MatchFold(field) != nil 206 }