Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ test: ## Run tests only
go test $(OPT)

test-coverage: ## Run tests and show coverage in browser
go test -v -coverprofile=$(COVERAGE) -covermode=count
goapp test -v -coverprofile=$(COVERAGE) -covermode=count
go tool cover -html=$(COVERAGE)
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
# go-entity-generator

[![GoDoc](https://godoc.org/github.com/delphinus/go-entity-generator?status.svg)](https://godoc.org/github.com/delphinus/go-entity-generator)
[![CircleCI](https://circleci.com/gh/delphinus/go-entity-generator.svg?style=svg)](https://circleci.com/gh/delphinus/go-entity-generator)
[![Coverage Status](https://coveralls.io/repos/github/delphinus/go-entity-generator/badge.svg)](https://coveralls.io/github/delphinus/go-entity-generator)

Generator to yield all entities from Datastore
Generator to yield all entities from Datastore.

docs in [godoc](https://godoc.org/github.com/delphinus/go-entity-generator).

## example

```go
type SomeItem struct {
ID int64 `datastore:"-" goon:"id"`
Name string `datastore:",noindex"`
}

func appender(ctx context.Context, entities []interface{}, i int, k *datastore.Key, parentKey *datastore.Key) []interface{} {
if k.IntID() == 0 {
log.Warningf(ctx, "SomeItem{} needs int64 key. But items[%d] has a string key: %v", i, k.StringID())
} else {
entities = append(entities, &SomeItem{ID: k.IntID()})
}
}

func someFunc(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
ctx, cancel := context.WithCancel(ctx)
defer cancel() // you should cancel before finishing.

ch := generator.New(ctx, &generator.Options{
Appender: appender,
Query: datastore.NewQuery("SomeItem"),
})

for unit := range ch {
if unit.Err != nil {
panic(err)
}

for _, e := range unit.Entities {
if s, ok := e.(*SomeItem); ok {
// some nice handling
}
}
}
}
```
14 changes: 13 additions & 1 deletion generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,14 @@ func New(ctx context.Context, o *Options) <-chan Unit {
if o == nil {
o = &Options{
ChunkSize: defaultChunkSize,
Query: datastore.NewQuery("__DUMMY__"),
}
log.Warningf(ctx, "set dummy query")
} else if o.ChunkSize == 0 {
o.ChunkSize = defaultChunkSize
} else if o.Query == nil {
o.Query = datastore.NewQuery("__DUMMY__")
log.Warningf(ctx, "set dummy query")
}

in := query(ctx, o)
Expand Down Expand Up @@ -120,7 +125,9 @@ func query(ctx context.Context, o *Options) <-chan Unit {
in <- Unit{nil, errors.WithStack(err)}
return
}
entities = o.Appender(ctx, entities, i, k, o.ParentKey)
if o.Appender != nil {
entities = o.Appender(ctx, entities, i, k, o.ParentKey)
}
}

if !isDone {
Expand Down Expand Up @@ -167,6 +174,11 @@ func getMulti(ctx context.Context, in <-chan Unit, o *Options) <-chan Unit {
go func(u Unit) {
defer wg.Done()

if len(u.Entities) == 0 {
out <- Unit{u.Entities, nil}
return
}

g := goon.FromContext(ctx)
if err := g.GetMulti(u.Entities); err != nil {
if !o.IgnoreErrFieldMismatch {
Expand Down
59 changes: 54 additions & 5 deletions generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,16 @@ func createSampleHoge(ctx context.Context) (*datastore.Key, error) {
return parentKey, nil
}

func appender(ctx context.Context, entities []interface{}, i int, k *datastore.Key, parentKey *datastore.Key) []interface{} {
return append(entities, &testHoge{
ID: k.IntID(),
Parent: parentKey,
})
}

func testFetch(ctx context.Context, expected int, o *Options) error {
o.Appender = func(ctx context.Context, entities []interface{}, i int, k *datastore.Key, parentKey *datastore.Key) []interface{} {
return append(entities, &testHoge{
ID: k.IntID(),
Parent: parentKey,
})
if o.Appender == nil {
o.Appender = appender
}
if o.ChunkSize == 0 {
o.ChunkSize = chunkSize
Expand Down Expand Up @@ -199,3 +203,48 @@ func TestChangeChunkSize(t *testing.T) {
t.Fatalf("error in testFetch: %+v", err)
}
}

func TestNoOptions(t *testing.T) {
ctx, cancel, err := testServer()
if err != nil {
t.Fatalf("error in testServer: %+v", err)
}
defer cancel()

_ = New(ctx, nil)
}

func TestNoQuery(t *testing.T) {
ctx, cancel, err := testServer()
if err != nil {
t.Fatalf("error in testServer: %+v", err)
}
defer cancel()

_ = New(ctx, &Options{ChunkSize: 5})
}

func TestIgnoreAll(t *testing.T) {
ctx, cancel, err := testServer()
if err != nil {
t.Fatalf("error in testServer: %+v", err)
}
defer cancel()

parentKey, err := createSampleHoge(ctx)
if err != nil {
t.Fatalf("error in createSampleHoge: %+v", err)
}

q := datastore.NewQuery("testHoge").Ancestor(parentKey)
if err := testFetch(ctx, 0, &Options{
Appender: func(ctx context.Context, entities []interface{}, i int, k *datastore.Key, parentKey *datastore.Key) []interface{} {
return []interface{}{}
},
IgnoreErrFieldMismatch: true,
ParentKey: parentKey,
Query: q,
}); err != nil {
t.Fatalf("error in testFetch: %+v", err)
}
}