Skip to content

Commit

Permalink
docstore: implement update queries (#2045)
Browse files Browse the repository at this point in the history
  • Loading branch information
jba authored May 14, 2019
1 parent 9848c06 commit 65ac5b3
Show file tree
Hide file tree
Showing 29 changed files with 442 additions and 138 deletions.
47 changes: 29 additions & 18 deletions internal/docstore/docstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,29 +302,40 @@ func (a *Action) toDriverAction() (*driver.Action, error) {
}
}
if a.mods != nil {
// Convert mods from a map to a slice of (fieldPath, value) pairs.
// The map is easier for users to write, but the slice is easier
// to process.
// TODO(jba): check for prefix
// Sort keys so tests are deterministic.
var keys []string
for k := range a.mods {
keys = append(keys, string(k))
}
sort.Strings(keys)
for _, k := range keys {
k := FieldPath(k)
v := a.mods[k]
fp, err := parseFieldPath(k)
if err != nil {
return nil, err
}
d.Mods = append(d.Mods, driver.Mod{FieldPath: fp, Value: v})
d.Mods, err = toDriverMods(a.mods)
if err != nil {
return nil, err
}
}
return d, nil
}

func toDriverMods(mods Mods) ([]driver.Mod, error) {
// Convert mods from a map to a slice of (fieldPath, value) pairs.
// The map is easier for users to write, but the slice is easier
// to process.
// TODO(jba): check for prefix

// Sort keys so tests are deterministic.
var keys []string
for k := range mods {
keys = append(keys, string(k))
}
sort.Strings(keys)

dmods := make([]driver.Mod, len(keys))
for i, k := range keys {
k := FieldPath(k)
v := mods[k]
fp, err := parseFieldPath(k)
if err != nil {
return nil, err
}
dmods[i] = driver.Mod{FieldPath: fp, Value: v}
}
return dmods, nil
}

// Create is a convenience for building and running a single-element action list.
// See ActionList.Create.
func (c *Collection) Create(ctx context.Context, doc Document) error {
Expand Down
3 changes: 3 additions & 0 deletions internal/docstore/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ type Collection interface {
// RunDeleteQuery deletes every document matched by the query.
RunDeleteQuery(context.Context, *Query) error

// RunUpdateQuery updates every document matched by the query.
RunUpdateQuery(context.Context, *Query, []Mod) error

// QueryPlan returns the plan for the query.
QueryPlan(*Query) (string, error)

Expand Down
61 changes: 48 additions & 13 deletions internal/docstore/drivertest/drivertest.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ func RunConformanceTests(t *testing.T, newHarness HarnessMaker, ct CodecTester,

t.Run("GetQuery", func(t *testing.T) { withTwoKeyCollection(t, newHarness, testGetQuery) })
t.Run("DeleteQuery", func(t *testing.T) { withTwoKeyCollection(t, newHarness, testDeleteQuery) })
t.Run("UpdateQuery", func(t *testing.T) { withTwoKeyCollection(t, newHarness, testUpdateQuery) })

asTests = append(asTests, verifyAsFailsOnNil{})
t.Run("As", func(t *testing.T) {
Expand Down Expand Up @@ -401,7 +402,6 @@ func testUpdate(t *testing.T, coll *ds.Collection) {
if err := coll.Put(ctx, doc); err != nil {
t.Fatal(err)
}

got := docmap{KeyField: doc[KeyField]}
errs := coll.Actions().Update(doc, ds.Mods{
"a": "X",
Expand Down Expand Up @@ -880,12 +880,7 @@ func testGetQuery(t *testing.T, coll *ds.Collection) {
for _, g := range got {
g.DocstoreRevision = nil
}
var want []*HighScore
for _, d := range queryDocuments {
if tc.want(d) {
want = append(want, d)
}
}
want := filterHighScores(queryDocuments, tc.want)
_, err := tc.q.Plan()
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -960,12 +955,7 @@ func testDeleteQuery(t *testing.T, coll *ds.Collection) {
for _, g := range got {
g.DocstoreRevision = nil
}
var want []*HighScore
for _, d := range prevWant {
if tc.want(d) {
want = append(want, d)
}
}
want := filterHighScores(prevWant, tc.want)
prevWant = want
diff := cmp.Diff(got, want, cmpopts.SortSlices(func(h1, h2 *HighScore) bool {
return h1.Game+"|"+h1.Player < h2.Game+"|"+h2.Player
Expand All @@ -983,6 +973,51 @@ func testDeleteQuery(t *testing.T, coll *ds.Collection) {
}
}

func testUpdateQuery(t *testing.T, coll *ds.Collection) {
ctx := context.Background()
if err := coll.Query().Update(ctx, nil); gcerrors.Code(err) == gcerrors.Unimplemented {
t.Skip("update queries not yet implemented")
}

cleanUpTable(t, newHighScore, coll)

addQueryDocuments(t, coll)

err := coll.Query().Where("Player", "=", "fran").Update(ctx, docstore.Mods{"Score": 13, "Time": nil})
if err != nil {
t.Fatal(err)
}
got := mustCollectHighScores(ctx, t, coll.Query().Get(ctx))
for _, g := range got {
g.DocstoreRevision = nil
}

want := filterHighScores(queryDocuments, func(h *HighScore) bool {
if h.Player == "fran" {
h.Score = 13
h.Time = time.Time{}
}
return true
})
diff := cmp.Diff(got, want, cmpopts.SortSlices(func(h1, h2 *HighScore) bool {
return h1.Game+"|"+h1.Player < h2.Game+"|"+h2.Player
}))
if diff != "" {
t.Error(diff)
}
}

func filterHighScores(hs []*HighScore, f func(*HighScore) bool) []*HighScore {
var res []*HighScore
for _, h := range hs {
c := *h // Copy in case f modifies its argument.
if f(&c) {
res = append(res, &c)
}
}
return res
}

// cleanUpTable delete all documents from this collection after test.
func cleanUpTable(t *testing.T, create func() interface{}, coll *docstore.Collection) {
if err := coll.Query().Delete(context.Background()); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions internal/docstore/dynamodocstore/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/aws/aws-sdk-go/service/dynamodb/expression"
"gocloud.dev/internal/docstore"
"gocloud.dev/internal/docstore/driver"
"gocloud.dev/internal/gcerr"
)

// TODO: support parallel scans (http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.ParallelScan)
Expand Down Expand Up @@ -494,3 +495,7 @@ func (c *collection) RunDeleteQuery(ctx context.Context, q *driver.Query) error
}
return docstore.ActionListError(alerr)
}

func (c *collection) RunUpdateQuery(ctx context.Context, q *driver.Query, mods []driver.Mod) error {
return gcerr.Newf(gcerr.Unimplemented, nil, "not implemented")
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 65ac5b3

Please sign in to comment.