gonm

package module
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2019 License: MIT Imports: 9 Imported by: 0

README

gonm

GoDoc

Package gonm automatically assigns a key from the interface, and autocahing interface local memory.

gonm wrapped Google Cloud Datastore. I used https://godoc.org/cloud.google.com/go/datastore and https://godoc.org/github.com/mjibson/goon as a reference

Documentation: https://godoc.org/github.com/komem3/gonm

Concept

Gonm generate key from ID of structure property, and this key will use for get or put. All structures are complemented with ID of structure property after these method are used. Also, gonm stores the results in a local cache by default. Therefore, the same fetch can be performed at high speed.

It is simple to use gonm.

type User struct {
     ID   int64 `datastore:"-"`
     Name string
}
gm := gonm.FromContext(ctx, dsClient)
user := &User{ID: 1}
err := gm.Get(user)

Properties

A key consists of an optional parent key, and parent key generate Parent of structure property. ID assumes int64 and string, and Parent assumes *datastore.Key like database api.

example: create child-parent relationship

type User struct {
     ID int64 `datastore:"-"`
     Name string
     Parent *datastore.Key `datastore:"-"`
}
gm := gonm.FromContext(ctx, dsClient)

parent := &User{Name: "Father"}
key, err := gm.Put(parent)
if err != nil {
    // TODO: Handle error.
}

child := &User{Name: "Jack", Parent: key}
_, err := gm.Put(child)

If you want to use other property as key id, you need to put id tag in structure tag. The same applies to the parent key and key name. For parent Key, you need to put parent tag in structure. For Key kind, you need to put kind tag in structure. Gonm returns ErrNoIdFiled when the id cannot be obtained from the received structure.

type CustomUser struct {
    Id string `datastore:"-" gonm:"id"`
    Kind string `datastore:"-" gonm:"kind"`
    Key *datastore.Key `datastore:"-" gonm:"parent"`
    Name string
}

Check https://godoc.org/cloud.google.com/go/datastore#hdr-Properties to lean more about datastore properties.

The PropertyLoadSaver Interface

Of course you can use PropertyLoadSaver Interface. However, be careful because gonm wraps the datastore.

 get flow:  Get of gonm -> key generate -> call get api -> Load of PropertyLoadSaver -> complemented with ID
 put flow:  Put of gonm -> key generate -> Save of PropertyLoadSaver -> call put api -> complemented with ID

Check https://godoc.org/cloud.google.com/go/datastore#hdr-The_PropertyLoadSaver_Interface to lean more about PropertyLoadSaver Interface.

Queries

Queries of gonm is very similar datastore queries. Gonm use datastore.Query. Gonm support Run and GetAll, but I reconmmend using GetKeysOnly.

q := datastore.NewQuery("User").Limit(2)

keys, cursor, err := gm.GetKeysOnly(q)
dst := make([]*User, len(keys))
if err := gm.GetMultiByKeys(keys, dst); err != nil {
   // TODO: Handle error.
}

Transactions

Gonm.RunInTransaction runs a function in a transaction.

Users := []*User{{ID: 1}, {ID: 2}}
_, err = gm.RunInTransaction(func(gm *Gonm) error {

    if err = gm.GetMulti(Users); err != nil {
        return err
    }
    Users[0].Name = "Hanako"
    if _, err = gm.PutMulti(Users); err != nil {
        return err
    }
    return nil
})

Google Cloud Datastore Emulator

To install and set up the emulator and its environment variables, see the documentation at https://cloud.google.com/datastore/docs/tools/datastore-emulator.

License

MIT

Documentation

Overview

Package gonm automatically assigns a key from the interface, and autocahing interface local memory.

gonm wrapped Google Cloud Datastore. I used https://godoc.org/cloud.google.com/go/datastore and https://godoc.org/github.com/mjibson/goon as a reference

Concept

Gonm generate key from ID of structure property, and this key will use for get or put. All structures are complemented with ID of structure property after these method are used. Also, gonm stores the results in a local cache by default. Therefore, the same fetch can be performed at high speed.

It is simple to use gonm.

type User struct {
	 ID   int64 `datastore:"-"`
	 Name string
}
gm := gonm.FromContext(ctx, dsClient)
user := &User{ID: 1}
err := gm.Get(user)

Properties

A key consists of an optional parent key, and parent key generate Parent of structure property. ID assumes int64 and string, and Parent assumes *datastore.Key like database api.

example: create child-parent relationship

type User struct {
	 ID int64 `datastore:"-"`
	 Name string
	 Parent *datastore.Key `datastore:"-"`
}
gm := gom.FromContext(ctx, dsClient)

parent := &User{Name: "Father"}
key, err := gm.Put(parent)
if err != nil {
    // TODO: Handle error.
}

child := &User{Name: "Jack", Parent: key}
_, err := gm.Put(child)

If you want to use other property as key id, you need to put id tag in structure tag. The same applies to the parent key and key name. For parent Key, you need to put parent tag in structure. For Key kind, you need to put kind tag in structure. Gonm returns ErrNoIDField when the id cannot be obtained from the received structure.

type CustomUser struct {
	Id string `datastore:"-" gonm:"id"`
	Kind string `datastore:"-" gonm:"kind"`
	Key *datastore.Key `datastore:"-" gonm:"parent"`
	Name string
}

Check https://godoc.org/cloud.google.com/go/datastore#hdr-Properties to lean more about datastore properties.

The PropertyLoadSaver Interface

Of course you can use PropertyLoadSaver Interface. However, be careful because gonm wraps the datastore.

get flow:  Get of gonm -> key generate -> call get api -> Load of PropertyLoadSaver -> complemented with ID
put flow:  Put of gonm -> key generate -> Save of PropertyLoadSaver -> call put api -> complemented with ID

Check https://godoc.org/cloud.google.com/go/datastore#hdr-The_PropertyLoadSaver_Interface to lean more about PropertyLoadSaver Interface.

Queries

Queries of gonm is very similar datastore queries. Gonm use datastore.Query. Gonm support Run and GetAll, but I reconmmend using GetKeysOnly.

q := datastore.NewQuery("User").Limit(2)

keys, cursor, err := gm.GetKeysOnly(q)
dst := make([]User, len(keys))
if err := gm.GetMultiByKeys(keys, dst); err != nil {
   // TODO: Handle error.
}

Transactions

Gonm.RunInTransaction runs a function in a transaction.

Users := []*User{{ID: 1}, {ID: 2}}
_, err = gm.RunInTransaction(func(gm *Gonm) error {

	if err = gm.GetMulti(Users); err != nil {
		return err
	}
	Users[0].Name = "Hanako"
	if _, err = gm.PutMulti(Users); err != nil {
		return err
	}
	return nil
})

Google Cloud Datastore Emulator

To install and set up the emulator and its environment variables, see the documentation at https://cloud.google.com/datastore/docs/tools/datastore-emulator.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInTransaction is returned when not available method in transaction
	ErrInTransaction = errors.New("gonm: transaction gonm is not available this method")
	// ErrNoIDField is returned when struct do not have ID field in tag
	ErrNoIDField = errors.New("gonm: At least one ID or id tag")
)

Functions

func Key

func Key(src interface{}) (*datastore.Key, error)

Key generate *datastore.Key from src.

func Kind

func Kind(src interface{}) string

Kind return struct name of src. This method is simple without looking tag of struct

Example
package main

import (
	"fmt"

	"github.com/komem3/gonm"
)

func main() {
	type User struct {
		ID   int64
		Name string
	}
	fmt.Println(gonm.Kind(User{}))
}
Output:

User

func KindWithTag

func KindWithTag(src interface{}) (string, error)

KindWithTag generate *datastore.Key.Kind from src.

If you do not need to look kind tag, you should use Key.

Example
package main

import (
	"fmt"

	"github.com/komem3/gonm"
)

func main() {
	type User struct {
		ID   int64
		Kind string `datastore:"-" gonm:"kind,user"`
	}

	kind, err := gonm.KindWithTag(User{})
	if err != nil {
		// TODO: Handle error.
	}
	fmt.Println(kind)
}
Output:

user

Types

type Gonm

type Gonm struct {
	// Client store generated datastore.Client. Datastore.Client close when Gonm.Close.
	// In transaction, Client is nil.
	Client *datastore.Client

	// Transaction store generated datastore.Transaction.
	// In not transaction, Transaction is nil.
	Transaction *datastore.Transaction

	// Errors store occurred error in method of Gonm.
	Errors datastore.MultiError

	Context context.Context
	// contains filtered or unexported fields
}

Gonm is main struct

func FromContext

func FromContext(ctx context.Context, dsClient *datastore.Client) *Gonm

FromContext generate Gonm from Context.

func (*Gonm) AllocateID

func (gm *Gonm) AllocateID(dst interface{}) (*datastore.Key, error)

AllocateID is accepts a incomplete keys and returns a complete keys that are guaranteed to be valid in the datastore.

Also, all structures are complemented with ID.

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	user := &User{Name: "Hanako"}
	key, err := gm.AllocateID(user)
	if err != nil {
		// TODO: Handle error.
	}

	// TODO: Use key.
	_ = key
}
Output:

func (*Gonm) AllocateIDs

func (gm *Gonm) AllocateIDs(dst interface{}) ([]*datastore.Key, error)

AllocateIDs is accepts a slice of incomplete keys and returns a slice of complete keys that are guaranteed to be valid in the datastore.

Also, all structures are complemented with IDs.

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	users := []*User{
		{Name: "Michel"},
		{Name: "Hanako"},
	}
	keys, err := gm.AllocateIDs(users)
	if err != nil {
		// TODO: Handle error.
	}

	// TODO: Use keys.
	_ = keys
}
Output:

func (*Gonm) CacheClear

func (gm *Gonm) CacheClear()

CacheClear clear local cache

func (*Gonm) Close

func (gm *Gonm) Close() error

Close close the Gonm

func (*Gonm) Delete

func (gm *Gonm) Delete(dst interface{}) error

Delete deletes the entity for the given *S.

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	if err := gm.Delete(&User{ID: 1}); err != nil {
		// TODO: Handle error.
	}
}
Output:

func (*Gonm) DeleteMulti

func (gm *Gonm) DeleteMulti(dst interface{}) error

DeleteMulti deletes the entity for the given []*S or []S.

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)
	users := []*User{{ID: 1}, {ID: 2}}
	if err := gm.DeleteMulti(users); err != nil {
		// TODO: Handle error.
	}
}
Output:

func (*Gonm) Get

func (gm *Gonm) Get(dst interface{}) error

Get loads the entity based on dst's key into dst.

If there is no such entity for the key, get returns datastore.ErrNoSuchEntity. Dst must be a *S, and returning datastore.ErrFieldMissMatch if dst is not struct pointer

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	user := &User{ID: 2}
	if err := gm.Get(user); err != nil {
		// TODO: Handle error.
	}
}
Output:

func (*Gonm) GetAll

func (gm *Gonm) GetAll(q *datastore.Query, dst interface{}) (keys []*datastore.Key, err error)

GetAll runs the provided query and returns all keys that match that query, as well as appending the values to dst.

Example
package main

import (
	"context"
	"fmt"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	var users []*User
	keys, err := gm.GetAll(datastore.NewQuery("User"), &users)

	if err != nil {
		// TODO: Handle error.
	}
	for i, key := range keys {
		fmt.Println(key)
		fmt.Println(users[i])
	}
}
Output:

func (*Gonm) GetByKey

func (gm *Gonm) GetByKey(key *datastore.Key, dst interface{}) error

GetByKey is getting object from datastore by datastore.Key.

Usage is almost the same as datastore.Client.Get. This method use cache.

func (*Gonm) GetConsistency

func (gm *Gonm) GetConsistency(dst interface{}) error

GetConsistency is get method without cache.

func (*Gonm) GetKeysOnly

func (gm *Gonm) GetKeysOnly(q *datastore.Query) (keys []*datastore.Key, cursor datastore.Cursor, err error)

GetKeysOnly run q.KeysOnly().

this method return key and cursor. That`s why assuming that combining this method with GetByKey,

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	q := datastore.NewQuery("User").Limit(2)

	keys, cursor, err := gm.GetKeysOnly(q)
	if err != nil {
		// TODO: Handle error.
	}

	dst := make([]User, len(keys))
	if err := gm.GetMultiByKeys(keys, dst); err != nil {
		// TODO: Handle error.
	}

	_ = cursor
}
Output:

func (*Gonm) GetMulti

func (gm *Gonm) GetMulti(dst interface{}) error

GetMulti is a batch version of Get.

Dst must have type *[]S, *[]*S or *[]P.

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	users := []*User{{ID: 1}, {ID: 2}}
	if err := gm.GetMulti(users); err != nil {
		// TODO: Handle error.
	}
}
Output:

func (*Gonm) GetMultiByKeys

func (gm *Gonm) GetMultiByKeys(keys []*datastore.Key, dst interface{}) error

GetMultiByKeys is getting object from datastore by datastore.Key.

Usage is almost the same as datastore.Client.GetMulti. this method use cache

func (*Gonm) GetMultiConsistency

func (gm *Gonm) GetMultiConsistency(dst interface{}) error

GetMultiConsistency is GetMulti method without cache.

func (*Gonm) Mutate

func (gm *Gonm) Mutate(gmuts ...*Mutation) (ret []*datastore.Key, err error)

Mutate run GonMutations. If this method run success, all structures are complemented with IDs. In transaction, Mutate return nil as []*datastore.Key when success

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	_, err := gm.Mutate(
		gonm.NewInsert(&User{ID: 1, Name: "Jack"}),
		gonm.NewUpsert(&User{ID: 2, Name: "Michael"}),
		gonm.NewUpdate(&User{ID: 3, Name: "Tom"}),
		gonm.NewDelete(&User{ID: 4}),
	)
	if err != nil {
		// TODO: Handle error.
	}
}
Output:

func (*Gonm) NewTransaction

func (gm *Gonm) NewTransaction(otps ...datastore.TransactionOption) (gmtx *Transaction, err error)

NewTransaction starts a new Transaction. Get, GetMulti, GetByKey, GetMultiByKeys, GetPut, PutMulti, Delete, and DeleteMulti are only method that can be used.

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	tx, err := gm.NewTransaction() // returns Transaction instead of Gonm
	if err != nil {
		// TODO: Handle error.
	}

	users := []*User{{ID: 1}, {ID: 2}}
	if err := tx.GetMulti(users); err != nil {
		// TODO: Handle error.
	}

	users[0].Name = "Change"
	if _, err = tx.PutMulti(users); err != nil {
		// TODO: Handle error.
	}

	if _, err := tx.Commit(); err != nil {
		// TODO: Handle error.
	}
}
Output:

func (*Gonm) Put

func (gm *Gonm) Put(src interface{}) (*datastore.Key, error)

Put method receive *S and put *S into datastore.

If src has no ID filed, this method return ErrNoIDField. Also, the structure is complemented with ID after this method. This method return nil as *datastore.Key when success in Transaction.

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	if _, err := gm.Put(&User{Name: "Tom"}); err != nil {
		// TODO: Handle error.
	}
}
Output:

func (*Gonm) PutMulti

func (gm *Gonm) PutMulti(src interface{}) ([]*datastore.Key, error)

PutMulti is a batch version of Put. Put receive []*S and put []*S into datastore.

Also, all structures are complemented with IDs after this method.

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	users := []*User{
		{Name: "Tom"},
		{Name: "Jack"},
	}
	if _, err := gm.PutMulti(users); err != nil {
		// TODO: Handle error.
	}
}
Output:

func (*Gonm) Run

func (gm *Gonm) Run(q *datastore.Query) (*datastore.Iterator, error)

Run runs the given query. If Transaction gonm use this method, return ErrInTransaction

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	q := datastore.NewQuery("User").Limit(2)
	it, err := gm.Run(q)
	if err != nil {
		// TODO: Handle error.
	}
	_ = it
}
Output:

func (*Gonm) RunInTransaction

func (gm *Gonm) RunInTransaction(f func(gm *Gonm) error, otps ...datastore.TransactionOption) (cmt *datastore.Commit, err error)

RunInTransaction runs f in Transaction.

Get, GetMulti, GetByKey, GetConsistency, GetMultiByKeys, GetMultiConsistency, GetPut, PutMulti, Delete, DeleteMulti, are only method that can be used. Also, Put and PutMulti in Gonm of Transaction do not return datastore.Key (return nil), but, all structures are complemented with IDs after transaction. If you want to get pending key, you should use NewTransaction or *Gonm.Transaction.Put(key, src).

Example
package main

import (
	"context"

	"cloud.google.com/go/datastore"
	"github.com/komem3/gonm"
)

type User struct {
	ID   int64
	Name string
}

var dsClient *datastore.Client

func main() {
	ctx := context.Background()
	gm := gonm.FromContext(ctx, dsClient)

	users := []*User{{ID: 1}, {ID: 2}}
	_, err := gm.RunInTransaction(func(gmtx *gonm.Gonm) error {
		if err := gmtx.GetMulti(users); err != nil {
			return err
		}

		users[0].Name = "Change"
		if _, err := gmtx.PutMulti(users); err != nil {
			return err
		}
		return nil
	})

	if err != nil {
		// TODO: Handle error.
	}
}
Output:

type Mutation

type Mutation struct {
	// contains filtered or unexported fields
}

Mutation is wrapper of datastore.Mutation

func NewDelete

func NewDelete(dst interface{}) (gmut *Mutation)

NewDelete generate Delete Mutation. Dst is required *S.

func NewInsert

func NewInsert(dst interface{}) (gmut *Mutation)

NewInsert generate Insert Mutation. Returning an error if k exist. Dst is required *S.

func NewUpdate

func NewUpdate(dst interface{}) (gmut *Mutation)

NewUpdate generate Update Mutation. Returning an error if k does not exist. Dst is required *S.

func NewUpsert

func NewUpsert(dst interface{}) (gmut *Mutation)

NewUpsert generate Upsert Mutation. Returning no error whether or not k exists. Dst is required *S.

type Transaction added in v0.8.1

type Transaction struct {
	Transaction *datastore.Transaction
	Context     context.Context
	// contains filtered or unexported fields
}

Transaction is Transaction mode of Gonm

func (*Transaction) Commit added in v0.8.1

func (gmtx *Transaction) Commit() (cm *datastore.Commit, err error)

Commit applies the enqueued operations atomically.

func (*Transaction) Delete added in v0.8.1

func (gmtx *Transaction) Delete(dst interface{}) error

Delete is similar as Gonm.Delete

func (*Transaction) DeleteMulti added in v0.8.1

func (gmtx *Transaction) DeleteMulti(dst interface{}) error

DeleteMulti is similar as Gonm.DeleteMulti

func (*Transaction) Get added in v0.8.1

func (gmtx *Transaction) Get(dst interface{}) error

Get is similar as Gonm.Get

func (*Transaction) GetMulti added in v0.8.1

func (gmtx *Transaction) GetMulti(dst interface{}) error

GetMulti is similar as Gonm.GetMulti

func (*Transaction) Mutate added in v0.8.1

func (gmtx *Transaction) Mutate(gmuts ...*Mutation) (ret []*datastore.Key, err error)

Mutate is similar as Gonm.Mutation

func (*Transaction) Put added in v0.8.1

func (gmtx *Transaction) Put(src interface{}) (*datastore.PendingKey, error)

Put is similar as Gonm.Put, but this method return datastore.PendingKey.

This method do not change incomple key to complete key. If you want to use datastore.Key, you may use Commit.commit(pendingKey)

func (*Transaction) PutMulti added in v0.8.1

func (gmtx *Transaction) PutMulti(src interface{}) ([]*datastore.PendingKey, error)

PutMulti is a bach version of Put.

func (*Transaction) Rollback added in v0.8.1

func (gmtx *Transaction) Rollback() (err error)

Rollback abandons a pending Transaction.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL