Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
dms3
go-datastore
Commits
bce485ce
Unverified
Commit
bce485ce
authored
Mar 22, 2019
by
Steven Allen
Committed by
GitHub
Mar 22, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #124 from ipfs/fix/sorted-limited-offset-mount-queries
Fix – sorted limited offset mount queries
parents
8e1699b5
18ef6442
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
550 additions
and
94 deletions
+550
-94
mount/mount.go
mount/mount.go
+149
-63
mount/mount_test.go
mount/mount_test.go
+314
-11
query/order.go
query/order.go
+18
-0
query/query_impl.go
query/query_impl.go
+5
-20
query/query_test.go
query/query_test.go
+64
-0
No files found.
mount/mount.go
View file @
bce485ce
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
package
mount
package
mount
import
(
import
(
"container/heap"
"errors"
"errors"
"fmt"
"fmt"
"sort"
"sort"
...
@@ -49,6 +50,105 @@ func (d *Datastore) lookup(key ds.Key) (ds.Datastore, ds.Key, ds.Key) {
...
@@ -49,6 +50,105 @@ func (d *Datastore) lookup(key ds.Key) (ds.Datastore, ds.Key, ds.Key) {
return
nil
,
ds
.
NewKey
(
"/"
),
key
return
nil
,
ds
.
NewKey
(
"/"
),
key
}
}
type
queryResults
struct
{
mount
ds
.
Key
results
query
.
Results
next
query
.
Result
}
func
(
qr
*
queryResults
)
advance
()
bool
{
if
qr
.
results
==
nil
{
return
false
}
qr
.
next
=
query
.
Result
{}
r
,
more
:=
qr
.
results
.
NextSync
()
if
!
more
{
err
:=
qr
.
results
.
Close
()
qr
.
results
=
nil
if
err
!=
nil
{
// One more result, the error.
qr
.
next
=
query
.
Result
{
Error
:
err
}
return
true
}
return
false
}
r
.
Key
=
qr
.
mount
.
Child
(
ds
.
RawKey
(
r
.
Key
))
.
String
()
qr
.
next
=
r
return
true
}
type
querySet
struct
{
query
query
.
Query
heads
[]
*
queryResults
}
func
(
h
*
querySet
)
Len
()
int
{
return
len
(
h
.
heads
)
}
func
(
h
*
querySet
)
Less
(
i
,
j
int
)
bool
{
return
query
.
Less
(
h
.
query
.
Orders
,
h
.
heads
[
i
]
.
next
.
Entry
,
h
.
heads
[
j
]
.
next
.
Entry
)
}
func
(
h
*
querySet
)
Swap
(
i
,
j
int
)
{
h
.
heads
[
i
],
h
.
heads
[
j
]
=
h
.
heads
[
j
],
h
.
heads
[
i
]
}
func
(
h
*
querySet
)
Push
(
x
interface
{})
{
h
.
heads
=
append
(
h
.
heads
,
x
.
(
*
queryResults
))
}
func
(
h
*
querySet
)
Pop
()
interface
{}
{
i
:=
len
(
h
.
heads
)
-
1
last
:=
h
.
heads
[
i
]
h
.
heads
[
i
]
=
nil
h
.
heads
=
h
.
heads
[
:
i
]
return
last
}
func
(
h
*
querySet
)
close
()
error
{
var
errs
[]
error
for
_
,
qr
:=
range
h
.
heads
{
err
:=
qr
.
results
.
Close
()
if
err
!=
nil
{
errs
=
append
(
errs
,
err
)
}
}
h
.
heads
=
nil
if
len
(
errs
)
>
0
{
return
errs
[
0
]
}
return
nil
}
func
(
h
*
querySet
)
addResults
(
mount
ds
.
Key
,
results
query
.
Results
)
{
r
:=
&
queryResults
{
results
:
results
,
mount
:
mount
,
}
if
r
.
advance
()
{
heap
.
Push
(
h
,
r
)
}
}
func
(
h
*
querySet
)
next
()
(
query
.
Result
,
bool
)
{
if
len
(
h
.
heads
)
==
0
{
return
query
.
Result
{},
false
}
head
:=
h
.
heads
[
0
]
next
:=
head
.
next
if
head
.
advance
()
{
heap
.
Fix
(
h
,
0
)
}
else
{
heap
.
Remove
(
h
,
0
)
}
return
next
,
true
}
// lookupAll returns all mounts that might contain keys that are descendant of <key>
// lookupAll returns all mounts that might contain keys that are descendant of <key>
//
//
// Matching: /ao/e
// Matching: /ao/e
...
@@ -121,73 +221,59 @@ func (d *Datastore) Delete(key ds.Key) error {
...
@@ -121,73 +221,59 @@ func (d *Datastore) Delete(key ds.Key) error {
return
cds
.
Delete
(
k
)
return
cds
.
Delete
(
k
)
}
}
func
(
d
*
Datastore
)
Query
(
q
query
.
Query
)
(
query
.
Results
,
error
)
{
func
(
d
*
Datastore
)
Query
(
master
query
.
Query
)
(
query
.
Results
,
error
)
{
if
len
(
q
.
Filters
)
>
0
||
childQuery
:=
query
.
Query
{
len
(
q
.
Orders
)
>
0
||
Prefix
:
master
.
Prefix
,
q
.
Limit
>
0
||
Limit
:
master
.
Limit
,
q
.
Offset
>
0
{
Orders
:
master
.
Orders
,
// TODO this is still overly simplistic, but the only callers are
KeysOnly
:
master
.
KeysOnly
,
// `ipfs refs local` and ipfs-ds-convert.
ReturnExpirations
:
master
.
ReturnExpirations
,
return
nil
,
errors
.
New
(
"mount only supports listing all prefixed keys in random order"
)
}
}
prefix
:=
ds
.
NewKey
(
q
.
Prefix
)
dses
,
mounts
,
rests
:=
d
.
lookupAll
(
prefix
)
// current itorator state
var
res
query
.
Results
var
mount
ds
.
Key
i
:=
0
return
query
.
ResultsFromIterator
(
q
,
query
.
Iterator
{
prefix
:=
ds
.
NewKey
(
childQuery
.
Prefix
)
Next
:
func
()
(
query
.
Result
,
bool
)
{
dses
,
mounts
,
rests
:=
d
.
lookupAll
(
prefix
)
var
r
query
.
Result
var
more
bool
for
try
:=
true
;
try
;
try
=
len
(
dses
)
>
i
{
queries
:=
&
querySet
{
if
res
==
nil
{
query
:
childQuery
,
if
len
(
dses
)
<=
i
{
heads
:
make
([]
*
queryResults
,
0
,
len
(
dses
)),
//This should not happen normally
return
query
.
Result
{},
false
}
}
dst
:=
dses
[
i
]
for
i
:=
range
dses
{
mount
=
mounts
[
i
]
mount
:=
mounts
[
i
]
dstore
:=
dses
[
i
]
rest
:=
rests
[
i
]
rest
:=
rests
[
i
]
q2
:=
q
qi
:=
childQuery
q2
.
Prefix
=
rest
.
String
()
qi
.
Prefix
=
rest
.
String
()
r
,
err
:=
dst
.
Query
(
q2
)
results
,
err
:=
dstore
.
Query
(
qi
)
if
err
!=
nil
{
if
err
!=
nil
{
return
query
.
Result
{
Error
:
err
},
false
_
=
queries
.
close
()
return
nil
,
err
}
}
res
=
r
queries
.
addResults
(
mount
,
results
)
}
}
r
,
more
=
res
.
NextSync
()
qr
:=
query
.
ResultsFromIterator
(
childQuery
,
query
.
Iterator
{
if
!
more
{
Next
:
queries
.
next
,
err
:=
res
.
Close
()
Close
:
queries
.
close
,
if
err
!=
nil
{
})
return
query
.
Result
{
Error
:
err
},
false
}
res
=
nil
i
++
if
len
(
master
.
Filters
)
>
0
{
more
=
len
(
dses
)
>
i
for
_
,
f
:=
range
master
.
Filters
{
}
else
{
qr
=
query
.
NaiveFilter
(
qr
,
f
)
break
}
}
}
}
r
.
Key
=
mount
.
Child
(
ds
.
RawKey
(
r
.
Key
))
.
String
()
if
master
.
Offset
>
0
{
return
r
,
more
qr
=
query
.
NaiveOffset
(
qr
,
master
.
Offset
)
},
Close
:
func
()
error
{
if
len
(
mounts
)
>
i
&&
res
!=
nil
{
return
res
.
Close
()
}
}
return
nil
},
if
childQuery
.
Limit
>
0
{
}),
nil
qr
=
query
.
NaiveLimit
(
qr
,
childQuery
.
Limit
)
}
return
qr
,
nil
}
}
func
(
d
*
Datastore
)
Close
()
error
{
func
(
d
*
Datastore
)
Close
()
error
{
...
...
mount/mount_test.go
View file @
bce485ce
...
@@ -4,9 +4,10 @@ import (
...
@@ -4,9 +4,10 @@ import (
"errors"
"errors"
"testing"
"testing"
"github.com/ipfs/go-datastore"
datastore
"github.com/ipfs/go-datastore"
mount
"github.com/ipfs/go-datastore/mount"
mount
"github.com/ipfs/go-datastore/mount"
"github.com/ipfs/go-datastore/query"
query
"github.com/ipfs/go-datastore/query"
sync
"github.com/ipfs/go-datastore/sync"
dstest
"github.com/ipfs/go-datastore/test"
dstest
"github.com/ipfs/go-datastore/test"
)
)
...
@@ -238,7 +239,7 @@ func TestQuerySimple(t *testing.T) {
...
@@ -238,7 +239,7 @@ func TestQuerySimple(t *testing.T) {
}
}
}
}
func
TestQuery
C
ross
(
t
*
testing
.
T
)
{
func
TestQuery
Ac
ross
Mounts
(
t
*
testing
.
T
)
{
mapds0
:=
datastore
.
NewMapDatastore
()
mapds0
:=
datastore
.
NewMapDatastore
()
mapds1
:=
datastore
.
NewMapDatastore
()
mapds1
:=
datastore
.
NewMapDatastore
()
mapds2
:=
datastore
.
NewMapDatastore
()
mapds2
:=
datastore
.
NewMapDatastore
()
...
@@ -262,6 +263,10 @@ func TestQueryCross(t *testing.T) {
...
@@ -262,6 +263,10 @@ func TestQueryCross(t *testing.T) {
}
}
entries
,
err
:=
res
.
Rest
()
entries
,
err
:=
res
.
Rest
()
if
err
!=
nil
{
if
err
!=
nil
{
err
=
res
.
Close
()
if
err
!=
nil
{
t
.
Errorf
(
"result.Close failed %d"
,
err
)
}
t
.
Fatalf
(
"Query Results.Rest fail: %v
\n
"
,
err
)
t
.
Fatalf
(
"Query Results.Rest fail: %v
\n
"
,
err
)
}
}
seen
:=
0
seen
:=
0
...
@@ -297,6 +302,308 @@ func TestQueryCross(t *testing.T) {
...
@@ -297,6 +302,308 @@ func TestQueryCross(t *testing.T) {
}
}
}
}
func
TestQueryAcrossMountsWithSort
(
t
*
testing
.
T
)
{
mapds0
:=
datastore
.
NewMapDatastore
()
mapds1
:=
datastore
.
NewMapDatastore
()
mapds2
:=
datastore
.
NewMapDatastore
()
m
:=
mount
.
New
([]
mount
.
Mount
{
{
Prefix
:
datastore
.
NewKey
(
"/zoo"
),
Datastore
:
mapds1
},
{
Prefix
:
datastore
.
NewKey
(
"/boo/5"
),
Datastore
:
mapds2
},
{
Prefix
:
datastore
.
NewKey
(
"/boo"
),
Datastore
:
mapds0
},
})
m
.
Put
(
datastore
.
NewKey
(
"/zoo/0"
),
[]
byte
(
"123"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/1"
),
[]
byte
(
"234"
))
m
.
Put
(
datastore
.
NewKey
(
"/boo/9"
),
[]
byte
(
"345"
))
m
.
Put
(
datastore
.
NewKey
(
"/boo/3"
),
[]
byte
(
"456"
))
m
.
Put
(
datastore
.
NewKey
(
"/boo/5/hello"
),
[]
byte
(
"789"
))
res
,
err
:=
m
.
Query
(
query
.
Query
{
Orders
:
[]
query
.
Order
{
query
.
OrderByKey
{}}})
if
err
!=
nil
{
t
.
Fatalf
(
"Query fail: %v
\n
"
,
err
)
}
entries
,
err
:=
res
.
Rest
()
if
err
!=
nil
{
t
.
Fatalf
(
"Query Results.Rest fail: %v
\n
"
,
err
)
}
expect
:=
[]
string
{
"/boo/3"
,
"/boo/5/hello"
,
"/boo/9"
,
"/zoo/0"
,
"/zoo/1"
,
}
if
len
(
entries
)
!=
len
(
expect
)
{
t
.
Fatalf
(
"expected %d entries, but got %d"
,
len
(
expect
),
len
(
entries
))
}
for
i
,
e
:=
range
expect
{
if
e
!=
entries
[
i
]
.
Key
{
t
.
Errorf
(
"expected key %s, but got %s"
,
e
,
entries
[
i
]
.
Key
)
}
}
err
=
res
.
Close
()
if
err
!=
nil
{
t
.
Errorf
(
"result.Close failed %d"
,
err
)
}
}
func
TestQueryLimitAcrossMountsWithSort
(
t
*
testing
.
T
)
{
mapds1
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
mapds2
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
mapds3
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
m
:=
mount
.
New
([]
mount
.
Mount
{
{
Prefix
:
datastore
.
NewKey
(
"/rok"
),
Datastore
:
mapds1
},
{
Prefix
:
datastore
.
NewKey
(
"/zoo"
),
Datastore
:
mapds2
},
{
Prefix
:
datastore
.
NewKey
(
"/noop"
),
Datastore
:
mapds3
},
})
m
.
Put
(
datastore
.
NewKey
(
"/rok/0"
),
[]
byte
(
"ghi"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/0"
),
[]
byte
(
"123"
))
m
.
Put
(
datastore
.
NewKey
(
"/rok/1"
),
[]
byte
(
"def"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/1"
),
[]
byte
(
"167"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/2"
),
[]
byte
(
"345"
))
m
.
Put
(
datastore
.
NewKey
(
"/rok/3"
),
[]
byte
(
"abc"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/3"
),
[]
byte
(
"456"
))
q
:=
query
.
Query
{
Limit
:
2
,
Orders
:
[]
query
.
Order
{
query
.
OrderByKeyDescending
{}}}
res
,
err
:=
m
.
Query
(
q
)
if
err
!=
nil
{
t
.
Fatalf
(
"Query fail: %v
\n
"
,
err
)
}
entries
,
err
:=
res
.
Rest
()
if
err
!=
nil
{
t
.
Fatalf
(
"Query Results.Rest fail: %v
\n
"
,
err
)
}
expect
:=
[]
string
{
"/zoo/3"
,
"/zoo/2"
,
}
if
len
(
entries
)
!=
len
(
expect
)
{
t
.
Fatalf
(
"expected %d entries, but got %d"
,
len
(
expect
),
len
(
entries
))
}
for
i
,
e
:=
range
expect
{
if
e
!=
entries
[
i
]
.
Key
{
t
.
Errorf
(
"expected key %s, but got %s"
,
e
,
entries
[
i
]
.
Key
)
}
}
err
=
res
.
Close
()
if
err
!=
nil
{
t
.
Errorf
(
"result.Close failed %d"
,
err
)
}
}
func
TestQueryLimitAndOffsetAcrossMountsWithSort
(
t
*
testing
.
T
)
{
mapds1
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
mapds2
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
mapds3
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
m
:=
mount
.
New
([]
mount
.
Mount
{
{
Prefix
:
datastore
.
NewKey
(
"/rok"
),
Datastore
:
mapds1
},
{
Prefix
:
datastore
.
NewKey
(
"/zoo"
),
Datastore
:
mapds2
},
{
Prefix
:
datastore
.
NewKey
(
"/noop"
),
Datastore
:
mapds3
},
})
m
.
Put
(
datastore
.
NewKey
(
"/rok/0"
),
[]
byte
(
"ghi"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/0"
),
[]
byte
(
"123"
))
m
.
Put
(
datastore
.
NewKey
(
"/rok/1"
),
[]
byte
(
"def"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/1"
),
[]
byte
(
"167"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/2"
),
[]
byte
(
"345"
))
m
.
Put
(
datastore
.
NewKey
(
"/rok/3"
),
[]
byte
(
"abc"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/3"
),
[]
byte
(
"456"
))
q
:=
query
.
Query
{
Limit
:
3
,
Offset
:
2
,
Orders
:
[]
query
.
Order
{
query
.
OrderByKey
{}}}
res
,
err
:=
m
.
Query
(
q
)
if
err
!=
nil
{
t
.
Fatalf
(
"Query fail: %v
\n
"
,
err
)
}
entries
,
err
:=
res
.
Rest
()
if
err
!=
nil
{
t
.
Fatalf
(
"Query Results.Rest fail: %v
\n
"
,
err
)
}
expect
:=
[]
string
{
"/rok/3"
,
"/zoo/0"
,
"/zoo/1"
,
}
if
len
(
entries
)
!=
len
(
expect
)
{
t
.
Fatalf
(
"expected %d entries, but got %d"
,
len
(
expect
),
len
(
entries
))
}
for
i
,
e
:=
range
expect
{
if
e
!=
entries
[
i
]
.
Key
{
t
.
Errorf
(
"expected key %s, but got %s"
,
e
,
entries
[
i
]
.
Key
)
}
}
err
=
res
.
Close
()
if
err
!=
nil
{
t
.
Errorf
(
"result.Close failed %d"
,
err
)
}
}
func
TestQueryFilterAcrossMountsWithSort
(
t
*
testing
.
T
)
{
mapds1
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
mapds2
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
mapds3
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
m
:=
mount
.
New
([]
mount
.
Mount
{
{
Prefix
:
datastore
.
NewKey
(
"/rok"
),
Datastore
:
mapds1
},
{
Prefix
:
datastore
.
NewKey
(
"/zoo"
),
Datastore
:
mapds2
},
{
Prefix
:
datastore
.
NewKey
(
"/noop"
),
Datastore
:
mapds3
},
})
m
.
Put
(
datastore
.
NewKey
(
"/rok/0"
),
[]
byte
(
"ghi"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/0"
),
[]
byte
(
"123"
))
m
.
Put
(
datastore
.
NewKey
(
"/rok/1"
),
[]
byte
(
"def"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/1"
),
[]
byte
(
"167"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/2"
),
[]
byte
(
"345"
))
m
.
Put
(
datastore
.
NewKey
(
"/rok/3"
),
[]
byte
(
"abc"
))
m
.
Put
(
datastore
.
NewKey
(
"/zoo/3"
),
[]
byte
(
"456"
))
f
:=
&
query
.
FilterKeyCompare
{
Op
:
query
.
Equal
,
Key
:
"/rok/3"
}
q
:=
query
.
Query
{
Filters
:
[]
query
.
Filter
{
f
}}
res
,
err
:=
m
.
Query
(
q
)
if
err
!=
nil
{
t
.
Fatalf
(
"Query fail: %v
\n
"
,
err
)
}
entries
,
err
:=
res
.
Rest
()
if
err
!=
nil
{
t
.
Fatalf
(
"Query Results.Rest fail: %v
\n
"
,
err
)
}
expect
:=
[]
string
{
"/rok/3"
,
}
if
len
(
entries
)
!=
len
(
expect
)
{
t
.
Fatalf
(
"expected %d entries, but got %d"
,
len
(
expect
),
len
(
entries
))
}
for
i
,
e
:=
range
expect
{
if
e
!=
entries
[
i
]
.
Key
{
t
.
Errorf
(
"expected key %s, but got %s"
,
e
,
entries
[
i
]
.
Key
)
}
}
err
=
res
.
Close
()
if
err
!=
nil
{
t
.
Errorf
(
"result.Close failed %d"
,
err
)
}
}
func
TestQueryLimitAndOffsetWithNoData
(
t
*
testing
.
T
)
{
mapds1
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
mapds2
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
m
:=
mount
.
New
([]
mount
.
Mount
{
{
Prefix
:
datastore
.
NewKey
(
"/rok"
),
Datastore
:
mapds1
},
{
Prefix
:
datastore
.
NewKey
(
"/zoo"
),
Datastore
:
mapds2
},
})
q
:=
query
.
Query
{
Limit
:
4
,
Offset
:
3
}
res
,
err
:=
m
.
Query
(
q
)
if
err
!=
nil
{
t
.
Fatalf
(
"Query fail: %v
\n
"
,
err
)
}
entries
,
err
:=
res
.
Rest
()
if
err
!=
nil
{
t
.
Fatalf
(
"Query Results.Rest fail: %v
\n
"
,
err
)
}
expect
:=
[]
string
{}
if
len
(
entries
)
!=
len
(
expect
)
{
t
.
Fatalf
(
"expected %d entries, but got %d"
,
len
(
expect
),
len
(
entries
))
}
err
=
res
.
Close
()
if
err
!=
nil
{
t
.
Errorf
(
"result.Close failed %d"
,
err
)
}
}
func
TestQueryLimitWithNotEnoughData
(
t
*
testing
.
T
)
{
mapds1
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
mapds2
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
m
:=
mount
.
New
([]
mount
.
Mount
{
{
Prefix
:
datastore
.
NewKey
(
"/rok"
),
Datastore
:
mapds1
},
{
Prefix
:
datastore
.
NewKey
(
"/zoo"
),
Datastore
:
mapds2
},
})
m
.
Put
(
datastore
.
NewKey
(
"/zoo/0"
),
[]
byte
(
"123"
))
m
.
Put
(
datastore
.
NewKey
(
"/rok/1"
),
[]
byte
(
"167"
))
q
:=
query
.
Query
{
Limit
:
4
}
res
,
err
:=
m
.
Query
(
q
)
if
err
!=
nil
{
t
.
Fatalf
(
"Query fail: %v
\n
"
,
err
)
}
entries
,
err
:=
res
.
Rest
()
if
err
!=
nil
{
t
.
Fatalf
(
"Query Results.Rest fail: %v
\n
"
,
err
)
}
expect
:=
[]
string
{
"/zoo/0"
,
"/rok/1"
,
}
if
len
(
entries
)
!=
len
(
expect
)
{
t
.
Fatalf
(
"expected %d entries, but got %d"
,
len
(
expect
),
len
(
entries
))
}
err
=
res
.
Close
()
if
err
!=
nil
{
t
.
Errorf
(
"result.Close failed %d"
,
err
)
}
}
func
TestQueryOffsetWithNotEnoughData
(
t
*
testing
.
T
)
{
mapds1
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
mapds2
:=
sync
.
MutexWrap
(
datastore
.
NewMapDatastore
())
m
:=
mount
.
New
([]
mount
.
Mount
{
{
Prefix
:
datastore
.
NewKey
(
"/rok"
),
Datastore
:
mapds1
},
{
Prefix
:
datastore
.
NewKey
(
"/zoo"
),
Datastore
:
mapds2
},
})
m
.
Put
(
datastore
.
NewKey
(
"/zoo/0"
),
[]
byte
(
"123"
))
m
.
Put
(
datastore
.
NewKey
(
"/rok/1"
),
[]
byte
(
"167"
))
q
:=
query
.
Query
{
Offset
:
4
}
res
,
err
:=
m
.
Query
(
q
)
if
err
!=
nil
{
t
.
Fatalf
(
"Query fail: %v
\n
"
,
err
)
}
entries
,
err
:=
res
.
Rest
()
if
err
!=
nil
{
t
.
Fatalf
(
"Query Results.Rest fail: %v
\n
"
,
err
)
}
expect
:=
[]
string
{}
if
len
(
entries
)
!=
len
(
expect
)
{
t
.
Fatalf
(
"expected %d entries, but got %d"
,
len
(
expect
),
len
(
entries
))
}
err
=
res
.
Close
()
if
err
!=
nil
{
t
.
Errorf
(
"result.Close failed %d"
,
err
)
}
}
func
TestLookupPrio
(
t
*
testing
.
T
)
{
func
TestLookupPrio
(
t
*
testing
.
T
)
{
mapds0
:=
datastore
.
NewMapDatastore
()
mapds0
:=
datastore
.
NewMapDatastore
()
mapds1
:=
datastore
.
NewMapDatastore
()
mapds1
:=
datastore
.
NewMapDatastore
()
...
@@ -353,14 +660,10 @@ func TestErrQueryClose(t *testing.T) {
...
@@ -353,14 +660,10 @@ func TestErrQueryClose(t *testing.T) {
m
.
Put
(
datastore
.
NewKey
(
"/baz"
),
[]
byte
(
"123"
))
m
.
Put
(
datastore
.
NewKey
(
"/baz"
),
[]
byte
(
"123"
))
qr
,
err
:=
m
.
Query
(
query
.
Query
{})
_
,
err
:=
m
.
Query
(
query
.
Query
{})
if
err
!=
nil
{
if
err
==
nil
{
t
.
Fatalf
(
"Query error: %v"
,
err
)
t
.
Fatal
(
"expected query to fail"
)
}
return
e
,
ok
:=
qr
.
NextSync
()
if
ok
!=
false
||
e
.
Error
==
nil
{
t
.
Errorf
(
"Query was ok or q.Error was nil"
)
}
}
}
}
...
...
query/order.go
View file @
bce485ce
...
@@ -46,3 +46,21 @@ type OrderByKeyDescending struct{}
...
@@ -46,3 +46,21 @@ type OrderByKeyDescending struct{}
func
(
o
OrderByKeyDescending
)
Compare
(
a
,
b
Entry
)
int
{
func
(
o
OrderByKeyDescending
)
Compare
(
a
,
b
Entry
)
int
{
return
-
strings
.
Compare
(
a
.
Key
,
b
.
Key
)
return
-
strings
.
Compare
(
a
.
Key
,
b
.
Key
)
}
}
// Less returns true if a comes before b with the requested orderings.
func
Less
(
orders
[]
Order
,
a
,
b
Entry
)
bool
{
for
_
,
cmp
:=
range
orders
{
switch
cmp
.
Compare
(
a
,
b
)
{
case
0
:
case
-
1
:
return
true
case
1
:
return
false
}
}
// This gives us a *stable* sort for free. We don't care
// preserving the order from the underlying datastore
// because it's undefined.
return
a
.
Key
<
b
.
Key
}
query/query_impl.go
View file @
bce485ce
...
@@ -24,7 +24,7 @@ func NaiveFilter(qr Results, filter Filter) Results {
...
@@ -24,7 +24,7 @@ func NaiveFilter(qr Results, filter Filter) Results {
}
}
}()
}()
return
DerivedResults
(
qr
,
ch
)
return
ResultsWithChan
(
qr
.
Query
()
,
ch
)
}
}
// NaiveLimit truncates the results to a given int limit
// NaiveLimit truncates the results to a given int limit
...
@@ -48,7 +48,7 @@ func NaiveLimit(qr Results, limit int) Results {
...
@@ -48,7 +48,7 @@ func NaiveLimit(qr Results, limit int) Results {
}
}
}()
}()
return
DerivedResults
(
qr
,
ch
)
return
ResultsWithChan
(
qr
.
Query
()
,
ch
)
}
}
// NaiveOffset skips a given number of results
// NaiveOffset skips a given number of results
...
@@ -72,7 +72,7 @@ func NaiveOffset(qr Results, offset int) Results {
...
@@ -72,7 +72,7 @@ func NaiveOffset(qr Results, offset int) Results {
}
}
}()
}()
return
DerivedResults
(
qr
,
ch
)
return
ResultsWithChan
(
qr
.
Query
()
,
ch
)
}
}
// NaiveOrder reorders results according to given orders.
// NaiveOrder reorders results according to given orders.
...
@@ -97,22 +97,7 @@ func NaiveOrder(qr Results, orders ...Order) Results {
...
@@ -97,22 +97,7 @@ func NaiveOrder(qr Results, orders ...Order) Results {
entries
=
append
(
entries
,
e
.
Entry
)
entries
=
append
(
entries
,
e
.
Entry
)
}
}
sort
.
Slice
(
entries
,
func
(
i
int
,
j
int
)
bool
{
sort
.
Slice
(
entries
,
func
(
i
int
,
j
int
)
bool
{
a
,
b
:=
entries
[
i
],
entries
[
j
]
return
Less
(
orders
,
entries
[
i
],
entries
[
j
])
for
_
,
cmp
:=
range
orders
{
switch
cmp
.
Compare
(
a
,
b
)
{
case
0
:
case
-
1
:
return
true
case
1
:
return
false
}
}
// This gives us a *stable* sort for free. We don't care
// preserving the order from the underlying datastore
// because it's undefined.
return
a
.
Key
<
b
.
Key
})
})
for
_
,
e
:=
range
entries
{
for
_
,
e
:=
range
entries
{
...
@@ -137,7 +122,7 @@ func NaiveQueryApply(q Query, qr Results) Results {
...
@@ -137,7 +122,7 @@ func NaiveQueryApply(q Query, qr Results) Results {
qr
=
NaiveOffset
(
qr
,
q
.
Offset
)
qr
=
NaiveOffset
(
qr
,
q
.
Offset
)
}
}
if
q
.
Limit
!=
0
{
if
q
.
Limit
!=
0
{
qr
=
NaiveLimit
(
qr
,
q
.
Offse
t
)
qr
=
NaiveLimit
(
qr
,
q
.
Limi
t
)
}
}
return
qr
return
qr
}
}
...
...
query/query_test.go
View file @
bce485ce
...
@@ -40,6 +40,70 @@ func testResults(t *testing.T, res Results, expect []string) {
...
@@ -40,6 +40,70 @@ func testResults(t *testing.T, res Results, expect []string) {
}
}
}
}
func
TestNaiveQueryApply
(
t
*
testing
.
T
)
{
testNaiveQueryApply
:=
func
(
t
*
testing
.
T
,
query
Query
,
keys
[]
string
,
expect
[]
string
)
{
e
:=
make
([]
Entry
,
len
(
keys
))
for
i
,
k
:=
range
keys
{
e
[
i
]
=
Entry
{
Key
:
k
}
}
res
:=
ResultsWithEntries
(
query
,
e
)
res
=
NaiveQueryApply
(
query
,
res
)
testResults
(
t
,
res
,
expect
)
}
q
:=
Query
{
Limit
:
2
}
testNaiveQueryApply
(
t
,
q
,
sampleKeys
,
[]
string
{
"/ab/c"
,
"/ab/cd"
,
})
q
=
Query
{
Offset
:
3
,
Limit
:
2
}
testNaiveQueryApply
(
t
,
q
,
sampleKeys
,
[]
string
{
"/abce"
,
"/abcf"
,
})
f
:=
&
FilterKeyCompare
{
Op
:
Equal
,
Key
:
"/ab"
}
q
=
Query
{
Filters
:
[]
Filter
{
f
}}
testNaiveQueryApply
(
t
,
q
,
sampleKeys
,
[]
string
{
"/ab"
,
})
q
=
Query
{
Prefix
:
"/ab"
}
testNaiveQueryApply
(
t
,
q
,
sampleKeys
,
[]
string
{
"/ab/c"
,
"/ab/cd"
,
"/abce"
,
"/abcf"
,
"/ab"
,
})
q
=
Query
{
Orders
:
[]
Order
{
OrderByKeyDescending
{}}}
testNaiveQueryApply
(
t
,
q
,
sampleKeys
,
[]
string
{
"/abcf"
,
"/abce"
,
"/ab/cd"
,
"/ab/c"
,
"/ab"
,
"/a"
,
})
q
=
Query
{
Limit
:
3
,
Offset
:
2
,
Prefix
:
"/ab"
,
Orders
:
[]
Order
{
OrderByKey
{}},
}
testNaiveQueryApply
(
t
,
q
,
sampleKeys
,
[]
string
{
"/ab/cd"
,
"/abce"
,
"/abcf"
,
})
}
func
TestLimit
(
t
*
testing
.
T
)
{
func
TestLimit
(
t
*
testing
.
T
)
{
testKeyLimit
:=
func
(
t
*
testing
.
T
,
limit
int
,
keys
[]
string
,
expect
[]
string
)
{
testKeyLimit
:=
func
(
t
*
testing
.
T
,
limit
int
,
keys
[]
string
,
expect
[]
string
)
{
e
:=
make
([]
Entry
,
len
(
keys
))
e
:=
make
([]
Entry
,
len
(
keys
))
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment