diff --git a/autobatch/autobatch.go b/autobatch/autobatch.go index 66d701e2bc5c90ba2357721cfd1770249819cbc3..4eb089bf5f4a806b0c613c65b3586861d3c18c45 100644 --- a/autobatch/autobatch.go +++ b/autobatch/autobatch.go @@ -8,7 +8,7 @@ import ( dsq "github.com/ipfs/go-datastore/query" ) -// Datastore implements a go-datatsore. +// Datastore implements a go-datastore. type Datastore struct { child ds.Batching diff --git a/basic_ds.go b/basic_ds.go index 08ae2af746b1ab349d01ad1768c33e1293deb260..98476767f1ddace487f097811f0b02162a0c1c3b 100644 --- a/basic_ds.go +++ b/basic_ds.go @@ -61,7 +61,7 @@ func (d *MapDatastore) Delete(key Key) (err error) { func (d *MapDatastore) Query(q dsq.Query) (dsq.Results, error) { re := make([]dsq.Entry, 0, len(d.values)) for k, v := range d.values { - e := dsq.Entry{Key: k.String()} + e := dsq.Entry{Key: k.String(), Size: len(v)} if !q.KeysOnly { e.Value = v } diff --git a/mount/mount.go b/mount/mount.go index 7c32a8271b24f773d77a9cdcafb2a1305fdbcc5c..c092c262a13a16b31886b39b361402b744635f97 100644 --- a/mount/mount.go +++ b/mount/mount.go @@ -227,6 +227,7 @@ func (d *Datastore) Query(master query.Query) (query.Results, error) { Orders: master.Orders, KeysOnly: master.KeysOnly, ReturnExpirations: master.ReturnExpirations, + ReturnsSizes: master.ReturnsSizes, } prefix := ds.NewKey(childQuery.Prefix) diff --git a/namespace/namespace_test.go b/namespace/namespace_test.go index e36828696dfad35bc9ed76b23f0e8ac9c50ace1a..3d9bd831022f04f97bb48a243375d231f826f50b 100644 --- a/namespace/namespace_test.go +++ b/namespace/namespace_test.go @@ -101,9 +101,9 @@ func (ks *DSSuite) TestQuery(c *C) { c.Check(err, Equals, nil) expect := []dsq.Entry{ - {Key: "/bar", Value: []byte("/foo/bar")}, - {Key: "/bar/baz", Value: []byte("/foo/bar/baz")}, - {Key: "/baz/abc", Value: []byte("/foo/baz/abc")}, + {Key: "/bar", Size: len([]byte("/foo/bar")), Value: []byte("/foo/bar")}, + {Key: "/bar/baz", Size: len([]byte("/foo/bar/baz")), Value: []byte("/foo/bar/baz")}, + {Key: "/baz/abc", Size: len([]byte("/foo/baz/abc")), Value: []byte("/foo/baz/abc")}, } results, err := qres.Rest() @@ -122,8 +122,8 @@ func (ks *DSSuite) TestQuery(c *C) { c.Check(err, Equals, nil) expect = []dsq.Entry{ - {Key: "/bar", Value: []byte("/foo/bar")}, - {Key: "/bar/baz", Value: []byte("/foo/bar/baz")}, + {Key: "/bar", Size: len([]byte("/foo/bar")), Value: []byte("/foo/bar")}, + {Key: "/bar/baz", Size: len([]byte("/foo/bar/baz")), Value: []byte("/foo/bar/baz")}, } results, err = qres.Rest() diff --git a/query/query.go b/query/query.go index 42e69c45a35cd2e17715bf2c97e7397165e0360f..b6bfa451fee699593dc1823ed34842137d5163fe 100644 --- a/query/query.go +++ b/query/query.go @@ -66,6 +66,9 @@ type Query struct { Offset int // skip given number of results KeysOnly bool // return only keys. ReturnExpirations bool // return expirations (see TTLDatastore) + ReturnsSizes bool // always return sizes. If not set, datastore impl can return + // // it anyway if it doesn't involve a performance cost. If KeysOnly + // // is not set, Size should always be set. } // String returns a string represenation of the Query for debugging/validation @@ -117,6 +120,8 @@ type Entry struct { Key string // cant be ds.Key because circular imports ...!!! Value []byte // Will be nil if KeysOnly has been passed. Expiration time.Time // Entry expiration timestamp if requested and supported (see TTLDatastore). + Size int // Might be -1 if the datastore doesn't support listing the size with KeysOnly + // // or if ReturnsSizes is not set } // Result is a special entry that includes an error, so that the client diff --git a/query/query_impl.go b/query/query_impl.go index 6c2e422b22a0e932b911b7aa2d7c875272bf6b11..175a4c2173d054a23ad86949795972230629b3b6 100644 --- a/query/query_impl.go +++ b/query/query_impl.go @@ -136,7 +136,7 @@ func NaiveQueryApply(q Query, qr Results) Results { func ResultEntriesFrom(keys []string, vals [][]byte) []Entry { re := make([]Entry, len(keys)) for i, k := range keys { - re[i] = Entry{Key: k, Value: vals[i]} + re[i] = Entry{Key: k, Size: len(vals[i]), Value: vals[i]} } return re } diff --git a/test/basic_tests.go b/test/basic_tests.go index 862a634f238a4be96160f1ec4409f77bee9c6e4a..b1c73de55f99a4828c93e01bdd7ab5873b62cea0 100644 --- a/test/basic_tests.go +++ b/test/basic_tests.go @@ -301,6 +301,10 @@ func SubtestFilter(t *testing.T, ds dstore.Datastore) { test(new(testFilter)) } +func SubtestReturnSizes(t *testing.T, ds dstore.Datastore) { + subtestQuery(t, ds, dsq.Query{ReturnsSizes: true}, 100) +} + func randValue() []byte { value := make([]byte, 64) rand.Read(value) @@ -315,6 +319,7 @@ func subtestQuery(t *testing.T, ds dstore.Datastore, q dsq.Query, count int) { value := randValue() input = append(input, dsq.Entry{ Key: key, + Size: len(value), Value: value, }) } @@ -375,7 +380,9 @@ func subtestQuery(t *testing.T, ds dstore.Datastore, q dsq.Query, count int) { if !q.KeysOnly && !bytes.Equal(actual[i].Value, expected[i].Value) { t.Errorf("value mismatch for result %d (key=%q)", i, expected[i].Key) } - + if q.ReturnsSizes && actual[i].Size <= 0 { + t.Errorf("for result %d, expected size > 0 with ReturnsSizes", i) + } } t.Log("deleting all keys") diff --git a/test/suite.go b/test/suite.go index 20d0f69d2220bd75ef2cf9291e5b71f74ee45527..3d1740115f474814da8aea023fb2775a9282edbb 100644 --- a/test/suite.go +++ b/test/suite.go @@ -18,6 +18,7 @@ var BasicSubtests = []func(t *testing.T, ds dstore.Datastore){ SubtestLimit, SubtestFilter, SubtestManyKeysAndQuery, + SubtestReturnSizes, } // BatchSubtests is a list of all basic batching datastore tests.