// 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 graph // Defines the struct which makes the TripleStore possible -- the triple. // // At its heart, it consists of three fields -- Subject, Predicate, and Object. // Three IDs that relate to each other. That's all there is to it. The triples // are the links in the graph, and the existence of node IDs is defined by the // fact that some triple in the graph mentions them. // // This means that a complete representation of the graph is equivalent to a // list of triples. The rest is just indexing for speed. // // Adding fields to the triple is not to be taken lightly. You'll see I mention // provenance, but don't as yet use it in any backing store. In general, there // can be features that can be turned on or off for any store, but I haven't // decided how to allow/disallow them yet. Another such example would be to add // a forward and reverse index field -- forward being "order the list of // objects pointed at by this subject with this predicate" such as first and // second children, top billing, what have you. // // There will never be that much in this file except for the definition, but // the consequences are not to be taken lightly. But do suggest cool features! import "fmt" // TODO(kortschak) Consider providing MashalJSON and UnmarshalJSON // instead of using struct tags. // Our triple struct, used throughout. type Triple struct { Subject string `json:"subject"` Predicate string `json:"predicate"` Object string `json:"object"` Provenance string `json:"provenance,omitempty"` } // Direction specifies an edge's type. type Direction byte // List of the valid directions of a triple. const ( Any Direction = iota Subject Predicate Object Provenance ) func (d Direction) Prefix() byte { switch d { case Any: return 'a' case Subject: return 's' case Predicate: return 'p' case Provenance: return 'c' case Object: return 'o' default: return '\x00' } } func (d Direction) String() string { switch d { case Any: return "any" case Subject: return "subject" case Predicate: return "predicate" case Provenance: return "provenance" case Object: return "object" default: return fmt.Sprint("illegal direction:", byte(d)) } } // 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 (t *Triple) Get(d Direction) string { switch d { case Subject: return t.Subject case Predicate: return t.Predicate case Provenance: return t.Provenance case Object: return t.Object default: panic(d.String()) } } func (t *Triple) Equals(o *Triple) bool { return *t == *o } // Pretty-prints a triple. func (t *Triple) String() string { // TODO(kortschak) String methods should generally not terminate in '\n'. return fmt.Sprintf("%s -- %s -> %s\n", t.Subject, t.Predicate, t.Object) } func (t *Triple) IsValid() bool { return t.Subject != "" && t.Predicate != "" && t.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-Triple format. func (t *Triple) NTriple() string { if t.Provenance == "" { //TODO(barakmich): Proper escaping. return fmt.Sprintf("%s %s %s .", t.Subject, t.Predicate, t.Object) } else { return fmt.Sprintf("%s %s %s %s .", t.Subject, t.Predicate, t.Object, t.Provenance) } }