merge to master
This commit is contained in:
commit
c64acabee0
30 changed files with 645 additions and 334 deletions
|
|
@ -153,6 +153,22 @@ func Next(it Iterator) (Value, bool) {
|
|||
return nil, false
|
||||
}
|
||||
|
||||
// Height is a convienence function to measure the height of an iterator tree.
|
||||
func Height(it Iterator, until Type) int {
|
||||
if it.Type() == until {
|
||||
return 1
|
||||
}
|
||||
subs := it.SubIterators()
|
||||
maxDepth := 0
|
||||
for _, sub := range subs {
|
||||
h := Height(sub, until)
|
||||
if h > maxDepth {
|
||||
maxDepth = h
|
||||
}
|
||||
}
|
||||
return maxDepth + 1
|
||||
}
|
||||
|
||||
// FixedIterator wraps iterators that are modifiable by addition of fixed value sets.
|
||||
type FixedIterator interface {
|
||||
Iterator
|
||||
|
|
@ -180,6 +196,7 @@ const (
|
|||
Fixed
|
||||
Not
|
||||
Optional
|
||||
Materialize
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -200,6 +217,7 @@ var (
|
|||
"fixed",
|
||||
"not",
|
||||
"optional",
|
||||
"materialize",
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ func (it *And) Optimize() (graph.Iterator, bool) {
|
|||
// now a permutation of itself, but the contents are unchanged.
|
||||
its = optimizeOrder(its)
|
||||
|
||||
its = materializeIts(its)
|
||||
|
||||
// Okay! At this point we have an optimized order.
|
||||
|
||||
// The easiest thing to do at this point is merely to create a new And iterator
|
||||
|
|
@ -293,6 +295,21 @@ func hasOneUsefulIterator(its []graph.Iterator) graph.Iterator {
|
|||
return nil
|
||||
}
|
||||
|
||||
func materializeIts(its []graph.Iterator) []graph.Iterator {
|
||||
var out []graph.Iterator
|
||||
for _, it := range its {
|
||||
stats := it.Stats()
|
||||
if stats.Size*stats.NextCost < stats.ContainsCost {
|
||||
if graph.Height(it, graph.Materialize) > 10 {
|
||||
out = append(out, NewMaterialize(it))
|
||||
continue
|
||||
}
|
||||
}
|
||||
out = append(out, it)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// and.Stats() lives here in and-iterator-optimize.go because it may
|
||||
// in the future return different statistics based on how it is optimized.
|
||||
// For now, however, it's pretty static.
|
||||
|
|
|
|||
273
graph/iterator/materialize_iterator.go
Normal file
273
graph/iterator/materialize_iterator.go
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
// Copyright 2014 The Cayley Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package iterator
|
||||
|
||||
// A simple iterator that, when first called Contains() or Next() upon, materializes the whole subiterator, stores it locally, and responds. Essentially a cache.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/barakmich/glog"
|
||||
|
||||
"github.com/google/cayley/graph"
|
||||
)
|
||||
|
||||
var abortMaterializeAt = 1000
|
||||
|
||||
type result struct {
|
||||
id graph.Value
|
||||
tags map[string]graph.Value
|
||||
}
|
||||
|
||||
type hasher interface {
|
||||
Hasher() interface{}
|
||||
}
|
||||
|
||||
type Materialize struct {
|
||||
uid uint64
|
||||
tags graph.Tagger
|
||||
containsMap map[graph.Value]int
|
||||
values [][]result
|
||||
index int
|
||||
subindex int
|
||||
subIt graph.Iterator
|
||||
hasRun bool
|
||||
aborted bool
|
||||
}
|
||||
|
||||
func NewMaterialize(sub graph.Iterator) *Materialize {
|
||||
return &Materialize{
|
||||
uid: NextUID(),
|
||||
containsMap: make(map[graph.Value]int),
|
||||
subIt: sub,
|
||||
index: -1,
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Materialize) UID() uint64 {
|
||||
return it.uid
|
||||
}
|
||||
|
||||
func (it *Materialize) Reset() {
|
||||
it.subIt.Reset()
|
||||
it.index = -1
|
||||
}
|
||||
|
||||
func (it *Materialize) Close() {
|
||||
it.subIt.Close()
|
||||
it.containsMap = nil
|
||||
it.values = nil
|
||||
it.hasRun = false
|
||||
}
|
||||
|
||||
func (it *Materialize) Tagger() *graph.Tagger {
|
||||
return &it.tags
|
||||
}
|
||||
|
||||
func (it *Materialize) TagResults(dst map[string]graph.Value) {
|
||||
if !it.hasRun {
|
||||
return
|
||||
}
|
||||
if it.aborted {
|
||||
it.subIt.TagResults(dst)
|
||||
return
|
||||
}
|
||||
if it.Result() == nil {
|
||||
return
|
||||
}
|
||||
for _, tag := range it.tags.Tags() {
|
||||
dst[tag] = it.Result()
|
||||
}
|
||||
for tag, value := range it.values[it.index][it.subindex].tags {
|
||||
dst[tag] = value
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Materialize) Clone() graph.Iterator {
|
||||
out := NewMaterialize(it.subIt.Clone())
|
||||
out.tags.CopyFrom(it)
|
||||
if it.hasRun {
|
||||
out.hasRun = true
|
||||
out.aborted = it.aborted
|
||||
out.values = it.values
|
||||
out.containsMap = it.containsMap
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Print some information about the iterator.
|
||||
func (it *Materialize) DebugString(indent int) string {
|
||||
return fmt.Sprintf("%s(%s tags: %s Size: %d\n%s)",
|
||||
strings.Repeat(" ", indent),
|
||||
it.Type(),
|
||||
it.tags.Tags(),
|
||||
len(it.values),
|
||||
it.subIt.DebugString(indent+4),
|
||||
)
|
||||
}
|
||||
|
||||
// Register this iterator as a Materialize iterator.
|
||||
func (it *Materialize) Type() graph.Type { return graph.Materialize }
|
||||
|
||||
// DEPRECATED
|
||||
func (it *Materialize) ResultTree() *graph.ResultTree {
|
||||
tree := graph.NewResultTree(it.Result())
|
||||
tree.AddSubtree(it.subIt.ResultTree())
|
||||
return tree
|
||||
}
|
||||
|
||||
func (it *Materialize) Result() graph.Value {
|
||||
if len(it.values) == 0 {
|
||||
return nil
|
||||
}
|
||||
if it.index == -1 {
|
||||
return nil
|
||||
}
|
||||
if it.index >= len(it.values) {
|
||||
return nil
|
||||
}
|
||||
return it.values[it.index][it.subindex].id
|
||||
}
|
||||
|
||||
func (it *Materialize) SubIterators() []graph.Iterator {
|
||||
return []graph.Iterator{it.subIt}
|
||||
}
|
||||
|
||||
func (it *Materialize) Optimize() (graph.Iterator, bool) {
|
||||
newSub, changed := it.subIt.Optimize()
|
||||
if changed {
|
||||
it.subIt = newSub
|
||||
if it.subIt.Type() == graph.Null {
|
||||
return it.subIt, true
|
||||
}
|
||||
}
|
||||
return it, false
|
||||
}
|
||||
|
||||
// Size is the number of values stored, if we've got them all.
|
||||
// Otherwise, guess based on the size of the subiterator.
|
||||
func (it *Materialize) Size() (int64, bool) {
|
||||
if it.hasRun {
|
||||
return int64(len(it.values)), true
|
||||
}
|
||||
return it.subIt.Size()
|
||||
}
|
||||
|
||||
// The entire point of Materialize is to amortize the cost by
|
||||
// putting it all up front.
|
||||
func (it *Materialize) Stats() graph.IteratorStats {
|
||||
overhead := int64(2)
|
||||
size, _ := it.Size()
|
||||
subitStats := it.subIt.Stats()
|
||||
return graph.IteratorStats{
|
||||
ContainsCost: overhead * subitStats.NextCost,
|
||||
NextCost: overhead * subitStats.NextCost,
|
||||
Size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Materialize) Next() (graph.Value, bool) {
|
||||
graph.NextLogIn(it)
|
||||
if !it.hasRun {
|
||||
it.materializeSet()
|
||||
}
|
||||
if it.aborted {
|
||||
return graph.Next(it.subIt)
|
||||
}
|
||||
|
||||
it.index++
|
||||
it.subindex = 0
|
||||
if it.index >= len(it.values) {
|
||||
return graph.NextLogOut(it, nil, false)
|
||||
}
|
||||
return graph.NextLogOut(it, it.Result(), true)
|
||||
}
|
||||
|
||||
func (it *Materialize) Contains(v graph.Value) bool {
|
||||
graph.ContainsLogIn(it, v)
|
||||
if !it.hasRun {
|
||||
it.materializeSet()
|
||||
}
|
||||
if it.aborted {
|
||||
return it.subIt.Contains(v)
|
||||
}
|
||||
key := v
|
||||
if h, ok := v.(hasher); ok {
|
||||
key = h.Hasher()
|
||||
}
|
||||
if i, ok := it.containsMap[key]; ok {
|
||||
it.index = i
|
||||
it.subindex = 0
|
||||
return graph.ContainsLogOut(it, v, true)
|
||||
}
|
||||
return graph.ContainsLogOut(it, v, false)
|
||||
}
|
||||
|
||||
func (it *Materialize) NextResult() bool {
|
||||
if !it.hasRun {
|
||||
it.materializeSet()
|
||||
}
|
||||
if it.aborted {
|
||||
return it.subIt.NextResult()
|
||||
}
|
||||
|
||||
it.subindex++
|
||||
if it.subindex >= len(it.values[it.index]) {
|
||||
// Don't go off the end of the world
|
||||
it.subindex--
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (it *Materialize) materializeSet() {
|
||||
i := 0
|
||||
for {
|
||||
id, ok := graph.Next(it.subIt)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
i += 1
|
||||
if i > abortMaterializeAt {
|
||||
it.aborted = true
|
||||
break
|
||||
}
|
||||
val := id
|
||||
if h, ok := id.(hasher); ok {
|
||||
val = h.Hasher()
|
||||
}
|
||||
if _, ok := it.containsMap[val]; !ok {
|
||||
it.containsMap[val] = len(it.values)
|
||||
it.values = append(it.values, nil)
|
||||
}
|
||||
index := it.containsMap[val]
|
||||
tags := make(map[string]graph.Value)
|
||||
it.subIt.TagResults(tags)
|
||||
it.values[index] = append(it.values[index], result{id: id, tags: tags})
|
||||
for it.subIt.NextResult() == true {
|
||||
tags := make(map[string]graph.Value)
|
||||
it.subIt.TagResults(tags)
|
||||
it.values[index] = append(it.values[index], result{id: id, tags: tags})
|
||||
}
|
||||
}
|
||||
if it.aborted {
|
||||
it.values = nil
|
||||
it.containsMap = nil
|
||||
it.subIt.Reset()
|
||||
}
|
||||
glog.Infof("Materialization List %d: %#v", it.values)
|
||||
it.hasRun = true
|
||||
}
|
||||
|
|
@ -36,11 +36,11 @@ func (qs *store) ValueOf(s string) graph.Value {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (qs *store) AddTriple(*quad.Quad) {}
|
||||
func (qs *store) AddTriple(quad.Quad) {}
|
||||
|
||||
func (qs *store) AddTripleSet([]*quad.Quad) {}
|
||||
func (qs *store) AddTripleSet([]quad.Quad) {}
|
||||
|
||||
func (qs *store) Quad(graph.Value) *quad.Quad { return &quad.Quad{} }
|
||||
func (qs *store) Quad(graph.Value) quad.Quad { return quad.Quad{} }
|
||||
|
||||
func (qs *store) TripleIterator(d quad.Direction, i graph.Value) graph.Iterator {
|
||||
return qs.iter
|
||||
|
|
@ -74,4 +74,4 @@ func (qs *store) Close() {}
|
|||
|
||||
func (qs *store) TripleDirection(graph.Value, quad.Direction) graph.Value { return 0 }
|
||||
|
||||
func (qs *store) RemoveTriple(t *quad.Quad) {}
|
||||
func (qs *store) RemoveTriple(t quad.Quad) {}
|
||||
|
|
|
|||
|
|
@ -117,8 +117,8 @@ func (it *AllIterator) Next() (graph.Value, bool) {
|
|||
it.Close()
|
||||
return nil, false
|
||||
}
|
||||
it.result = out
|
||||
return out, true
|
||||
it.result = Token(out)
|
||||
return it.result, true
|
||||
}
|
||||
|
||||
func (it *AllIterator) ResultTree() *graph.ResultTree {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ type Iterator struct {
|
|||
}
|
||||
|
||||
func NewIterator(prefix string, d quad.Direction, value graph.Value, qs *TripleStore) graph.Iterator {
|
||||
vb := value.([]byte)
|
||||
vb := value.(Token)
|
||||
p := make([]byte, 0, 2+qs.hasher.Size())
|
||||
p = append(p, []byte(prefix)...)
|
||||
p = append(p, []byte(vb[1:])...)
|
||||
|
|
@ -107,7 +107,7 @@ func (it *Iterator) TagResults(dst map[string]graph.Value) {
|
|||
}
|
||||
|
||||
func (it *Iterator) Clone() graph.Iterator {
|
||||
out := NewIterator(it.originalPrefix, it.dir, it.checkId, it.qs)
|
||||
out := NewIterator(it.originalPrefix, it.dir, Token(it.checkId), it.qs)
|
||||
out.Tagger().CopyFrom(it)
|
||||
return out
|
||||
}
|
||||
|
|
@ -145,12 +145,12 @@ func (it *Iterator) Next() (graph.Value, bool) {
|
|||
}
|
||||
out := make([]byte, len(it.iter.Key()))
|
||||
copy(out, it.iter.Key())
|
||||
it.result = out
|
||||
it.result = Token(out)
|
||||
ok := it.iter.Next()
|
||||
if !ok {
|
||||
it.Close()
|
||||
}
|
||||
return out, true
|
||||
return Token(out), true
|
||||
}
|
||||
it.Close()
|
||||
it.result = nil
|
||||
|
|
@ -227,7 +227,7 @@ func PositionOf(prefix []byte, d quad.Direction, qs *TripleStore) int {
|
|||
}
|
||||
|
||||
func (it *Iterator) Contains(v graph.Value) bool {
|
||||
val := v.([]byte)
|
||||
val := v.(Token)
|
||||
if val[0] == 'z' {
|
||||
return false
|
||||
}
|
||||
|
|
@ -248,7 +248,7 @@ func (it *Iterator) Contains(v graph.Value) bool {
|
|||
}
|
||||
|
||||
func (it *Iterator) Size() (int64, bool) {
|
||||
return it.qs.SizeOf(it.checkId), true
|
||||
return it.qs.SizeOf(Token(it.checkId)), true
|
||||
}
|
||||
|
||||
func (it *Iterator) DebugString(indent int) string {
|
||||
|
|
@ -260,7 +260,7 @@ func (it *Iterator) DebugString(indent int) string {
|
|||
it.tags.Tags(),
|
||||
it.dir,
|
||||
size,
|
||||
it.qs.NameOf(it.checkId),
|
||||
it.qs.NameOf(Token(it.checkId)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import (
|
|||
"github.com/google/cayley/writer"
|
||||
)
|
||||
|
||||
func makeTripleSet() []*quad.Quad {
|
||||
tripleSet := []*quad.Quad{
|
||||
func makeTripleSet() []quad.Quad {
|
||||
tripleSet := []quad.Quad{
|
||||
{"A", "follows", "B", ""},
|
||||
{"C", "follows", "B", ""},
|
||||
{"C", "follows", "D", ""},
|
||||
|
|
@ -44,7 +44,7 @@ func makeTripleSet() []*quad.Quad {
|
|||
return tripleSet
|
||||
}
|
||||
|
||||
func iteratedTriples(qs graph.TripleStore, it graph.Iterator) []*quad.Quad {
|
||||
func iteratedTriples(qs graph.TripleStore, it graph.Iterator) []quad.Quad {
|
||||
var res ordered
|
||||
for {
|
||||
val, ok := graph.Next(it)
|
||||
|
|
@ -57,7 +57,7 @@ func iteratedTriples(qs graph.TripleStore, it graph.Iterator) []*quad.Quad {
|
|||
return res
|
||||
}
|
||||
|
||||
type ordered []*quad.Quad
|
||||
type ordered []quad.Quad
|
||||
|
||||
func (o ordered) Len() int { return len(o) }
|
||||
func (o ordered) Less(i, j int) bool {
|
||||
|
|
@ -145,7 +145,7 @@ func TestLoadDatabase(t *testing.T) {
|
|||
}
|
||||
|
||||
w, _ := writer.NewSingleReplication(qs, nil)
|
||||
w.AddQuad(&quad.Quad{"Something", "points_to", "Something Else", "context"})
|
||||
qs.AddQuad(quad.Quad{"Something", "points_to", "Something Else", "context"})
|
||||
for _, pq := range []string{"Something", "points_to", "Something Else", "context"} {
|
||||
if got := qs.NameOf(qs.ValueOf(pq)); got != pq {
|
||||
t.Errorf("Failed to roundtrip %q, got:%q expect:%q", pq, got, pq)
|
||||
|
|
@ -179,7 +179,7 @@ func TestLoadDatabase(t *testing.T) {
|
|||
t.Errorf("Unexpected triplestore size, got:%d expect:5", s)
|
||||
}
|
||||
|
||||
w.RemoveQuad(&quad.Quad{"A", "follows", "B", ""})
|
||||
w.RemoveQuad(quad.Quad{"A", "follows", "B", ""})
|
||||
if s := qs.Size(); s != 10 {
|
||||
t.Errorf("Unexpected triplestore size after RemoveTriple, got:%d expect:10", s)
|
||||
}
|
||||
|
|
@ -307,7 +307,7 @@ func TestSetIterator(t *testing.T) {
|
|||
w, _ := writer.NewSingleReplication(qs, nil)
|
||||
w.AddQuadSet(makeTripleSet())
|
||||
|
||||
expect := []*quad.Quad{
|
||||
expect := []quad.Quad{
|
||||
{"C", "follows", "B", ""},
|
||||
{"C", "follows", "D", ""},
|
||||
}
|
||||
|
|
@ -332,7 +332,7 @@ func TestSetIterator(t *testing.T) {
|
|||
// Object iterator.
|
||||
it = qs.TripleIterator(quad.Object, qs.ValueOf("F"))
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "follows", "F", ""},
|
||||
{"E", "follows", "F", ""},
|
||||
}
|
||||
|
|
@ -345,7 +345,7 @@ func TestSetIterator(t *testing.T) {
|
|||
and.AddSubIterator(qs.TripleIterator(quad.Subject, qs.ValueOf("B")))
|
||||
and.AddSubIterator(it)
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "follows", "F", ""},
|
||||
}
|
||||
if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) {
|
||||
|
|
@ -355,7 +355,7 @@ func TestSetIterator(t *testing.T) {
|
|||
// Predicate iterator.
|
||||
it = qs.TripleIterator(quad.Predicate, qs.ValueOf("status"))
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "status", "cool", "status_graph"},
|
||||
{"D", "status", "cool", "status_graph"},
|
||||
{"G", "status", "cool", "status_graph"},
|
||||
|
|
@ -368,7 +368,7 @@ func TestSetIterator(t *testing.T) {
|
|||
// Label iterator.
|
||||
it = qs.TripleIterator(quad.Label, qs.ValueOf("status_graph"))
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "status", "cool", "status_graph"},
|
||||
{"D", "status", "cool", "status_graph"},
|
||||
{"G", "status", "cool", "status_graph"},
|
||||
|
|
@ -384,7 +384,7 @@ func TestSetIterator(t *testing.T) {
|
|||
and.AddSubIterator(qs.TripleIterator(quad.Subject, qs.ValueOf("B")))
|
||||
and.AddSubIterator(it)
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "status", "cool", "status_graph"},
|
||||
}
|
||||
if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) {
|
||||
|
|
@ -397,7 +397,7 @@ func TestSetIterator(t *testing.T) {
|
|||
and.AddSubIterator(it)
|
||||
and.AddSubIterator(qs.TripleIterator(quad.Subject, qs.ValueOf("B")))
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "status", "cool", "status_graph"},
|
||||
}
|
||||
if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@ const (
|
|||
DefaultWriteBufferSize = 20
|
||||
)
|
||||
|
||||
type Token []byte
|
||||
|
||||
func (t Token) Hasher() interface{} {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
type TripleStore struct {
|
||||
dbOpts *opt.Options
|
||||
db *leveldb.DB
|
||||
|
|
@ -134,7 +140,7 @@ func (qa *TripleStore) createDeltaKeyFor(d *graph.Delta) []byte {
|
|||
return key
|
||||
}
|
||||
|
||||
func (qs *TripleStore) createKeyFor(d [4]quad.Direction, triple *quad.Quad) []byte {
|
||||
func (qs *TripleStore) createKeyFor(d [4]quad.Direction, triple quad.Quad) []byte {
|
||||
key := make([]byte, 0, 2+(qs.hasher.Size()*4))
|
||||
// TODO(kortschak) Remove dependence on String() method.
|
||||
key = append(key, []byte{d[0].Prefix(), d[1].Prefix()}...)
|
||||
|
|
@ -153,7 +159,7 @@ func (qs *TripleStore) createValueKeyFor(s string) []byte {
|
|||
}
|
||||
|
||||
type IndexEntry struct {
|
||||
*quad.Quad
|
||||
quad.Quad
|
||||
History []int64
|
||||
}
|
||||
|
||||
|
|
@ -175,7 +181,7 @@ func (qs *TripleStore) ApplyDeltas(deltas []*graph.Delta) error {
|
|||
return err
|
||||
}
|
||||
batch.Put(qs.createDeltaKeyFor(d), bytes)
|
||||
err = qs.buildQuadWrite(batch, &d.Quad, d.ID, d.Action == graph.Add)
|
||||
err = qs.buildQuadWrite(batch, d.Quad, d.ID, d.Action == graph.Add)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -209,7 +215,7 @@ func (qs *TripleStore) ApplyDeltas(deltas []*graph.Delta) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (qs *TripleStore) buildQuadWrite(batch *leveldb.Batch, q *quad.Quad, id int64, isAdd bool) error {
|
||||
func (qs *TripleStore) buildQuadWrite(batch *leveldb.Batch, q quad.Quad, id int64, isAdd bool) error {
|
||||
var entry IndexEntry
|
||||
data, err := qs.db.Get(qs.createKeyFor(spo, q), qs.readopts)
|
||||
if err != nil && err != leveldb.ErrNotFound {
|
||||
|
|
@ -316,23 +322,23 @@ func (qs *TripleStore) Close() {
|
|||
qs.open = false
|
||||
}
|
||||
|
||||
func (qs *TripleStore) Quad(k graph.Value) *quad.Quad {
|
||||
func (qs *TripleStore) Quad(k graph.Value) quad.Quad {
|
||||
var triple quad.Quad
|
||||
b, err := qs.db.Get(k.([]byte), qs.readopts)
|
||||
b, err := qs.db.Get(k.(Token), qs.readopts)
|
||||
if err != nil && err != leveldb.ErrNotFound {
|
||||
glog.Error("Error: couldn't get triple from DB.")
|
||||
return &quad.Quad{}
|
||||
return quad.Quad{}
|
||||
}
|
||||
if err == leveldb.ErrNotFound {
|
||||
// No harm, no foul.
|
||||
return &quad.Quad{}
|
||||
return quad.Quad{}
|
||||
}
|
||||
err = json.Unmarshal(b, &triple)
|
||||
if err != nil {
|
||||
glog.Error("Error: couldn't reconstruct triple.")
|
||||
return &quad.Quad{}
|
||||
return quad.Quad{}
|
||||
}
|
||||
return &triple
|
||||
return triple
|
||||
}
|
||||
|
||||
func (qs *TripleStore) convertStringToByteHash(s string) []byte {
|
||||
|
|
@ -344,7 +350,7 @@ func (qs *TripleStore) convertStringToByteHash(s string) []byte {
|
|||
}
|
||||
|
||||
func (qs *TripleStore) ValueOf(s string) graph.Value {
|
||||
return qs.createValueKeyFor(s)
|
||||
return Token(qs.createValueKeyFor(s))
|
||||
}
|
||||
|
||||
func (qs *TripleStore) valueData(value_key []byte) ValueData {
|
||||
|
|
@ -372,14 +378,14 @@ func (qs *TripleStore) NameOf(k graph.Value) string {
|
|||
glog.V(2).Info("k was nil")
|
||||
return ""
|
||||
}
|
||||
return qs.valueData(k.([]byte)).Name
|
||||
return qs.valueData(k.(Token)).Name
|
||||
}
|
||||
|
||||
func (qs *TripleStore) SizeOf(k graph.Value) int64 {
|
||||
if k == nil {
|
||||
return 0
|
||||
}
|
||||
return int64(qs.valueData(k.([]byte)).Size)
|
||||
return int64(qs.valueData(k.(Token)).Size)
|
||||
}
|
||||
|
||||
func (qs *TripleStore) getInt64ForKey(key string, empty int64) (int64, error) {
|
||||
|
|
@ -453,17 +459,17 @@ func (qs *TripleStore) TriplesAllIterator() graph.Iterator {
|
|||
}
|
||||
|
||||
func (qs *TripleStore) TripleDirection(val graph.Value, d quad.Direction) graph.Value {
|
||||
v := val.([]uint8)
|
||||
v := val.(Token)
|
||||
offset := PositionOf(v[0:2], d, qs)
|
||||
if offset != -1 {
|
||||
return append([]byte("z"), v[offset:offset+qs.hasher.Size()]...)
|
||||
return Token(append([]byte("z"), v[offset:offset+qs.hasher.Size()]...))
|
||||
} else {
|
||||
return qs.Quad(val).Get(d)
|
||||
return Token(qs.Quad(val).Get(d))
|
||||
}
|
||||
}
|
||||
|
||||
func compareBytes(a, b graph.Value) bool {
|
||||
return bytes.Equal(a.([]uint8), b.([]uint8))
|
||||
return bytes.Equal(a.(Token), b.(Token))
|
||||
}
|
||||
|
||||
func (qs *TripleStore) FixedIterator() graph.FixedIterator {
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ func (ts *TripleStore) ApplyDeltas(deltas []*graph.Delta) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ts *TripleStore) quadExists(t *quad.Quad) (bool, int64) {
|
||||
func (ts *TripleStore) quadExists(t quad.Quad) (bool, int64) {
|
||||
smallest := -1
|
||||
var smallest_tree *llrb.LLRB
|
||||
for d := quad.Subject; d <= quad.Label; d++ {
|
||||
|
|
@ -151,7 +151,7 @@ func (ts *TripleStore) quadExists(t *quad.Quad) (bool, int64) {
|
|||
break
|
||||
}
|
||||
ival := val.(int64)
|
||||
if t.Equals(&ts.log[ival].Quad) {
|
||||
if t == ts.log[ival].Quad {
|
||||
return true, ival
|
||||
}
|
||||
}
|
||||
|
|
@ -159,7 +159,7 @@ func (ts *TripleStore) quadExists(t *quad.Quad) (bool, int64) {
|
|||
}
|
||||
|
||||
func (ts *TripleStore) AddDelta(d *graph.Delta) error {
|
||||
if exists, _ := ts.quadExists(&d.Quad); exists {
|
||||
if exists, _ := ts.quadExists(d.Quad); exists {
|
||||
return graph.ErrQuadExists
|
||||
}
|
||||
var quadID int64
|
||||
|
|
@ -197,7 +197,7 @@ func (ts *TripleStore) RemoveDelta(d *graph.Delta) error {
|
|||
var prevQuadID int64
|
||||
var exists bool
|
||||
prevQuadID = 0
|
||||
if exists, prevQuadID = ts.quadExists(&d.Quad); !exists {
|
||||
if exists, prevQuadID = ts.quadExists(d.Quad); !exists {
|
||||
return graph.ErrQuadNotExist
|
||||
}
|
||||
|
||||
|
|
@ -210,8 +210,8 @@ func (ts *TripleStore) RemoveDelta(d *graph.Delta) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ts *TripleStore) Quad(index graph.Value) *quad.Quad {
|
||||
return &ts.log[index.(int64)].Quad
|
||||
func (ts *TripleStore) Quad(index graph.Value) quad.Quad {
|
||||
return ts.log[index.(int64)].Quad
|
||||
}
|
||||
|
||||
func (ts *TripleStore) TripleIterator(d quad.Direction, value graph.Value) graph.Iterator {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import (
|
|||
// \-->|#D#|------------->+---+
|
||||
// +---+
|
||||
//
|
||||
var simpleGraph = []*quad.Quad{
|
||||
var simpleGraph = []quad.Quad{
|
||||
{"A", "follows", "B", ""},
|
||||
{"C", "follows", "B", ""},
|
||||
{"C", "follows", "D", ""},
|
||||
|
|
@ -52,7 +52,7 @@ var simpleGraph = []*quad.Quad{
|
|||
{"G", "status", "cool", "status_graph"},
|
||||
}
|
||||
|
||||
func makeTestStore(data []*quad.Quad) (*TripleStore, graph.QuadWriter, []pair) {
|
||||
func makeTestStore(data []quad.Quad) (*TripleStore, graph.QuadWriter, []pair) {
|
||||
seen := make(map[string]struct{})
|
||||
ts := newTripleStore()
|
||||
var (
|
||||
|
|
@ -178,7 +178,7 @@ func TestLinksToOptimization(t *testing.T) {
|
|||
func TestRemoveTriple(t *testing.T) {
|
||||
ts, w, _ := makeTestStore(simpleGraph)
|
||||
|
||||
w.RemoveQuad(&quad.Quad{"E", "follows", "F", ""})
|
||||
w.RemoveQuad(quad.Quad{"E", "follows", "F", ""})
|
||||
|
||||
fixed := ts.FixedIterator()
|
||||
fixed.Add(ts.ValueOf("E"))
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ func newTripleStore(addr string, options graph.Options) (graph.TripleStore, erro
|
|||
return &qs, nil
|
||||
}
|
||||
|
||||
func (qs *TripleStore) getIdForTriple(t *quad.Quad) string {
|
||||
func (qs *TripleStore) getIdForTriple(t quad.Quad) string {
|
||||
id := qs.ConvertStringToByteHash(t.Subject)
|
||||
id += qs.ConvertStringToByteHash(t.Predicate)
|
||||
id += qs.ConvertStringToByteHash(t.Object)
|
||||
|
|
@ -150,7 +150,7 @@ func (qs *TripleStore) updateNodeBy(node_name string, inc int) {
|
|||
}
|
||||
}
|
||||
|
||||
func (qs *TripleStore) writeTriple(t *quad.Quad) bool {
|
||||
func (qs *TripleStore) writeTriple(t quad.Quad) bool {
|
||||
tripledoc := bson.M{
|
||||
"_id": qs.getIdForTriple(t),
|
||||
"Subject": t.Subject,
|
||||
|
|
@ -170,7 +170,7 @@ func (qs *TripleStore) writeTriple(t *quad.Quad) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (qs *TripleStore) AddTriple(t *quad.Quad) {
|
||||
func (qs *TripleStore) AddTriple(t quad.Quad) {
|
||||
_ = qs.writeTriple(t)
|
||||
qs.updateNodeBy(t.Subject, 1)
|
||||
qs.updateNodeBy(t.Predicate, 1)
|
||||
|
|
@ -180,7 +180,7 @@ func (qs *TripleStore) AddTriple(t *quad.Quad) {
|
|||
}
|
||||
}
|
||||
|
||||
func (qs *TripleStore) AddTripleSet(in []*quad.Quad) {
|
||||
func (qs *TripleStore) AddTripleSet(in []quad.Quad) {
|
||||
qs.session.SetSafe(nil)
|
||||
ids := make(map[string]int)
|
||||
for _, t := range in {
|
||||
|
|
@ -200,7 +200,7 @@ func (qs *TripleStore) AddTripleSet(in []*quad.Quad) {
|
|||
qs.session.SetSafe(&mgo.Safe{})
|
||||
}
|
||||
|
||||
func (qs *TripleStore) RemoveTriple(t *quad.Quad) {
|
||||
func (qs *TripleStore) RemoveTriple(t quad.Quad) {
|
||||
err := qs.db.C("triples").RemoveId(qs.getIdForTriple(t))
|
||||
if err == mgo.ErrNotFound {
|
||||
return
|
||||
|
|
@ -216,13 +216,13 @@ func (qs *TripleStore) RemoveTriple(t *quad.Quad) {
|
|||
}
|
||||
}
|
||||
|
||||
func (qs *TripleStore) Quad(val graph.Value) *quad.Quad {
|
||||
func (qs *TripleStore) Quad(val graph.Value) quad.Quad {
|
||||
var bsonDoc bson.M
|
||||
err := qs.db.C("triples").FindId(val.(string)).One(&bsonDoc)
|
||||
if err != nil {
|
||||
glog.Errorf("Error: Couldn't retrieve triple %s %v", val, err)
|
||||
}
|
||||
return &quad.Quad{
|
||||
return quad.Quad{
|
||||
bsonDoc["Subject"].(string),
|
||||
bsonDoc["Predicate"].(string),
|
||||
bsonDoc["Object"].(string),
|
||||
|
|
|
|||
|
|
@ -28,14 +28,17 @@ import (
|
|||
"github.com/google/cayley/quad"
|
||||
)
|
||||
|
||||
// Defines an opaque "triple store value" type. However the backend wishes to
|
||||
// implement it, a Value is merely a token to a triple or a node that the backing
|
||||
// store itself understands, and the base iterators pass around.
|
||||
// Value defines an opaque "triple store value" type. However the backend wishes
|
||||
// to implement it, a Value is merely a token to a triple or a node that the
|
||||
// backing store itself understands, and the base iterators pass around.
|
||||
//
|
||||
// For example, in a very traditional, graphd-style graph, these are int64s
|
||||
// (guids of the primitives). In a very direct sort of graph, these could be
|
||||
// pointers to structs, or merely triples, or whatever works best for the
|
||||
// backing store.
|
||||
//
|
||||
// These must be comparable, or implement a `Hasher() interface{}` function
|
||||
// so that they may be stored in maps.
|
||||
type Value interface{}
|
||||
|
||||
type TripleStore interface {
|
||||
|
|
@ -44,7 +47,7 @@ type TripleStore interface {
|
|||
ApplyDeltas([]*Delta) error
|
||||
|
||||
// Given an opaque token, returns the triple for that token from the store.
|
||||
Quad(Value) *quad.Quad
|
||||
Quad(Value) quad.Quad
|
||||
|
||||
// Given a direction and a token, creates an iterator of links which have
|
||||
// that node token in that directional field.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue