merge to master

This commit is contained in:
Barak Michener 2014-08-06 16:21:57 -04:00
commit c64acabee0
30 changed files with 645 additions and 334 deletions

View file

@ -44,7 +44,7 @@ If you prefer to build from source, see the documentation on the wiki at [How to
`cd` to the directory and give it a quick test with: `cd` to the directory and give it a quick test with:
``` ```
./cayley repl --dbpath=testdata.nt ./cayley repl --dbpath=testdata.nq
``` ```
You should see a `cayley>` REPL prompt. Go ahead and give it a try: You should see a `cayley>` REPL prompt. Go ahead and give it a try:

View file

@ -348,7 +348,7 @@ func TestQueries(t *testing.T) {
// TODO(kortschak) Be more rigorous in this result validation. // TODO(kortschak) Be more rigorous in this result validation.
if len(got) != len(test.expect) { if len(got) != len(test.expect) {
t.Errorf("Unexpected number of results, got:%d expect:%d.", len(got), len(test.expect)) t.Errorf("Unexpected number of results, got:%d expect:%d on %s.", len(got), len(test.expect), test.message)
} }
} }
} }
@ -358,17 +358,18 @@ func runBench(n int, b *testing.B) {
b.Skip() b.Skip()
} }
prepare(b) prepare(b)
ses := gremlin.NewSession(ts, cfg.Timeout, true) b.StopTimer()
_, err := ses.InputParses(benchmarkQueries[n].query)
if err != nil {
b.Fatalf("Failed to parse benchmark gremlin %s: %v", benchmarkQueries[n].message, err)
}
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
c := make(chan interface{}, 5) c := make(chan interface{}, 5)
ses := gremlin.NewSession(ts, cfg.Timeout, true)
// Do the parsing we know works.
ses.InputParses(benchmarkQueries[n].query)
b.StartTimer()
go ses.ExecInput(benchmarkQueries[n].query, c, 100) go ses.ExecInput(benchmarkQueries[n].query, c, 100)
for _ = range c { for _ = range c {
} }
b.StopTimer()
} }
} }
@ -392,18 +393,18 @@ func BenchmarkNetAndSpeed(b *testing.B) {
runBench(4, b) runBench(4, b)
} }
func BenchmarkKeannuAndNet(b *testing.B) { func BenchmarkKeanuAndNet(b *testing.B) {
runBench(5, b) runBench(5, b)
} }
func BenchmarkKeannuAndSpeed(b *testing.B) { func BenchmarkKeanuAndSpeed(b *testing.B) {
runBench(6, b) runBench(6, b)
} }
func BenchmarkKeannuOther(b *testing.B) { func BenchmarkKeanuOther(b *testing.B) {
runBench(7, b) runBench(7, b)
} }
func BenchmarkKeannuBullockOther(b *testing.B) { func BenchmarkKeanuBullockOther(b *testing.B) {
runBench(8, b) runBench(8, b)
} }

View file

@ -57,7 +57,7 @@ func Load(ts graph.TripleStore, cfg *config.Config, path string) error {
return err return err
} }
block := make([]*quad.Quad, 0, cfg.LoadSize) block := make([]quad.Quad, 0, cfg.LoadSize)
for { for {
t, err := dec.Unmarshal() t, err := dec.Unmarshal()
if err != nil { if err != nil {

View file

@ -114,7 +114,7 @@ func Repl(ts graph.TripleStore, queryLanguage string, cfg *config.Config) error
if bytes.HasPrefix(line, []byte(":a")) { if bytes.HasPrefix(line, []byte(":a")) {
var tripleStmt = line[3:] var tripleStmt = line[3:]
triple, err := cquads.Parse(string(tripleStmt)) triple, err := cquads.Parse(string(tripleStmt))
if triple == nil { if !triple.IsValid() {
if err != nil { if err != nil {
fmt.Printf("not a valid triple: %v\n", err) fmt.Printf("not a valid triple: %v\n", err)
} }
@ -128,7 +128,7 @@ func Repl(ts graph.TripleStore, queryLanguage string, cfg *config.Config) error
if bytes.HasPrefix(line, []byte(":d")) { if bytes.HasPrefix(line, []byte(":d")) {
var tripleStmt = line[3:] var tripleStmt = line[3:]
triple, err := cquads.Parse(string(tripleStmt)) triple, err := cquads.Parse(string(tripleStmt))
if triple == nil { if !triple.IsValid() {
if err != nil { if err != nil {
fmt.Printf("not a valid triple: %v\n", err) fmt.Printf("not a valid triple: %v\n", err)
} }

View file

@ -153,6 +153,22 @@ func Next(it Iterator) (Value, bool) {
return nil, false 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. // FixedIterator wraps iterators that are modifiable by addition of fixed value sets.
type FixedIterator interface { type FixedIterator interface {
Iterator Iterator
@ -180,6 +196,7 @@ const (
Fixed Fixed
Not Not
Optional Optional
Materialize
) )
var ( var (
@ -200,6 +217,7 @@ var (
"fixed", "fixed",
"not", "not",
"optional", "optional",
"materialize",
} }
) )

View file

@ -70,6 +70,8 @@ func (it *And) Optimize() (graph.Iterator, bool) {
// now a permutation of itself, but the contents are unchanged. // now a permutation of itself, but the contents are unchanged.
its = optimizeOrder(its) its = optimizeOrder(its)
its = materializeIts(its)
// Okay! At this point we have an optimized order. // 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 // 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 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 // and.Stats() lives here in and-iterator-optimize.go because it may
// in the future return different statistics based on how it is optimized. // in the future return different statistics based on how it is optimized.
// For now, however, it's pretty static. // For now, however, it's pretty static.

View 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
}

View file

@ -36,11 +36,11 @@ func (qs *store) ValueOf(s string) graph.Value {
return nil 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 { func (qs *store) TripleIterator(d quad.Direction, i graph.Value) graph.Iterator {
return qs.iter 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) TripleDirection(graph.Value, quad.Direction) graph.Value { return 0 }
func (qs *store) RemoveTriple(t *quad.Quad) {} func (qs *store) RemoveTriple(t quad.Quad) {}

View file

@ -117,8 +117,8 @@ func (it *AllIterator) Next() (graph.Value, bool) {
it.Close() it.Close()
return nil, false return nil, false
} }
it.result = out it.result = Token(out)
return out, true return it.result, true
} }
func (it *AllIterator) ResultTree() *graph.ResultTree { func (it *AllIterator) ResultTree() *graph.ResultTree {

View file

@ -44,7 +44,7 @@ type Iterator struct {
} }
func NewIterator(prefix string, d quad.Direction, value graph.Value, qs *TripleStore) graph.Iterator { 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 := make([]byte, 0, 2+qs.hasher.Size())
p = append(p, []byte(prefix)...) p = append(p, []byte(prefix)...)
p = append(p, []byte(vb[1:])...) 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 { 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) out.Tagger().CopyFrom(it)
return out return out
} }
@ -145,12 +145,12 @@ func (it *Iterator) Next() (graph.Value, bool) {
} }
out := make([]byte, len(it.iter.Key())) out := make([]byte, len(it.iter.Key()))
copy(out, it.iter.Key()) copy(out, it.iter.Key())
it.result = out it.result = Token(out)
ok := it.iter.Next() ok := it.iter.Next()
if !ok { if !ok {
it.Close() it.Close()
} }
return out, true return Token(out), true
} }
it.Close() it.Close()
it.result = nil 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 { func (it *Iterator) Contains(v graph.Value) bool {
val := v.([]byte) val := v.(Token)
if val[0] == 'z' { if val[0] == 'z' {
return false return false
} }
@ -248,7 +248,7 @@ func (it *Iterator) Contains(v graph.Value) bool {
} }
func (it *Iterator) Size() (int64, 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 { func (it *Iterator) DebugString(indent int) string {
@ -260,7 +260,7 @@ func (it *Iterator) DebugString(indent int) string {
it.tags.Tags(), it.tags.Tags(),
it.dir, it.dir,
size, size,
it.qs.NameOf(it.checkId), it.qs.NameOf(Token(it.checkId)),
) )
} }

View file

@ -27,8 +27,8 @@ import (
"github.com/google/cayley/writer" "github.com/google/cayley/writer"
) )
func makeTripleSet() []*quad.Quad { func makeTripleSet() []quad.Quad {
tripleSet := []*quad.Quad{ tripleSet := []quad.Quad{
{"A", "follows", "B", ""}, {"A", "follows", "B", ""},
{"C", "follows", "B", ""}, {"C", "follows", "B", ""},
{"C", "follows", "D", ""}, {"C", "follows", "D", ""},
@ -44,7 +44,7 @@ func makeTripleSet() []*quad.Quad {
return tripleSet 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 var res ordered
for { for {
val, ok := graph.Next(it) val, ok := graph.Next(it)
@ -57,7 +57,7 @@ func iteratedTriples(qs graph.TripleStore, it graph.Iterator) []*quad.Quad {
return res return res
} }
type ordered []*quad.Quad type ordered []quad.Quad
func (o ordered) Len() int { return len(o) } func (o ordered) Len() int { return len(o) }
func (o ordered) Less(i, j int) bool { func (o ordered) Less(i, j int) bool {
@ -145,7 +145,7 @@ func TestLoadDatabase(t *testing.T) {
} }
w, _ := writer.NewSingleReplication(qs, nil) 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"} { for _, pq := range []string{"Something", "points_to", "Something Else", "context"} {
if got := qs.NameOf(qs.ValueOf(pq)); got != pq { if got := qs.NameOf(qs.ValueOf(pq)); got != pq {
t.Errorf("Failed to roundtrip %q, got:%q expect:%q", 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) 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 { if s := qs.Size(); s != 10 {
t.Errorf("Unexpected triplestore size after RemoveTriple, got:%d expect:10", s) 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, _ := writer.NewSingleReplication(qs, nil)
w.AddQuadSet(makeTripleSet()) w.AddQuadSet(makeTripleSet())
expect := []*quad.Quad{ expect := []quad.Quad{
{"C", "follows", "B", ""}, {"C", "follows", "B", ""},
{"C", "follows", "D", ""}, {"C", "follows", "D", ""},
} }
@ -332,7 +332,7 @@ func TestSetIterator(t *testing.T) {
// Object iterator. // Object iterator.
it = qs.TripleIterator(quad.Object, qs.ValueOf("F")) it = qs.TripleIterator(quad.Object, qs.ValueOf("F"))
expect = []*quad.Quad{ expect = []quad.Quad{
{"B", "follows", "F", ""}, {"B", "follows", "F", ""},
{"E", "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(qs.TripleIterator(quad.Subject, qs.ValueOf("B")))
and.AddSubIterator(it) and.AddSubIterator(it)
expect = []*quad.Quad{ expect = []quad.Quad{
{"B", "follows", "F", ""}, {"B", "follows", "F", ""},
} }
if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) { if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) {
@ -355,7 +355,7 @@ func TestSetIterator(t *testing.T) {
// Predicate iterator. // Predicate iterator.
it = qs.TripleIterator(quad.Predicate, qs.ValueOf("status")) it = qs.TripleIterator(quad.Predicate, qs.ValueOf("status"))
expect = []*quad.Quad{ expect = []quad.Quad{
{"B", "status", "cool", "status_graph"}, {"B", "status", "cool", "status_graph"},
{"D", "status", "cool", "status_graph"}, {"D", "status", "cool", "status_graph"},
{"G", "status", "cool", "status_graph"}, {"G", "status", "cool", "status_graph"},
@ -368,7 +368,7 @@ func TestSetIterator(t *testing.T) {
// Label iterator. // Label iterator.
it = qs.TripleIterator(quad.Label, qs.ValueOf("status_graph")) it = qs.TripleIterator(quad.Label, qs.ValueOf("status_graph"))
expect = []*quad.Quad{ expect = []quad.Quad{
{"B", "status", "cool", "status_graph"}, {"B", "status", "cool", "status_graph"},
{"D", "status", "cool", "status_graph"}, {"D", "status", "cool", "status_graph"},
{"G", "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(qs.TripleIterator(quad.Subject, qs.ValueOf("B")))
and.AddSubIterator(it) and.AddSubIterator(it)
expect = []*quad.Quad{ expect = []quad.Quad{
{"B", "status", "cool", "status_graph"}, {"B", "status", "cool", "status_graph"},
} }
if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) { if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) {
@ -397,7 +397,7 @@ func TestSetIterator(t *testing.T) {
and.AddSubIterator(it) and.AddSubIterator(it)
and.AddSubIterator(qs.TripleIterator(quad.Subject, qs.ValueOf("B"))) and.AddSubIterator(qs.TripleIterator(quad.Subject, qs.ValueOf("B")))
expect = []*quad.Quad{ expect = []quad.Quad{
{"B", "status", "cool", "status_graph"}, {"B", "status", "cool", "status_graph"},
} }
if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) { if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) {

View file

@ -43,6 +43,12 @@ const (
DefaultWriteBufferSize = 20 DefaultWriteBufferSize = 20
) )
type Token []byte
func (t Token) Hasher() interface{} {
return string(t)
}
type TripleStore struct { type TripleStore struct {
dbOpts *opt.Options dbOpts *opt.Options
db *leveldb.DB db *leveldb.DB
@ -134,7 +140,7 @@ func (qa *TripleStore) createDeltaKeyFor(d *graph.Delta) []byte {
return key 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)) key := make([]byte, 0, 2+(qs.hasher.Size()*4))
// TODO(kortschak) Remove dependence on String() method. // TODO(kortschak) Remove dependence on String() method.
key = append(key, []byte{d[0].Prefix(), d[1].Prefix()}...) key = append(key, []byte{d[0].Prefix(), d[1].Prefix()}...)
@ -153,7 +159,7 @@ func (qs *TripleStore) createValueKeyFor(s string) []byte {
} }
type IndexEntry struct { type IndexEntry struct {
*quad.Quad quad.Quad
History []int64 History []int64
} }
@ -175,7 +181,7 @@ func (qs *TripleStore) ApplyDeltas(deltas []*graph.Delta) error {
return err return err
} }
batch.Put(qs.createDeltaKeyFor(d), bytes) 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 { if err != nil {
return err return err
} }
@ -209,7 +215,7 @@ func (qs *TripleStore) ApplyDeltas(deltas []*graph.Delta) error {
return nil 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 var entry IndexEntry
data, err := qs.db.Get(qs.createKeyFor(spo, q), qs.readopts) data, err := qs.db.Get(qs.createKeyFor(spo, q), qs.readopts)
if err != nil && err != leveldb.ErrNotFound { if err != nil && err != leveldb.ErrNotFound {
@ -316,23 +322,23 @@ func (qs *TripleStore) Close() {
qs.open = false 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 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 { if err != nil && err != leveldb.ErrNotFound {
glog.Error("Error: couldn't get triple from DB.") glog.Error("Error: couldn't get triple from DB.")
return &quad.Quad{} return quad.Quad{}
} }
if err == leveldb.ErrNotFound { if err == leveldb.ErrNotFound {
// No harm, no foul. // No harm, no foul.
return &quad.Quad{} return quad.Quad{}
} }
err = json.Unmarshal(b, &triple) err = json.Unmarshal(b, &triple)
if err != nil { if err != nil {
glog.Error("Error: couldn't reconstruct triple.") glog.Error("Error: couldn't reconstruct triple.")
return &quad.Quad{} return quad.Quad{}
} }
return &triple return triple
} }
func (qs *TripleStore) convertStringToByteHash(s string) []byte { 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 { 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 { 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") glog.V(2).Info("k was nil")
return "" return ""
} }
return qs.valueData(k.([]byte)).Name return qs.valueData(k.(Token)).Name
} }
func (qs *TripleStore) SizeOf(k graph.Value) int64 { func (qs *TripleStore) SizeOf(k graph.Value) int64 {
if k == nil { if k == nil {
return 0 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) { 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 { 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) offset := PositionOf(v[0:2], d, qs)
if offset != -1 { 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 { } else {
return qs.Quad(val).Get(d) return Token(qs.Quad(val).Get(d))
} }
} }
func compareBytes(a, b graph.Value) bool { 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 { func (qs *TripleStore) FixedIterator() graph.FixedIterator {

View file

@ -120,7 +120,7 @@ func (ts *TripleStore) ApplyDeltas(deltas []*graph.Delta) error {
return nil return nil
} }
func (ts *TripleStore) quadExists(t *quad.Quad) (bool, int64) { func (ts *TripleStore) quadExists(t quad.Quad) (bool, int64) {
smallest := -1 smallest := -1
var smallest_tree *llrb.LLRB var smallest_tree *llrb.LLRB
for d := quad.Subject; d <= quad.Label; d++ { for d := quad.Subject; d <= quad.Label; d++ {
@ -151,7 +151,7 @@ func (ts *TripleStore) quadExists(t *quad.Quad) (bool, int64) {
break break
} }
ival := val.(int64) ival := val.(int64)
if t.Equals(&ts.log[ival].Quad) { if t == ts.log[ival].Quad {
return true, ival return true, ival
} }
} }
@ -159,7 +159,7 @@ func (ts *TripleStore) quadExists(t *quad.Quad) (bool, int64) {
} }
func (ts *TripleStore) AddDelta(d *graph.Delta) error { 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 return graph.ErrQuadExists
} }
var quadID int64 var quadID int64
@ -197,7 +197,7 @@ func (ts *TripleStore) RemoveDelta(d *graph.Delta) error {
var prevQuadID int64 var prevQuadID int64
var exists bool var exists bool
prevQuadID = 0 prevQuadID = 0
if exists, prevQuadID = ts.quadExists(&d.Quad); !exists { if exists, prevQuadID = ts.quadExists(d.Quad); !exists {
return graph.ErrQuadNotExist return graph.ErrQuadNotExist
} }
@ -210,8 +210,8 @@ func (ts *TripleStore) RemoveDelta(d *graph.Delta) error {
return nil return nil
} }
func (ts *TripleStore) Quad(index graph.Value) *quad.Quad { func (ts *TripleStore) Quad(index graph.Value) quad.Quad {
return &ts.log[index.(int64)].Quad return ts.log[index.(int64)].Quad
} }
func (ts *TripleStore) TripleIterator(d quad.Direction, value graph.Value) graph.Iterator { func (ts *TripleStore) TripleIterator(d quad.Direction, value graph.Value) graph.Iterator {

View file

@ -38,7 +38,7 @@ import (
// \-->|#D#|------------->+---+ // \-->|#D#|------------->+---+
// +---+ // +---+
// //
var simpleGraph = []*quad.Quad{ var simpleGraph = []quad.Quad{
{"A", "follows", "B", ""}, {"A", "follows", "B", ""},
{"C", "follows", "B", ""}, {"C", "follows", "B", ""},
{"C", "follows", "D", ""}, {"C", "follows", "D", ""},
@ -52,7 +52,7 @@ var simpleGraph = []*quad.Quad{
{"G", "status", "cool", "status_graph"}, {"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{}) seen := make(map[string]struct{})
ts := newTripleStore() ts := newTripleStore()
var ( var (
@ -178,7 +178,7 @@ func TestLinksToOptimization(t *testing.T) {
func TestRemoveTriple(t *testing.T) { func TestRemoveTriple(t *testing.T) {
ts, w, _ := makeTestStore(simpleGraph) ts, w, _ := makeTestStore(simpleGraph)
w.RemoveQuad(&quad.Quad{"E", "follows", "F", ""}) w.RemoveQuad(quad.Quad{"E", "follows", "F", ""})
fixed := ts.FixedIterator() fixed := ts.FixedIterator()
fixed.Add(ts.ValueOf("E")) fixed.Add(ts.ValueOf("E"))

View file

@ -91,7 +91,7 @@ func newTripleStore(addr string, options graph.Options) (graph.TripleStore, erro
return &qs, nil 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.Subject)
id += qs.ConvertStringToByteHash(t.Predicate) id += qs.ConvertStringToByteHash(t.Predicate)
id += qs.ConvertStringToByteHash(t.Object) 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{ tripledoc := bson.M{
"_id": qs.getIdForTriple(t), "_id": qs.getIdForTriple(t),
"Subject": t.Subject, "Subject": t.Subject,
@ -170,7 +170,7 @@ func (qs *TripleStore) writeTriple(t *quad.Quad) bool {
return true return true
} }
func (qs *TripleStore) AddTriple(t *quad.Quad) { func (qs *TripleStore) AddTriple(t quad.Quad) {
_ = qs.writeTriple(t) _ = qs.writeTriple(t)
qs.updateNodeBy(t.Subject, 1) qs.updateNodeBy(t.Subject, 1)
qs.updateNodeBy(t.Predicate, 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) qs.session.SetSafe(nil)
ids := make(map[string]int) ids := make(map[string]int)
for _, t := range in { for _, t := range in {
@ -200,7 +200,7 @@ func (qs *TripleStore) AddTripleSet(in []*quad.Quad) {
qs.session.SetSafe(&mgo.Safe{}) 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)) err := qs.db.C("triples").RemoveId(qs.getIdForTriple(t))
if err == mgo.ErrNotFound { if err == mgo.ErrNotFound {
return 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 var bsonDoc bson.M
err := qs.db.C("triples").FindId(val.(string)).One(&bsonDoc) err := qs.db.C("triples").FindId(val.(string)).One(&bsonDoc)
if err != nil { if err != nil {
glog.Errorf("Error: Couldn't retrieve triple %s %v", val, err) glog.Errorf("Error: Couldn't retrieve triple %s %v", val, err)
} }
return &quad.Quad{ return quad.Quad{
bsonDoc["Subject"].(string), bsonDoc["Subject"].(string),
bsonDoc["Predicate"].(string), bsonDoc["Predicate"].(string),
bsonDoc["Object"].(string), bsonDoc["Object"].(string),

View file

@ -28,14 +28,17 @@ import (
"github.com/google/cayley/quad" "github.com/google/cayley/quad"
) )
// Defines an opaque "triple store value" type. However the backend wishes to // Value defines an opaque "triple store value" type. However the backend wishes
// implement it, a Value is merely a token to a triple or a node that the backing // to implement it, a Value is merely a token to a triple or a node that the
// store itself understands, and the base iterators pass around. // backing store itself understands, and the base iterators pass around.
// //
// For example, in a very traditional, graphd-style graph, these are int64s // 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 // (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 // pointers to structs, or merely triples, or whatever works best for the
// backing store. // backing store.
//
// These must be comparable, or implement a `Hasher() interface{}` function
// so that they may be stored in maps.
type Value interface{} type Value interface{}
type TripleStore interface { type TripleStore interface {
@ -44,7 +47,7 @@ type TripleStore interface {
ApplyDeltas([]*Delta) error ApplyDeltas([]*Delta) error
// Given an opaque token, returns the triple for that token from the store. // 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 // Given a direction and a token, creates an iterator of links which have
// that node token in that directional field. // that node token in that directional field.

View file

@ -25,7 +25,7 @@ import (
var parseTests = []struct { var parseTests = []struct {
message string message string
input string input string
expect []*quad.Quad expect []quad.Quad
err error err error
}{ }{
{ {
@ -34,7 +34,7 @@ var parseTests = []struct {
{"subject": "foo", "predicate": "bar", "object": "baz"}, {"subject": "foo", "predicate": "bar", "object": "baz"},
{"subject": "foo", "predicate": "bar", "object": "baz", "label": "graph"} {"subject": "foo", "predicate": "bar", "object": "baz", "label": "graph"}
]`, ]`,
expect: []*quad.Quad{ expect: []quad.Quad{
{"foo", "bar", "baz", ""}, {"foo", "bar", "baz", ""},
{"foo", "bar", "baz", "graph"}, {"foo", "bar", "baz", "graph"},
}, },
@ -45,7 +45,7 @@ var parseTests = []struct {
input: `[ input: `[
{"subject": "foo", "predicate": "bar", "object": "foo", "something_else": "extra data"} {"subject": "foo", "predicate": "bar", "object": "foo", "something_else": "extra data"}
]`, ]`,
expect: []*quad.Quad{ expect: []quad.Quad{
{"foo", "bar", "foo", ""}, {"foo", "bar", "foo", ""},
}, },
err: nil, err: nil,
@ -56,7 +56,7 @@ var parseTests = []struct {
{"subject": "foo", "predicate": "bar"} {"subject": "foo", "predicate": "bar"}
]`, ]`,
expect: nil, expect: nil,
err: fmt.Errorf("Invalid triple at index %d. %v", 0, &quad.Quad{"foo", "bar", "", ""}), err: fmt.Errorf("Invalid triple at index %d. %v", 0, quad.Quad{"foo", "bar", "", ""}),
}, },
} }

View file

@ -29,8 +29,8 @@ import (
"github.com/google/cayley/quad/nquads" "github.com/google/cayley/quad/nquads"
) )
func ParseJsonToTripleList(jsonBody []byte) ([]*quad.Quad, error) { func ParseJsonToTripleList(jsonBody []byte) ([]quad.Quad, error) {
var tripleList []*quad.Quad var tripleList []quad.Quad
err := json.Unmarshal(jsonBody, &tripleList) err := json.Unmarshal(jsonBody, &tripleList)
if err != nil { if err != nil {
return nil, err return nil, err
@ -83,7 +83,7 @@ func (api *Api) ServeV1WriteNQuad(w http.ResponseWriter, r *http.Request, params
var ( var (
n int n int
block = make([]*quad.Quad, 0, blockSize) block = make([]quad.Quad, 0, blockSize)
) )
for { for {
t, err := dec.Unmarshal() t, err := dec.Unmarshal()

View file

@ -31,14 +31,6 @@ import (
"github.com/google/cayley/quad" "github.com/google/cayley/quad"
) )
// Parse returns a valid quad.Quad or a non-nil error. Parse does
// handle comments except where the comment placement does not prevent
// a complete valid quad.Quad from being defined.
func Parse(str string) (*quad.Quad, error) {
q, err := parse([]rune(str))
return &q, err
}
// Decoder implements simplified N-Quad document parsing. // Decoder implements simplified N-Quad document parsing.
type Decoder struct { type Decoder struct {
r *bufio.Reader r *bufio.Reader
@ -52,14 +44,14 @@ func NewDecoder(r io.Reader) *Decoder {
} }
// Unmarshal returns the next valid N-Quad as a quad.Quad, or an error. // Unmarshal returns the next valid N-Quad as a quad.Quad, or an error.
func (dec *Decoder) Unmarshal() (*quad.Quad, error) { func (dec *Decoder) Unmarshal() (quad.Quad, error) {
dec.line = dec.line[:0] dec.line = dec.line[:0]
var line []byte var line []byte
for { for {
for { for {
l, pre, err := dec.r.ReadLine() l, pre, err := dec.r.ReadLine()
if err != nil { if err != nil {
return nil, err return quad.Quad{}, err
} }
dec.line = append(dec.line, l...) dec.line = append(dec.line, l...)
if !pre { if !pre {
@ -73,9 +65,9 @@ func (dec *Decoder) Unmarshal() (*quad.Quad, error) {
} }
triple, err := Parse(string(line)) triple, err := Parse(string(line))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse %q: %v", dec.line, err) return quad.Quad{}, fmt.Errorf("failed to parse %q: %v", dec.line, err)
} }
if triple == nil { if !triple.IsValid() {
return dec.Unmarshal() return dec.Unmarshal()
} }
return triple, nil return triple, nil

View file

@ -31,7 +31,7 @@ import (
var testNTriples = []struct { var testNTriples = []struct {
message string message string
input string input string
expect *quad.Quad expect quad.Quad
err error err error
}{ }{
// Tests from original nquads. // Tests from original nquads.
@ -40,7 +40,7 @@ var testNTriples = []struct {
{ {
message: "parse simple triples", message: "parse simple triples",
input: "this is valid .", input: "this is valid .",
expect: &quad.Quad{ expect: quad.Quad{
Subject: "this", Subject: "this",
Predicate: "is", Predicate: "is",
Object: "valid", Object: "valid",
@ -50,7 +50,7 @@ var testNTriples = []struct {
{ {
message: "parse quoted triples", message: "parse quoted triples",
input: `this is "valid too" .`, input: `this is "valid too" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "this", Subject: "this",
Predicate: "is", Predicate: "is",
Object: "valid too", Object: "valid too",
@ -60,7 +60,7 @@ var testNTriples = []struct {
{ {
message: "parse escaped quoted triples", message: "parse escaped quoted triples",
input: `he said "\"That's all folks\"" .`, input: `he said "\"That's all folks\"" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "he", Subject: "he",
Predicate: "said", Predicate: "said",
Object: `"That's all folks"`, Object: `"That's all folks"`,
@ -70,7 +70,7 @@ var testNTriples = []struct {
{ {
message: "parse an example real triple", message: "parse an example real triple",
input: `":/guid/9202a8c04000641f80000000010c843c" "name" "George Morris" .`, input: `":/guid/9202a8c04000641f80000000010c843c" "name" "George Morris" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: ":/guid/9202a8c04000641f80000000010c843c", Subject: ":/guid/9202a8c04000641f80000000010c843c",
Predicate: "name", Predicate: "name",
Object: "George Morris", Object: "George Morris",
@ -80,7 +80,7 @@ var testNTriples = []struct {
{ {
message: "parse a pathologically spaced triple", message: "parse a pathologically spaced triple",
input: "foo is \"\\tA big tough\\r\\nDeal\\\\\" .", input: "foo is \"\\tA big tough\\r\\nDeal\\\\\" .",
expect: &quad.Quad{ expect: quad.Quad{
Subject: "foo", Subject: "foo",
Predicate: "is", Predicate: "is",
Object: "\tA big tough\r\nDeal\\", Object: "\tA big tough\r\nDeal\\",
@ -92,7 +92,7 @@ var testNTriples = []struct {
{ {
message: "parse a simple quad", message: "parse a simple quad",
input: "this is valid quad .", input: "this is valid quad .",
expect: &quad.Quad{ expect: quad.Quad{
Subject: "this", Subject: "this",
Predicate: "is", Predicate: "is",
Object: "valid", Object: "valid",
@ -102,7 +102,7 @@ var testNTriples = []struct {
{ {
message: "parse a quoted quad", message: "parse a quoted quad",
input: `this is valid "quad thing" .`, input: `this is valid "quad thing" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "this", Subject: "this",
Predicate: "is", Predicate: "is",
Object: "valid", Object: "valid",
@ -112,7 +112,7 @@ var testNTriples = []struct {
{ {
message: "parse crazy escaped quads", message: "parse crazy escaped quads",
input: `"\"this" "\"is" "\"valid" "\"quad thing".`, input: `"\"this" "\"is" "\"valid" "\"quad thing".`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: `"this`, Subject: `"this`,
Predicate: `"is`, Predicate: `"is`,
Object: `"valid`, Object: `"valid`,
@ -124,7 +124,7 @@ var testNTriples = []struct {
{ {
message: "handle simple case with comments", message: "handle simple case with comments",
input: "<http://example/s> <http://example/p> <http://example/o> . # comment", input: "<http://example/s> <http://example/p> <http://example/o> . # comment",
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example/s", Subject: "http://example/s",
Predicate: "http://example/p", Predicate: "http://example/p",
Object: "http://example/o", Object: "http://example/o",
@ -134,7 +134,7 @@ var testNTriples = []struct {
{ {
message: "handle simple case with comments", message: "handle simple case with comments",
input: "<http://example/s> <http://example/p> _:o . # comment", input: "<http://example/s> <http://example/p> _:o . # comment",
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example/s", Subject: "http://example/s",
Predicate: "http://example/p", Predicate: "http://example/p",
Object: "_:o", Object: "_:o",
@ -144,7 +144,7 @@ var testNTriples = []struct {
{ {
message: "handle simple case with comments", message: "handle simple case with comments",
input: "<http://example/s> <http://example/p> \"o\" . # comment", input: "<http://example/s> <http://example/p> \"o\" . # comment",
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example/s", Subject: "http://example/s",
Predicate: "http://example/p", Predicate: "http://example/p",
Object: "o", Object: "o",
@ -154,7 +154,7 @@ var testNTriples = []struct {
{ {
message: "handle simple case with comments", message: "handle simple case with comments",
input: "<http://example/s> <http://example/p> \"o\"^^<http://example/dt> . # comment", input: "<http://example/s> <http://example/p> \"o\"^^<http://example/dt> . # comment",
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example/s", Subject: "http://example/s",
Predicate: "http://example/p", Predicate: "http://example/p",
Object: `"o"^^<http://example/dt>`, Object: `"o"^^<http://example/dt>`,
@ -164,7 +164,7 @@ var testNTriples = []struct {
{ {
message: "handle simple case with comments", message: "handle simple case with comments",
input: "<http://example/s> <http://example/p> \"o\"@en . # comment", input: "<http://example/s> <http://example/p> \"o\"@en . # comment",
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example/s", Subject: "http://example/s",
Predicate: "http://example/p", Predicate: "http://example/p",
Object: `"o"@en`, Object: `"o"@en`,
@ -177,7 +177,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with commment", message: "parse triple with commment",
input: `_:100000 </film/performance/actor> </en/larry_fine_1902> . # example from 30movies`, input: `_:100000 </film/performance/actor> </en/larry_fine_1902> . # example from 30movies`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:100000", Subject: "_:100000",
Predicate: "/film/performance/actor", Predicate: "/film/performance/actor",
Object: "/en/larry_fine_1902", Object: "/en/larry_fine_1902",
@ -189,7 +189,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with commment", message: "parse triple with commment",
input: `_:10011 </film/performance/character> "Tomás de Torquemada" . # example from 30movies with unicode`, input: `_:10011 </film/performance/character> "Tomás de Torquemada" . # example from 30movies with unicode`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:10011", Subject: "_:10011",
Predicate: "/film/performance/character", Predicate: "/film/performance/character",
Object: "Tomás de Torquemada", Object: "Tomás de Torquemada",
@ -202,7 +202,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with commment", message: "parse triple with commment",
input: `<http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> . # comments here`, input: `<http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> . # comments here`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://one.example/subject1", Subject: "http://one.example/subject1",
Predicate: "http://one.example/predicate1", Predicate: "http://one.example/predicate1",
Object: "http://one.example/object1", Object: "http://one.example/object1",
@ -213,7 +213,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with blank subject node, literal object and no comment (1)", message: "parse triple with blank subject node, literal object and no comment (1)",
input: `_:subject1 <http://an.example/predicate1> "object1" .`, input: `_:subject1 <http://an.example/predicate1> "object1" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:subject1", Subject: "_:subject1",
Predicate: "http://an.example/predicate1", Predicate: "http://an.example/predicate1",
Object: "object1", Object: "object1",
@ -224,7 +224,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with blank subject node, literal object and no comment (2)", message: "parse triple with blank subject node, literal object and no comment (2)",
input: `_:subject2 <http://an.example/predicate2> "object2" .`, input: `_:subject2 <http://an.example/predicate2> "object2" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:subject2", Subject: "_:subject2",
Predicate: "http://an.example/predicate2", Predicate: "http://an.example/predicate2",
Object: "object2", Object: "object2",
@ -237,7 +237,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with three IRIREFs", message: "parse triple with three IRIREFs",
input: `<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green-goblin> .`, input: `<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green-goblin> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/#spiderman", Subject: "http://example.org/#spiderman",
Predicate: "http://www.perceive.net/schemas/relationship/enemyOf", Predicate: "http://www.perceive.net/schemas/relationship/enemyOf",
Object: "http://example.org/#green-goblin", Object: "http://example.org/#green-goblin",
@ -250,7 +250,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with blank node labelled subject and object and IRIREF predicate (1)", message: "parse triple with blank node labelled subject and object and IRIREF predicate (1)",
input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob .`, input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:alice", Subject: "_:alice",
Predicate: "http://xmlns.com/foaf/0.1/knows", Predicate: "http://xmlns.com/foaf/0.1/knows",
Object: "_:bob", Object: "_:bob",
@ -261,7 +261,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with blank node labelled subject and object and IRIREF predicate (2)", message: "parse triple with blank node labelled subject and object and IRIREF predicate (2)",
input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice .`, input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:bob", Subject: "_:bob",
Predicate: "http://xmlns.com/foaf/0.1/knows", Predicate: "http://xmlns.com/foaf/0.1/knows",
Object: "_:alice", Object: "_:alice",
@ -274,7 +274,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with commment", message: "parse quad with commment",
input: `<http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> <http://example.org/graph3> . # comments here`, input: `<http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> <http://example.org/graph3> . # comments here`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://one.example/subject1", Subject: "http://one.example/subject1",
Predicate: "http://one.example/predicate1", Predicate: "http://one.example/predicate1",
Object: "http://one.example/object1", Object: "http://one.example/object1",
@ -285,7 +285,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with blank subject node, literal object, IRIREF predicate and label, and no comment (1)", message: "parse quad with blank subject node, literal object, IRIREF predicate and label, and no comment (1)",
input: `_:subject1 <http://an.example/predicate1> "object1" <http://example.org/graph1> .`, input: `_:subject1 <http://an.example/predicate1> "object1" <http://example.org/graph1> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:subject1", Subject: "_:subject1",
Predicate: "http://an.example/predicate1", Predicate: "http://an.example/predicate1",
Object: "object1", Object: "object1",
@ -296,7 +296,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with blank subject node, literal object, IRIREF predicate and label, and no comment (2)", message: "parse quad with blank subject node, literal object, IRIREF predicate and label, and no comment (2)",
input: `_:subject2 <http://an.example/predicate2> "object2" <http://example.org/graph5> .`, input: `_:subject2 <http://an.example/predicate2> "object2" <http://example.org/graph5> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:subject2", Subject: "_:subject2",
Predicate: "http://an.example/predicate2", Predicate: "http://an.example/predicate2",
Object: "object2", Object: "object2",
@ -309,7 +309,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with all IRIREF parts", message: "parse quad with all IRIREF parts",
input: `<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green-goblin> <http://example.org/graphs/spiderman> .`, input: `<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green-goblin> <http://example.org/graphs/spiderman> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/#spiderman", Subject: "http://example.org/#spiderman",
Predicate: "http://www.perceive.net/schemas/relationship/enemyOf", Predicate: "http://www.perceive.net/schemas/relationship/enemyOf",
Object: "http://example.org/#green-goblin", Object: "http://example.org/#green-goblin",
@ -322,7 +322,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with blank node labelled subject and object and IRIREF predicate and label (1)", message: "parse quad with blank node labelled subject and object and IRIREF predicate and label (1)",
input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob <http://example.org/graphs/john> .`, input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob <http://example.org/graphs/john> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:alice", Subject: "_:alice",
Predicate: "http://xmlns.com/foaf/0.1/knows", Predicate: "http://xmlns.com/foaf/0.1/knows",
Object: "_:bob", Object: "_:bob",
@ -333,7 +333,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with blank node labelled subject and object and IRIREF predicate and label (2)", message: "parse quad with blank node labelled subject and object and IRIREF predicate and label (2)",
input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice <http://example.org/graphs/james> .`, input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice <http://example.org/graphs/james> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:bob", Subject: "_:bob",
Predicate: "http://xmlns.com/foaf/0.1/knows", Predicate: "http://xmlns.com/foaf/0.1/knows",
Object: "_:alice", Object: "_:alice",
@ -346,7 +346,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with all IRIREF parts", message: "parse triple with all IRIREF parts",
input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> .`, input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
Object: "http://xmlns.com/foaf/0.1/Person", Object: "http://xmlns.com/foaf/0.1/Person",
@ -357,7 +357,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with all IRIREF parts", message: "parse triple with all IRIREF parts",
input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/knows> <http://example.org/alice#me> .`, input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/knows> <http://example.org/alice#me> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://xmlns.com/foaf/0.1/knows", Predicate: "http://xmlns.com/foaf/0.1/knows",
Object: "http://example.org/alice#me", Object: "http://example.org/alice#me",
@ -368,7 +368,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with IRIREF schema on literal object", message: "parse triple with IRIREF schema on literal object",
input: `<http://example.org/bob#me> <http://schema.org/birthDate> "1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date> .`, input: `<http://example.org/bob#me> <http://schema.org/birthDate> "1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://schema.org/birthDate", Predicate: "http://schema.org/birthDate",
Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`, Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`,
@ -379,7 +379,7 @@ var testNTriples = []struct {
{ {
message: "parse commented IRIREF in triple", message: "parse commented IRIREF in triple",
input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/topic_interest> <http://www.wikidata.org/entity/Q12418> .`, input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/topic_interest> <http://www.wikidata.org/entity/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://xmlns.com/foaf/0.1/topic_interest", Predicate: "http://xmlns.com/foaf/0.1/topic_interest",
Object: "http://www.wikidata.org/entity/Q12418", Object: "http://www.wikidata.org/entity/Q12418",
@ -390,7 +390,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with literal subject", message: "parse triple with literal subject",
input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/title> "Mona Lisa" .`, input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/title> "Mona Lisa" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://www.wikidata.org/entity/Q12418", Subject: "http://www.wikidata.org/entity/Q12418",
Predicate: "http://purl.org/dc/terms/title", Predicate: "http://purl.org/dc/terms/title",
Object: "Mona Lisa", Object: "Mona Lisa",
@ -401,7 +401,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with all IRIREF parts (1)", message: "parse triple with all IRIREF parts (1)",
input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/creator> <http://dbpedia.org/resource/Leonardo_da_Vinci> .`, input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/creator> <http://dbpedia.org/resource/Leonardo_da_Vinci> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://www.wikidata.org/entity/Q12418", Subject: "http://www.wikidata.org/entity/Q12418",
Predicate: "http://purl.org/dc/terms/creator", Predicate: "http://purl.org/dc/terms/creator",
Object: "http://dbpedia.org/resource/Leonardo_da_Vinci", Object: "http://dbpedia.org/resource/Leonardo_da_Vinci",
@ -412,7 +412,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with all IRIREF parts (2)", message: "parse triple with all IRIREF parts (2)",
input: `<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619> <http://purl.org/dc/terms/subject> <http://www.wikidata.org/entity/Q12418> .`, input: `<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619> <http://purl.org/dc/terms/subject> <http://www.wikidata.org/entity/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619", Subject: "http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619",
Predicate: "http://purl.org/dc/terms/subject", Predicate: "http://purl.org/dc/terms/subject",
Object: "http://www.wikidata.org/entity/Q12418", Object: "http://www.wikidata.org/entity/Q12418",
@ -425,7 +425,7 @@ var testNTriples = []struct {
{ {
message: "parse commented IRIREF in quad (1)", message: "parse commented IRIREF in quad (1)",
input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> <http://example.org/bob> .`, input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> <http://example.org/bob> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
Object: "http://xmlns.com/foaf/0.1/Person", Object: "http://xmlns.com/foaf/0.1/Person",
@ -436,7 +436,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with all IRIREF parts", message: "parse quad with all IRIREF parts",
input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/knows> <http://example.org/alice#me> <http://example.org/bob> .`, input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/knows> <http://example.org/alice#me> <http://example.org/bob> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://xmlns.com/foaf/0.1/knows", Predicate: "http://xmlns.com/foaf/0.1/knows",
Object: "http://example.org/alice#me", Object: "http://example.org/alice#me",
@ -447,7 +447,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with IRIREF schema on literal object", message: "parse quad with IRIREF schema on literal object",
input: `<http://example.org/bob#me> <http://schema.org/birthDate> "1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date> <http://example.org/bob> .`, input: `<http://example.org/bob#me> <http://schema.org/birthDate> "1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date> <http://example.org/bob> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://schema.org/birthDate", Predicate: "http://schema.org/birthDate",
Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`, Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`,
@ -458,7 +458,7 @@ var testNTriples = []struct {
{ {
message: "parse commented IRIREF in quad (2)", message: "parse commented IRIREF in quad (2)",
input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/topic_interest> <http://www.wikidata.org/entity/Q12418> <http://example.org/bob> .`, input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/topic_interest> <http://www.wikidata.org/entity/Q12418> <http://example.org/bob> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://xmlns.com/foaf/0.1/topic_interest", Predicate: "http://xmlns.com/foaf/0.1/topic_interest",
Object: "http://www.wikidata.org/entity/Q12418", Object: "http://www.wikidata.org/entity/Q12418",
@ -469,7 +469,7 @@ var testNTriples = []struct {
{ {
message: "parse literal object and colon qualified label in quad", message: "parse literal object and colon qualified label in quad",
input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/title> "Mona Lisa" <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`, input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/title> "Mona Lisa" <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://www.wikidata.org/entity/Q12418", Subject: "http://www.wikidata.org/entity/Q12418",
Predicate: "http://purl.org/dc/terms/title", Predicate: "http://purl.org/dc/terms/title",
Object: "Mona Lisa", Object: "Mona Lisa",
@ -480,7 +480,7 @@ var testNTriples = []struct {
{ {
message: "parse all IRIREF parts with colon qualified label in quad (1)", message: "parse all IRIREF parts with colon qualified label in quad (1)",
input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/creator> <http://dbpedia.org/resource/Leonardo_da_Vinci> <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`, input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/creator> <http://dbpedia.org/resource/Leonardo_da_Vinci> <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://www.wikidata.org/entity/Q12418", Subject: "http://www.wikidata.org/entity/Q12418",
Predicate: "http://purl.org/dc/terms/creator", Predicate: "http://purl.org/dc/terms/creator",
Object: "http://dbpedia.org/resource/Leonardo_da_Vinci", Object: "http://dbpedia.org/resource/Leonardo_da_Vinci",
@ -491,7 +491,7 @@ var testNTriples = []struct {
{ {
message: "parse all IRIREF parts with colon qualified label in quad (2)", message: "parse all IRIREF parts with colon qualified label in quad (2)",
input: `<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619> <http://purl.org/dc/terms/subject> <http://www.wikidata.org/entity/Q12418> <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`, input: `<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619> <http://purl.org/dc/terms/subject> <http://www.wikidata.org/entity/Q12418> <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619", Subject: "http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619",
Predicate: "http://purl.org/dc/terms/subject", Predicate: "http://purl.org/dc/terms/subject",
Object: "http://www.wikidata.org/entity/Q12418", Object: "http://www.wikidata.org/entity/Q12418",
@ -502,7 +502,7 @@ var testNTriples = []struct {
{ {
message: "parse all IRIREF parts (quad section - 1)", message: "parse all IRIREF parts (quad section - 1)",
input: `<http://example.org/bob> <http://purl.org/dc/terms/publisher> <http://example.org> .`, input: `<http://example.org/bob> <http://purl.org/dc/terms/publisher> <http://example.org> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob", Subject: "http://example.org/bob",
Predicate: "http://purl.org/dc/terms/publisher", Predicate: "http://purl.org/dc/terms/publisher",
Object: "http://example.org", Object: "http://example.org",
@ -513,7 +513,7 @@ var testNTriples = []struct {
{ {
message: "parse all IRIREF parts (quad section - 2)", message: "parse all IRIREF parts (quad section - 2)",
input: `<http://example.org/bob> <http://purl.org/dc/terms/rights> <http://creativecommons.org/licenses/by/3.0/> .`, input: `<http://example.org/bob> <http://purl.org/dc/terms/rights> <http://creativecommons.org/licenses/by/3.0/> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob", Subject: "http://example.org/bob",
Predicate: "http://purl.org/dc/terms/rights", Predicate: "http://purl.org/dc/terms/rights",
Object: "http://creativecommons.org/licenses/by/3.0/", Object: "http://creativecommons.org/licenses/by/3.0/",
@ -526,31 +526,31 @@ var testNTriples = []struct {
{ {
message: "parse empty", message: "parse empty",
input: ``, input: ``,
expect: &quad.Quad{}, expect: quad.Quad{},
err: quad.ErrIncomplete, err: quad.ErrIncomplete,
}, },
{ {
message: "parse commented", message: "parse commented",
input: `# is a comment`, input: `# is a comment`,
expect: &quad.Quad{}, expect: quad.Quad{},
err: fmt.Errorf("%v: unexpected rune '#' at 0", quad.ErrInvalid), err: fmt.Errorf("%v: unexpected rune '#' at 0", quad.ErrInvalid),
}, },
{ {
message: "parse commented internal (1)", message: "parse commented internal (1)",
input: `is # a comment`, input: `is # a comment`,
expect: &quad.Quad{Subject: "is"}, expect: quad.Quad{Subject: "is"},
err: fmt.Errorf("%v: unexpected rune '#' at 3", quad.ErrInvalid), err: fmt.Errorf("%v: unexpected rune '#' at 3", quad.ErrInvalid),
}, },
{ {
message: "parse commented internal (2)", message: "parse commented internal (2)",
input: `is a # comment`, input: `is a # comment`,
expect: &quad.Quad{Subject: "is", Predicate: "a"}, expect: quad.Quad{Subject: "is", Predicate: "a"},
err: fmt.Errorf("%v: unexpected rune '#' at 5", quad.ErrInvalid), err: fmt.Errorf("%v: unexpected rune '#' at 5", quad.ErrInvalid),
}, },
{ {
message: "parse incomplete quad (1)", message: "parse incomplete quad (1)",
input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> .`, input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
Object: "", Object: "",
@ -561,7 +561,7 @@ var testNTriples = []struct {
{ {
message: "parse incomplete quad (2)", message: "parse incomplete quad (2)",
input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> .`, input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "http://example.org/bob#me", Subject: "http://example.org/bob#me",
Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
Object: "", Object: "",
@ -773,7 +773,7 @@ func TestUnescape(t *testing.T) {
} }
} }
var result *quad.Quad var result quad.Quad
func BenchmarkParser(b *testing.B) { func BenchmarkParser(b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,12 @@ import (
write data; write data;
}%% }%%
func parse(data []rune) (quad.Quad, error) { // Parse returns a valid quad.Quad or a non-nil error. Parse does
// handle comments except where the comment placement does not prevent
// a complete valid quad.Quad from being defined.
func Parse(statement string) (quad.Quad, error) {
data := []rune(statement)
var ( var (
cs, p int cs, p int
pe = len(data) pe = len(data)

View file

@ -30,14 +30,6 @@ import (
"github.com/google/cayley/quad" "github.com/google/cayley/quad"
) )
// Parse returns a valid quad.Quad or a non-nil error. Parse does
// handle comments except where the comment placement does not prevent
// a complete valid quad.Quad from being defined.
func Parse(str string) (*quad.Quad, error) {
q, err := parse([]rune(str))
return &q, err
}
// Decoder implements N-Quad document parsing according to the RDF // Decoder implements N-Quad document parsing according to the RDF
// 1.1 N-Quads specification. // 1.1 N-Quads specification.
type Decoder struct { type Decoder struct {
@ -52,14 +44,14 @@ func NewDecoder(r io.Reader) *Decoder {
} }
// Unmarshal returns the next valid N-Quad as a quad.Quad, or an error. // Unmarshal returns the next valid N-Quad as a quad.Quad, or an error.
func (dec *Decoder) Unmarshal() (*quad.Quad, error) { func (dec *Decoder) Unmarshal() (quad.Quad, error) {
dec.line = dec.line[:0] dec.line = dec.line[:0]
var line []byte var line []byte
for { for {
for { for {
l, pre, err := dec.r.ReadLine() l, pre, err := dec.r.ReadLine()
if err != nil { if err != nil {
return nil, err return quad.Quad{}, err
} }
dec.line = append(dec.line, l...) dec.line = append(dec.line, l...)
if !pre { if !pre {
@ -73,9 +65,9 @@ func (dec *Decoder) Unmarshal() (*quad.Quad, error) {
} }
triple, err := Parse(string(line)) triple, err := Parse(string(line))
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse %q: %v", dec.line, err) return quad.Quad{}, fmt.Errorf("failed to parse %q: %v", dec.line, err)
} }
if triple == nil { if !triple.IsValid() {
return dec.Unmarshal() return dec.Unmarshal()
} }
return triple, nil return triple, nil

View file

@ -31,7 +31,7 @@ import (
var testNTriples = []struct { var testNTriples = []struct {
message string message string
input string input string
expect *quad.Quad expect quad.Quad
err error err error
}{ }{
// Tests taken from http://www.w3.org/TR/n-quads/ and http://www.w3.org/TR/n-triples/. // Tests taken from http://www.w3.org/TR/n-quads/ and http://www.w3.org/TR/n-triples/.
@ -40,7 +40,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with commment", message: "parse triple with commment",
input: `_:100000 </film/performance/actor> </en/larry_fine_1902> . # example from 30movies`, input: `_:100000 </film/performance/actor> </en/larry_fine_1902> . # example from 30movies`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:100000", Subject: "_:100000",
Predicate: "</film/performance/actor>", Predicate: "</film/performance/actor>",
Object: "</en/larry_fine_1902>", Object: "</en/larry_fine_1902>",
@ -52,7 +52,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with commment", message: "parse triple with commment",
input: `_:10011 </film/performance/character> "Tomás de Torquemada" . # example from 30movies with unicode`, input: `_:10011 </film/performance/character> "Tomás de Torquemada" . # example from 30movies with unicode`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:10011", Subject: "_:10011",
Predicate: "</film/performance/character>", Predicate: "</film/performance/character>",
Object: `"Tomás de Torquemada"`, Object: `"Tomás de Torquemada"`,
@ -65,7 +65,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with commment", message: "parse triple with commment",
input: `<http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> . # comments here`, input: `<http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> . # comments here`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://one.example/subject1>", Subject: "<http://one.example/subject1>",
Predicate: "<http://one.example/predicate1>", Predicate: "<http://one.example/predicate1>",
Object: "<http://one.example/object1>", Object: "<http://one.example/object1>",
@ -76,7 +76,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with blank subject node, literal object and no comment (1)", message: "parse triple with blank subject node, literal object and no comment (1)",
input: `_:subject1 <http://an.example/predicate1> "object1" .`, input: `_:subject1 <http://an.example/predicate1> "object1" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:subject1", Subject: "_:subject1",
Predicate: "<http://an.example/predicate1>", Predicate: "<http://an.example/predicate1>",
Object: `"object1"`, Object: `"object1"`,
@ -87,7 +87,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with blank subject node, literal object and no comment (2)", message: "parse triple with blank subject node, literal object and no comment (2)",
input: `_:subject2 <http://an.example/predicate2> "object2" .`, input: `_:subject2 <http://an.example/predicate2> "object2" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:subject2", Subject: "_:subject2",
Predicate: "<http://an.example/predicate2>", Predicate: "<http://an.example/predicate2>",
Object: `"object2"`, Object: `"object2"`,
@ -100,7 +100,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with three IRIREFs", message: "parse triple with three IRIREFs",
input: `<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green-goblin> .`, input: `<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green-goblin> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/#spiderman>", Subject: "<http://example.org/#spiderman>",
Predicate: "<http://www.perceive.net/schemas/relationship/enemyOf>", Predicate: "<http://www.perceive.net/schemas/relationship/enemyOf>",
Object: "<http://example.org/#green-goblin>", Object: "<http://example.org/#green-goblin>",
@ -113,7 +113,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with blank node labelled subject and object and IRIREF predicate (1)", message: "parse triple with blank node labelled subject and object and IRIREF predicate (1)",
input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob .`, input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:alice", Subject: "_:alice",
Predicate: "<http://xmlns.com/foaf/0.1/knows>", Predicate: "<http://xmlns.com/foaf/0.1/knows>",
Object: "_:bob", Object: "_:bob",
@ -124,7 +124,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with blank node labelled subject and object and IRIREF predicate (2)", message: "parse triple with blank node labelled subject and object and IRIREF predicate (2)",
input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice .`, input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:bob", Subject: "_:bob",
Predicate: "<http://xmlns.com/foaf/0.1/knows>", Predicate: "<http://xmlns.com/foaf/0.1/knows>",
Object: "_:alice", Object: "_:alice",
@ -137,7 +137,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with commment", message: "parse quad with commment",
input: `<http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> <http://example.org/graph3> . # comments here`, input: `<http://one.example/subject1> <http://one.example/predicate1> <http://one.example/object1> <http://example.org/graph3> . # comments here`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://one.example/subject1>", Subject: "<http://one.example/subject1>",
Predicate: "<http://one.example/predicate1>", Predicate: "<http://one.example/predicate1>",
Object: "<http://one.example/object1>", Object: "<http://one.example/object1>",
@ -148,7 +148,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with blank subject node, literal object, IRIREF predicate and label, and no comment (1)", message: "parse quad with blank subject node, literal object, IRIREF predicate and label, and no comment (1)",
input: `_:subject1 <http://an.example/predicate1> "object1" <http://example.org/graph1> .`, input: `_:subject1 <http://an.example/predicate1> "object1" <http://example.org/graph1> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:subject1", Subject: "_:subject1",
Predicate: "<http://an.example/predicate1>", Predicate: "<http://an.example/predicate1>",
Object: `"object1"`, Object: `"object1"`,
@ -159,7 +159,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with blank subject node, literal object, IRIREF predicate and label, and no comment (2)", message: "parse quad with blank subject node, literal object, IRIREF predicate and label, and no comment (2)",
input: `_:subject2 <http://an.example/predicate2> "object2" <http://example.org/graph5> .`, input: `_:subject2 <http://an.example/predicate2> "object2" <http://example.org/graph5> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:subject2", Subject: "_:subject2",
Predicate: "<http://an.example/predicate2>", Predicate: "<http://an.example/predicate2>",
Object: `"object2"`, Object: `"object2"`,
@ -172,7 +172,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with all IRIREF parts", message: "parse quad with all IRIREF parts",
input: `<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green-goblin> <http://example.org/graphs/spiderman> .`, input: `<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green-goblin> <http://example.org/graphs/spiderman> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/#spiderman>", Subject: "<http://example.org/#spiderman>",
Predicate: "<http://www.perceive.net/schemas/relationship/enemyOf>", Predicate: "<http://www.perceive.net/schemas/relationship/enemyOf>",
Object: "<http://example.org/#green-goblin>", Object: "<http://example.org/#green-goblin>",
@ -185,7 +185,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with blank node labelled subject and object and IRIREF predicate and label (1)", message: "parse quad with blank node labelled subject and object and IRIREF predicate and label (1)",
input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob <http://example.org/graphs/john> .`, input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob <http://example.org/graphs/john> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:alice", Subject: "_:alice",
Predicate: "<http://xmlns.com/foaf/0.1/knows>", Predicate: "<http://xmlns.com/foaf/0.1/knows>",
Object: "_:bob", Object: "_:bob",
@ -196,7 +196,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with blank node labelled subject and object and IRIREF predicate and label (2)", message: "parse quad with blank node labelled subject and object and IRIREF predicate and label (2)",
input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice <http://example.org/graphs/james> .`, input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice <http://example.org/graphs/james> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "_:bob", Subject: "_:bob",
Predicate: "<http://xmlns.com/foaf/0.1/knows>", Predicate: "<http://xmlns.com/foaf/0.1/knows>",
Object: "_:alice", Object: "_:alice",
@ -209,7 +209,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with all IRIREF parts", message: "parse triple with all IRIREF parts",
input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> .`, input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>", Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>",
Object: "<http://xmlns.com/foaf/0.1/Person>", Object: "<http://xmlns.com/foaf/0.1/Person>",
@ -220,7 +220,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with all IRIREF parts", message: "parse triple with all IRIREF parts",
input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/knows> <http://example.org/alice#me> .`, input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/knows> <http://example.org/alice#me> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://xmlns.com/foaf/0.1/knows>", Predicate: "<http://xmlns.com/foaf/0.1/knows>",
Object: "<http://example.org/alice#me>", Object: "<http://example.org/alice#me>",
@ -231,7 +231,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with IRIREF schema on literal object", message: "parse triple with IRIREF schema on literal object",
input: `<http://example.org/bob#me> <http://schema.org/birthDate> "1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date> .`, input: `<http://example.org/bob#me> <http://schema.org/birthDate> "1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://schema.org/birthDate>", Predicate: "<http://schema.org/birthDate>",
Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`, Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`,
@ -242,7 +242,7 @@ var testNTriples = []struct {
{ {
message: "parse commented IRIREF in triple", message: "parse commented IRIREF in triple",
input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/topic_interest> <http://www.wikidata.org/entity/Q12418> .`, input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/topic_interest> <http://www.wikidata.org/entity/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://xmlns.com/foaf/0.1/topic_interest>", Predicate: "<http://xmlns.com/foaf/0.1/topic_interest>",
Object: "<http://www.wikidata.org/entity/Q12418>", Object: "<http://www.wikidata.org/entity/Q12418>",
@ -253,7 +253,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with literal subject", message: "parse triple with literal subject",
input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/title> "Mona Lisa" .`, input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/title> "Mona Lisa" .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://www.wikidata.org/entity/Q12418>", Subject: "<http://www.wikidata.org/entity/Q12418>",
Predicate: "<http://purl.org/dc/terms/title>", Predicate: "<http://purl.org/dc/terms/title>",
Object: `"Mona Lisa"`, Object: `"Mona Lisa"`,
@ -264,7 +264,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with all IRIREF parts (1)", message: "parse triple with all IRIREF parts (1)",
input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/creator> <http://dbpedia.org/resource/Leonardo_da_Vinci> .`, input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/creator> <http://dbpedia.org/resource/Leonardo_da_Vinci> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://www.wikidata.org/entity/Q12418>", Subject: "<http://www.wikidata.org/entity/Q12418>",
Predicate: "<http://purl.org/dc/terms/creator>", Predicate: "<http://purl.org/dc/terms/creator>",
Object: "<http://dbpedia.org/resource/Leonardo_da_Vinci>", Object: "<http://dbpedia.org/resource/Leonardo_da_Vinci>",
@ -275,7 +275,7 @@ var testNTriples = []struct {
{ {
message: "parse triple with all IRIREF parts (2)", message: "parse triple with all IRIREF parts (2)",
input: `<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619> <http://purl.org/dc/terms/subject> <http://www.wikidata.org/entity/Q12418> .`, input: `<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619> <http://purl.org/dc/terms/subject> <http://www.wikidata.org/entity/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619>", Subject: "<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619>",
Predicate: "<http://purl.org/dc/terms/subject>", Predicate: "<http://purl.org/dc/terms/subject>",
Object: "<http://www.wikidata.org/entity/Q12418>", Object: "<http://www.wikidata.org/entity/Q12418>",
@ -288,7 +288,7 @@ var testNTriples = []struct {
{ {
message: "parse commented IRIREF in quad (1)", message: "parse commented IRIREF in quad (1)",
input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> <http://example.org/bob> .`, input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> <http://example.org/bob> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>", Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>",
Object: "<http://xmlns.com/foaf/0.1/Person>", Object: "<http://xmlns.com/foaf/0.1/Person>",
@ -299,7 +299,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with all IRIREF parts", message: "parse quad with all IRIREF parts",
input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/knows> <http://example.org/alice#me> <http://example.org/bob> .`, input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/knows> <http://example.org/alice#me> <http://example.org/bob> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://xmlns.com/foaf/0.1/knows>", Predicate: "<http://xmlns.com/foaf/0.1/knows>",
Object: "<http://example.org/alice#me>", Object: "<http://example.org/alice#me>",
@ -310,7 +310,7 @@ var testNTriples = []struct {
{ {
message: "parse quad with IRIREF schema on literal object", message: "parse quad with IRIREF schema on literal object",
input: `<http://example.org/bob#me> <http://schema.org/birthDate> "1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date> <http://example.org/bob> .`, input: `<http://example.org/bob#me> <http://schema.org/birthDate> "1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date> <http://example.org/bob> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://schema.org/birthDate>", Predicate: "<http://schema.org/birthDate>",
Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`, Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`,
@ -321,7 +321,7 @@ var testNTriples = []struct {
{ {
message: "parse commented IRIREF in quad (2)", message: "parse commented IRIREF in quad (2)",
input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/topic_interest> <http://www.wikidata.org/entity/Q12418> <http://example.org/bob> .`, input: `<http://example.org/bob#me> <http://xmlns.com/foaf/0.1/topic_interest> <http://www.wikidata.org/entity/Q12418> <http://example.org/bob> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://xmlns.com/foaf/0.1/topic_interest>", Predicate: "<http://xmlns.com/foaf/0.1/topic_interest>",
Object: "<http://www.wikidata.org/entity/Q12418>", Object: "<http://www.wikidata.org/entity/Q12418>",
@ -332,7 +332,7 @@ var testNTriples = []struct {
{ {
message: "parse literal object and colon qualified label in quad", message: "parse literal object and colon qualified label in quad",
input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/title> "Mona Lisa" <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`, input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/title> "Mona Lisa" <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://www.wikidata.org/entity/Q12418>", Subject: "<http://www.wikidata.org/entity/Q12418>",
Predicate: "<http://purl.org/dc/terms/title>", Predicate: "<http://purl.org/dc/terms/title>",
Object: `"Mona Lisa"`, Object: `"Mona Lisa"`,
@ -343,7 +343,7 @@ var testNTriples = []struct {
{ {
message: "parse all IRIREF parts with colon qualified label in quad (1)", message: "parse all IRIREF parts with colon qualified label in quad (1)",
input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/creator> <http://dbpedia.org/resource/Leonardo_da_Vinci> <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`, input: `<http://www.wikidata.org/entity/Q12418> <http://purl.org/dc/terms/creator> <http://dbpedia.org/resource/Leonardo_da_Vinci> <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://www.wikidata.org/entity/Q12418>", Subject: "<http://www.wikidata.org/entity/Q12418>",
Predicate: "<http://purl.org/dc/terms/creator>", Predicate: "<http://purl.org/dc/terms/creator>",
Object: "<http://dbpedia.org/resource/Leonardo_da_Vinci>", Object: "<http://dbpedia.org/resource/Leonardo_da_Vinci>",
@ -354,7 +354,7 @@ var testNTriples = []struct {
{ {
message: "parse all IRIREF parts with colon qualified label in quad (2)", message: "parse all IRIREF parts with colon qualified label in quad (2)",
input: `<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619> <http://purl.org/dc/terms/subject> <http://www.wikidata.org/entity/Q12418> <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`, input: `<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619> <http://purl.org/dc/terms/subject> <http://www.wikidata.org/entity/Q12418> <https://www.wikidata.org/wiki/Special:EntityData/Q12418> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619>", Subject: "<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619>",
Predicate: "<http://purl.org/dc/terms/subject>", Predicate: "<http://purl.org/dc/terms/subject>",
Object: "<http://www.wikidata.org/entity/Q12418>", Object: "<http://www.wikidata.org/entity/Q12418>",
@ -365,7 +365,7 @@ var testNTriples = []struct {
{ {
message: "parse all IRIREF parts (quad section - 1)", message: "parse all IRIREF parts (quad section - 1)",
input: `<http://example.org/bob> <http://purl.org/dc/terms/publisher> <http://example.org> .`, input: `<http://example.org/bob> <http://purl.org/dc/terms/publisher> <http://example.org> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob>", Subject: "<http://example.org/bob>",
Predicate: "<http://purl.org/dc/terms/publisher>", Predicate: "<http://purl.org/dc/terms/publisher>",
Object: "<http://example.org>", Object: "<http://example.org>",
@ -376,7 +376,7 @@ var testNTriples = []struct {
{ {
message: "parse all IRIREF parts (quad section - 2)", message: "parse all IRIREF parts (quad section - 2)",
input: `<http://example.org/bob> <http://purl.org/dc/terms/rights> <http://creativecommons.org/licenses/by/3.0/> .`, input: `<http://example.org/bob> <http://purl.org/dc/terms/rights> <http://creativecommons.org/licenses/by/3.0/> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob>", Subject: "<http://example.org/bob>",
Predicate: "<http://purl.org/dc/terms/rights>", Predicate: "<http://purl.org/dc/terms/rights>",
Object: "<http://creativecommons.org/licenses/by/3.0/>", Object: "<http://creativecommons.org/licenses/by/3.0/>",
@ -389,19 +389,19 @@ var testNTriples = []struct {
{ {
message: "parse empty", message: "parse empty",
input: ``, input: ``,
expect: &quad.Quad{}, expect: quad.Quad{},
err: quad.ErrIncomplete, err: quad.ErrIncomplete,
}, },
{ {
message: "parse commented", message: "parse commented",
input: `# comment`, input: `# comment`,
expect: &quad.Quad{}, expect: quad.Quad{},
err: fmt.Errorf("%v: unexpected rune '#' at 0", quad.ErrInvalid), err: fmt.Errorf("%v: unexpected rune '#' at 0", quad.ErrInvalid),
}, },
{ {
message: "parse incomplete quad", message: "parse incomplete quad",
input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> .`, input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>", Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>",
Object: "", Object: "",
@ -412,7 +412,7 @@ var testNTriples = []struct {
{ {
message: "parse incomplete quad", message: "parse incomplete quad",
input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> .`, input: `<http://example.org/bob#me> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> .`,
expect: &quad.Quad{ expect: quad.Quad{
Subject: "<http://example.org/bob#me>", Subject: "<http://example.org/bob#me>",
Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>", Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>",
Object: "", Object: "",
@ -580,7 +580,7 @@ func TestUnescape(t *testing.T) {
} }
} }
var result *quad.Quad var result quad.Quad
func BenchmarkParser(b *testing.B) { func BenchmarkParser(b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {

View file

@ -37,7 +37,12 @@ const quads_en_statement int = 1
// line 34 "parse.rl" // line 34 "parse.rl"
func parse(data []rune) (quad.Quad, error) { // Parse returns a valid quad.Quad or a non-nil error. Parse does
// handle comments except where the comment placement does not prevent
// a complete valid quad.Quad from being defined.
func Parse(statement string) (quad.Quad, error) {
data := []rune(statement)
var ( var (
cs, p int cs, p int
pe = len(data) pe = len(data)
@ -54,15 +59,15 @@ func parse(data []rune) (quad.Quad, error) {
) )
// line 58 "parse.go" // line 63 "parse.go"
{ {
cs = quads_start cs = quads_start
} }
// line 53 "parse.rl" // line 58 "parse.rl"
// line 66 "parse.go" // line 71 "parse.go"
{ {
if p == pe { if p == pe {
goto _test_eof goto _test_eof
@ -286,7 +291,7 @@ tr0:
return q, quad.ErrIncomplete return q, quad.ErrIncomplete
goto st0 goto st0
// line 290 "parse.go" // line 295 "parse.go"
st_case_0: st_case_0:
st0: st0:
cs = 0 cs = 0
@ -310,7 +315,7 @@ tr120:
goto _test_eof2 goto _test_eof2
} }
st_case_2: st_case_2:
// line 314 "parse.go" // line 319 "parse.go"
switch data[p] { switch data[p] {
case 62: case 62:
goto st3 goto st3
@ -351,7 +356,7 @@ tr121:
goto _test_eof3 goto _test_eof3
} }
st_case_3: st_case_3:
// line 355 "parse.go" // line 360 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto tr7 goto tr7
@ -377,7 +382,7 @@ tr7:
goto _test_eof4 goto _test_eof4
} }
st_case_4: st_case_4:
// line 381 "parse.go" // line 386 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto st4 goto st4
@ -422,7 +427,7 @@ tr108:
goto _test_eof5 goto _test_eof5
} }
st_case_5: st_case_5:
// line 426 "parse.go" // line 431 "parse.go"
switch data[p] { switch data[p] {
case 62: case 62:
goto st6 goto st6
@ -463,7 +468,7 @@ tr109:
goto _test_eof6 goto _test_eof6
} }
st_case_6: st_case_6:
// line 467 "parse.go" // line 472 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto tr14 goto tr14
@ -493,7 +498,7 @@ tr14:
goto _test_eof7 goto _test_eof7
} }
st_case_7: st_case_7:
// line 497 "parse.go" // line 502 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto st7 goto st7
@ -542,7 +547,7 @@ tr79:
goto _test_eof8 goto _test_eof8
} }
st_case_8: st_case_8:
// line 546 "parse.go" // line 551 "parse.go"
switch data[p] { switch data[p] {
case 34: case 34:
goto st9 goto st9
@ -574,7 +579,7 @@ tr80:
goto _test_eof9 goto _test_eof9
} }
st_case_9: st_case_9:
// line 578 "parse.go" // line 583 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto tr25 goto tr25
@ -628,7 +633,7 @@ tr96:
goto _test_eof10 goto _test_eof10
} }
st_case_10: st_case_10:
// line 632 "parse.go" // line 637 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto st10 goto st10
@ -669,7 +674,7 @@ tr39:
goto _test_eof88 goto _test_eof88
} }
st_case_88: st_case_88:
// line 673 "parse.go" // line 678 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto st88 goto st88
@ -690,7 +695,7 @@ tr127:
goto _test_eof89 goto _test_eof89
} }
st_case_89: st_case_89:
// line 694 "parse.go" // line 699 "parse.go"
goto st89 goto st89
tr27: tr27:
// line 54 "actions.rl" // line 54 "actions.rl"
@ -727,7 +732,7 @@ tr50:
goto _test_eof11 goto _test_eof11
} }
st_case_11: st_case_11:
// line 731 "parse.go" // line 736 "parse.go"
switch data[p] { switch data[p] {
case 62: case 62:
goto st12 goto st12
@ -768,7 +773,7 @@ tr51:
goto _test_eof12 goto _test_eof12
} }
st_case_12: st_case_12:
// line 772 "parse.go" // line 777 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto tr38 goto tr38
@ -794,7 +799,7 @@ tr38:
goto _test_eof13 goto _test_eof13
} }
st_case_13: st_case_13:
// line 798 "parse.go" // line 803 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto st13 goto st13
@ -816,7 +821,7 @@ tr52:
goto _test_eof14 goto _test_eof14
} }
st_case_14: st_case_14:
// line 820 "parse.go" // line 825 "parse.go"
switch data[p] { switch data[p] {
case 85: case 85:
goto st15 goto st15
@ -1029,7 +1034,7 @@ tr34:
goto _test_eof24 goto _test_eof24
} }
st_case_24: st_case_24:
// line 1033 "parse.go" // line 1038 "parse.go"
if data[p] == 58 { if data[p] == 58 {
goto st25 goto st25
} }
@ -1211,7 +1216,7 @@ tr55:
goto _test_eof90 goto _test_eof90
} }
st_case_90: st_case_90:
// line 1215 "parse.go" // line 1220 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto st88 goto st88
@ -1522,7 +1527,7 @@ tr73:
goto _test_eof34 goto _test_eof34
} }
st_case_34: st_case_34:
// line 1526 "parse.go" // line 1531 "parse.go"
switch data[p] { switch data[p] {
case 62: case 62:
goto st35 goto st35
@ -1563,7 +1568,7 @@ tr74:
goto _test_eof35 goto _test_eof35
} }
st_case_35: st_case_35:
// line 1567 "parse.go" // line 1572 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto tr25 goto tr25
@ -1589,7 +1594,7 @@ tr75:
goto _test_eof36 goto _test_eof36
} }
st_case_36: st_case_36:
// line 1593 "parse.go" // line 1598 "parse.go"
switch data[p] { switch data[p] {
case 85: case 85:
goto st37 goto st37
@ -1786,7 +1791,7 @@ tr81:
goto _test_eof46 goto _test_eof46
} }
st_case_46: st_case_46:
// line 1790 "parse.go" // line 1795 "parse.go"
switch data[p] { switch data[p] {
case 34: case 34:
goto st47 goto st47
@ -2006,7 +2011,7 @@ tr21:
goto _test_eof56 goto _test_eof56
} }
st_case_56: st_case_56:
// line 2010 "parse.go" // line 2015 "parse.go"
if data[p] == 58 { if data[p] == 58 {
goto st57 goto st57
} }
@ -2190,7 +2195,7 @@ tr90:
goto _test_eof91 goto _test_eof91
} }
st_case_91: st_case_91:
// line 2194 "parse.go" // line 2199 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto st88 goto st88
@ -2377,7 +2382,7 @@ tr91:
goto _test_eof60 goto _test_eof60
} }
st_case_60: st_case_60:
// line 2381 "parse.go" // line 2386 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto tr25 goto tr25
@ -2582,7 +2587,7 @@ tr95:
goto _test_eof62 goto _test_eof62
} }
st_case_62: st_case_62:
// line 2586 "parse.go" // line 2591 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto tr96 goto tr96
@ -2691,7 +2696,7 @@ tr97:
goto _test_eof92 goto _test_eof92
} }
st_case_92: st_case_92:
// line 2695 "parse.go" // line 2700 "parse.go"
switch data[p] { switch data[p] {
case 9: case 9:
goto st88 goto st88
@ -2869,7 +2874,7 @@ tr110:
goto _test_eof64 goto _test_eof64
} }
st_case_64: st_case_64:
// line 2873 "parse.go" // line 2878 "parse.go"
switch data[p] { switch data[p] {
case 85: case 85:
goto st65 goto st65
@ -3066,7 +3071,7 @@ tr122:
goto _test_eof74 goto _test_eof74
} }
st_case_74: st_case_74:
// line 3070 "parse.go" // line 3075 "parse.go"
switch data[p] { switch data[p] {
case 85: case 85:
goto st75 goto st75
@ -3263,7 +3268,7 @@ tr3:
goto _test_eof84 goto _test_eof84
} }
st_case_84: st_case_84:
// line 3267 "parse.go" // line 3272 "parse.go"
if data[p] == 58 { if data[p] == 58 {
goto st85 goto st85
} }
@ -3639,14 +3644,14 @@ tr3:
return q, nil return q, nil
// line 3643 "parse.go" // line 3648 "parse.go"
} }
} }
_out: {} _out: {}
} }
// line 55 "parse.rl" // line 60 "parse.rl"
return quad.Quad{}, quad.ErrInvalid return quad.Quad{}, quad.ErrInvalid
} }

View file

@ -33,7 +33,12 @@ import (
write data; write data;
}%% }%%
func parse(data []rune) (quad.Quad, error) { // Parse returns a valid quad.Quad or a non-nil error. Parse does
// handle comments except where the comment placement does not prevent
// a complete valid quad.Quad from being defined.
func Parse(statement string) (quad.Quad, error) {
data := []rune(statement)
var ( var (
cs, p int cs, p int
pe = len(data) pe = len(data)

View file

@ -100,11 +100,8 @@ func (d Direction) String() string {
} }
} }
// TODO(kortschak) Consider writing methods onto the concrete type
// instead of the pointer. This needs benchmarking to make the decision.
// Per-field accessor for triples // Per-field accessor for triples
func (q *Quad) Get(d Direction) string { func (q Quad) Get(d Direction) string {
switch d { switch d {
case Subject: case Subject:
return q.Subject return q.Subject
@ -119,25 +116,17 @@ func (q *Quad) Get(d Direction) string {
} }
} }
func (q *Quad) Equals(o *Quad) bool {
return *q == *o
}
// Pretty-prints a triple. // Pretty-prints a triple.
func (q *Quad) String() string { func (q Quad) String() string {
return fmt.Sprintf("%s -- %s -> %s", q.Subject, q.Predicate, q.Object) return fmt.Sprintf("%s -- %s -> %s", q.Subject, q.Predicate, q.Object)
} }
func (q *Quad) IsValid() bool { func (q Quad) IsValid() bool {
return q.Subject != "" && q.Predicate != "" && q.Object != "" return q.Subject != "" && q.Predicate != "" && q.Object != ""
} }
// TODO(kortschak) NTriple looks like a good candidate for conversion
// to MarshalText() (text []byte, err error) and then move parsing code
// from nquads to here to provide UnmarshalText(text []byte) error.
// Prints a triple in N-Quad format. // Prints a triple in N-Quad format.
func (q *Quad) NTriple() string { func (q Quad) NTriple() string {
if q.Label == "" { if q.Label == "" {
//TODO(barakmich): Proper escaping. //TODO(barakmich): Proper escaping.
return fmt.Sprintf("%s %s %s .", q.Subject, q.Predicate, q.Object) return fmt.Sprintf("%s %s %s .", q.Subject, q.Predicate, q.Object)
@ -147,5 +136,5 @@ func (q *Quad) NTriple() string {
} }
type Unmarshaler interface { type Unmarshaler interface {
Unmarshal() (*Quad, error) Unmarshal() (Quad, error)
} }

View file

@ -38,7 +38,7 @@ import (
// \-->|#D#|------------->+---+ // \-->|#D#|------------->+---+
// +---+ // +---+
// //
var simpleGraph = []*quad.Quad{ var simpleGraph = []quad.Quad{
{"A", "follows", "B", ""}, {"A", "follows", "B", ""},
{"C", "follows", "B", ""}, {"C", "follows", "B", ""},
{"C", "follows", "D", ""}, {"C", "follows", "D", ""},
@ -52,7 +52,7 @@ var simpleGraph = []*quad.Quad{
{"G", "status", "cool", "status_graph"}, {"G", "status", "cool", "status_graph"},
} }
func makeTestSession(data []*quad.Quad) *Session { func makeTestSession(data []quad.Quad) *Session {
ts, _ := graph.NewTripleStore("memstore", "", nil) ts, _ := graph.NewTripleStore("memstore", "", nil)
for _, t := range data { for _, t := range data {
ts.AddTriple(t) ts.AddTriple(t)
@ -246,7 +246,7 @@ var testQueries = []struct {
}, },
} }
func runQueryGetTag(g []*quad.Quad, query string, tag string) []string { func runQueryGetTag(g []quad.Quad, query string, tag string) []string {
js := makeTestSession(g) js := makeTestSession(g)
c := make(chan interface{}, 5) c := make(chan interface{}, 5)
js.ExecInput(query, c, -1) js.ExecInput(query, c, -1)

View file

@ -37,7 +37,7 @@ import (
// \-->|#D#|------------->+---+ // \-->|#D#|------------->+---+
// +---+ // +---+
// //
var simpleGraph = []*quad.Quad{ var simpleGraph = []quad.Quad{
{"A", "follows", "B", ""}, {"A", "follows", "B", ""},
{"C", "follows", "B", ""}, {"C", "follows", "B", ""},
{"C", "follows", "D", ""}, {"C", "follows", "D", ""},
@ -51,7 +51,7 @@ var simpleGraph = []*quad.Quad{
{"G", "status", "cool", "status_graph"}, {"G", "status", "cool", "status_graph"},
} }
func makeTestSession(data []*quad.Quad) *Session { func makeTestSession(data []quad.Quad) *Session {
ts, _ := graph.NewTripleStore("memstore", "", nil) ts, _ := graph.NewTripleStore("memstore", "", nil)
for _, t := range data { for _, t := range data {
ts.AddTriple(t) ts.AddTriple(t)
@ -165,7 +165,7 @@ var testQueries = []struct {
}, },
} }
func runQuery(g []*quad.Quad, query string) interface{} { func runQuery(g []quad.Quad, query string) interface{} {
s := makeTestSession(g) s := makeTestSession(g)
c := make(chan interface{}, 5) c := make(chan interface{}, 5)
go s.ExecInput(query, c, -1) go s.ExecInput(query, c, -1)

View file

@ -32,21 +32,21 @@ func TestBadParse(t *testing.T) {
var testQueries = []struct { var testQueries = []struct {
message string message string
add *quad.Quad add quad.Quad
query string query string
typ graph.Type typ graph.Type
expect string expect string
}{ }{
{ {
message: "get a single triple linkage", message: "get a single triple linkage",
add: &quad.Quad{"i", "can", "win", ""}, add: quad.Quad{"i", "can", "win", ""},
query: "($a (:can \"win\"))", query: "($a (:can \"win\"))",
typ: graph.And, typ: graph.And,
expect: "i", expect: "i",
}, },
{ {
message: "get a single triple linkage", message: "get a single triple linkage",
add: &quad.Quad{"i", "can", "win", ""}, add: quad.Quad{"i", "can", "win", ""},
query: "(\"i\" (:can $a))", query: "(\"i\" (:can $a))",
typ: graph.And, typ: graph.And,
expect: "i", expect: "i",
@ -60,7 +60,7 @@ func TestMemstoreBackedSexp(t *testing.T) {
t.Errorf(`Incorrect type for empty query, got:%q expect: "null"`, it.Type()) t.Errorf(`Incorrect type for empty query, got:%q expect: "null"`, it.Type())
} }
for _, test := range testQueries { for _, test := range testQueries {
if test.add != nil { if test.add.IsValid() {
ts.AddTriple(test.add) ts.AddTriple(test.add)
} }
it := BuildIteratorTreeForQuery(ts, test.query) it := BuildIteratorTreeForQuery(ts, test.query)
@ -79,8 +79,8 @@ func TestMemstoreBackedSexp(t *testing.T) {
func TestTreeConstraintParse(t *testing.T) { func TestTreeConstraintParse(t *testing.T) {
ts, _ := graph.NewTripleStore("memstore", "", nil) ts, _ := graph.NewTripleStore("memstore", "", nil)
ts.AddTriple(&quad.Quad{"i", "like", "food", ""}) ts.AddTriple(quad.Quad{"i", "like", "food", ""})
ts.AddTriple(&quad.Quad{"food", "is", "good", ""}) ts.AddTriple(quad.Quad{"food", "is", "good", ""})
query := "(\"i\"\n" + query := "(\"i\"\n" +
"(:like\n" + "(:like\n" +
"($a (:is :good))))" "($a (:is :good))))"
@ -99,8 +99,8 @@ func TestTreeConstraintParse(t *testing.T) {
func TestTreeConstraintTagParse(t *testing.T) { func TestTreeConstraintTagParse(t *testing.T) {
ts, _ := graph.NewTripleStore("memstore", "", nil) ts, _ := graph.NewTripleStore("memstore", "", nil)
ts.AddTriple(&quad.Quad{"i", "like", "food", ""}) ts.AddTriple(quad.Quad{"i", "like", "food", ""})
ts.AddTriple(&quad.Quad{"food", "is", "good", ""}) ts.AddTriple(quad.Quad{"food", "is", "good", ""})
query := "(\"i\"\n" + query := "(\"i\"\n" +
"(:like\n" + "(:like\n" +
"($a (:is :good))))" "($a (:is :good))))"
@ -119,7 +119,7 @@ func TestTreeConstraintTagParse(t *testing.T) {
func TestMultipleConstraintParse(t *testing.T) { func TestMultipleConstraintParse(t *testing.T) {
ts, _ := graph.NewTripleStore("memstore", "", nil) ts, _ := graph.NewTripleStore("memstore", "", nil)
for _, tv := range []*quad.Quad{ for _, tv := range []quad.Quad{
{"i", "like", "food", ""}, {"i", "like", "food", ""},
{"i", "like", "beer", ""}, {"i", "like", "beer", ""},
{"you", "like", "beer", ""}, {"you", "like", "beer", ""},