go.mercari.io/datastore@v1.8.2/aedatastore/convert.go (about) 1 package aedatastore 2 3 import ( 4 "context" 5 "fmt" 6 7 w "go.mercari.io/datastore" 8 "google.golang.org/api/iterator" 9 "google.golang.org/appengine" 10 "google.golang.org/appengine/datastore" 11 ) 12 13 func namespaceFromContext(ctx context.Context) string { 14 if ctx == nil { 15 panic("ctx is nil") 16 } 17 return datastore.NewIncompleteKey(ctx, "FooBarTest", nil).Namespace() 18 } 19 20 func toOriginalKey(key w.Key) *datastore.Key { 21 if key == nil { 22 return nil 23 } 24 25 keyImpl := key.(*keyImpl) 26 ctx := keyImpl.ctx 27 28 // NOTE appengine.Namespace 呼ぶのは内部でregexpで値チェックしてるので遅い可能性がある…?(のでif文つけてる 29 if namespaceFromContext(ctx) != key.Namespace() { 30 var err error 31 ctx, err = appengine.Namespace(ctx, key.Namespace()) 32 if err != nil { 33 panic(err) 34 } 35 } 36 37 origPK := toOriginalKey(key.ParentKey()) 38 return datastore.NewKey(ctx, key.Kind(), key.Name(), key.ID(), origPK) 39 } 40 41 func toOriginalKeys(keys []w.Key) []*datastore.Key { 42 if keys == nil { 43 return nil 44 } 45 46 origKeys := make([]*datastore.Key, len(keys)) 47 for idx, key := range keys { 48 origKeys[idx] = toOriginalKey(key) 49 } 50 51 return origKeys 52 } 53 54 func toWrapperKey(ctx context.Context, key *datastore.Key) *keyImpl { 55 if key == nil { 56 return nil 57 } 58 59 return &keyImpl{ 60 ctx: ctx, 61 kind: key.Kind(), 62 id: key.IntID(), 63 name: key.StringID(), 64 parent: toWrapperKey(ctx, key.Parent()), 65 namespace: key.Namespace(), 66 } 67 } 68 69 func toOriginalPendingKey(pKey w.PendingKey) *datastore.Key { 70 if pKey == nil { 71 return nil 72 } 73 pk, ok := pKey.StoredContext().Value(contextPendingKey{}).(*pendingKeyImpl) 74 if !ok { 75 return nil 76 } 77 78 if pk == nil || pk.key == nil { 79 return nil 80 } 81 82 return pk.key 83 } 84 85 func toWrapperKeys(ctx context.Context, keys []*datastore.Key) []w.Key { 86 if keys == nil { 87 return nil 88 } 89 90 wKeys := make([]w.Key, len(keys)) 91 for idx, key := range keys { 92 wKeys[idx] = toWrapperKey(ctx, key) 93 } 94 95 return wKeys 96 } 97 98 func toWrapperPendingKey(ctx context.Context, key *datastore.Key) *pendingKeyImpl { 99 if key == nil { 100 return nil 101 } 102 103 return &pendingKeyImpl{ 104 ctx: ctx, 105 key: key, 106 } 107 } 108 109 func toWrapperPendingKeys(ctx context.Context, keys []*datastore.Key) []w.PendingKey { 110 if keys == nil { 111 return nil 112 } 113 114 wKeys := make([]w.PendingKey, len(keys)) 115 for idx, key := range keys { 116 wKeys[idx] = toWrapperPendingKey(ctx, key) 117 } 118 119 return wKeys 120 } 121 122 func toWrapperError(err error) error { 123 if err == nil { 124 return nil 125 } 126 127 switch { 128 case err == datastore.ErrNoSuchEntity: 129 return w.ErrNoSuchEntity 130 131 case err == datastore.Done: 132 // align to Cloud Datastore API. 133 return iterator.Done 134 135 case err == datastore.ErrConcurrentTransaction: 136 return w.ErrConcurrentTransaction 137 138 case err == datastore.ErrInvalidEntityType: 139 return w.ErrInvalidEntityType 140 141 case err == datastore.ErrInvalidKey: 142 return w.ErrInvalidKey 143 144 default: 145 switch err := err.(type) { 146 case *datastore.ErrFieldMismatch: 147 return &w.ErrFieldMismatch{ 148 StructType: err.StructType, 149 FieldName: err.FieldName, 150 Reason: err.Reason, 151 } 152 153 case appengine.MultiError: 154 merr := err 155 newErr := make(w.MultiError, 0, len(merr)) 156 for _, err := range merr { 157 if err != nil { 158 newErr = append(newErr, toWrapperError(err)) 159 continue 160 } 161 162 newErr = append(newErr, nil) 163 } 164 return newErr 165 } 166 167 return err 168 } 169 } 170 171 func toOriginalValue(v interface{}) (interface{}, error) { 172 switch v := v.(type) { 173 case []interface{}: 174 vs := v 175 origVs := make([]interface{}, 0, len(v)) 176 for _, v := range vs { 177 origV, err := toOriginalValue(v) 178 if err != nil { 179 return nil, err 180 } 181 origVs = append(origVs, origV) 182 } 183 return origVs, nil 184 185 case *w.Entity, []*w.Entity: 186 return nil, w.ErrInvalidEntityType 187 188 case w.Key: 189 return toOriginalKey(v), nil 190 case []w.Key: 191 return toOriginalKeys(v), nil 192 193 case w.GeoPoint: 194 return appengine.GeoPoint{Lat: v.Lat, Lng: v.Lng}, nil 195 case []w.GeoPoint: 196 vs := v 197 origVs := make([]appengine.GeoPoint, 0, len(v)) 198 for _, v := range vs { 199 origV, err := toOriginalValue(v) 200 if err != nil { 201 return nil, err 202 } 203 origVs = append(origVs, origV.(appengine.GeoPoint)) 204 } 205 return origVs, nil 206 207 default: 208 return v, nil 209 } 210 } 211 212 func toWrapperValue(ctx context.Context, v interface{}) interface{} { 213 switch v := v.(type) { 214 case []interface{}: 215 vs := v 216 wVs := make([]interface{}, 0, len(v)) 217 for _, v := range vs { 218 wVs = append(wVs, toWrapperValue(ctx, v)) 219 } 220 return wVs 221 222 case *datastore.Key: 223 return toWrapperKey(ctx, v) 224 case []*datastore.Key: 225 return toWrapperKeys(ctx, v) 226 227 case appengine.GeoPoint: 228 return w.GeoPoint{Lat: v.Lat, Lng: v.Lng} 229 case []appengine.GeoPoint: 230 vs := v 231 wVs := make([]w.GeoPoint, 0, len(v)) 232 for _, v := range vs { 233 wVs = append(wVs, toWrapperValue(ctx, v).(w.GeoPoint)) 234 } 235 return wVs 236 237 default: 238 return v 239 } 240 } 241 242 func toOriginalProperty(p w.Property) (datastore.Property, error) { 243 v, err := toOriginalValue(p.Value) 244 if err != nil { 245 return datastore.Property{}, err 246 } 247 origP := datastore.Property{ 248 Name: p.Name, 249 Value: v, 250 NoIndex: p.NoIndex, 251 } 252 return origP, nil 253 } 254 255 func toOriginalPropertyList(ps w.PropertyList) (datastore.PropertyList, error) { 256 // NOTE Cloud Datastore側の仕様に寄せているため、PropertyのValueが[]interface{}の場合がある 257 // その場合、1要素毎に分解してMultiple=trueをセットしてやらないといけない 258 259 if ps == nil { 260 return nil, nil 261 } 262 263 newPs := make([]datastore.Property, 0, len(ps)) 264 for _, p := range ps { 265 switch v := p.Value.(type) { 266 case []interface{}: 267 newV := make([]datastore.Property, 0, len(v)) 268 for _, pV := range v { 269 origV, err := toOriginalValue(pV) 270 if err != nil { 271 return nil, err 272 } 273 origP, err := toOriginalProperty(w.Property{ 274 Name: p.Name, 275 Value: origV, 276 NoIndex: p.NoIndex, 277 }) 278 if err != nil { 279 return nil, err 280 } 281 origP.Multiple = true 282 newV = append(newV, origP) 283 } 284 newPs = append(newPs, newV...) 285 286 case []*w.Entity: 287 newV := make([]datastore.Property, 0, len(v)) 288 for _, pV := range v { 289 origV, err := toOriginalValue(pV) 290 if err != nil { 291 return nil, err 292 } 293 origP, err := toOriginalProperty(w.Property{ 294 Name: p.Name, 295 Value: origV, 296 NoIndex: p.NoIndex, 297 }) 298 if err != nil { 299 return nil, err 300 } 301 origP.Multiple = true 302 newV = append(newV, origP) 303 } 304 newPs = append(newPs, newV...) 305 306 case []w.Key: 307 newV := make([]datastore.Property, 0, len(v)) 308 for _, pV := range v { 309 origV, err := toOriginalValue(pV) 310 if err != nil { 311 return nil, err 312 } 313 origP, err := toOriginalProperty(w.Property{ 314 Name: p.Name, 315 Value: origV, 316 NoIndex: p.NoIndex, 317 }) 318 if err != nil { 319 return nil, err 320 } 321 origP.Multiple = true 322 newV = append(newV, origP) 323 } 324 newPs = append(newPs, newV...) 325 326 case []w.GeoPoint: 327 newV := make([]datastore.Property, 0, len(v)) 328 for _, pV := range v { 329 origV, err := toOriginalValue(pV) 330 if err != nil { 331 return nil, err 332 } 333 origP, err := toOriginalProperty(w.Property{ 334 Name: p.Name, 335 Value: origV, 336 NoIndex: p.NoIndex, 337 }) 338 if err != nil { 339 return nil, err 340 } 341 origP.Multiple = true 342 newV = append(newV, origP) 343 } 344 newPs = append(newPs, newV...) 345 346 default: 347 newP, err := toOriginalProperty(p) 348 if err != nil { 349 return nil, err 350 } 351 newPs = append(newPs, newP) 352 } 353 } 354 355 return newPs, nil 356 } 357 358 func toOriginalPropertyListList(pss []w.PropertyList) ([]datastore.PropertyList, error) { 359 if pss == nil { 360 return nil, nil 361 } 362 363 newPss := make([]datastore.PropertyList, 0, len(pss)) 364 for _, ps := range pss { 365 newPs, err := toOriginalPropertyList(ps) 366 if err != nil { 367 return nil, err 368 } 369 newPss = append(newPss, newPs) 370 } 371 372 return newPss, nil 373 } 374 375 func toWrapperProperty(ctx context.Context, p datastore.Property) w.Property { 376 return w.Property{ 377 Name: p.Name, 378 Value: toWrapperValue(ctx, p.Value), 379 NoIndex: p.NoIndex, 380 } 381 } 382 383 func toWrapperPropertyMulti(ctx context.Context, ps []datastore.Property) w.Property { 384 var name string 385 var noIndex bool 386 vs := make([]interface{}, 0, len(ps)) 387 for _, p := range ps { 388 if name == "" { 389 name = p.Name 390 noIndex = p.NoIndex 391 } else if name != p.Name { 392 panic(fmt.Sprintf("property name mismatch: %s - %s", name, p.Name)) 393 } 394 vs = append(vs, toWrapperValue(ctx, p.Value)) 395 } 396 return w.Property{ 397 Name: name, 398 Value: vs, 399 NoIndex: noIndex, 400 } 401 } 402 403 func toWrapperPropertyList(ctx context.Context, ps datastore.PropertyList) w.PropertyList { 404 // NOTE Cloud Datastore側の仕様に寄せているため、Multiple=trueの場合、 405 // 同名の要素を集めて[]interface{}にしてやらないといけない 406 // `datastore:",flatten" を使っていない場合のサポートは考えない 407 408 if ps == nil { 409 return nil 410 } 411 412 var multiMap map[string]bool 413 newPs := make([]w.Property, 0, len(ps)) 414 for idx, p := range ps { 415 if p.Multiple { 416 if multiMap == nil { 417 multiMap = make(map[string]bool) 418 } else if multiMap[p.Name] { 419 continue 420 } 421 422 subPs := make([]datastore.Property, 0) 423 subPs = append(subPs, p) 424 for _, p2 := range ps[idx+1:] { 425 if p.Name == p2.Name { 426 subPs = append(subPs, p2) 427 } 428 } 429 newPs = append(newPs, toWrapperPropertyMulti(ctx, subPs)) 430 431 multiMap[p.Name] = true 432 433 } else { 434 newPs = append(newPs, toWrapperProperty(ctx, p)) 435 } 436 } 437 438 return newPs 439 } 440 441 func toWrapperPropertyListList(ctx context.Context, pss []datastore.PropertyList) []w.PropertyList { 442 if pss == nil { 443 return nil 444 } 445 446 newPss := make([]w.PropertyList, 0, len(pss)) 447 for _, ps := range pss { 448 newPss = append(newPss, toWrapperPropertyList(ctx, ps)) 449 } 450 451 return newPss 452 }