package rdf
import "gonum.org/v1/gonum/graph/formats/rdf"
Package rdf implements decoding the RDF 1.1 N-Quads line-based plain text
format for encoding an RDF dataset.
N-Quad parsing is performed as defined by http://www.w3.org/TR/n-quads/
Code:play
Output:Example (Graph)¶
package main
import (
"fmt"
"log"
"os"
"strings"
"text/tabwriter"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/encoding"
"gonum.org/v1/gonum/graph/encoding/dot"
"gonum.org/v1/gonum/graph/formats/rdf"
"gonum.org/v1/gonum/graph/multi"
)
// dotNode implements graph.Node and dot.Node to allow the
// RDF term value to be given to the DOT encoder.
type dotNode struct {
rdf.Term
}
func (n dotNode) DOTID() string { return n.Term.Value }
// dotLine implements graph.Line and encoding.Attributer to
// allow the line's RDF term value to be given to the DOT
// encoder and for the nodes to be shimmed to the dotNode
// type.
//
// Because the graph here is directed and we are not performing
// any line reversals, it is safe not to implement the
// ReversedLine method on dotLine; it will never be called.
type dotLine struct {
*rdf.Statement
}
func (l dotLine) From() graph.Node { return dotNode{l.Subject} }
func (l dotLine) To() graph.Node { return dotNode{l.Object} }
func (l dotLine) Attributes() []encoding.Attribute {
return []encoding.Attribute{{Key: "label", Value: l.Predicate.Value}}
}
func main() {
const statements = `
_:alice <http://xmlns.com/foaf/0.1/knows> _:bob .
_:alice <http://xmlns.com/foaf/0.1/givenName> "Alice" .
_:alice <http://xmlns.com/foaf/0.1/familyName> "Smith" .
_:bob <http://xmlns.com/foaf/0.1/knows> _:alice .
_:bob <http://xmlns.com/foaf/0.1/givenName> "Bob" .
_:bob <http://xmlns.com/foaf/0.1/familyName> "Smith" .
`
// Decode the statement stream and insert the lines into a multigraph.
g := multi.NewDirectedGraph()
dec := rdf.NewDecoder(strings.NewReader(statements))
for {
l, err := dec.Unmarshal()
if err != nil {
break
}
// Wrap the line with a shim type to allow the RDF values
// to be passed to the DOT marshaling routine.
g.SetLine(dotLine{l})
}
// Marshal the graph into DOT.
b, err := dot.MarshalMulti(g, "smiths", "", "\t")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n\n", b)
// Get the ID look-up table.
w := tabwriter.NewWriter(os.Stdout, 0, 4, 1, ' ', 0)
fmt.Fprintln(w, "Term\tID")
for t, id := range dec.Terms() {
fmt.Fprintf(w, "%s\t%d\n", t, id)
}
w.Flush()
}
digraph smiths {
// Node definitions.
"_:alice";
"_:bob";
"Alice";
"Smith";
"Bob";
// Edge definitions.
"_:alice" -> "_:bob" [label=<http://xmlns.com/foaf/0.1/knows>];
"_:alice" -> "Alice" [label=<http://xmlns.com/foaf/0.1/givenName>];
"_:alice" -> "Smith" [label=<http://xmlns.com/foaf/0.1/familyName>];
"_:bob" -> "_:alice" [label=<http://xmlns.com/foaf/0.1/knows>];
"_:bob" -> "Smith" [label=<http://xmlns.com/foaf/0.1/familyName>];
"_:bob" -> "Bob" [label=<http://xmlns.com/foaf/0.1/givenName>];
}
Term ID
_:alice 1
_:bob 2
<http://xmlns.com/foaf/0.1/knows> 3
"Alice" 4
<http://xmlns.com/foaf/0.1/givenName> 5
"Smith" 6
<http://xmlns.com/foaf/0.1/familyName> 7
"Bob" 8
Index ¶
- Variables
- func ConnectedByAny(e graph.Edge, with func(*Statement) bool) bool
- func IsoCanonicalHashes(statements []*Statement, decomp, dist bool, h hash.Hash, zero []byte) (hashes map[string][]byte, terms map[string]map[string]bool)
- func Isomorphic(a, b []*Statement, decomp bool, h hash.Hash) bool
- type Decoder
- func NewDecoder(r io.Reader) *Decoder
- func (dec *Decoder) Reset(r io.Reader)
- func (dec *Decoder) Terms() map[string]int64
- func (dec *Decoder) Unmarshal() (*Statement, error)
- type Graph
- func NewGraph() *Graph
- func (g *Graph) AddStatement(s *Statement)
- func (g *Graph) AllStatements() *Statements
- func (g *Graph) Edge(uid, vid int64) graph.Edge
- func (g *Graph) Edges() graph.Edges
- func (g *Graph) From(id int64) graph.Nodes
- func (g *Graph) FromSubject(t Term) graph.Nodes
- func (g *Graph) HasEdgeBetween(xid, yid int64) bool
- func (g *Graph) HasEdgeFromTo(uid, vid int64) bool
- func (g *Graph) Lines(uid, vid int64) graph.Lines
- func (g *Graph) Node(id int64) graph.Node
- func (g *Graph) Nodes() graph.Nodes
- func (g *Graph) Predicates() []Term
- func (g *Graph) Query(from ...Term) Query
- func (g *Graph) RemoveStatement(s *Statement)
- func (g *Graph) RemoveTerm(t Term)
- func (g *Graph) Statements(uid, vid int64) *Statements
- func (g *Graph) TermFor(text string) (term Term, ok bool)
- func (g *Graph) To(id int64) graph.Nodes
- func (g *Graph) ToObject(t Term) graph.Nodes
- type Kind
- type Query
- func NewQuery(g graph.Directed, from ...Term) Query
- func (q Query) And(p Query) Query
- func (q Query) HasAllIn(fn func(s *Statement) bool) Query
- func (q Query) HasAllOut(fn func(s *Statement) bool) Query
- func (q Query) HasAnyIn(fn func(s *Statement) bool) Query
- func (q Query) HasAnyOut(fn func(s *Statement) bool) Query
- func (q Query) In(fn func(s *Statement) bool) Query
- func (q Query) Len() int
- func (q Query) Not(p Query) Query
- func (q Query) Or(p Query) Query
- func (q Query) Out(fn func(s *Statement) bool) Query
- func (q Query) Repeat(fn func(Query) (q Query, ok bool)) Query
- func (q Query) Result() []Term
- func (q Query) Unique() Query
- type Statement
- func C14n(dst, src []*Statement, terms map[string]map[string]bool) ([]*Statement, error)
- func Deduplicate(s []*Statement) []*Statement
- func Lean(g []*Statement) ([]*Statement, error)
- func ParseNQuad(statement string) (*Statement, error)
- func URDNA2015(dst, src []*Statement) ([]*Statement, error)
- func URGNA2012(dst, src []*Statement) ([]*Statement, error)
- func (s *Statement) From() graph.Node
- func (s *Statement) ID() int64
- func (s *Statement) ReversedEdge() graph.Edge
- func (s *Statement) ReversedLine() graph.Line
- func (s *Statement) String() string
- func (s *Statement) To() graph.Node
- type Statements
- type Term
- func NewBlankTerm(label string) (Term, error)
- func NewIRITerm(iri string) (Term, error)
- func NewLiteralTerm(text, qual string) (Term, error)
- func (t Term) ID() int64
- func (t Term) Parts() (text, qual string, kind Kind, err error)
- Bugs
Examples ¶
- package (Graph)
- C14n
- Graph
- IsoCanonicalHashes (IsomorphicParts)
- IsoCanonicalHashes (Isomorphisms)
- Lean
- Query
- Statement.ReversedLine
- URDNA2015
- URGNA2012
Variables ¶
var ( ErrInvalid = errors.New("invalid N-Quad") ErrIncomplete = errors.New("incomplete N-Quad") ErrInvalidTerm = errors.New("invalid term") ErrIncompleteTerm = errors.New("incomplete term") )
Functions ¶
func ConnectedByAny ¶
ConnectedByAny is a helper function to for simplifying graph traversal conditions.
func IsoCanonicalHashes ¶
func IsoCanonicalHashes(statements []*Statement, decomp, dist bool, h hash.Hash, zero []byte) (hashes map[string][]byte, terms map[string]map[string]bool)
IsoCanonicalHashes returns a mapping between the nodes of the RDF graph dataset described by the given statements using the provided hash function. If decomp is true, the graphs are decomposed before hashing. If dist is true the input graph is decomposed into identical splits, the entire graph will be hashed to distinguish nodes. If decomp is false, dist has no effect. Blank node hashes are initially set to the value of zero. Hash values are provided for literal and IRI nodes as well as for blank node. The hash input for literal nodes includes the quotes and the input for IRI nodes first removes the angle quotes around the IRI, although these are included in the map keys.
Note that hashes returned by IsoCanonicalHashes with decomp=true are not comparable with hashes returned by IsoCanonicalHashes with decomp=false.
See http://aidanhogan.com/docs/rdf-canonicalisation.pdf for details of
the hashing algorithm.
Code:play
Output: Code:play
Output:Example (IsomorphicParts)¶
package main
import (
"crypto/md5"
"fmt"
"os"
"slices"
"strings"
"text/tabwriter"
"gonum.org/v1/gonum/graph/formats/rdf"
)
func main() {
for _, statements := range []string{
`
# Part 1
_:a <ex:q> <ex:p> .
_:b <ex:q> <ex:p> .
_:c <ex:p> _:a .
_:d <ex:p> _:b .
_:c <ex:r> _:d .
# Part 2
_:a1 <ex:q> <ex:p> .
_:b1 <ex:q> <ex:p> .
_:c1 <ex:p> _:a1 .
_:d1 <ex:p> _:b1 .
_:c1 <ex:r> _:d1 .
`,
} {
// Decode the statement stream.
dec := rdf.NewDecoder(strings.NewReader(statements))
var s []*rdf.Statement
for {
l, err := dec.Unmarshal()
if err != nil {
break
}
s = append(s, l)
}
// Get the node label to hash look-up table. This time
// we will decompose the dataset into splits and not
// distinguish nodes. This will then group nodes from
// the two isomorphic parts. Otherwise each node in
// the complete dataset would get a unique hash.
hashes, _ := rdf.IsoCanonicalHashes(s, true, false, md5.New(), make([]byte, 16))
// Get all the blank nodes.
var blanks []string
for k := range hashes {
if strings.HasPrefix(k, "_:") {
blanks = append(blanks, k)
}
}
slices.Sort(blanks)
if len(blanks) == 0 {
fmt.Println("No blank nodes.")
} else {
w := tabwriter.NewWriter(os.Stdout, 0, 4, 1, ' ', 0)
fmt.Fprintln(w, "Node\tHash")
for _, k := range blanks {
fmt.Fprintf(w, "%s\t%032x\n", k, hashes[k])
}
w.Flush()
}
fmt.Println()
}
}
Node Hash
_:a d4db6df055d5611e9d8aa6ea621561d1
_:a1 d4db6df055d5611e9d8aa6ea621561d1
_:b ad70e47f2b026064c7f0922060512b9a
_:b1 ad70e47f2b026064c7f0922060512b9a
_:c dafd81e6fa603d3e11c898d631e8673f
_:c1 dafd81e6fa603d3e11c898d631e8673f
_:d 7e318557b09444e88791721becc2a8e7
_:d1 7e318557b09444e88791721becc2a8e7
Example (Isomorphisms)¶
package main
import (
"crypto/md5"
"fmt"
"os"
"slices"
"strings"
"text/tabwriter"
"gonum.org/v1/gonum/graph/formats/rdf"
)
func main() {
for _, statements := range []string{
`
<https://example.com/1> <https://example.com/2> <https://example.com/3> .
<https://example.com/3> <https://example.com/4> <https://example.com/5> .
`,
`
_:a <ex:q> <ex:p> .
_:b <ex:q> <ex:p> .
_:c <ex:p> _:a .
_:d <ex:p> _:b .
_:c <ex:r> _:d .
`,
`
_:a1 <ex:q> <ex:p> .
_:b1 <ex:q> <ex:p> .
_:c1 <ex:p> _:a1 .
_:d1 <ex:p> _:b1 .
_:c1 <ex:r> _:d1 .
`,
`
# G
<ex:p> <ex:q> _:a .
<ex:p> <ex:q> _:b .
<ex:s> <ex:p> _:a .
<ex:s> <ex:r> _:c .
_:c <ex:p> _:b .
# H
<ex:p> <ex:q> _:d .
<ex:p> <ex:q> _:e .
_:f <ex:p> _:d .
_:f <ex:r> _:g .
_:g <ex:p> _:e .
`,
`
_:greet <l:is> "hola"@es .
`,
} {
// Decode the statement stream.
dec := rdf.NewDecoder(strings.NewReader(statements))
var s []*rdf.Statement
for {
l, err := dec.Unmarshal()
if err != nil {
break
}
s = append(s, l)
}
// Get the node label to hash look-up table.
hashes, _ := rdf.IsoCanonicalHashes(s, false, true, md5.New(), make([]byte, 16))
// Get all the blank nodes.
var blanks []string
for k := range hashes {
if strings.HasPrefix(k, "_:") {
blanks = append(blanks, k)
}
}
slices.Sort(blanks)
if len(blanks) == 0 {
fmt.Println("No blank nodes.")
} else {
w := tabwriter.NewWriter(os.Stdout, 0, 4, 1, ' ', 0)
fmt.Fprintln(w, "Node\tHash")
for _, k := range blanks {
fmt.Fprintf(w, "%s\t%032x\n", k, hashes[k])
}
w.Flush()
}
fmt.Println()
}
}
No blank nodes.
Node Hash
_:a d4db6df055d5611e9d8aa6ea621561d1
_:b ad70e47f2b026064c7f0922060512b9a
_:c dafd81e6fa603d3e11c898d631e8673f
_:d 7e318557b09444e88791721becc2a8e7
Node Hash
_:a1 d4db6df055d5611e9d8aa6ea621561d1
_:b1 ad70e47f2b026064c7f0922060512b9a
_:c1 dafd81e6fa603d3e11c898d631e8673f
_:d1 7e318557b09444e88791721becc2a8e7
Node Hash
_:a 44ad49b6df3aea91ddbcef932c93e3b4
_:b ba3ffd8b271a8545b1a3a9042e75ce4b
_:c 34e1bd90b6758b4a766e000128caa6a6
_:d eb2a47c1032f623647d0497a2ff74052
_:e 1d9ed02f28d87e555feb904688bc2449
_:f d3b00d36ea503dcc8d234e4405feab81
_:g 55127e4624c0a4fe5990933a48840af8
Node Hash
_:greet 0d9ba18a037a3fa67e46fce821fe51b4
func Isomorphic ¶
Isomorphic returns whether the RDF graph datasets a and b are isomorphic, where there is a bijective mapping between blank nodes in a and b using the given hash function. If decomp is true, the graphs are decomposed before canonicalization.
Types ¶
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder is an RDF stream decoder. Statements returned by calls to the Unmarshal method have their Terms' UID fields set so that unique terms will have unique IDs and so can be used directly in a graph.Multi, or in a graph.Graph if all predicate terms are identical. IDs created by the decoder all exist within a single namespace and so Terms can be uniquely identified by their UID. Term UIDs are based from 1 to allow RDF-aware client graphs to assign ID if no ID has been assigned.
func NewDecoder ¶
NewDecoder returns a new Decoder that takes input from r.
func (*Decoder) Reset ¶
Reset resets the decoder to use the provided io.Reader, retaining the existing Term ID mapping.
func (*Decoder) Terms ¶
Terms returns the mapping between terms and graph node IDs constructed during decoding the RDF statement stream.
func (*Decoder) Unmarshal ¶
Unmarshal returns the next statement from the input stream.
type Graph ¶
type Graph struct {
// contains filtered or unexported fields
}
Graph implements an RDF graph satisfying the graph.Graph and graph.Multigraph
interfaces.
Code:play
Example¶
package main
import (
"io"
"log"
"os"
"gonum.org/v1/gonum/graph/formats/rdf"
)
func main() {
f, err := os.Open("path/to/graph.nq")
if err != nil {
log.Fatal(err)
}
dec := rdf.NewDecoder(f)
var statements []*rdf.Statement
for {
s, err := dec.Unmarshal()
if err != nil {
if err != io.EOF {
log.Fatalf("error during decoding: %v", err)
}
break
}
// Statements can be filtered at this point to exclude unwanted
// or irrelevant parts of the graph.
statements = append(statements, s)
}
f.Close()
// Canonicalize blank nodes to reduce memory footprint.
statements, err = rdf.URDNA2015(statements, statements)
if err != nil {
log.Fatal(err)
}
g := rdf.NewGraph()
for _, s := range statements {
g.AddStatement(s)
}
// Do something with the graph.
}
func NewGraph ¶
func NewGraph() *Graph
NewGraph returns a new empty Graph.
func (*Graph) AddStatement ¶
AddStatement adds s to the graph. It panics if Term UIDs in the statement are not consistent with existing terms in the graph. Statements must not be altered while being held by the graph. If the UID fields of the terms in s are zero, they will be set to values consistent with the rest of the graph on return, mutating the parameter, otherwise the UIDs must match terms that already exist in the graph. The statement must be a valid RDF statement otherwise AddStatement will panic.
func (*Graph) AllStatements ¶
func (g *Graph) AllStatements() *Statements
AllStatements returns an iterator of the statements that make up the graph.
func (*Graph) Edge ¶
Edge returns the edge from u to v if such an edge exists and nil otherwise. The node v must be directly reachable from u as defined by the From method. The returned graph.Edge is a multi.Edge if an edge exists.
func (*Graph) Edges ¶
Edges returns all the edges in the graph. Each edge in the returned slice is a multi.Edge.
func (*Graph) From ¶
From returns all nodes in g that can be reached directly from n.
The returned graph.Nodes is only valid until the next mutation of the receiver.
func (*Graph) FromSubject ¶
FromSubject returns all nodes in g that can be reached directly from an RDF subject term.
The returned graph.Nodes is only valid until the next mutation of the receiver.
func (*Graph) HasEdgeBetween ¶
HasEdgeBetween returns whether an edge exists between nodes x and y without considering direction.
func (*Graph) HasEdgeFromTo ¶
HasEdgeFromTo returns whether an edge exists in the graph from u to v.
func (*Graph) Lines ¶
Lines returns the lines from u to v if such any such lines exists and nil otherwise. The node v must be directly reachable from u as defined by the From method.
func (*Graph) Node ¶
Node returns the node with the given ID if it exists in the graph, and nil otherwise.
func (*Graph) Nodes ¶
Nodes returns all the nodes in the graph.
The returned graph.Nodes is only valid until the next mutation of the receiver.
func (*Graph) Predicates ¶
Predicates returns a slice of all the predicates used in the graph.
func (*Graph) Query ¶
Query returns a query of the receiver starting from the given nodes. Queries may not be mixed between distinct graphs.
func (*Graph) RemoveStatement ¶
RemoveStatement removes s from the graph, leaving the terminal nodes if they are part of another statement. If the statement does not exist in g it is a no-op.
func (*Graph) RemoveTerm ¶
RemoveTerm removes t and any statements referencing t from the graph. If the term is a predicate, all statements with the predicate are removed. If the term does not exist it is a no-op.
func (*Graph) Statements ¶
func (g *Graph) Statements(uid, vid int64) *Statements
Statements returns an iterator of the statements that connect the subject term node u to the object term node v.
func (*Graph) TermFor ¶
TermFor returns the Term for the given text. The text must be an exact match for the Term's Value field.
func (*Graph) To ¶
To returns all nodes in g that can reach directly to n.
The returned graph.Nodes is only valid until the next mutation of the receiver.
func (*Graph) ToObject ¶
ToObject returns all nodes in g that can reach directly to an RDF object term.
The returned graph.Nodes is only valid until the next mutation of the receiver.
type Kind ¶
type Kind int
Kind represents the kind of an RDF term.
const ( // Invalid is an invalid RDF term. Invalid Kind = iota // IRI is the kind of an IRI term. // https://www.w3.org/TR/n-quads/#sec-iri IRI // Literal is the kind of an RDF literal. // https://www.w3.org/TR/n-quads/#sec-literals Literal // Blank is the kind of an RDF blank node term. // https://www.w3.org/TR/n-quads/#BNodes Blank )
func (Kind) String ¶
type Query ¶
type Query struct {
// contains filtered or unexported fields
}
Query represents a step in an RDF graph query. The methods on Query
provide a simple graph query language.
Code:play
Output:Example¶
package main
import (
"fmt"
"io"
"log"
"strings"
"gonum.org/v1/gonum/graph/formats/rdf"
)
const gods = `
_:alcmene <l:type> "human" .
_:alcmene <p:name> "Alcmene" .
_:cerberus <a:lives> _:cerberushome .
_:cerberus <l:type> "monster" .
_:cerberus <p:name> "Cerberus" .
_:cerberushome <p:location> _:tartarus .
_:cronos <l:type> "titan" .
_:cronos <p:name> "Cronos" .
_:hades <a:lives> _:hadeshome .
_:hades <h:brother> _:poseidon .
_:hades <h:brother> _:zeus .
_:hades <h:pet> _:cerberus .
_:hades <l:type> "god" .
_:hades <p:name> "Hades" .
_:hadeshome <p:location> _:tartarus .
_:hadeshome <p:reason> "it is peaceful" .
_:heracles <a:battled> _:cerberus .
_:heracles <a:battled> _:hydra .
_:heracles <a:battled> _:nemean .
_:heracles <h:father> _:zeus .
_:heracles <h:mother> _:alcmene .
_:heracles <l:type> "demigod" .
_:heracles <p:name> "Heracles" .
_:hydra <l:type> "monster" .
_:hydra <p:name> "Lernean Hydra" .
_:nemean <l:type> "monster" .
_:nemean <p:name> "Nemean Lion" .
_:olympus <l:type> "location" .
_:olympus <p:name> "Olympus" .
_:poseidon <a:lives> _:poseidonhome .
_:poseidon <h:brother> _:hades .
_:poseidon <h:brother> _:zeus .
_:poseidon <l:type> "god" .
_:poseidon <p:name> "Poseidon" .
_:poseidonhome <p:location> _:sea .
_:poseidonhome <p:reason> "it was given to him" .
_:sea <l:type> "location" .
_:sea <p:name> "Sea" .
_:tartarus <l:type> "location" .
_:tartarus <p:name> "Tartarus" .
_:theseus <a:battled> _:cerberus .
_:theseus <h:father> _:poseidon .
_:theseus <l:type> "human" .
_:theseus <p:name> "Theseus" .
_:zeus <a:lives> _:zeushome .
_:zeus <h:brother> _:hades .
_:zeus <h:brother> _:poseidon .
_:zeus <h:father> _:cronos .
_:zeus <l:type> "god" .
_:zeus <p:name> "Zeus" .
_:zeushome <p:location> _:olympus .
_:zeushome <p:reason> "he can see everything" .
`
func main() {
g := rdf.NewGraph()
dec := rdf.NewDecoder(strings.NewReader(gods))
for {
s, err := dec.Unmarshal()
if err != nil {
if err != io.EOF {
log.Fatalf("error during decoding: %v", err)
}
break
}
g.AddStatement(s)
}
it := g.Nodes()
nodes := make([]rdf.Term, 0, it.Len())
for it.Next() {
nodes = append(nodes, it.Node().(rdf.Term))
}
// Construct a query start point. This can be reused. If a specific
// node is already known it can be used to reduce the work required here.
heracles := g.Query(nodes...).In(func(s *rdf.Statement) bool {
// Traverse in from the name "Heracles".
return s.Predicate.Value == "<p:name>" && s.Object.Value == `"Heracles"`
})
// father and name filter statements on their predicate values. These
// are used in the queries that follow.
father := func(s *rdf.Statement) bool {
// Traverse across <h:father>.
return s.Predicate.Value == "<h:father>"
}
name := func(s *rdf.Statement) bool {
// Traverse across <p:name>.
return s.Predicate.Value == "<p:name>"
}
// g.V().has('name', 'heracles').out('father').out('father').values('name')
for _, r := range heracles.
Out(father). // Traverse out across <h:father> to get to Zeus.
Out(father). // and again to get to Cronos.
Out(name). // Retrieve the name by traversing the <p:name> edges.
Result() {
fmt.Printf("Heracles' grandfather: %s\n", r.Value)
}
// g.V().has('name', 'heracles').repeat(out('father')).emit().values('name')
var i int
heracles.Repeat(func(q rdf.Query) (rdf.Query, bool) {
q = q.Out(father)
for _, r := range q.Out(name).Result() {
fmt.Printf("Heracles' lineage %d: %s\n", i, r.Value)
}
i++
return q, true
})
// parents and typ are helper filters for queries below.
parents := func(s *rdf.Statement) bool {
// Traverse across <h:father> or <h:mother>
return s.Predicate.Value == "<h:father>" || s.Predicate.Value == "<h:mother>"
}
typ := func(s *rdf.Statement) bool {
// Traverse across <l:type>.
return s.Predicate.Value == "<l:type>"
}
// g.V(heracles).out('father', 'mother').label()
for _, r := range heracles.Out(parents).Out(typ).Result() {
fmt.Printf("Heracles' parents' types: %s\n", r.Value)
}
// battled is a helper filter for queries below.
battled := func(s *rdf.Statement) bool {
// Traverse across <a:battled>.
return s.Predicate.Value == "<a:battled>"
}
// g.V(heracles).out('battled').label()
for _, r := range heracles.Out(battled).Out(typ).Result() {
fmt.Printf("Heracles' antagonists' types: %s\n", r.Value)
}
// g.V(heracles).out('battled').valueMap()
for _, r := range heracles.Out(battled).Result() {
m := make(map[string]string)
g.Query(r).Out(func(s *rdf.Statement) bool {
// Store any p: namespace in the map.
if strings.HasPrefix(s.Predicate.Value, "<p:") {
prop := strings.TrimSuffix(strings.TrimPrefix(s.Predicate.Value, "<p:"), ">")
m[prop] = s.Object.Value
}
// But don't store the result into the query.
return false
})
fmt.Println(m)
}
// g.V(heracles).as('h').out('battled').in('battled').where(neq('h')).values('name')
for _, r := range heracles.Out(battled).In(battled).Not(heracles).Out(name).Result() {
fmt.Printf("Heracles' allies: %s\n", r.Value)
}
// Construct a query start point for Hades, this time using a restricted
// starting set only including the name. It would also be possible to
// start directly from a query with the term _:hades, but that depends
// on the blank node identity, which may be altered, for example by
// canonicalization.
h, ok := g.TermFor(`"Hades"`)
if !ok {
log.Fatal("could not find term for Hades")
}
hades := g.Query(h).In(name)
// g.V(hades).as('x').out('lives').in('lives').where(neq('x')).values('name')
//
// This is more complex with RDF since properties are encoded by
// attachment to anonymous blank nodes, so we take two steps, the
// first to the blank node for where Hades lives and then the second
// to get the actual location.
lives := func(s *rdf.Statement) bool {
// Traverse across <a:lives>.
return s.Predicate.Value == "<a:lives>"
}
location := func(s *rdf.Statement) bool {
// Traverse across <p:location>.
return s.Predicate.Value == "<p:location>"
}
for _, r := range hades.Out(lives).Out(location).In(location).In(lives).Not(hades).Out(name).Result() {
fmt.Printf("Hades lives with: %s\n", r.Value)
}
// g.V(hades).out('brother').as('god').out('lives').as('place').select('god', 'place').by('name')
brother := func(s *rdf.Statement) bool {
// Traverse across <h:brother>.
return s.Predicate.Value == "<h:brother>"
}
for _, r := range hades.Out(brother).Result() {
m := make(map[string]string)
as := func(key string) func(s *rdf.Statement) bool {
return func(s *rdf.Statement) bool {
// Store any <p:name> objects in the map.
if s.Predicate.Value == "<p:name>" {
m[key] = s.Object.Value
}
// But don't store the result into the query.
return false
}
}
sub := g.Query(r)
sub.Out(as("god"))
sub.Out(lives).Out(location).Out(as("place"))
fmt.Println(m)
}
// The query above but with the reason for their choice.
for _, r := range hades.Out(brother).Result() {
m := make(map[string]string)
// as stores the query result under the provided key
// for m, and if cont is not nil, allows the chain
// to continue.
as := func(query, key string, cont func(s *rdf.Statement) bool) func(s *rdf.Statement) bool {
return func(s *rdf.Statement) bool {
// Store any objects matching the query in the map.
if s.Predicate.Value == query {
m[key] = s.Object.Value
}
// Continue with chain if cont is not nil and
// the statement satisfies its condition.
if cont == nil {
return false
}
return cont(s)
}
}
sub := g.Query(r)
sub.Out(as("<p:name>", "god", nil))
sub.Out(lives).
Out(as("<p:reason>", "reason", location)).
Out(as("<p:name>", "place", nil))
fmt.Println(m)
}
}
Heracles' grandfather: "Cronos"
Heracles' lineage 0: "Zeus"
Heracles' lineage 1: "Cronos"
Heracles' parents' types: "god"
Heracles' parents' types: "human"
Heracles' antagonists' types: "monster"
Heracles' antagonists' types: "monster"
Heracles' antagonists' types: "monster"
map[name:"Cerberus"]
map[name:"Lernean Hydra"]
map[name:"Nemean Lion"]
Heracles' allies: "Theseus"
Hades lives with: "Cerberus"
map[god:"Zeus" place:"Olympus"]
map[god:"Poseidon" place:"Sea"]
map[god:"Zeus" place:"Olympus" reason:"he can see everything"]
map[god:"Poseidon" place:"Sea" reason:"it was given to him"]
func NewQuery ¶
NewQuery returns a query of g starting from the given nodes. Queries may not be mixed between distinct graphs. The type of g must be comparable. Query operations only consider edges that are represented by a *Statement or is an edge with lines held in a graph.Lines with at least one *Statement.
func (Query) And ¶
And returns a query that holds the conjunction of q and p.
func (Query) HasAllIn ¶
HasAllIn returns a query holding nodes from the receiver's initial set where all incoming statements satisfy fn. The query short circuits, so fn is not called after the first failure to match.
func (Query) HasAllOut ¶
HasAllOut returns a query holding nodes from the receiver's initial set where all outgoing statements satisfy fn. The query short circuits, so fn is not called after the first failure to match.
func (Query) HasAnyIn ¶
HasAnyIn returns a query holding nodes from the receiver's initial set where any incoming statements satisfies fn. The query short circuits, so fn is not called after the first match.
func (Query) HasAnyOut ¶
HasAnyOut returns a query holding nodes from the receiver's initial set where any outgoing statements satisfies fn. The query short circuits, so fn is not called after the first match.
func (Query) In ¶
In returns a query holding nodes reachable in from the receiver's starting nodes via statements that satisfy fn.
func (Query) Len ¶
Len returns the number of terms held by the query.
func (Query) Not ¶
Not returns a query that holds q less p.
func (Query) Or ¶
Or returns a query that holds the disjunction of q and p.
func (Query) Out ¶
Out returns a query holding nodes reachable out from the receiver's starting nodes via statements that satisfy fn.
func (Query) Repeat ¶
Repeat repeatedly calls fn on q until the set of results is empty or ok is false, and then returns the result. If the last non-empty result is wanted, fn should return its input and false when the partial traversal returns an empty result.
result := start.Repeat(func(q rdf.Query) (rdf.Query, bool) { r := q.Out(condition) if r.Len() == 0 { return q, false } return r, true }).Result()
func (Query) Result ¶
Result returns the terms held by the query.
func (Query) Unique ¶
Unique returns a copy of the receiver that contains only one instance of each term.
type Statement ¶
Statement is an RDF statement. It implements the graph.Edge and graph.Line interfaces.
func C14n ¶
C14n performs a relabeling of the statements in src based on the terms obtained from IsoCanonicalHashes, placing the results in dst and returning them. The relabeling scheme is the same as for the Universal RDF Dataset Normalization Algorithm, blank terms are ordered lexically by their hash value and then given a blank label with the prefix "_:c14n" and an identifier counter corresponding to the label's sort rank.
If dst is nil, it is allocated, otherwise the length of dst must match the
length of src.
Code:play
Output:Example¶
package main
import (
"crypto/md5"
"fmt"
"log"
"strings"
"gonum.org/v1/gonum/graph/formats/rdf"
)
func main() {
for _, statements := range []string{
`
_:a <ex:q> <ex:p> .
_:b <ex:q> <ex:p> .
_:c <ex:p> _:a .
_:d <ex:p> _:b .
_:c <ex:r> _:d .
`,
`
_:c1 <ex:p> _:a1 .
_:b1 <ex:q> <ex:p> .
_:d1 <ex:p> _:b1 .
_:a1 <ex:q> <ex:p> .
_:c1 <ex:r> _:d1 .
`,
} {
// Decode the statement stream.
dec := rdf.NewDecoder(strings.NewReader(statements))
var s []*rdf.Statement
for {
l, err := dec.Unmarshal()
if err != nil {
break
}
s = append(s, l)
}
// Get the hash to term label look-up table.
_, terms := rdf.IsoCanonicalHashes(s, false, true, md5.New(), make([]byte, 16))
relabeled, err := rdf.C14n(nil, s, terms)
if err != nil {
log.Fatal(err)
}
for _, s := range relabeled {
fmt.Println(s)
}
fmt.Println()
}
}
_:c14n0 <ex:p> _:c14n1 .
_:c14n1 <ex:q> <ex:p> .
_:c14n2 <ex:q> <ex:p> .
_:c14n3 <ex:p> _:c14n2 .
_:c14n3 <ex:r> _:c14n0 .
_:c14n0 <ex:p> _:c14n1 .
_:c14n1 <ex:q> <ex:p> .
_:c14n2 <ex:q> <ex:p> .
_:c14n3 <ex:p> _:c14n2 .
_:c14n3 <ex:r> _:c14n0 .
func Deduplicate ¶
Deduplicate removes duplicate statements in s, working in place, and returns the deduplicated slice with statements sorted in lexical order. Term UID fields are not considered and their values may be lost during deduplication.
func Lean ¶
Lean returns an RDF core of g that entails g. If g contains any non-zero labels, Lean will return a non-nil error and a core of g assuming no graph labels exist.
See http://aidanhogan.com/docs/rdf-canonicalisation.pdf for details of
the algorithm.
Code:play
Output:Example¶
package main
import (
"fmt"
"strings"
"gonum.org/v1/gonum/graph/formats/rdf"
)
func main() {
for i, statements := range []string{
0: `
_:author1 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:gonum .
_:author3 <ex:contributesTo> _:gonum .
`,
1: `
_:author1 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:gonum .
_:author3 <ex:contributesTo> _:gonum .
_:gonum <ex:dependsOn> _:go .
`,
2: `
_:author1 <ex:contributesTo> _:go .
_:author1 <ex:notContributesTo> _:gonum .
_:author2 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:gonum .
_:author3 <ex:contributesTo> _:gonum .
_:gonum <ex:dependsOn> _:go .
`,
3: `
_:author1 <ex:is> "Alice" .
_:author1 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:gonum .
_:author3 <ex:contributesTo> _:gonum .
_:gonum <ex:dependsOn> _:go .
`,
4: `
_:author1 <ex:contributesTo> _:go .
_:author1 <ex:notContributesTo> _:gonum .
_:author2 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:gonum .
_:author3 <ex:contributesTo> _:gonum .
_:author3 <ex:notContributesTo> _:go .
_:gonum <ex:dependsOn> _:go .
`,
5: `
_:author1 <ex:is> "Alice" .
_:author2 <ex:is> "Bob" .
_:author1 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:gonum .
_:author3 <ex:contributesTo> _:gonum .
_:gonum <ex:dependsOn> _:go .
`,
6: `
_:author1 <ex:is> "Alice" .
_:author2 <ex:is> "Bob" .
_:author3 <ex:is> "Charlie" .
_:author1 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:go .
_:author2 <ex:contributesTo> _:gonum .
_:author3 <ex:contributesTo> _:gonum .
_:gonum <ex:dependsOn> _:go .
`,
} {
// Decode the statement stream.
dec := rdf.NewDecoder(strings.NewReader(statements))
var s []*rdf.Statement
for {
l, err := dec.Unmarshal()
if err != nil {
break
}
s = append(s, l)
}
// Lean the graph to remove redundant statements.
lean, err := rdf.Lean(s)
if err != nil {
fmt.Println(err)
}
// Canonicalize the blank nodes in-place.
_, err = rdf.URDNA2015(lean, lean)
if err != nil {
fmt.Println(err)
continue
}
fmt.Printf("%d:\n", i)
for _, s := range lean {
fmt.Println(s)
}
fmt.Println()
}
}
0:
_:c14n0 <ex:contributesTo> _:c14n1 .
1:
_:c14n0 <ex:contributesTo> _:c14n1 .
_:c14n0 <ex:contributesTo> _:c14n2 .
_:c14n2 <ex:dependsOn> _:c14n1 .
2:
_:c14n0 <ex:contributesTo> _:c14n1 .
_:c14n0 <ex:contributesTo> _:c14n3 .
_:c14n2 <ex:contributesTo> _:c14n1 .
_:c14n2 <ex:notContributesTo> _:c14n3 .
_:c14n3 <ex:dependsOn> _:c14n1 .
3:
_:c14n0 <ex:contributesTo> _:c14n1 .
_:c14n0 <ex:contributesTo> _:c14n3 .
_:c14n2 <ex:contributesTo> _:c14n1 .
_:c14n2 <ex:is> "Alice" .
_:c14n3 <ex:dependsOn> _:c14n1 .
4:
_:c14n0 <ex:contributesTo> _:c14n1 .
_:c14n0 <ex:contributesTo> _:c14n2 .
_:c14n2 <ex:dependsOn> _:c14n1 .
_:c14n3 <ex:contributesTo> _:c14n1 .
_:c14n3 <ex:notContributesTo> _:c14n2 .
_:c14n4 <ex:contributesTo> _:c14n2 .
_:c14n4 <ex:notContributesTo> _:c14n1 .
5:
_:c14n1 <ex:contributesTo> _:c14n0 .
_:c14n1 <ex:contributesTo> _:c14n3 .
_:c14n1 <ex:is> "Bob" .
_:c14n2 <ex:contributesTo> _:c14n0 .
_:c14n2 <ex:is> "Alice" .
_:c14n3 <ex:dependsOn> _:c14n0 .
6:
_:c14n0 <ex:dependsOn> _:c14n1 .
_:c14n2 <ex:contributesTo> _:c14n0 .
_:c14n2 <ex:contributesTo> _:c14n1 .
_:c14n2 <ex:is> "Bob" .
_:c14n3 <ex:contributesTo> _:c14n1 .
_:c14n3 <ex:is> "Alice" .
_:c14n4 <ex:contributesTo> _:c14n0 .
_:c14n4 <ex:is> "Charlie" .
func ParseNQuad ¶
ParseNQuad parses the statement and returns the corresponding Statement. All Term UID fields are zero on return.
func URDNA2015 ¶
URDNA2015 applies the Universal RDF Dataset Normalization Algorithm 2015 to the statements in src, placing the result in dst and returning it. If dst is nil a slice of statements will be allocated. If dst is not nil and not the same length as src, URDNA2015 will return an error.
See https://json-ld.github.io/rdf-dataset-canonicalization/spec/index.html for details.
Code:play
Output:Example¶
package main
import (
"fmt"
"log"
"strings"
"gonum.org/v1/gonum/graph/formats/rdf"
)
func main() {
for _, statements := range []string{
`
_:a <ex:q> <ex:p> .
_:b <ex:q> <ex:p> .
_:c <ex:p> _:a .
_:d <ex:p> _:b .
_:c <ex:r> _:d .
`,
`
_:c1 <ex:p> _:a1 .
_:b1 <ex:q> <ex:p> .
_:d1 <ex:p> _:b1 .
_:a1 <ex:q> <ex:p> .
_:c1 <ex:r> _:d1 .
`,
} {
// Decode the statement stream.
dec := rdf.NewDecoder(strings.NewReader(statements))
var s []*rdf.Statement
for {
l, err := dec.Unmarshal()
if err != nil {
break
}
s = append(s, l)
}
relabeled, err := rdf.URDNA2015(nil, s)
if err != nil {
log.Fatal(err)
}
for _, s := range relabeled {
fmt.Println(s)
}
fmt.Println()
}
}
_:c14n0 <ex:p> _:c14n2 .
_:c14n1 <ex:p> _:c14n3 .
_:c14n1 <ex:r> _:c14n0 .
_:c14n2 <ex:q> <ex:p> .
_:c14n3 <ex:q> <ex:p> .
_:c14n0 <ex:p> _:c14n2 .
_:c14n1 <ex:p> _:c14n3 .
_:c14n1 <ex:r> _:c14n0 .
_:c14n2 <ex:q> <ex:p> .
_:c14n3 <ex:q> <ex:p> .
func URGNA2012 ¶
URGNA2012 applies the Universal RDF Graph Normalization Algorithm 2012 to the statements in src, placing the result in dst and returning it. If dst is nil a slice of statements will be allocated. If dst is not nil and not the same length as src, URGNA2012 will return an error.
See https://json-ld.github.io/rdf-dataset-canonicalization/spec/index.html for details.
Code:play
Output:Example¶
package main
import (
"fmt"
"log"
"strings"
"gonum.org/v1/gonum/graph/formats/rdf"
)
func main() {
for _, statements := range []string{
`
_:a <ex:q> <ex:p> .
_:b <ex:q> <ex:p> .
_:c <ex:p> _:a .
_:d <ex:p> _:b .
_:c <ex:r> _:d .
`,
`
_:c1 <ex:p> _:a1 .
_:b1 <ex:q> <ex:p> .
_:d1 <ex:p> _:b1 .
_:a1 <ex:q> <ex:p> .
_:c1 <ex:r> _:d1 .
`,
} {
// Decode the statement stream.
dec := rdf.NewDecoder(strings.NewReader(statements))
var s []*rdf.Statement
for {
l, err := dec.Unmarshal()
if err != nil {
break
}
s = append(s, l)
}
relabeled, err := rdf.URGNA2012(nil, s)
if err != nil {
log.Fatal(err)
}
for _, s := range relabeled {
fmt.Println(s)
}
fmt.Println()
}
}
_:c14n0 <ex:p> _:c14n3 .
_:c14n0 <ex:r> _:c14n1 .
_:c14n1 <ex:p> _:c14n2 .
_:c14n2 <ex:q> <ex:p> .
_:c14n3 <ex:q> <ex:p> .
_:c14n0 <ex:p> _:c14n3 .
_:c14n0 <ex:r> _:c14n1 .
_:c14n1 <ex:p> _:c14n2 .
_:c14n2 <ex:q> <ex:p> .
_:c14n3 <ex:q> <ex:p> .
func (*Statement) From ¶
From returns the subject of the statement.
func (*Statement) ID ¶
ID returns the UID of the Predicate field.
func (*Statement) ReversedEdge ¶
ReversedEdge returns the receiver unaltered. If there is a semantically valid edge reversal operation for the data, the user should implement this by wrapping Statement in a type performing that operation. See the ReversedLine example for details.
func (*Statement) ReversedLine ¶
ReversedLine returns the receiver unaltered. If there is a semantically
valid line reversal operation for the data, the user should implement
this by wrapping Statement in a type performing that operation.
Code:play
Output:Example¶
package main
import (
"fmt"
"log"
"strings"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/encoding"
"gonum.org/v1/gonum/graph/encoding/dot"
"gonum.org/v1/gonum/graph/formats/rdf"
"gonum.org/v1/gonum/graph/multi"
)
// foodNode implements graph.Node, dot.Node and encoding.Attributer
// to allow the RDF term value to be given to the DOT encoder.
type foodNode struct {
rdf.Term
}
func (n foodNode) DOTID() string {
text, _, kind, err := n.Term.Parts()
if err != nil {
return fmt.Sprintf("error:%s", n.Term.Value)
}
switch kind {
case rdf.Blank:
return n.Term.Value
case rdf.IRI:
return text
case rdf.Literal:
return fmt.Sprintf("%q", text)
default:
return fmt.Sprintf("invalid:%s", n.Term.Value)
}
}
func (n foodNode) Attributes() []encoding.Attribute {
_, qual, _, err := n.Term.Parts()
if err != nil {
return []encoding.Attribute{{Key: "error", Value: err.Error()}}
}
if qual == "" {
return nil
}
parts := strings.Split(qual, ":")
return []encoding.Attribute{{Key: parts[0], Value: parts[1]}}
}
// foodLine implements graph.Line and encoding.Attributer to
// allow the line's RDF term value to be given to the DOT
// encoder and for the nodes to be shimmed to the foodNode
// type.
//
// It also implements line reversal for the semantics of
// a food web with some taxonomic information.
type foodLine struct {
*rdf.Statement
}
func (l foodLine) From() graph.Node { return foodNode{l.Subject} }
func (l foodLine) To() graph.Node { return foodNode{l.Object} }
func (l foodLine) ReversedLine() graph.Line {
if l.Predicate.Value == "<tax:is>" {
// This should remain unreversed, so return as is.
return l
}
s := *l.Statement
// Reverse the line end points.
s.Subject, s.Object = s.Object, s.Subject
// Invert the semantics of the predicate.
switch s.Predicate.Value {
case "<eco:eats>":
s.Predicate.Value = "<eco:eaten-by>"
case "<eco:eaten-by>":
s.Predicate.Value = "<eco:eats>"
case "<tax:is-a>":
s.Predicate.Value = "<tax:includes>"
case "<tax:includes>":
s.Predicate.Value = "<tax:is-a>"
default:
panic("invalid predicate")
}
// All IDs returned by the RDF parser are positive, so
// sign reverse the edge ID to avoid any collisions.
s.Predicate.UID *= -1
return foodLine{&s}
}
func (l foodLine) Attributes() []encoding.Attribute {
text, _, _, err := l.Predicate.Parts()
if err != nil {
return []encoding.Attribute{{Key: "error", Value: err.Error()}}
}
parts := strings.Split(text, ":")
return []encoding.Attribute{{Key: parts[0], Value: parts[1]}}
}
// expand copies src into dst, adding the reversal of each line if it is
// distinct.
func expand(dst, src *multi.DirectedGraph) {
it := src.Edges()
for it.Next() {
lit := it.Edge().(multi.Edge)
for lit.Next() {
l := lit.Line()
r := l.ReversedLine()
dst.SetLine(l)
if l == r {
continue
}
dst.SetLine(r)
}
}
}
func main() {
const statements = `
_:wolf <tax:is-a> _:animal .
_:wolf <tax:is> "Wolf"^^<tax:common> .
_:wolf <tax:is> "Canis lupus"^^<tax:binomial> .
_:wolf <eco:eats> _:sheep .
_:sheep <tax:is-a> _:animal .
_:sheep <tax:is> "Sheep"^^<tax:common> .
_:sheep <tax:is> "Ovis aries"^^<tax:binomial> .
_:sheep <eco:eats> _:grass .
_:grass <tax:is-a> _:plant .
_:grass <tax:is> "Grass"^^<tax:common> .
_:grass <tax:is> "Lolium perenne"^^<tax:binomial> .
_:grass <tax:is> "Festuca rubra"^^<tax:binomial> .
_:grass <tax:is> "Poa pratensis"^^<tax:binomial> .
`
// Decode the statement stream and insert the lines into a multigraph.
g := multi.NewDirectedGraph()
dec := rdf.NewDecoder(strings.NewReader(statements))
for {
l, err := dec.Unmarshal()
if err != nil {
break
}
// Wrap the line with a shim type to allow the RDF values
// to be passed to the DOT marshaling routine.
g.SetLine(foodLine{l})
}
h := multi.NewDirectedGraph()
expand(h, g)
// Marshal the graph into DOT.
b, err := dot.MarshalMulti(h, "food web", "", "\t")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n\n", b)
}
digraph "food web" {
// Node definitions.
"_:wolf";
"_:animal";
"Wolf" [tax=common];
"Canis lupus" [tax=binomial];
"_:sheep";
"Sheep" [tax=common];
"Ovis aries" [tax=binomial];
"_:grass";
"_:plant";
"Grass" [tax=common];
"Lolium perenne" [tax=binomial];
"Festuca rubra" [tax=binomial];
"Poa pratensis" [tax=binomial];
// Edge definitions.
"_:wolf" -> "_:animal" [tax="is-a"];
"_:wolf" -> "Wolf" [tax=is];
"_:wolf" -> "Canis lupus" [tax=is];
"_:wolf" -> "_:sheep" [eco=eats];
"_:animal" -> "_:wolf" [tax=includes];
"_:animal" -> "_:sheep" [tax=includes];
"_:sheep" -> "_:wolf" [eco="eaten-by"];
"_:sheep" -> "_:animal" [tax="is-a"];
"_:sheep" -> "Sheep" [tax=is];
"_:sheep" -> "Ovis aries" [tax=is];
"_:sheep" -> "_:grass" [eco=eats];
"_:grass" -> "_:sheep" [eco="eaten-by"];
"_:grass" -> "_:plant" [tax="is-a"];
"_:grass" -> "Grass" [tax=is];
"_:grass" -> "Lolium perenne" [tax=is];
"_:grass" -> "Festuca rubra" [tax=is];
"_:grass" -> "Poa pratensis" [tax=is];
"_:plant" -> "_:grass" [tax=includes];
}
func (*Statement) String ¶
String returns the RDF 1.1 N-Quad formatted statement.
func (*Statement) To ¶
To returns the object of the statement.
type Statements ¶
type Statements struct {
// contains filtered or unexported fields
}
Statements is an RDF statement iterator.
func (*Statements) Next ¶
func (s *Statements) Next() bool
Next returns whether the iterator holds any additional statements.
func (*Statements) Statement ¶
func (s *Statements) Statement() *Statement
Statement returns the current statement.
type Term ¶
type Term struct { // Value is the text value of term. Value string // UID is the unique ID for the term // in a collection of RDF terms. UID int64 }
Term is an RDF term. It implements the graph.Node interface.
func NewBlankTerm ¶
NewBlankTerm returns a Term based on the provided RDF blank node label. The label should not include the "_:" prefix. The returned Term will not have the UID set.
func NewIRITerm ¶
NewIRITerm returns a Term based on the provided IRI which must be valid and include a scheme. The returned Term will not have the UID set.
func NewLiteralTerm ¶
NewLiteralTerm returns a Term based on the literal text and an optional qualifier which may either be a "@"-prefixed language tag or a valid IRI. The text will be escaped if necessary and quoted, and if an IRI is given it will be escaped if necessary. The returned Term will not have the UID set.
func (Term) ID ¶
ID returns the value of the Term's UID field.
func (Term) Parts ¶
Parts returns the parts of the term and the kind of the term. IRI node text is returned as a valid IRI with the quoting angle brackets removed and escape sequences interpreted, and blank nodes are stripped of the "_:" prefix. When the term is a literal, qual will either be empty, an unescaped IRI, or an RDF language tag prefixed with an @ symbol. The literal text is returned unquoted and unescaped.
Bugs ¶
☞ Graph leaning does not take into account graph label terms since the formal semantics for a multiple graph data model have not been defined. See https://www.w3.org/TR/rdf11-datasets/#declaring.
Source Files ¶
check.go disjoint_set.go doc.go equi_canonical.go extract.go graph.go iso_canonical.go kind_string.go nodebug.go parse.go query.go rdf.go urna.go
- Version
- v0.15.1 (latest)
- Published
- Aug 16, 2024
- Platform
- linux/amd64
- Imports
- 22 packages
- Last checked
- 12 hours ago –
Tools for package owners.