Merge branch 'master' into nexter
Conflicts: graph/leveldb/all_iterator.go graph/leveldb/iterator.go graph/memstore/triplestore.go query/gremlin/finals.go
This commit is contained in:
commit
62785d25c2
37 changed files with 882 additions and 467 deletions
|
|
@ -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:
|
||||
```
|
||||
./cayley repl --dbpath=testdata.nt
|
||||
./cayley repl --dbpath=testdata.nq
|
||||
```
|
||||
|
||||
You should see a `cayley>` REPL prompt. Go ahead and give it a try:
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
"db_path": "30kmoviedata.nq.gz",
|
||||
"read_only": true,
|
||||
"load_size": 10000,
|
||||
"gremlin_timeout": 10
|
||||
"timeout": 10
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package main
|
|||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/cayley/config"
|
||||
"github.com/google/cayley/db"
|
||||
|
|
@ -292,9 +293,9 @@ var m2_actors = movie2.Save("name","movie2").Follow(filmToActor)
|
|||
var (
|
||||
once sync.Once
|
||||
cfg = &config.Config{
|
||||
DatabasePath: "30kmoviedata.nq.gz",
|
||||
DatabaseType: "memstore",
|
||||
GremlinTimeout: 300,
|
||||
DatabasePath: "30kmoviedata.nq.gz",
|
||||
DatabaseType: "memstore",
|
||||
Timeout: 300 * time.Second,
|
||||
}
|
||||
|
||||
ts graph.TripleStore
|
||||
|
|
@ -316,7 +317,7 @@ func TestQueries(t *testing.T) {
|
|||
if testing.Short() && test.long {
|
||||
continue
|
||||
}
|
||||
ses := gremlin.NewSession(ts, cfg.GremlinTimeout, true)
|
||||
ses := gremlin.NewSession(ts, cfg.Timeout, true)
|
||||
_, err := ses.InputParses(test.query)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse benchmark gremlin %s: %v", test.message, err)
|
||||
|
|
@ -333,7 +334,7 @@ func TestQueries(t *testing.T) {
|
|||
if j == nil && err == nil {
|
||||
continue
|
||||
}
|
||||
if err != nil && err.Error() == "Query Timeout" {
|
||||
if err == gremlin.ErrKillTimeout {
|
||||
timedOut = true
|
||||
continue
|
||||
}
|
||||
|
|
@ -347,7 +348,7 @@ func TestQueries(t *testing.T) {
|
|||
|
||||
// TODO(kortschak) Be more rigorous in this result validation.
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -357,17 +358,18 @@ func runBench(n int, b *testing.B) {
|
|||
b.Skip()
|
||||
}
|
||||
prepare(b)
|
||||
ses := gremlin.NewSession(ts, cfg.GremlinTimeout, true)
|
||||
_, err := ses.InputParses(benchmarkQueries[n].query)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to parse benchmark gremlin %s: %v", benchmarkQueries[n].message, err)
|
||||
}
|
||||
b.StopTimer()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
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)
|
||||
for _ = range c {
|
||||
}
|
||||
b.StopTimer()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -391,18 +393,18 @@ func BenchmarkNetAndSpeed(b *testing.B) {
|
|||
runBench(4, b)
|
||||
}
|
||||
|
||||
func BenchmarkKeannuAndNet(b *testing.B) {
|
||||
func BenchmarkKeanuAndNet(b *testing.B) {
|
||||
runBench(5, b)
|
||||
}
|
||||
|
||||
func BenchmarkKeannuAndSpeed(b *testing.B) {
|
||||
func BenchmarkKeanuAndSpeed(b *testing.B) {
|
||||
runBench(6, b)
|
||||
}
|
||||
|
||||
func BenchmarkKeannuOther(b *testing.B) {
|
||||
func BenchmarkKeanuOther(b *testing.B) {
|
||||
runBench(7, b)
|
||||
}
|
||||
|
||||
func BenchmarkKeannuBullockOther(b *testing.B) {
|
||||
func BenchmarkKeanuBullockOther(b *testing.B) {
|
||||
runBench(8, b)
|
||||
}
|
||||
|
|
|
|||
103
config/config.go
103
config/config.go
|
|
@ -17,29 +17,112 @@ package config
|
|||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/barakmich/glog"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
DatabaseType string
|
||||
DatabasePath string
|
||||
DatabaseOptions map[string]interface{}
|
||||
ListenHost string
|
||||
ListenPort string
|
||||
ReadOnly bool
|
||||
Timeout time.Duration
|
||||
LoadSize int
|
||||
}
|
||||
|
||||
type config struct {
|
||||
DatabaseType string `json:"database"`
|
||||
DatabasePath string `json:"db_path"`
|
||||
DatabaseOptions map[string]interface{} `json:"db_options"`
|
||||
ListenHost string `json:"listen_host"`
|
||||
ListenPort string `json:"listen_port"`
|
||||
ReadOnly bool `json:"read_only"`
|
||||
GremlinTimeout int `json:"gremlin_timeout"`
|
||||
Timeout duration `json:"timeout"`
|
||||
LoadSize int `json:"load_size"`
|
||||
}
|
||||
|
||||
var databasePath = flag.String("dbpath", "/tmp/testdb", "Path to the database.")
|
||||
var databaseBackend = flag.String("db", "memstore", "Database Backend.")
|
||||
var host = flag.String("host", "0.0.0.0", "Host to listen on (defaults to all).")
|
||||
var loadSize = flag.Int("load_size", 10000, "Size of triplesets to load")
|
||||
var port = flag.String("port", "64210", "Port to listen on.")
|
||||
var readOnly = flag.Bool("read_only", false, "Disable writing via HTTP.")
|
||||
var gremlinTimeout = flag.Int("gremlin_timeout", 30, "Number of seconds until an individual query times out.")
|
||||
func (c *Config) UnmarshalJSON(data []byte) error {
|
||||
var t config
|
||||
err := json.Unmarshal(data, &t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*c = Config{
|
||||
DatabaseType: t.DatabaseType,
|
||||
DatabasePath: t.DatabasePath,
|
||||
DatabaseOptions: t.DatabaseOptions,
|
||||
ListenHost: t.ListenHost,
|
||||
ListenPort: t.ListenPort,
|
||||
ReadOnly: t.ReadOnly,
|
||||
Timeout: time.Duration(t.Timeout),
|
||||
LoadSize: t.LoadSize,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(config{
|
||||
DatabaseType: c.DatabaseType,
|
||||
DatabasePath: c.DatabasePath,
|
||||
DatabaseOptions: c.DatabaseOptions,
|
||||
ListenHost: c.ListenHost,
|
||||
ListenPort: c.ListenPort,
|
||||
ReadOnly: c.ReadOnly,
|
||||
Timeout: duration(c.Timeout),
|
||||
LoadSize: c.LoadSize,
|
||||
})
|
||||
}
|
||||
|
||||
// duration is a time.Duration that satisfies the
|
||||
// json.UnMarshaler and json.Marshaler interfaces.
|
||||
type duration time.Duration
|
||||
|
||||
// UnmarshalJSON unmarshals a duration according to the following scheme:
|
||||
// * If the element is absent the duration is zero.
|
||||
// * If the element is parsable as a time.Duration, the parsed value is kept.
|
||||
// * If the element is parsable as a number, that number of seconds is kept.
|
||||
func (d *duration) UnmarshalJSON(data []byte) error {
|
||||
if len(data) == 0 {
|
||||
*d = 0
|
||||
return nil
|
||||
}
|
||||
text := string(data)
|
||||
t, err := time.ParseDuration(text)
|
||||
if err == nil {
|
||||
*d = duration(t)
|
||||
return nil
|
||||
}
|
||||
i, err := strconv.ParseInt(text, 10, 64)
|
||||
if err == nil {
|
||||
*d = duration(time.Duration(i) * time.Second)
|
||||
return nil
|
||||
}
|
||||
// This hack is to get around strconv.ParseFloat
|
||||
// not handling e-notation for integers.
|
||||
f, err := strconv.ParseFloat(text, 64)
|
||||
*d = duration(time.Duration(f) * time.Second)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *duration) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf("%q", *d)), nil
|
||||
}
|
||||
|
||||
var (
|
||||
databasePath = flag.String("dbpath", "/tmp/testdb", "Path to the database.")
|
||||
databaseBackend = flag.String("db", "memstore", "Database Backend.")
|
||||
host = flag.String("host", "0.0.0.0", "Host to listen on (defaults to all).")
|
||||
loadSize = flag.Int("load_size", 10000, "Size of triplesets to load")
|
||||
port = flag.String("port", "64210", "Port to listen on.")
|
||||
readOnly = flag.Bool("read_only", false, "Disable writing via HTTP.")
|
||||
timeout = flag.Duration("timeout", 30*time.Second, "Elapsed time until an individual query times out.")
|
||||
)
|
||||
|
||||
func ParseConfigFromFile(filename string) *Config {
|
||||
config := &Config{}
|
||||
|
|
@ -100,8 +183,8 @@ func ParseConfigFromFlagsAndFile(fileFlag string) *Config {
|
|||
config.ListenPort = *port
|
||||
}
|
||||
|
||||
if config.GremlinTimeout == 0 {
|
||||
config.GremlinTimeout = *gremlinTimeout
|
||||
if config.Timeout == 0 {
|
||||
config.Timeout = *timeout
|
||||
}
|
||||
|
||||
if config.LoadSize == 0 {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ func Load(ts graph.TripleStore, cfg *config.Config, path string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
block := make([]*quad.Quad, 0, cfg.LoadSize)
|
||||
block := make([]quad.Quad, 0, cfg.LoadSize)
|
||||
for {
|
||||
t, err := dec.Unmarshal()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ func Repl(ts graph.TripleStore, queryLanguage string, cfg *config.Config) error
|
|||
case "gremlin":
|
||||
fallthrough
|
||||
default:
|
||||
ses = gremlin.NewSession(ts, cfg.GremlinTimeout, true)
|
||||
ses = gremlin.NewSession(ts, cfg.Timeout, true)
|
||||
}
|
||||
buf := bufio.NewReader(os.Stdin)
|
||||
var line []byte
|
||||
|
|
@ -114,7 +114,7 @@ func Repl(ts graph.TripleStore, queryLanguage string, cfg *config.Config) error
|
|||
if bytes.HasPrefix(line, []byte(":a")) {
|
||||
var tripleStmt = line[3:]
|
||||
triple, err := cquads.Parse(string(tripleStmt))
|
||||
if triple == nil {
|
||||
if !triple.IsValid() {
|
||||
if err != nil {
|
||||
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")) {
|
||||
var tripleStmt = line[3:]
|
||||
triple, err := cquads.Parse(string(tripleStmt))
|
||||
if triple == nil {
|
||||
if !triple.IsValid() {
|
||||
if err != nil {
|
||||
fmt.Printf("not a valid triple: %v\n", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,12 +72,12 @@ All command line flags take precedence over the configuration file.
|
|||
|
||||
## Language Options
|
||||
|
||||
#### **`gremlin_timeout`**
|
||||
#### **`timeout`**
|
||||
|
||||
* Type: Integer
|
||||
* Type: Integer or String
|
||||
* Default: 30
|
||||
|
||||
The value in seconds of the maximum length of time the Javascript runtime should run until cancelling the query and returning a 408 Timeout. A negative value means no limit.
|
||||
The maximum length of time the Javascript runtime should run until cancelling the query and returning a 408 Timeout. When timeout is an integer is is interpretted as seconds, when it is a string it is [parsed](http://golang.org/pkg/time/#ParseDuration) as a Go time.Duration. A negative duration means no limit.
|
||||
|
||||
## Per-Database Options
|
||||
|
||||
|
|
|
|||
|
|
@ -157,6 +157,22 @@ func Next(it Iterator) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Height is a convienence function to measure the height of an iterator tree.
|
||||
func Height(it Iterator, until Type) int {
|
||||
if it.Type() == until {
|
||||
return 1
|
||||
}
|
||||
subs := it.SubIterators()
|
||||
maxDepth := 0
|
||||
for _, sub := range subs {
|
||||
h := Height(sub, until)
|
||||
if h > maxDepth {
|
||||
maxDepth = h
|
||||
}
|
||||
}
|
||||
return maxDepth + 1
|
||||
}
|
||||
|
||||
// FixedIterator wraps iterators that are modifiable by addition of fixed value sets.
|
||||
type FixedIterator interface {
|
||||
Iterator
|
||||
|
|
@ -184,6 +200,7 @@ const (
|
|||
Fixed
|
||||
Not
|
||||
Optional
|
||||
Materialize
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -204,6 +221,7 @@ var (
|
|||
"fixed",
|
||||
"not",
|
||||
"optional",
|
||||
"materialize",
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ func (it *And) Optimize() (graph.Iterator, bool) {
|
|||
// now a permutation of itself, but the contents are unchanged.
|
||||
its = optimizeOrder(its)
|
||||
|
||||
its = materializeIts(its)
|
||||
|
||||
// Okay! At this point we have an optimized order.
|
||||
|
||||
// The easiest thing to do at this point is merely to create a new And iterator
|
||||
|
|
@ -293,6 +295,21 @@ func hasOneUsefulIterator(its []graph.Iterator) graph.Iterator {
|
|||
return nil
|
||||
}
|
||||
|
||||
func materializeIts(its []graph.Iterator) []graph.Iterator {
|
||||
var out []graph.Iterator
|
||||
for _, it := range its {
|
||||
stats := it.Stats()
|
||||
if stats.Size*stats.NextCost < stats.ContainsCost {
|
||||
if graph.Height(it, graph.Materialize) > 10 {
|
||||
out = append(out, NewMaterialize(it))
|
||||
continue
|
||||
}
|
||||
}
|
||||
out = append(out, it)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// and.Stats() lives here in and-iterator-optimize.go because it may
|
||||
// in the future return different statistics based on how it is optimized.
|
||||
// For now, however, it's pretty static.
|
||||
|
|
|
|||
273
graph/iterator/materialize_iterator.go
Normal file
273
graph/iterator/materialize_iterator.go
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
// Copyright 2014 The Cayley Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package iterator
|
||||
|
||||
// A simple iterator that, when first called Contains() or Next() upon, materializes the whole subiterator, stores it locally, and responds. Essentially a cache.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/barakmich/glog"
|
||||
|
||||
"github.com/google/cayley/graph"
|
||||
)
|
||||
|
||||
var abortMaterializeAt = 1000
|
||||
|
||||
type result struct {
|
||||
id graph.Value
|
||||
tags map[string]graph.Value
|
||||
}
|
||||
|
||||
type Keyer interface {
|
||||
Key() 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.(Keyer); ok {
|
||||
key = h.Key()
|
||||
}
|
||||
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.(Keyer); ok {
|
||||
val = h.Key()
|
||||
}
|
||||
if _, ok := it.containsMap[val]; !ok {
|
||||
it.containsMap[val] = len(it.values)
|
||||
it.values = append(it.values, nil)
|
||||
}
|
||||
index := it.containsMap[val]
|
||||
tags := make(map[string]graph.Value)
|
||||
it.subIt.TagResults(tags)
|
||||
it.values[index] = append(it.values[index], result{id: id, tags: tags})
|
||||
for it.subIt.NextResult() == true {
|
||||
tags := make(map[string]graph.Value)
|
||||
it.subIt.TagResults(tags)
|
||||
it.values[index] = append(it.values[index], result{id: id, tags: tags})
|
||||
}
|
||||
}
|
||||
if it.aborted {
|
||||
it.values = nil
|
||||
it.containsMap = nil
|
||||
it.subIt.Reset()
|
||||
}
|
||||
glog.Infof("Materialization List %d: %#v", it.values)
|
||||
it.hasRun = true
|
||||
}
|
||||
|
|
@ -36,11 +36,11 @@ func (qs *store) ValueOf(s string) graph.Value {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (qs *store) AddTriple(*quad.Quad) {}
|
||||
func (qs *store) AddTriple(quad.Quad) {}
|
||||
|
||||
func (qs *store) AddTripleSet([]*quad.Quad) {}
|
||||
func (qs *store) AddTripleSet([]quad.Quad) {}
|
||||
|
||||
func (qs *store) Quad(graph.Value) *quad.Quad { return &quad.Quad{} }
|
||||
func (qs *store) Quad(graph.Value) quad.Quad { return quad.Quad{} }
|
||||
|
||||
func (qs *store) TripleIterator(d quad.Direction, i graph.Value) graph.Iterator {
|
||||
return qs.iter
|
||||
|
|
@ -74,4 +74,4 @@ func (qs *store) Close() {}
|
|||
|
||||
func (qs *store) TripleDirection(graph.Value, quad.Direction) graph.Value { return 0 }
|
||||
|
||||
func (qs *store) RemoveTriple(t *quad.Quad) {}
|
||||
func (qs *store) RemoveTriple(t quad.Quad) {}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ func (it *AllIterator) Next() bool {
|
|||
it.Close()
|
||||
return false
|
||||
}
|
||||
it.result = out
|
||||
it.result = Token(out)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ type Iterator struct {
|
|||
}
|
||||
|
||||
func NewIterator(prefix string, d quad.Direction, value graph.Value, qs *TripleStore) *Iterator {
|
||||
vb := value.([]byte)
|
||||
vb := value.(Token)
|
||||
p := make([]byte, 0, 2+qs.hasher.Size())
|
||||
p = append(p, []byte(prefix)...)
|
||||
p = append(p, []byte(vb[1:])...)
|
||||
|
|
@ -105,7 +105,7 @@ func (it *Iterator) TagResults(dst map[string]graph.Value) {
|
|||
}
|
||||
|
||||
func (it *Iterator) Clone() graph.Iterator {
|
||||
out := NewIterator(it.originalPrefix, it.dir, it.checkId, it.qs)
|
||||
out := NewIterator(it.originalPrefix, it.dir, Token(it.checkId), it.qs)
|
||||
out.tags.CopyFrom(it)
|
||||
return out
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ func (it *Iterator) Next() bool {
|
|||
if bytes.HasPrefix(it.iter.Key(), it.nextPrefix) {
|
||||
out := make([]byte, len(it.iter.Key()))
|
||||
copy(out, it.iter.Key())
|
||||
it.result = out
|
||||
it.result = Token(out)
|
||||
ok := it.iter.Next()
|
||||
if !ok {
|
||||
it.Close()
|
||||
|
|
@ -216,7 +216,7 @@ func PositionOf(prefix []byte, d quad.Direction, qs *TripleStore) int {
|
|||
}
|
||||
|
||||
func (it *Iterator) Contains(v graph.Value) bool {
|
||||
val := v.([]byte)
|
||||
val := v.(Token)
|
||||
if val[0] == 'z' {
|
||||
return false
|
||||
}
|
||||
|
|
@ -227,7 +227,7 @@ func (it *Iterator) Contains(v graph.Value) bool {
|
|||
}
|
||||
} else {
|
||||
nameForDir := it.qs.Quad(v).Get(it.dir)
|
||||
hashForDir := it.qs.ValueOf(nameForDir).([]byte)
|
||||
hashForDir := it.qs.ValueOf(nameForDir).(Token)
|
||||
if bytes.Equal(hashForDir, it.checkId) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -236,12 +236,20 @@ func (it *Iterator) Contains(v graph.Value) bool {
|
|||
}
|
||||
|
||||
func (it *Iterator) Size() (int64, bool) {
|
||||
return it.qs.SizeOf(it.checkId), true
|
||||
return it.qs.SizeOf(Token(it.checkId)), true
|
||||
}
|
||||
|
||||
func (it *Iterator) DebugString(indent int) string {
|
||||
size, _ := it.Size()
|
||||
return fmt.Sprintf("%s(%s %d tags: %v dir: %s size:%d %s)", strings.Repeat(" ", indent), it.Type(), it.UID(), it.tags.Tags(), it.dir, size, it.qs.NameOf(it.checkId))
|
||||
return fmt.Sprintf("%s(%s %d tags: %v dir: %s size:%d %s)",
|
||||
strings.Repeat(" ", indent),
|
||||
it.Type(),
|
||||
it.UID(),
|
||||
it.tags.Tags(),
|
||||
it.dir,
|
||||
size,
|
||||
it.qs.NameOf(Token(it.checkId)),
|
||||
)
|
||||
}
|
||||
|
||||
var levelDBType graph.Type
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ import (
|
|||
"github.com/google/cayley/quad"
|
||||
)
|
||||
|
||||
func makeTripleSet() []*quad.Quad {
|
||||
tripleSet := []*quad.Quad{
|
||||
func makeTripleSet() []quad.Quad {
|
||||
tripleSet := []quad.Quad{
|
||||
{"A", "follows", "B", ""},
|
||||
{"C", "follows", "B", ""},
|
||||
{"C", "follows", "D", ""},
|
||||
|
|
@ -43,7 +43,7 @@ func makeTripleSet() []*quad.Quad {
|
|||
return tripleSet
|
||||
}
|
||||
|
||||
func iteratedTriples(qs graph.TripleStore, it graph.Iterator) []*quad.Quad {
|
||||
func iteratedTriples(qs graph.TripleStore, it graph.Iterator) []quad.Quad {
|
||||
var res ordered
|
||||
for graph.Next(it) {
|
||||
res = append(res, qs.Quad(it.Result()))
|
||||
|
|
@ -52,7 +52,7 @@ func iteratedTriples(qs graph.TripleStore, it graph.Iterator) []*quad.Quad {
|
|||
return res
|
||||
}
|
||||
|
||||
type ordered []*quad.Quad
|
||||
type ordered []quad.Quad
|
||||
|
||||
func (o ordered) Len() int { return len(o) }
|
||||
func (o ordered) Less(i, j int) bool {
|
||||
|
|
@ -135,7 +135,7 @@ func TestLoadDatabase(t *testing.T) {
|
|||
t.Error("Failed to create leveldb TripleStore.")
|
||||
}
|
||||
|
||||
qs.AddTriple(&quad.Quad{"Something", "points_to", "Something Else", "context"})
|
||||
qs.AddTriple(quad.Quad{"Something", "points_to", "Something Else", "context"})
|
||||
for _, pq := range []string{"Something", "points_to", "Something Else", "context"} {
|
||||
if got := qs.NameOf(qs.ValueOf(pq)); got != pq {
|
||||
t.Errorf("Failed to roundtrip %q, got:%q expect:%q", pq, got, pq)
|
||||
|
|
@ -168,7 +168,7 @@ func TestLoadDatabase(t *testing.T) {
|
|||
t.Errorf("Unexpected triplestore size, got:%d expect:5", s)
|
||||
}
|
||||
|
||||
qs.RemoveTriple(&quad.Quad{"A", "follows", "B", ""})
|
||||
qs.RemoveTriple(quad.Quad{"A", "follows", "B", ""})
|
||||
if s := qs.Size(); s != 10 {
|
||||
t.Errorf("Unexpected triplestore size after RemoveTriple, got:%d expect:10", s)
|
||||
}
|
||||
|
|
@ -293,7 +293,7 @@ func TestSetIterator(t *testing.T) {
|
|||
|
||||
qs.AddTripleSet(makeTripleSet())
|
||||
|
||||
expect := []*quad.Quad{
|
||||
expect := []quad.Quad{
|
||||
{"C", "follows", "B", ""},
|
||||
{"C", "follows", "D", ""},
|
||||
}
|
||||
|
|
@ -318,7 +318,7 @@ func TestSetIterator(t *testing.T) {
|
|||
// Object iterator.
|
||||
it = qs.TripleIterator(quad.Object, qs.ValueOf("F"))
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "follows", "F", ""},
|
||||
{"E", "follows", "F", ""},
|
||||
}
|
||||
|
|
@ -331,7 +331,7 @@ func TestSetIterator(t *testing.T) {
|
|||
and.AddSubIterator(qs.TripleIterator(quad.Subject, qs.ValueOf("B")))
|
||||
and.AddSubIterator(it)
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "follows", "F", ""},
|
||||
}
|
||||
if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) {
|
||||
|
|
@ -341,7 +341,7 @@ func TestSetIterator(t *testing.T) {
|
|||
// Predicate iterator.
|
||||
it = qs.TripleIterator(quad.Predicate, qs.ValueOf("status"))
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "status", "cool", "status_graph"},
|
||||
{"D", "status", "cool", "status_graph"},
|
||||
{"G", "status", "cool", "status_graph"},
|
||||
|
|
@ -354,7 +354,7 @@ func TestSetIterator(t *testing.T) {
|
|||
// Label iterator.
|
||||
it = qs.TripleIterator(quad.Label, qs.ValueOf("status_graph"))
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "status", "cool", "status_graph"},
|
||||
{"D", "status", "cool", "status_graph"},
|
||||
{"G", "status", "cool", "status_graph"},
|
||||
|
|
@ -370,7 +370,7 @@ func TestSetIterator(t *testing.T) {
|
|||
and.AddSubIterator(qs.TripleIterator(quad.Subject, qs.ValueOf("B")))
|
||||
and.AddSubIterator(it)
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "status", "cool", "status_graph"},
|
||||
}
|
||||
if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) {
|
||||
|
|
@ -383,7 +383,7 @@ func TestSetIterator(t *testing.T) {
|
|||
and.AddSubIterator(it)
|
||||
and.AddSubIterator(qs.TripleIterator(quad.Subject, qs.ValueOf("B")))
|
||||
|
||||
expect = []*quad.Quad{
|
||||
expect = []quad.Quad{
|
||||
{"B", "status", "cool", "status_graph"},
|
||||
}
|
||||
if got := iteratedTriples(qs, and); !reflect.DeepEqual(got, expect) {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,12 @@ const (
|
|||
DefaultWriteBufferSize = 20
|
||||
)
|
||||
|
||||
type Token []byte
|
||||
|
||||
func (t Token) Key() interface{} {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
type TripleStore struct {
|
||||
dbOpts *opt.Options
|
||||
db *leveldb.DB
|
||||
|
|
@ -116,7 +122,7 @@ func (qs *TripleStore) Size() int64 {
|
|||
return qs.size
|
||||
}
|
||||
|
||||
func (qs *TripleStore) createKeyFor(d [3]quad.Direction, triple *quad.Quad) []byte {
|
||||
func (qs *TripleStore) createKeyFor(d [3]quad.Direction, triple quad.Quad) []byte {
|
||||
key := make([]byte, 0, 2+(qs.hasher.Size()*3))
|
||||
// TODO(kortschak) Remove dependence on String() method.
|
||||
key = append(key, []byte{d[0].Prefix(), d[1].Prefix()}...)
|
||||
|
|
@ -126,7 +132,7 @@ func (qs *TripleStore) createKeyFor(d [3]quad.Direction, triple *quad.Quad) []by
|
|||
return key
|
||||
}
|
||||
|
||||
func (qs *TripleStore) createProvKeyFor(d [3]quad.Direction, triple *quad.Quad) []byte {
|
||||
func (qs *TripleStore) createProvKeyFor(d [3]quad.Direction, triple quad.Quad) []byte {
|
||||
key := make([]byte, 0, 2+(qs.hasher.Size()*4))
|
||||
// TODO(kortschak) Remove dependence on String() method.
|
||||
key = append(key, []byte{quad.Label.Prefix(), d[0].Prefix()}...)
|
||||
|
|
@ -144,7 +150,7 @@ func (qs *TripleStore) createValueKeyFor(s string) []byte {
|
|||
return key
|
||||
}
|
||||
|
||||
func (qs *TripleStore) AddTriple(t *quad.Quad) {
|
||||
func (qs *TripleStore) AddTriple(t quad.Quad) {
|
||||
batch := &leveldb.Batch{}
|
||||
qs.buildWrite(batch, t)
|
||||
err := qs.db.Write(batch, qs.writeopts)
|
||||
|
|
@ -163,7 +169,7 @@ var (
|
|||
pso = [3]quad.Direction{quad.Predicate, quad.Subject, quad.Object}
|
||||
)
|
||||
|
||||
func (qs *TripleStore) RemoveTriple(t *quad.Quad) {
|
||||
func (qs *TripleStore) RemoveTriple(t quad.Quad) {
|
||||
_, err := qs.db.Get(qs.createKeyFor(spo, t), qs.readopts)
|
||||
if err != nil && err != leveldb.ErrNotFound {
|
||||
glog.Error("Couldn't access DB to confirm deletion")
|
||||
|
|
@ -192,8 +198,8 @@ func (qs *TripleStore) RemoveTriple(t *quad.Quad) {
|
|||
qs.size--
|
||||
}
|
||||
|
||||
func (qs *TripleStore) buildTripleWrite(batch *leveldb.Batch, t *quad.Quad) {
|
||||
bytes, err := json.Marshal(*t)
|
||||
func (qs *TripleStore) buildTripleWrite(batch *leveldb.Batch, t quad.Quad) {
|
||||
bytes, err := json.Marshal(t)
|
||||
if err != nil {
|
||||
glog.Errorf("Couldn't write to buffer for triple %s: %s", t, err)
|
||||
return
|
||||
|
|
@ -206,7 +212,7 @@ func (qs *TripleStore) buildTripleWrite(batch *leveldb.Batch, t *quad.Quad) {
|
|||
}
|
||||
}
|
||||
|
||||
func (qs *TripleStore) buildWrite(batch *leveldb.Batch, t *quad.Quad) {
|
||||
func (qs *TripleStore) buildWrite(batch *leveldb.Batch, t quad.Quad) {
|
||||
qs.buildTripleWrite(batch, t)
|
||||
qs.UpdateValueKeyBy(t.Get(quad.Subject), 1, nil)
|
||||
qs.UpdateValueKeyBy(t.Get(quad.Predicate), 1, nil)
|
||||
|
|
@ -267,7 +273,7 @@ func (qs *TripleStore) UpdateValueKeyBy(name string, amount int, batch *leveldb.
|
|||
}
|
||||
}
|
||||
|
||||
func (qs *TripleStore) AddTripleSet(t_s []*quad.Quad) {
|
||||
func (qs *TripleStore) AddTripleSet(t_s []quad.Quad) {
|
||||
batch := &leveldb.Batch{}
|
||||
newTs := len(t_s)
|
||||
resizeMap := make(map[string]int)
|
||||
|
|
@ -306,23 +312,23 @@ func (qs *TripleStore) Close() {
|
|||
qs.open = false
|
||||
}
|
||||
|
||||
func (qs *TripleStore) Quad(k graph.Value) *quad.Quad {
|
||||
func (qs *TripleStore) Quad(k graph.Value) quad.Quad {
|
||||
var triple quad.Quad
|
||||
b, err := qs.db.Get(k.([]byte), qs.readopts)
|
||||
b, err := qs.db.Get(k.(Token), qs.readopts)
|
||||
if err != nil && err != leveldb.ErrNotFound {
|
||||
glog.Error("Error: couldn't get triple from DB.")
|
||||
return &quad.Quad{}
|
||||
return quad.Quad{}
|
||||
}
|
||||
if err == leveldb.ErrNotFound {
|
||||
// No harm, no foul.
|
||||
return &quad.Quad{}
|
||||
return quad.Quad{}
|
||||
}
|
||||
err = json.Unmarshal(b, &triple)
|
||||
if err != nil {
|
||||
glog.Error("Error: couldn't reconstruct triple.")
|
||||
return &quad.Quad{}
|
||||
return quad.Quad{}
|
||||
}
|
||||
return &triple
|
||||
return triple
|
||||
}
|
||||
|
||||
func (qs *TripleStore) convertStringToByteHash(s string) []byte {
|
||||
|
|
@ -334,7 +340,7 @@ func (qs *TripleStore) convertStringToByteHash(s string) []byte {
|
|||
}
|
||||
|
||||
func (qs *TripleStore) ValueOf(s string) graph.Value {
|
||||
return qs.createValueKeyFor(s)
|
||||
return Token(qs.createValueKeyFor(s))
|
||||
}
|
||||
|
||||
func (qs *TripleStore) valueData(value_key []byte) ValueData {
|
||||
|
|
@ -362,14 +368,14 @@ func (qs *TripleStore) NameOf(k graph.Value) string {
|
|||
glog.V(2).Info("k was nil")
|
||||
return ""
|
||||
}
|
||||
return qs.valueData(k.([]byte)).Name
|
||||
return qs.valueData(k.(Token)).Name
|
||||
}
|
||||
|
||||
func (qs *TripleStore) SizeOf(k graph.Value) int64 {
|
||||
if k == nil {
|
||||
return 0
|
||||
}
|
||||
return int64(qs.valueData(k.([]byte)).Size)
|
||||
return int64(qs.valueData(k.(Token)).Size)
|
||||
}
|
||||
|
||||
func (qs *TripleStore) getSize() {
|
||||
|
|
@ -432,17 +438,17 @@ func (qs *TripleStore) TriplesAllIterator() graph.Iterator {
|
|||
}
|
||||
|
||||
func (qs *TripleStore) TripleDirection(val graph.Value, d quad.Direction) graph.Value {
|
||||
v := val.([]uint8)
|
||||
v := val.(Token)
|
||||
offset := PositionOf(v[0:2], d, qs)
|
||||
if offset != -1 {
|
||||
return append([]byte("z"), v[offset:offset+qs.hasher.Size()]...)
|
||||
return Token(append([]byte("z"), v[offset:offset+qs.hasher.Size()]...))
|
||||
} else {
|
||||
return qs.Quad(val).Get(d)
|
||||
return Token(qs.Quad(val).Get(d))
|
||||
}
|
||||
}
|
||||
|
||||
func compareBytes(a, b graph.Value) bool {
|
||||
return bytes.Equal(a.([]uint8), b.([]uint8))
|
||||
return bytes.Equal(a.(Token), b.(Token))
|
||||
}
|
||||
|
||||
func (qs *TripleStore) FixedIterator() graph.FixedIterator {
|
||||
|
|
|
|||
|
|
@ -101,13 +101,13 @@ func newTripleStore() *TripleStore {
|
|||
return &ts
|
||||
}
|
||||
|
||||
func (ts *TripleStore) AddTripleSet(triples []*quad.Quad) {
|
||||
func (ts *TripleStore) AddTripleSet(triples []quad.Quad) {
|
||||
for _, t := range triples {
|
||||
ts.AddTriple(t)
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *TripleStore) tripleExists(t *quad.Quad) (bool, int64) {
|
||||
func (ts *TripleStore) tripleExists(t quad.Quad) (bool, int64) {
|
||||
smallest := -1
|
||||
var smallest_tree *llrb.LLRB
|
||||
for d := quad.Subject; d <= quad.Label; d++ {
|
||||
|
|
@ -134,19 +134,19 @@ func (ts *TripleStore) tripleExists(t *quad.Quad) (bool, int64) {
|
|||
|
||||
for it.Next() {
|
||||
val := it.Result()
|
||||
if t.Equals(&ts.triples[val.(int64)]) {
|
||||
if t == ts.triples[val.(int64)] {
|
||||
return true, val.(int64)
|
||||
}
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
func (ts *TripleStore) AddTriple(t *quad.Quad) {
|
||||
func (ts *TripleStore) AddTriple(t quad.Quad) {
|
||||
if exists, _ := ts.tripleExists(t); exists {
|
||||
return
|
||||
}
|
||||
var tripleID int64
|
||||
ts.triples = append(ts.triples, *t)
|
||||
ts.triples = append(ts.triples, t)
|
||||
tripleID = ts.tripleIdCounter
|
||||
ts.size++
|
||||
ts.tripleIdCounter++
|
||||
|
|
@ -175,7 +175,7 @@ func (ts *TripleStore) AddTriple(t *quad.Quad) {
|
|||
// TODO(barakmich): Add VIP indexing
|
||||
}
|
||||
|
||||
func (ts *TripleStore) RemoveTriple(t *quad.Quad) {
|
||||
func (ts *TripleStore) RemoveTriple(t quad.Quad) {
|
||||
var tripleID int64
|
||||
var exists bool
|
||||
tripleID = 0
|
||||
|
|
@ -221,8 +221,8 @@ func (ts *TripleStore) RemoveTriple(t *quad.Quad) {
|
|||
}
|
||||
}
|
||||
|
||||
func (ts *TripleStore) Quad(index graph.Value) *quad.Quad {
|
||||
return &ts.triples[index.(int64)]
|
||||
func (ts *TripleStore) Quad(index graph.Value) quad.Quad {
|
||||
return ts.triples[index.(int64)]
|
||||
}
|
||||
|
||||
func (ts *TripleStore) TripleIterator(d quad.Direction, value graph.Value) graph.Iterator {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import (
|
|||
// \-->|#D#|------------->+---+
|
||||
// +---+
|
||||
//
|
||||
var simpleGraph = []*quad.Quad{
|
||||
var simpleGraph = []quad.Quad{
|
||||
{"A", "follows", "B", ""},
|
||||
{"C", "follows", "B", ""},
|
||||
{"C", "follows", "D", ""},
|
||||
|
|
@ -51,7 +51,7 @@ var simpleGraph = []*quad.Quad{
|
|||
{"G", "status", "cool", "status_graph"},
|
||||
}
|
||||
|
||||
func makeTestStore(data []*quad.Quad) (*TripleStore, []pair) {
|
||||
func makeTestStore(data []quad.Quad) (*TripleStore, []pair) {
|
||||
seen := make(map[string]struct{})
|
||||
ts := newTripleStore()
|
||||
var (
|
||||
|
|
@ -174,7 +174,7 @@ func TestLinksToOptimization(t *testing.T) {
|
|||
func TestRemoveTriple(t *testing.T) {
|
||||
ts, _ := makeTestStore(simpleGraph)
|
||||
|
||||
ts.RemoveTriple(&quad.Quad{"E", "follows", "F", ""})
|
||||
ts.RemoveTriple(quad.Quad{"E", "follows", "F", ""})
|
||||
|
||||
fixed := ts.FixedIterator()
|
||||
fixed.Add(ts.ValueOf("E"))
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ func newTripleStore(addr string, options graph.Options) (graph.TripleStore, erro
|
|||
return &qs, nil
|
||||
}
|
||||
|
||||
func (qs *TripleStore) getIdForTriple(t *quad.Quad) string {
|
||||
func (qs *TripleStore) getIdForTriple(t quad.Quad) string {
|
||||
id := qs.ConvertStringToByteHash(t.Subject)
|
||||
id += qs.ConvertStringToByteHash(t.Predicate)
|
||||
id += qs.ConvertStringToByteHash(t.Object)
|
||||
|
|
@ -150,7 +150,7 @@ func (qs *TripleStore) updateNodeBy(node_name string, inc int) {
|
|||
}
|
||||
}
|
||||
|
||||
func (qs *TripleStore) writeTriple(t *quad.Quad) bool {
|
||||
func (qs *TripleStore) writeTriple(t quad.Quad) bool {
|
||||
tripledoc := bson.M{
|
||||
"_id": qs.getIdForTriple(t),
|
||||
"Subject": t.Subject,
|
||||
|
|
@ -170,7 +170,7 @@ func (qs *TripleStore) writeTriple(t *quad.Quad) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (qs *TripleStore) AddTriple(t *quad.Quad) {
|
||||
func (qs *TripleStore) AddTriple(t quad.Quad) {
|
||||
_ = qs.writeTriple(t)
|
||||
qs.updateNodeBy(t.Subject, 1)
|
||||
qs.updateNodeBy(t.Predicate, 1)
|
||||
|
|
@ -180,7 +180,7 @@ func (qs *TripleStore) AddTriple(t *quad.Quad) {
|
|||
}
|
||||
}
|
||||
|
||||
func (qs *TripleStore) AddTripleSet(in []*quad.Quad) {
|
||||
func (qs *TripleStore) AddTripleSet(in []quad.Quad) {
|
||||
qs.session.SetSafe(nil)
|
||||
ids := make(map[string]int)
|
||||
for _, t := range in {
|
||||
|
|
@ -200,7 +200,7 @@ func (qs *TripleStore) AddTripleSet(in []*quad.Quad) {
|
|||
qs.session.SetSafe(&mgo.Safe{})
|
||||
}
|
||||
|
||||
func (qs *TripleStore) RemoveTriple(t *quad.Quad) {
|
||||
func (qs *TripleStore) RemoveTriple(t quad.Quad) {
|
||||
err := qs.db.C("triples").RemoveId(qs.getIdForTriple(t))
|
||||
if err == mgo.ErrNotFound {
|
||||
return
|
||||
|
|
@ -216,13 +216,13 @@ func (qs *TripleStore) RemoveTriple(t *quad.Quad) {
|
|||
}
|
||||
}
|
||||
|
||||
func (qs *TripleStore) Quad(val graph.Value) *quad.Quad {
|
||||
func (qs *TripleStore) Quad(val graph.Value) quad.Quad {
|
||||
var bsonDoc bson.M
|
||||
err := qs.db.C("triples").FindId(val.(string)).One(&bsonDoc)
|
||||
if err != nil {
|
||||
glog.Errorf("Error: Couldn't retrieve triple %s %v", val, err)
|
||||
}
|
||||
return &quad.Quad{
|
||||
return quad.Quad{
|
||||
bsonDoc["Subject"].(string),
|
||||
bsonDoc["Predicate"].(string),
|
||||
bsonDoc["Object"].(string),
|
||||
|
|
|
|||
|
|
@ -28,29 +28,32 @@ import (
|
|||
"github.com/google/cayley/quad"
|
||||
)
|
||||
|
||||
// Defines an opaque "triple store value" type. However the backend wishes to
|
||||
// implement it, a Value is merely a token to a triple or a node that the backing
|
||||
// store itself understands, and the base iterators pass around.
|
||||
// Value defines an opaque "triple store value" type. However the backend wishes
|
||||
// to implement it, a Value is merely a token to a triple or a node that the
|
||||
// backing store itself understands, and the base iterators pass around.
|
||||
//
|
||||
// For example, in a very traditional, graphd-style graph, these are int64s
|
||||
// (guids of the primitives). In a very direct sort of graph, these could be
|
||||
// pointers to structs, or merely triples, or whatever works best for the
|
||||
// backing store.
|
||||
//
|
||||
// These must be comparable, or implement a `Key() interface{}` function
|
||||
// so that they may be stored in maps.
|
||||
type Value interface{}
|
||||
|
||||
type TripleStore interface {
|
||||
// Add a triple to the store.
|
||||
AddTriple(*quad.Quad)
|
||||
AddTriple(quad.Quad)
|
||||
|
||||
// Add a set of triples to the store, atomically if possible.
|
||||
AddTripleSet([]*quad.Quad)
|
||||
AddTripleSet([]quad.Quad)
|
||||
|
||||
// Removes a triple matching the given one from the database,
|
||||
// if it exists. Does nothing otherwise.
|
||||
RemoveTriple(*quad.Quad)
|
||||
RemoveTriple(quad.Quad)
|
||||
|
||||
// Given an opaque token, returns the triple for that token from the store.
|
||||
Quad(Value) *quad.Quad
|
||||
Quad(Value) quad.Quad
|
||||
|
||||
// Given a direction and a token, creates an iterator of links which have
|
||||
// that node token in that directional field.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
var parseTests = []struct {
|
||||
message string
|
||||
input string
|
||||
expect []*quad.Quad
|
||||
expect []quad.Quad
|
||||
err error
|
||||
}{
|
||||
{
|
||||
|
|
@ -34,7 +34,7 @@ var parseTests = []struct {
|
|||
{"subject": "foo", "predicate": "bar", "object": "baz"},
|
||||
{"subject": "foo", "predicate": "bar", "object": "baz", "label": "graph"}
|
||||
]`,
|
||||
expect: []*quad.Quad{
|
||||
expect: []quad.Quad{
|
||||
{"foo", "bar", "baz", ""},
|
||||
{"foo", "bar", "baz", "graph"},
|
||||
},
|
||||
|
|
@ -45,7 +45,7 @@ var parseTests = []struct {
|
|||
input: `[
|
||||
{"subject": "foo", "predicate": "bar", "object": "foo", "something_else": "extra data"}
|
||||
]`,
|
||||
expect: []*quad.Quad{
|
||||
expect: []quad.Quad{
|
||||
{"foo", "bar", "foo", ""},
|
||||
},
|
||||
err: nil,
|
||||
|
|
@ -56,7 +56,7 @@ var parseTests = []struct {
|
|||
{"subject": "foo", "predicate": "bar"}
|
||||
]`,
|
||||
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", "", ""}),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ func (api *Api) ServeV1Query(w http.ResponseWriter, r *http.Request, params http
|
|||
var ses query.HttpSession
|
||||
switch params.ByName("query_lang") {
|
||||
case "gremlin":
|
||||
ses = gremlin.NewSession(api.ts, api.config.GremlinTimeout, false)
|
||||
ses = gremlin.NewSession(api.ts, api.config.Timeout, false)
|
||||
case "mql":
|
||||
ses = mql.NewSession(api.ts)
|
||||
default:
|
||||
|
|
@ -119,7 +119,7 @@ func (api *Api) ServeV1Shape(w http.ResponseWriter, r *http.Request, params http
|
|||
var ses query.HttpSession
|
||||
switch params.ByName("query_lang") {
|
||||
case "gremlin":
|
||||
ses = gremlin.NewSession(api.ts, api.config.GremlinTimeout, false)
|
||||
ses = gremlin.NewSession(api.ts, api.config.Timeout, false)
|
||||
case "mql":
|
||||
ses = mql.NewSession(api.ts)
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ import (
|
|||
"github.com/google/cayley/quad/nquads"
|
||||
)
|
||||
|
||||
func ParseJsonToTripleList(jsonBody []byte) ([]*quad.Quad, error) {
|
||||
var tripleList []*quad.Quad
|
||||
func ParseJsonToTripleList(jsonBody []byte) ([]quad.Quad, error) {
|
||||
var tripleList []quad.Quad
|
||||
err := json.Unmarshal(jsonBody, &tripleList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -83,7 +83,7 @@ func (api *Api) ServeV1WriteNQuad(w http.ResponseWriter, r *http.Request, params
|
|||
var (
|
||||
n int
|
||||
|
||||
block = make([]*quad.Quad, 0, blockSize)
|
||||
block = make([]quad.Quad, 0, blockSize)
|
||||
)
|
||||
for {
|
||||
t, err := dec.Unmarshal()
|
||||
|
|
|
|||
|
|
@ -31,14 +31,6 @@ import (
|
|||
"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.
|
||||
type Decoder struct {
|
||||
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.
|
||||
func (dec *Decoder) Unmarshal() (*quad.Quad, error) {
|
||||
func (dec *Decoder) Unmarshal() (quad.Quad, error) {
|
||||
dec.line = dec.line[:0]
|
||||
var line []byte
|
||||
for {
|
||||
for {
|
||||
l, pre, err := dec.r.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return quad.Quad{}, err
|
||||
}
|
||||
dec.line = append(dec.line, l...)
|
||||
if !pre {
|
||||
|
|
@ -73,9 +65,9 @@ func (dec *Decoder) Unmarshal() (*quad.Quad, error) {
|
|||
}
|
||||
triple, err := Parse(string(line))
|
||||
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 triple, nil
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import (
|
|||
var testNTriples = []struct {
|
||||
message string
|
||||
input string
|
||||
expect *quad.Quad
|
||||
expect quad.Quad
|
||||
err error
|
||||
}{
|
||||
// Tests from original nquads.
|
||||
|
|
@ -40,7 +40,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse simple triples",
|
||||
input: "this is valid .",
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "this",
|
||||
Predicate: "is",
|
||||
Object: "valid",
|
||||
|
|
@ -50,7 +50,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse quoted triples",
|
||||
input: `this is "valid too" .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "this",
|
||||
Predicate: "is",
|
||||
Object: "valid too",
|
||||
|
|
@ -60,7 +60,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse escaped quoted triples",
|
||||
input: `he said "\"That's all folks\"" .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "he",
|
||||
Predicate: "said",
|
||||
Object: `"That's all folks"`,
|
||||
|
|
@ -70,7 +70,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse an example real triple",
|
||||
input: `":/guid/9202a8c04000641f80000000010c843c" "name" "George Morris" .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: ":/guid/9202a8c04000641f80000000010c843c",
|
||||
Predicate: "name",
|
||||
Object: "George Morris",
|
||||
|
|
@ -80,7 +80,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse a pathologically spaced triple",
|
||||
input: "foo is \"\\tA big tough\\r\\nDeal\\\\\" .",
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "foo",
|
||||
Predicate: "is",
|
||||
Object: "\tA big tough\r\nDeal\\",
|
||||
|
|
@ -92,7 +92,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse a simple quad",
|
||||
input: "this is valid quad .",
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "this",
|
||||
Predicate: "is",
|
||||
Object: "valid",
|
||||
|
|
@ -102,7 +102,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse a quoted quad",
|
||||
input: `this is valid "quad thing" .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "this",
|
||||
Predicate: "is",
|
||||
Object: "valid",
|
||||
|
|
@ -112,7 +112,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse crazy escaped quads",
|
||||
input: `"\"this" "\"is" "\"valid" "\"quad thing".`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: `"this`,
|
||||
Predicate: `"is`,
|
||||
Object: `"valid`,
|
||||
|
|
@ -124,7 +124,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "handle simple case with comments",
|
||||
input: "<http://example/s> <http://example/p> <http://example/o> . # comment",
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example/s",
|
||||
Predicate: "http://example/p",
|
||||
Object: "http://example/o",
|
||||
|
|
@ -134,7 +134,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "handle simple case with comments",
|
||||
input: "<http://example/s> <http://example/p> _:o . # comment",
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example/s",
|
||||
Predicate: "http://example/p",
|
||||
Object: "_:o",
|
||||
|
|
@ -144,7 +144,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "handle simple case with comments",
|
||||
input: "<http://example/s> <http://example/p> \"o\" . # comment",
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example/s",
|
||||
Predicate: "http://example/p",
|
||||
Object: "o",
|
||||
|
|
@ -154,7 +154,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "handle simple case with comments",
|
||||
input: "<http://example/s> <http://example/p> \"o\"^^<http://example/dt> . # comment",
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example/s",
|
||||
Predicate: "http://example/p",
|
||||
Object: `"o"^^<http://example/dt>`,
|
||||
|
|
@ -164,7 +164,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "handle simple case with comments",
|
||||
input: "<http://example/s> <http://example/p> \"o\"@en . # comment",
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example/s",
|
||||
Predicate: "http://example/p",
|
||||
Object: `"o"@en`,
|
||||
|
|
@ -177,7 +177,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with commment",
|
||||
input: `_:100000 </film/performance/actor> </en/larry_fine_1902> . # example from 30movies`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:100000",
|
||||
Predicate: "/film/performance/actor",
|
||||
Object: "/en/larry_fine_1902",
|
||||
|
|
@ -189,7 +189,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with commment",
|
||||
input: `_:10011 </film/performance/character> "Tomás de Torquemada" . # example from 30movies with unicode`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:10011",
|
||||
Predicate: "/film/performance/character",
|
||||
Object: "Tomás de Torquemada",
|
||||
|
|
@ -202,7 +202,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with commment",
|
||||
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",
|
||||
Predicate: "http://one.example/predicate1",
|
||||
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)",
|
||||
input: `_:subject1 <http://an.example/predicate1> "object1" .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:subject1",
|
||||
Predicate: "http://an.example/predicate1",
|
||||
Object: "object1",
|
||||
|
|
@ -224,7 +224,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with blank subject node, literal object and no comment (2)",
|
||||
input: `_:subject2 <http://an.example/predicate2> "object2" .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:subject2",
|
||||
Predicate: "http://an.example/predicate2",
|
||||
Object: "object2",
|
||||
|
|
@ -237,7 +237,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with three IRIREFs",
|
||||
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",
|
||||
Predicate: "http://www.perceive.net/schemas/relationship/enemyOf",
|
||||
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)",
|
||||
input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:alice",
|
||||
Predicate: "http://xmlns.com/foaf/0.1/knows",
|
||||
Object: "_:bob",
|
||||
|
|
@ -261,7 +261,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with blank node labelled subject and object and IRIREF predicate (2)",
|
||||
input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:bob",
|
||||
Predicate: "http://xmlns.com/foaf/0.1/knows",
|
||||
Object: "_:alice",
|
||||
|
|
@ -274,7 +274,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse quad with commment",
|
||||
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",
|
||||
Predicate: "http://one.example/predicate1",
|
||||
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)",
|
||||
input: `_:subject1 <http://an.example/predicate1> "object1" <http://example.org/graph1> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:subject1",
|
||||
Predicate: "http://an.example/predicate1",
|
||||
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)",
|
||||
input: `_:subject2 <http://an.example/predicate2> "object2" <http://example.org/graph5> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:subject2",
|
||||
Predicate: "http://an.example/predicate2",
|
||||
Object: "object2",
|
||||
|
|
@ -309,7 +309,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example.org/#spiderman",
|
||||
Predicate: "http://www.perceive.net/schemas/relationship/enemyOf",
|
||||
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)",
|
||||
input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob <http://example.org/graphs/john> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:alice",
|
||||
Predicate: "http://xmlns.com/foaf/0.1/knows",
|
||||
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)",
|
||||
input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice <http://example.org/graphs/james> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:bob",
|
||||
Predicate: "http://xmlns.com/foaf/0.1/knows",
|
||||
Object: "_:alice",
|
||||
|
|
@ -346,7 +346,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example.org/bob#me",
|
||||
Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
|
||||
Object: "http://xmlns.com/foaf/0.1/Person",
|
||||
|
|
@ -357,7 +357,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example.org/bob#me",
|
||||
Predicate: "http://xmlns.com/foaf/0.1/knows",
|
||||
Object: "http://example.org/alice#me",
|
||||
|
|
@ -368,7 +368,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example.org/bob#me",
|
||||
Predicate: "http://schema.org/birthDate",
|
||||
Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`,
|
||||
|
|
@ -379,7 +379,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example.org/bob#me",
|
||||
Predicate: "http://xmlns.com/foaf/0.1/topic_interest",
|
||||
Object: "http://www.wikidata.org/entity/Q12418",
|
||||
|
|
@ -390,7 +390,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with literal subject",
|
||||
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",
|
||||
Predicate: "http://purl.org/dc/terms/title",
|
||||
Object: "Mona Lisa",
|
||||
|
|
@ -401,7 +401,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://www.wikidata.org/entity/Q12418",
|
||||
Predicate: "http://purl.org/dc/terms/creator",
|
||||
Object: "http://dbpedia.org/resource/Leonardo_da_Vinci",
|
||||
|
|
@ -412,7 +412,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619",
|
||||
Predicate: "http://purl.org/dc/terms/subject",
|
||||
Object: "http://www.wikidata.org/entity/Q12418",
|
||||
|
|
@ -425,7 +425,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example.org/bob#me",
|
||||
Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
|
||||
Object: "http://xmlns.com/foaf/0.1/Person",
|
||||
|
|
@ -436,7 +436,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example.org/bob#me",
|
||||
Predicate: "http://xmlns.com/foaf/0.1/knows",
|
||||
Object: "http://example.org/alice#me",
|
||||
|
|
@ -447,7 +447,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example.org/bob#me",
|
||||
Predicate: "http://schema.org/birthDate",
|
||||
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)",
|
||||
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",
|
||||
Predicate: "http://xmlns.com/foaf/0.1/topic_interest",
|
||||
Object: "http://www.wikidata.org/entity/Q12418",
|
||||
|
|
@ -469,7 +469,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://www.wikidata.org/entity/Q12418",
|
||||
Predicate: "http://purl.org/dc/terms/title",
|
||||
Object: "Mona Lisa",
|
||||
|
|
@ -480,7 +480,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://www.wikidata.org/entity/Q12418",
|
||||
Predicate: "http://purl.org/dc/terms/creator",
|
||||
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)",
|
||||
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",
|
||||
Predicate: "http://purl.org/dc/terms/subject",
|
||||
Object: "http://www.wikidata.org/entity/Q12418",
|
||||
|
|
@ -502,7 +502,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse all IRIREF parts (quad section - 1)",
|
||||
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",
|
||||
Predicate: "http://purl.org/dc/terms/publisher",
|
||||
Object: "http://example.org",
|
||||
|
|
@ -513,7 +513,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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/> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "http://example.org/bob",
|
||||
Predicate: "http://purl.org/dc/terms/rights",
|
||||
Object: "http://creativecommons.org/licenses/by/3.0/",
|
||||
|
|
@ -526,31 +526,31 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse empty",
|
||||
input: ``,
|
||||
expect: &quad.Quad{},
|
||||
expect: quad.Quad{},
|
||||
err: quad.ErrIncomplete,
|
||||
},
|
||||
{
|
||||
message: "parse commented",
|
||||
input: `# is a comment`,
|
||||
expect: &quad.Quad{},
|
||||
expect: quad.Quad{},
|
||||
err: fmt.Errorf("%v: unexpected rune '#' at 0", quad.ErrInvalid),
|
||||
},
|
||||
{
|
||||
message: "parse commented internal (1)",
|
||||
input: `is # a comment`,
|
||||
expect: &quad.Quad{Subject: "is"},
|
||||
expect: quad.Quad{Subject: "is"},
|
||||
err: fmt.Errorf("%v: unexpected rune '#' at 3", quad.ErrInvalid),
|
||||
},
|
||||
{
|
||||
message: "parse commented internal (2)",
|
||||
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),
|
||||
},
|
||||
{
|
||||
message: "parse incomplete quad (1)",
|
||||
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",
|
||||
Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
|
||||
Object: "",
|
||||
|
|
@ -561,7 +561,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse incomplete quad (2)",
|
||||
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",
|
||||
Predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
|
||||
Object: "",
|
||||
|
|
@ -773,7 +773,7 @@ func TestUnescape(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var result *quad.Quad
|
||||
var result quad.Quad
|
||||
|
||||
func BenchmarkParser(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -33,7 +33,12 @@ import (
|
|||
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 (
|
||||
cs, p int
|
||||
pe = len(data)
|
||||
|
|
|
|||
|
|
@ -30,14 +30,6 @@ import (
|
|||
"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
|
||||
// 1.1 N-Quads specification.
|
||||
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.
|
||||
func (dec *Decoder) Unmarshal() (*quad.Quad, error) {
|
||||
func (dec *Decoder) Unmarshal() (quad.Quad, error) {
|
||||
dec.line = dec.line[:0]
|
||||
var line []byte
|
||||
for {
|
||||
for {
|
||||
l, pre, err := dec.r.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return quad.Quad{}, err
|
||||
}
|
||||
dec.line = append(dec.line, l...)
|
||||
if !pre {
|
||||
|
|
@ -73,9 +65,9 @@ func (dec *Decoder) Unmarshal() (*quad.Quad, error) {
|
|||
}
|
||||
triple, err := Parse(string(line))
|
||||
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 triple, nil
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import (
|
|||
var testNTriples = []struct {
|
||||
message string
|
||||
input string
|
||||
expect *quad.Quad
|
||||
expect quad.Quad
|
||||
err error
|
||||
}{
|
||||
// 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",
|
||||
input: `_:100000 </film/performance/actor> </en/larry_fine_1902> . # example from 30movies`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:100000",
|
||||
Predicate: "</film/performance/actor>",
|
||||
Object: "</en/larry_fine_1902>",
|
||||
|
|
@ -52,7 +52,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with commment",
|
||||
input: `_:10011 </film/performance/character> "Tomás de Torquemada" . # example from 30movies with unicode`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:10011",
|
||||
Predicate: "</film/performance/character>",
|
||||
Object: `"Tomás de Torquemada"`,
|
||||
|
|
@ -65,7 +65,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with commment",
|
||||
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>",
|
||||
Predicate: "<http://one.example/predicate1>",
|
||||
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)",
|
||||
input: `_:subject1 <http://an.example/predicate1> "object1" .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:subject1",
|
||||
Predicate: "<http://an.example/predicate1>",
|
||||
Object: `"object1"`,
|
||||
|
|
@ -87,7 +87,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with blank subject node, literal object and no comment (2)",
|
||||
input: `_:subject2 <http://an.example/predicate2> "object2" .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:subject2",
|
||||
Predicate: "<http://an.example/predicate2>",
|
||||
Object: `"object2"`,
|
||||
|
|
@ -100,7 +100,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with three IRIREFs",
|
||||
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>",
|
||||
Predicate: "<http://www.perceive.net/schemas/relationship/enemyOf>",
|
||||
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)",
|
||||
input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:alice",
|
||||
Predicate: "<http://xmlns.com/foaf/0.1/knows>",
|
||||
Object: "_:bob",
|
||||
|
|
@ -124,7 +124,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with blank node labelled subject and object and IRIREF predicate (2)",
|
||||
input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:bob",
|
||||
Predicate: "<http://xmlns.com/foaf/0.1/knows>",
|
||||
Object: "_:alice",
|
||||
|
|
@ -137,7 +137,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse quad with commment",
|
||||
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>",
|
||||
Predicate: "<http://one.example/predicate1>",
|
||||
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)",
|
||||
input: `_:subject1 <http://an.example/predicate1> "object1" <http://example.org/graph1> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:subject1",
|
||||
Predicate: "<http://an.example/predicate1>",
|
||||
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)",
|
||||
input: `_:subject2 <http://an.example/predicate2> "object2" <http://example.org/graph5> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:subject2",
|
||||
Predicate: "<http://an.example/predicate2>",
|
||||
Object: `"object2"`,
|
||||
|
|
@ -172,7 +172,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://example.org/#spiderman>",
|
||||
Predicate: "<http://www.perceive.net/schemas/relationship/enemyOf>",
|
||||
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)",
|
||||
input: `_:alice <http://xmlns.com/foaf/0.1/knows> _:bob <http://example.org/graphs/john> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:alice",
|
||||
Predicate: "<http://xmlns.com/foaf/0.1/knows>",
|
||||
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)",
|
||||
input: `_:bob <http://xmlns.com/foaf/0.1/knows> _:alice <http://example.org/graphs/james> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "_:bob",
|
||||
Predicate: "<http://xmlns.com/foaf/0.1/knows>",
|
||||
Object: "_:alice",
|
||||
|
|
@ -209,7 +209,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://example.org/bob#me>",
|
||||
Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>",
|
||||
Object: "<http://xmlns.com/foaf/0.1/Person>",
|
||||
|
|
@ -220,7 +220,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://example.org/bob#me>",
|
||||
Predicate: "<http://xmlns.com/foaf/0.1/knows>",
|
||||
Object: "<http://example.org/alice#me>",
|
||||
|
|
@ -231,7 +231,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://example.org/bob#me>",
|
||||
Predicate: "<http://schema.org/birthDate>",
|
||||
Object: `"1990-07-04"^^<http://www.w3.org/2001/XMLSchema#date>`,
|
||||
|
|
@ -242,7 +242,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://example.org/bob#me>",
|
||||
Predicate: "<http://xmlns.com/foaf/0.1/topic_interest>",
|
||||
Object: "<http://www.wikidata.org/entity/Q12418>",
|
||||
|
|
@ -253,7 +253,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse triple with literal subject",
|
||||
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>",
|
||||
Predicate: "<http://purl.org/dc/terms/title>",
|
||||
Object: `"Mona Lisa"`,
|
||||
|
|
@ -264,7 +264,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://www.wikidata.org/entity/Q12418>",
|
||||
Predicate: "<http://purl.org/dc/terms/creator>",
|
||||
Object: "<http://dbpedia.org/resource/Leonardo_da_Vinci>",
|
||||
|
|
@ -275,7 +275,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://data.europeana.eu/item/04802/243FA8618938F4117025F17A8B813C5F9AA4D619>",
|
||||
Predicate: "<http://purl.org/dc/terms/subject>",
|
||||
Object: "<http://www.wikidata.org/entity/Q12418>",
|
||||
|
|
@ -288,7 +288,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://example.org/bob#me>",
|
||||
Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>",
|
||||
Object: "<http://xmlns.com/foaf/0.1/Person>",
|
||||
|
|
@ -299,7 +299,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://example.org/bob#me>",
|
||||
Predicate: "<http://xmlns.com/foaf/0.1/knows>",
|
||||
Object: "<http://example.org/alice#me>",
|
||||
|
|
@ -310,7 +310,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://example.org/bob#me>",
|
||||
Predicate: "<http://schema.org/birthDate>",
|
||||
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)",
|
||||
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>",
|
||||
Predicate: "<http://xmlns.com/foaf/0.1/topic_interest>",
|
||||
Object: "<http://www.wikidata.org/entity/Q12418>",
|
||||
|
|
@ -332,7 +332,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://www.wikidata.org/entity/Q12418>",
|
||||
Predicate: "<http://purl.org/dc/terms/title>",
|
||||
Object: `"Mona Lisa"`,
|
||||
|
|
@ -343,7 +343,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://www.wikidata.org/entity/Q12418>",
|
||||
Predicate: "<http://purl.org/dc/terms/creator>",
|
||||
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)",
|
||||
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>",
|
||||
Predicate: "<http://purl.org/dc/terms/subject>",
|
||||
Object: "<http://www.wikidata.org/entity/Q12418>",
|
||||
|
|
@ -365,7 +365,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse all IRIREF parts (quad section - 1)",
|
||||
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>",
|
||||
Predicate: "<http://purl.org/dc/terms/publisher>",
|
||||
Object: "<http://example.org>",
|
||||
|
|
@ -376,7 +376,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
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/> .`,
|
||||
expect: &quad.Quad{
|
||||
expect: quad.Quad{
|
||||
Subject: "<http://example.org/bob>",
|
||||
Predicate: "<http://purl.org/dc/terms/rights>",
|
||||
Object: "<http://creativecommons.org/licenses/by/3.0/>",
|
||||
|
|
@ -389,19 +389,19 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse empty",
|
||||
input: ``,
|
||||
expect: &quad.Quad{},
|
||||
expect: quad.Quad{},
|
||||
err: quad.ErrIncomplete,
|
||||
},
|
||||
{
|
||||
message: "parse commented",
|
||||
input: `# comment`,
|
||||
expect: &quad.Quad{},
|
||||
expect: quad.Quad{},
|
||||
err: fmt.Errorf("%v: unexpected rune '#' at 0", quad.ErrInvalid),
|
||||
},
|
||||
{
|
||||
message: "parse incomplete quad",
|
||||
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>",
|
||||
Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>",
|
||||
Object: "",
|
||||
|
|
@ -412,7 +412,7 @@ var testNTriples = []struct {
|
|||
{
|
||||
message: "parse incomplete quad",
|
||||
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>",
|
||||
Predicate: "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>",
|
||||
Object: "",
|
||||
|
|
@ -580,7 +580,7 @@ func TestUnescape(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var result *quad.Quad
|
||||
var result quad.Quad
|
||||
|
||||
func BenchmarkParser(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,12 @@ const quads_en_statement int = 1
|
|||
// 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 (
|
||||
cs, p int
|
||||
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
|
||||
}
|
||||
|
||||
// line 53 "parse.rl"
|
||||
// line 58 "parse.rl"
|
||||
|
||||
|
||||
// line 66 "parse.go"
|
||||
// line 71 "parse.go"
|
||||
{
|
||||
if p == pe {
|
||||
goto _test_eof
|
||||
|
|
@ -286,7 +291,7 @@ tr0:
|
|||
return q, quad.ErrIncomplete
|
||||
|
||||
goto st0
|
||||
// line 290 "parse.go"
|
||||
// line 295 "parse.go"
|
||||
st_case_0:
|
||||
st0:
|
||||
cs = 0
|
||||
|
|
@ -310,7 +315,7 @@ tr120:
|
|||
goto _test_eof2
|
||||
}
|
||||
st_case_2:
|
||||
// line 314 "parse.go"
|
||||
// line 319 "parse.go"
|
||||
switch data[p] {
|
||||
case 62:
|
||||
goto st3
|
||||
|
|
@ -351,7 +356,7 @@ tr121:
|
|||
goto _test_eof3
|
||||
}
|
||||
st_case_3:
|
||||
// line 355 "parse.go"
|
||||
// line 360 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto tr7
|
||||
|
|
@ -377,7 +382,7 @@ tr7:
|
|||
goto _test_eof4
|
||||
}
|
||||
st_case_4:
|
||||
// line 381 "parse.go"
|
||||
// line 386 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto st4
|
||||
|
|
@ -422,7 +427,7 @@ tr108:
|
|||
goto _test_eof5
|
||||
}
|
||||
st_case_5:
|
||||
// line 426 "parse.go"
|
||||
// line 431 "parse.go"
|
||||
switch data[p] {
|
||||
case 62:
|
||||
goto st6
|
||||
|
|
@ -463,7 +468,7 @@ tr109:
|
|||
goto _test_eof6
|
||||
}
|
||||
st_case_6:
|
||||
// line 467 "parse.go"
|
||||
// line 472 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto tr14
|
||||
|
|
@ -493,7 +498,7 @@ tr14:
|
|||
goto _test_eof7
|
||||
}
|
||||
st_case_7:
|
||||
// line 497 "parse.go"
|
||||
// line 502 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto st7
|
||||
|
|
@ -542,7 +547,7 @@ tr79:
|
|||
goto _test_eof8
|
||||
}
|
||||
st_case_8:
|
||||
// line 546 "parse.go"
|
||||
// line 551 "parse.go"
|
||||
switch data[p] {
|
||||
case 34:
|
||||
goto st9
|
||||
|
|
@ -574,7 +579,7 @@ tr80:
|
|||
goto _test_eof9
|
||||
}
|
||||
st_case_9:
|
||||
// line 578 "parse.go"
|
||||
// line 583 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto tr25
|
||||
|
|
@ -628,7 +633,7 @@ tr96:
|
|||
goto _test_eof10
|
||||
}
|
||||
st_case_10:
|
||||
// line 632 "parse.go"
|
||||
// line 637 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto st10
|
||||
|
|
@ -669,7 +674,7 @@ tr39:
|
|||
goto _test_eof88
|
||||
}
|
||||
st_case_88:
|
||||
// line 673 "parse.go"
|
||||
// line 678 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto st88
|
||||
|
|
@ -690,7 +695,7 @@ tr127:
|
|||
goto _test_eof89
|
||||
}
|
||||
st_case_89:
|
||||
// line 694 "parse.go"
|
||||
// line 699 "parse.go"
|
||||
goto st89
|
||||
tr27:
|
||||
// line 54 "actions.rl"
|
||||
|
|
@ -727,7 +732,7 @@ tr50:
|
|||
goto _test_eof11
|
||||
}
|
||||
st_case_11:
|
||||
// line 731 "parse.go"
|
||||
// line 736 "parse.go"
|
||||
switch data[p] {
|
||||
case 62:
|
||||
goto st12
|
||||
|
|
@ -768,7 +773,7 @@ tr51:
|
|||
goto _test_eof12
|
||||
}
|
||||
st_case_12:
|
||||
// line 772 "parse.go"
|
||||
// line 777 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto tr38
|
||||
|
|
@ -794,7 +799,7 @@ tr38:
|
|||
goto _test_eof13
|
||||
}
|
||||
st_case_13:
|
||||
// line 798 "parse.go"
|
||||
// line 803 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto st13
|
||||
|
|
@ -816,7 +821,7 @@ tr52:
|
|||
goto _test_eof14
|
||||
}
|
||||
st_case_14:
|
||||
// line 820 "parse.go"
|
||||
// line 825 "parse.go"
|
||||
switch data[p] {
|
||||
case 85:
|
||||
goto st15
|
||||
|
|
@ -1029,7 +1034,7 @@ tr34:
|
|||
goto _test_eof24
|
||||
}
|
||||
st_case_24:
|
||||
// line 1033 "parse.go"
|
||||
// line 1038 "parse.go"
|
||||
if data[p] == 58 {
|
||||
goto st25
|
||||
}
|
||||
|
|
@ -1211,7 +1216,7 @@ tr55:
|
|||
goto _test_eof90
|
||||
}
|
||||
st_case_90:
|
||||
// line 1215 "parse.go"
|
||||
// line 1220 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto st88
|
||||
|
|
@ -1522,7 +1527,7 @@ tr73:
|
|||
goto _test_eof34
|
||||
}
|
||||
st_case_34:
|
||||
// line 1526 "parse.go"
|
||||
// line 1531 "parse.go"
|
||||
switch data[p] {
|
||||
case 62:
|
||||
goto st35
|
||||
|
|
@ -1563,7 +1568,7 @@ tr74:
|
|||
goto _test_eof35
|
||||
}
|
||||
st_case_35:
|
||||
// line 1567 "parse.go"
|
||||
// line 1572 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto tr25
|
||||
|
|
@ -1589,7 +1594,7 @@ tr75:
|
|||
goto _test_eof36
|
||||
}
|
||||
st_case_36:
|
||||
// line 1593 "parse.go"
|
||||
// line 1598 "parse.go"
|
||||
switch data[p] {
|
||||
case 85:
|
||||
goto st37
|
||||
|
|
@ -1786,7 +1791,7 @@ tr81:
|
|||
goto _test_eof46
|
||||
}
|
||||
st_case_46:
|
||||
// line 1790 "parse.go"
|
||||
// line 1795 "parse.go"
|
||||
switch data[p] {
|
||||
case 34:
|
||||
goto st47
|
||||
|
|
@ -2006,7 +2011,7 @@ tr21:
|
|||
goto _test_eof56
|
||||
}
|
||||
st_case_56:
|
||||
// line 2010 "parse.go"
|
||||
// line 2015 "parse.go"
|
||||
if data[p] == 58 {
|
||||
goto st57
|
||||
}
|
||||
|
|
@ -2190,7 +2195,7 @@ tr90:
|
|||
goto _test_eof91
|
||||
}
|
||||
st_case_91:
|
||||
// line 2194 "parse.go"
|
||||
// line 2199 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto st88
|
||||
|
|
@ -2377,7 +2382,7 @@ tr91:
|
|||
goto _test_eof60
|
||||
}
|
||||
st_case_60:
|
||||
// line 2381 "parse.go"
|
||||
// line 2386 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto tr25
|
||||
|
|
@ -2582,7 +2587,7 @@ tr95:
|
|||
goto _test_eof62
|
||||
}
|
||||
st_case_62:
|
||||
// line 2586 "parse.go"
|
||||
// line 2591 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto tr96
|
||||
|
|
@ -2691,7 +2696,7 @@ tr97:
|
|||
goto _test_eof92
|
||||
}
|
||||
st_case_92:
|
||||
// line 2695 "parse.go"
|
||||
// line 2700 "parse.go"
|
||||
switch data[p] {
|
||||
case 9:
|
||||
goto st88
|
||||
|
|
@ -2869,7 +2874,7 @@ tr110:
|
|||
goto _test_eof64
|
||||
}
|
||||
st_case_64:
|
||||
// line 2873 "parse.go"
|
||||
// line 2878 "parse.go"
|
||||
switch data[p] {
|
||||
case 85:
|
||||
goto st65
|
||||
|
|
@ -3066,7 +3071,7 @@ tr122:
|
|||
goto _test_eof74
|
||||
}
|
||||
st_case_74:
|
||||
// line 3070 "parse.go"
|
||||
// line 3075 "parse.go"
|
||||
switch data[p] {
|
||||
case 85:
|
||||
goto st75
|
||||
|
|
@ -3263,7 +3268,7 @@ tr3:
|
|||
goto _test_eof84
|
||||
}
|
||||
st_case_84:
|
||||
// line 3267 "parse.go"
|
||||
// line 3272 "parse.go"
|
||||
if data[p] == 58 {
|
||||
goto st85
|
||||
}
|
||||
|
|
@ -3639,14 +3644,14 @@ tr3:
|
|||
|
||||
return q, nil
|
||||
|
||||
// line 3643 "parse.go"
|
||||
// line 3648 "parse.go"
|
||||
}
|
||||
}
|
||||
|
||||
_out: {}
|
||||
}
|
||||
|
||||
// line 55 "parse.rl"
|
||||
// line 60 "parse.rl"
|
||||
|
||||
return quad.Quad{}, quad.ErrInvalid
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,12 @@ import (
|
|||
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 (
|
||||
cs, p int
|
||||
pe = len(data)
|
||||
|
|
|
|||
21
quad/quad.go
21
quad/quad.go
|
|
@ -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
|
||||
func (q *Quad) Get(d Direction) string {
|
||||
func (q Quad) Get(d Direction) string {
|
||||
switch d {
|
||||
case 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.
|
||||
func (q *Quad) String() string {
|
||||
func (q Quad) String() string {
|
||||
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 != ""
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (q *Quad) NTriple() string {
|
||||
func (q Quad) NTriple() string {
|
||||
if q.Label == "" {
|
||||
//TODO(barakmich): Proper escaping.
|
||||
return fmt.Sprintf("%s %s %s .", q.Subject, q.Predicate, q.Object)
|
||||
|
|
@ -147,5 +136,5 @@ func (q *Quad) NTriple() string {
|
|||
}
|
||||
|
||||
type Unmarshaler interface {
|
||||
Unmarshal() (*Quad, error)
|
||||
Unmarshal() (Quad, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ func setupGremlin(env *otto.Otto, ses *Session) {
|
|||
graph.Set("Emit", func(call otto.FunctionCall) otto.Value {
|
||||
value := call.Argument(0)
|
||||
if value.IsDefined() {
|
||||
ses.SendResult(&GremlinResult{metaresult: false, err: "", val: &value, actualResults: nil})
|
||||
ses.SendResult(&Result{val: &value})
|
||||
}
|
||||
return otto.NullValue()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -148,8 +148,10 @@ func runIteratorToArray(it graph.Iterator, ses *Session, limit int) []map[string
|
|||
count := 0
|
||||
it, _ = it.Optimize()
|
||||
for {
|
||||
if ses.doHalt {
|
||||
select {
|
||||
case <-ses.kill:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
if !graph.Next(it) {
|
||||
break
|
||||
|
|
@ -161,9 +163,11 @@ func runIteratorToArray(it graph.Iterator, ses *Session, limit int) []map[string
|
|||
if limit >= 0 && count >= limit {
|
||||
break
|
||||
}
|
||||
for it.NextPath() == true {
|
||||
if ses.doHalt {
|
||||
for it.NextPath() {
|
||||
select {
|
||||
case <-ses.kill:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
tags := make(map[string]graph.Value)
|
||||
it.TagResults(tags)
|
||||
|
|
@ -183,8 +187,10 @@ func runIteratorToArrayNoTags(it graph.Iterator, ses *Session, limit int) []stri
|
|||
count := 0
|
||||
it, _ = it.Optimize()
|
||||
for {
|
||||
if ses.doHalt {
|
||||
select {
|
||||
case <-ses.kill:
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
if !graph.Next(it) {
|
||||
break
|
||||
|
|
@ -203,8 +209,10 @@ func runIteratorWithCallback(it graph.Iterator, ses *Session, callback otto.Valu
|
|||
count := 0
|
||||
it, _ = it.Optimize()
|
||||
for {
|
||||
if ses.doHalt {
|
||||
select {
|
||||
case <-ses.kill:
|
||||
return
|
||||
default:
|
||||
}
|
||||
if !graph.Next(it) {
|
||||
break
|
||||
|
|
@ -217,9 +225,11 @@ func runIteratorWithCallback(it graph.Iterator, ses *Session, callback otto.Valu
|
|||
if limit >= 0 && count >= limit {
|
||||
break
|
||||
}
|
||||
for it.NextPath() == true {
|
||||
if ses.doHalt {
|
||||
for it.NextPath() {
|
||||
select {
|
||||
case <-ses.kill:
|
||||
return
|
||||
default:
|
||||
}
|
||||
tags := make(map[string]graph.Value)
|
||||
it.TagResults(tags)
|
||||
|
|
@ -235,34 +245,35 @@ func runIteratorWithCallback(it graph.Iterator, ses *Session, callback otto.Valu
|
|||
}
|
||||
|
||||
func runIteratorOnSession(it graph.Iterator, ses *Session) {
|
||||
if ses.lookingForQueryShape {
|
||||
iterator.OutputQueryShapeForIterator(it, ses.ts, ses.queryShape)
|
||||
if ses.wantShape {
|
||||
iterator.OutputQueryShapeForIterator(it, ses.ts, ses.shape)
|
||||
return
|
||||
}
|
||||
it, _ = it.Optimize()
|
||||
glog.V(2).Infoln(it.DebugString(0))
|
||||
for {
|
||||
// TODO(barakmich): Better halting.
|
||||
if ses.doHalt {
|
||||
select {
|
||||
case <-ses.kill:
|
||||
return
|
||||
default:
|
||||
}
|
||||
if !graph.Next(it) {
|
||||
break
|
||||
}
|
||||
tags := make(map[string]graph.Value)
|
||||
it.TagResults(tags)
|
||||
cont := ses.SendResult(&GremlinResult{metaresult: false, err: "", val: nil, actualResults: &tags})
|
||||
if !cont {
|
||||
if !ses.SendResult(&Result{actualResults: &tags}) {
|
||||
break
|
||||
}
|
||||
for it.NextPath() == true {
|
||||
if ses.doHalt {
|
||||
for it.NextPath() {
|
||||
select {
|
||||
case <-ses.kill:
|
||||
return
|
||||
default:
|
||||
}
|
||||
tags := make(map[string]graph.Value)
|
||||
it.TagResults(tags)
|
||||
cont := ses.SendResult(&GremlinResult{metaresult: false, err: "", val: nil, actualResults: &tags})
|
||||
if !cont {
|
||||
if !ses.SendResult(&Result{actualResults: &tags}) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,9 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/google/cayley/graph"
|
||||
_ "github.com/google/cayley/graph/memstore"
|
||||
"github.com/google/cayley/quad"
|
||||
|
||||
_ "github.com/google/cayley/graph/memstore"
|
||||
)
|
||||
|
||||
// This is a simple test graph.
|
||||
|
|
@ -37,7 +38,7 @@ import (
|
|||
// \-->|#D#|------------->+---+
|
||||
// +---+
|
||||
//
|
||||
var simpleGraph = []*quad.Quad{
|
||||
var simpleGraph = []quad.Quad{
|
||||
{"A", "follows", "B", ""},
|
||||
{"C", "follows", "B", ""},
|
||||
{"C", "follows", "D", ""},
|
||||
|
|
@ -51,7 +52,7 @@ var simpleGraph = []*quad.Quad{
|
|||
{"G", "status", "cool", "status_graph"},
|
||||
}
|
||||
|
||||
func makeTestSession(data []*quad.Quad) *Session {
|
||||
func makeTestSession(data []quad.Quad) *Session {
|
||||
ts, _ := graph.NewTripleStore("memstore", "", nil)
|
||||
for _, t := range data {
|
||||
ts.AddTriple(t)
|
||||
|
|
@ -245,14 +246,14 @@ 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)
|
||||
c := make(chan interface{}, 5)
|
||||
js.ExecInput(query, c, -1)
|
||||
|
||||
var results []string
|
||||
for res := range c {
|
||||
data := res.(*GremlinResult)
|
||||
data := res.(*Result)
|
||||
if data.val == nil {
|
||||
val := (*data.actualResults)[tag]
|
||||
if val != nil {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/robertkrimen/otto"
|
||||
|
|
@ -26,45 +27,42 @@ import (
|
|||
"github.com/google/cayley/query"
|
||||
)
|
||||
|
||||
var ErrKillTimeout = errors.New("query timed out")
|
||||
|
||||
type Session struct {
|
||||
ts graph.TripleStore
|
||||
currentChannel chan interface{}
|
||||
env *otto.Otto
|
||||
debug bool
|
||||
limit int
|
||||
count int
|
||||
dataOutput []interface{}
|
||||
lookingForQueryShape bool
|
||||
queryShape map[string]interface{}
|
||||
err error
|
||||
script *otto.Script
|
||||
doHalt bool
|
||||
timeoutSec time.Duration
|
||||
emptyEnv *otto.Otto
|
||||
ts graph.TripleStore
|
||||
results chan interface{}
|
||||
env *otto.Otto
|
||||
envLock sync.Mutex
|
||||
debug bool
|
||||
limit int
|
||||
count int
|
||||
dataOutput []interface{}
|
||||
wantShape bool
|
||||
shape map[string]interface{}
|
||||
err error
|
||||
script *otto.Script
|
||||
kill chan struct{}
|
||||
timeout time.Duration
|
||||
emptyEnv *otto.Otto
|
||||
}
|
||||
|
||||
func NewSession(inputTripleStore graph.TripleStore, timeoutSec int, persist bool) *Session {
|
||||
var g Session
|
||||
g.ts = inputTripleStore
|
||||
func NewSession(ts graph.TripleStore, timeout time.Duration, persist bool) *Session {
|
||||
g := Session{
|
||||
ts: ts,
|
||||
limit: -1,
|
||||
timeout: timeout,
|
||||
}
|
||||
g.env = BuildEnviron(&g)
|
||||
g.limit = -1
|
||||
g.count = 0
|
||||
g.lookingForQueryShape = false
|
||||
if persist {
|
||||
g.emptyEnv = g.env
|
||||
}
|
||||
if timeoutSec < 0 {
|
||||
g.timeoutSec = time.Duration(-1)
|
||||
} else {
|
||||
g.timeoutSec = time.Duration(timeoutSec)
|
||||
}
|
||||
g.ClearJson()
|
||||
return &g
|
||||
}
|
||||
|
||||
type GremlinResult struct {
|
||||
type Result struct {
|
||||
metaresult bool
|
||||
err string
|
||||
err error
|
||||
val *otto.Value
|
||||
actualResults *map[string]graph.Value
|
||||
}
|
||||
|
|
@ -73,13 +71,13 @@ func (s *Session) ToggleDebug() {
|
|||
s.debug = !s.debug
|
||||
}
|
||||
|
||||
func (s *Session) GetQuery(input string, output_struct chan map[string]interface{}) {
|
||||
defer close(output_struct)
|
||||
s.queryShape = make(map[string]interface{})
|
||||
s.lookingForQueryShape = true
|
||||
func (s *Session) GetQuery(input string, out chan map[string]interface{}) {
|
||||
defer close(out)
|
||||
s.shape = make(map[string]interface{})
|
||||
s.wantShape = true
|
||||
s.env.Run(input)
|
||||
output_struct <- s.queryShape
|
||||
s.queryShape = nil
|
||||
out <- s.shape
|
||||
s.shape = nil
|
||||
}
|
||||
|
||||
func (s *Session) InputParses(input string) (query.ParseResult, error) {
|
||||
|
|
@ -91,15 +89,17 @@ func (s *Session) InputParses(input string) (query.ParseResult, error) {
|
|||
return query.Parsed, nil
|
||||
}
|
||||
|
||||
func (s *Session) SendResult(result *GremlinResult) bool {
|
||||
func (s *Session) SendResult(r *Result) bool {
|
||||
if s.limit >= 0 && s.limit == s.count {
|
||||
return false
|
||||
}
|
||||
if s.doHalt {
|
||||
select {
|
||||
case <-s.kill:
|
||||
return false
|
||||
default:
|
||||
}
|
||||
if s.currentChannel != nil {
|
||||
s.currentChannel <- result
|
||||
if s.results != nil {
|
||||
s.results <- r
|
||||
s.count++
|
||||
if s.limit >= 0 && s.limit == s.count {
|
||||
return false
|
||||
|
|
@ -110,42 +110,46 @@ func (s *Session) SendResult(result *GremlinResult) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
var halt = errors.New("Query Timeout")
|
||||
|
||||
func (s *Session) runUnsafe(input interface{}) (otto.Value, error) {
|
||||
s.doHalt = false
|
||||
s.kill = make(chan struct{})
|
||||
defer func() {
|
||||
if caught := recover(); caught != nil {
|
||||
if caught == halt {
|
||||
s.err = halt
|
||||
if r := recover(); r != nil {
|
||||
if r == ErrKillTimeout {
|
||||
s.err = ErrKillTimeout
|
||||
return
|
||||
}
|
||||
panic(caught) // Something else happened, repanic!
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
s.env.Interrupt = make(chan func(), 1) // The buffer prevents blocking
|
||||
// Use buffered chan to prevent blocking.
|
||||
s.env.Interrupt = make(chan func(), 1)
|
||||
|
||||
if s.timeoutSec != -1 {
|
||||
if s.timeout >= 0 {
|
||||
go func() {
|
||||
time.Sleep(s.timeoutSec * time.Second) // Stop after two seconds
|
||||
s.doHalt = true
|
||||
time.Sleep(s.timeout)
|
||||
close(s.kill)
|
||||
s.envLock.Lock()
|
||||
defer s.envLock.Unlock()
|
||||
if s.env != nil {
|
||||
s.env.Interrupt <- func() {
|
||||
panic(halt)
|
||||
panic(ErrKillTimeout)
|
||||
}
|
||||
s.env = s.emptyEnv
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return s.env.Run(input) // Here be dragons (risky code)
|
||||
s.envLock.Lock()
|
||||
env := s.env
|
||||
s.envLock.Unlock()
|
||||
return env.Run(input)
|
||||
}
|
||||
|
||||
func (s *Session) ExecInput(input string, out chan interface{}, limit int) {
|
||||
defer close(out)
|
||||
s.err = nil
|
||||
s.currentChannel = out
|
||||
s.results = out
|
||||
var err error
|
||||
var value otto.Value
|
||||
if s.script == nil {
|
||||
|
|
@ -153,28 +157,23 @@ func (s *Session) ExecInput(input string, out chan interface{}, limit int) {
|
|||
} else {
|
||||
value, err = s.runUnsafe(s.script)
|
||||
}
|
||||
if err != nil {
|
||||
out <- &GremlinResult{metaresult: true,
|
||||
err: err.Error(),
|
||||
val: &value,
|
||||
actualResults: nil}
|
||||
} else {
|
||||
out <- &GremlinResult{metaresult: true,
|
||||
err: "",
|
||||
val: &value,
|
||||
actualResults: nil}
|
||||
out <- &Result{
|
||||
metaresult: true,
|
||||
err: err,
|
||||
val: &value,
|
||||
}
|
||||
s.currentChannel = nil
|
||||
s.results = nil
|
||||
s.script = nil
|
||||
s.envLock.Lock()
|
||||
s.env = s.emptyEnv
|
||||
return
|
||||
s.envLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *Session) ToText(result interface{}) string {
|
||||
data := result.(*GremlinResult)
|
||||
data := result.(*Result)
|
||||
if data.metaresult {
|
||||
if data.err != "" {
|
||||
return fmt.Sprintln("Error: ", data.err)
|
||||
if data.err != nil {
|
||||
return fmt.Sprintf("Error: %v\n", data.err)
|
||||
}
|
||||
if data.val != nil {
|
||||
s, _ := data.val.Export()
|
||||
|
|
@ -221,8 +220,8 @@ func (s *Session) ToText(result interface{}) string {
|
|||
}
|
||||
|
||||
// Web stuff
|
||||
func (ses *Session) BuildJson(result interface{}) {
|
||||
data := result.(*GremlinResult)
|
||||
func (s *Session) BuildJson(result interface{}) {
|
||||
data := result.(*Result)
|
||||
if !data.metaresult {
|
||||
if data.val == nil {
|
||||
obj := make(map[string]string)
|
||||
|
|
@ -235,33 +234,34 @@ func (ses *Session) BuildJson(result interface{}) {
|
|||
}
|
||||
sort.Strings(tagKeys)
|
||||
for _, k := range tagKeys {
|
||||
obj[k] = ses.ts.NameOf((*tags)[k])
|
||||
obj[k] = s.ts.NameOf((*tags)[k])
|
||||
}
|
||||
ses.dataOutput = append(ses.dataOutput, obj)
|
||||
s.dataOutput = append(s.dataOutput, obj)
|
||||
} else {
|
||||
if data.val.IsObject() {
|
||||
export, _ := data.val.Export()
|
||||
ses.dataOutput = append(ses.dataOutput, export)
|
||||
s.dataOutput = append(s.dataOutput, export)
|
||||
} else {
|
||||
strVersion, _ := data.val.ToString()
|
||||
ses.dataOutput = append(ses.dataOutput, strVersion)
|
||||
s.dataOutput = append(s.dataOutput, strVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (ses *Session) GetJson() ([]interface{}, error) {
|
||||
defer ses.ClearJson()
|
||||
if ses.err != nil {
|
||||
return nil, ses.err
|
||||
func (s *Session) GetJson() ([]interface{}, error) {
|
||||
defer s.ClearJson()
|
||||
if s.err != nil {
|
||||
return nil, s.err
|
||||
}
|
||||
if ses.doHalt {
|
||||
return nil, halt
|
||||
select {
|
||||
case <-s.kill:
|
||||
return nil, ErrKillTimeout
|
||||
default:
|
||||
return s.dataOutput, nil
|
||||
}
|
||||
return ses.dataOutput, nil
|
||||
}
|
||||
|
||||
func (ses *Session) ClearJson() {
|
||||
ses.dataOutput = nil
|
||||
func (s *Session) ClearJson() {
|
||||
s.dataOutput = nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import (
|
|||
// \-->|#D#|------------->+---+
|
||||
// +---+
|
||||
//
|
||||
var simpleGraph = []*quad.Quad{
|
||||
var simpleGraph = []quad.Quad{
|
||||
{"A", "follows", "B", ""},
|
||||
{"C", "follows", "B", ""},
|
||||
{"C", "follows", "D", ""},
|
||||
|
|
@ -51,7 +51,7 @@ var simpleGraph = []*quad.Quad{
|
|||
{"G", "status", "cool", "status_graph"},
|
||||
}
|
||||
|
||||
func makeTestSession(data []*quad.Quad) *Session {
|
||||
func makeTestSession(data []quad.Quad) *Session {
|
||||
ts, _ := graph.NewTripleStore("memstore", "", nil)
|
||||
for _, t := range data {
|
||||
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)
|
||||
c := make(chan interface{}, 5)
|
||||
go s.ExecInput(query, c, -1)
|
||||
|
|
|
|||
|
|
@ -32,21 +32,21 @@ func TestBadParse(t *testing.T) {
|
|||
|
||||
var testQueries = []struct {
|
||||
message string
|
||||
add *quad.Quad
|
||||
add quad.Quad
|
||||
query string
|
||||
typ graph.Type
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
message: "get a single triple linkage",
|
||||
add: &quad.Quad{"i", "can", "win", ""},
|
||||
add: quad.Quad{"i", "can", "win", ""},
|
||||
query: "($a (:can \"win\"))",
|
||||
typ: graph.And,
|
||||
expect: "i",
|
||||
},
|
||||
{
|
||||
message: "get a single triple linkage",
|
||||
add: &quad.Quad{"i", "can", "win", ""},
|
||||
add: quad.Quad{"i", "can", "win", ""},
|
||||
query: "(\"i\" (:can $a))",
|
||||
typ: graph.And,
|
||||
expect: "i",
|
||||
|
|
@ -60,7 +60,7 @@ func TestMemstoreBackedSexp(t *testing.T) {
|
|||
t.Errorf(`Incorrect type for empty query, got:%q expect: "null"`, it.Type())
|
||||
}
|
||||
for _, test := range testQueries {
|
||||
if test.add != nil {
|
||||
if test.add.IsValid() {
|
||||
ts.AddTriple(test.add)
|
||||
}
|
||||
it := BuildIteratorTreeForQuery(ts, test.query)
|
||||
|
|
@ -79,8 +79,8 @@ func TestMemstoreBackedSexp(t *testing.T) {
|
|||
|
||||
func TestTreeConstraintParse(t *testing.T) {
|
||||
ts, _ := graph.NewTripleStore("memstore", "", nil)
|
||||
ts.AddTriple(&quad.Quad{"i", "like", "food", ""})
|
||||
ts.AddTriple(&quad.Quad{"food", "is", "good", ""})
|
||||
ts.AddTriple(quad.Quad{"i", "like", "food", ""})
|
||||
ts.AddTriple(quad.Quad{"food", "is", "good", ""})
|
||||
query := "(\"i\"\n" +
|
||||
"(:like\n" +
|
||||
"($a (:is :good))))"
|
||||
|
|
@ -99,8 +99,8 @@ func TestTreeConstraintParse(t *testing.T) {
|
|||
|
||||
func TestTreeConstraintTagParse(t *testing.T) {
|
||||
ts, _ := graph.NewTripleStore("memstore", "", nil)
|
||||
ts.AddTriple(&quad.Quad{"i", "like", "food", ""})
|
||||
ts.AddTriple(&quad.Quad{"food", "is", "good", ""})
|
||||
ts.AddTriple(quad.Quad{"i", "like", "food", ""})
|
||||
ts.AddTriple(quad.Quad{"food", "is", "good", ""})
|
||||
query := "(\"i\"\n" +
|
||||
"(:like\n" +
|
||||
"($a (:is :good))))"
|
||||
|
|
@ -118,7 +118,7 @@ func TestTreeConstraintTagParse(t *testing.T) {
|
|||
|
||||
func TestMultipleConstraintParse(t *testing.T) {
|
||||
ts, _ := graph.NewTripleStore("memstore", "", nil)
|
||||
for _, tv := range []*quad.Quad{
|
||||
for _, tv := range []quad.Quad{
|
||||
{"i", "like", "food", ""},
|
||||
{"i", "like", "beer", ""},
|
||||
{"you", "like", "beer", ""},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue