// 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 quad defines quad and triple handling. package quad // 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 // label, 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 ( "errors" "fmt" ) var ( ErrInvalid = errors.New("invalid N-Quad") ErrIncomplete = errors.New("incomplete N-Quad") ) // Our triple struct, used throughout. type Quad struct { Subject string `json:"subject"` Predicate string `json:"predicate"` Object string `json:"object"` Label string `json:"label,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 Label ) func (d Direction) Prefix() byte { switch d { case Any: return 'a' case Subject: return 's' case Predicate: return 'p' case Label: 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 Label: return "label" 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 (q Quad) Get(d Direction) string { switch d { case Subject: return q.Subject case Predicate: return q.Predicate case Label: return q.Label case Object: return q.Object default: panic(d.String()) } } func (q Quad) Equals(o Quad) bool { return q == o } // Pretty-prints a triple. func (q Quad) String() string { return fmt.Sprintf("%s -- %s -> %s", q.Subject, q.Predicate, q.Object) } 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 { if q.Label == "" { //TODO(barakmich): Proper escaping. return fmt.Sprintf("%s %s %s .", q.Subject, q.Predicate, q.Object) } else { return fmt.Sprintf("%s %s %s %s .", q.Subject, q.Predicate, q.Object, q.Label) } } type Unmarshaler interface { Unmarshal() (Quad, error) }