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  }