Files
gonum/graph/formats/rdf/query.go
Dan Kortschak 5f0141ca4c all: run gofmt and generate all packages
Changes made in dsp/fourier/internal/fftpack break the formatting used
there, so these are reverted. There will be complaints in CI.

[git-generate]
gofmt -w .
go generate gonum.org/v1/gonum/blas
go generate gonum.org/v1/gonum/blas/gonum
go generate gonum.org/v1/gonum/unit
go generate gonum.org/v1/gonum/unit/constant
go generate gonum.org/v1/gonum/graph/formats/dot
go generate gonum.org/v1/gonum/graph/formats/rdf
go generate gonum.org/v1/gonum/stat/card

git checkout -- dsp/fourier/internal/fftpack
2022-08-06 07:05:17 +09:30

283 lines
6.9 KiB
Go

// Copyright ©2022 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rdf
import (
"sort"
"gonum.org/v1/gonum/graph"
)
// Query represents a step in an RDF graph query. The methods on Query
// provide a simple graph query language.
type Query struct {
g graph.Directed
terms []Term
}
// 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 NewQuery(g graph.Directed, from ...Term) Query {
return Query{g: g, terms: from}
}
// Query returns a query of the receiver starting from the given nodes.
// Queries may not be mixed between distinct graphs.
func (g *Graph) Query(from ...Term) Query {
return Query{g: g, terms: from}
}
// Out returns a query holding nodes reachable out from the receiver's
// starting nodes via statements that satisfy fn.
func (q Query) Out(fn func(s *Statement) bool) Query {
r := Query{g: q.g}
for _, s := range q.terms {
it := q.g.From(s.ID())
for it.Next() {
if ConnectedByAny(q.g.Edge(s.ID(), it.Node().ID()), fn) {
r.terms = append(r.terms, it.Node().(Term))
}
}
}
return r
}
// In returns a query holding nodes reachable in from the receiver's
// starting nodes via statements that satisfy fn.
func (q Query) In(fn func(s *Statement) bool) Query {
r := Query{g: q.g}
for _, s := range q.terms {
it := q.g.To(s.ID())
for it.Next() {
if ConnectedByAny(q.g.Edge(it.Node().ID(), s.ID()), fn) {
r.terms = append(r.terms, it.Node().(Term))
}
}
}
return r
}
// 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 (q Query) HasAllOut(fn func(s *Statement) bool) Query {
r := Query{g: q.g}
notFn := not(fn)
loop:
for _, s := range q.terms {
it := q.g.From(s.ID())
for it.Next() {
if ConnectedByAny(q.g.Edge(s.ID(), it.Node().ID()), notFn) {
continue loop
}
}
r.terms = append(r.terms, s)
}
return r
}
// 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 (q Query) HasAllIn(fn func(s *Statement) bool) Query {
r := Query{g: q.g}
notFn := not(fn)
loop:
for _, s := range q.terms {
it := q.g.To(s.ID())
for it.Next() {
if ConnectedByAny(q.g.Edge(it.Node().ID(), s.ID()), notFn) {
continue loop
}
}
r.terms = append(r.terms, s)
}
return r
}
// 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 (q Query) HasAnyOut(fn func(s *Statement) bool) Query {
r := Query{g: q.g}
for _, s := range q.terms {
it := q.g.From(s.ID())
for it.Next() {
if ConnectedByAny(q.g.Edge(s.ID(), it.Node().ID()), fn) {
r.terms = append(r.terms, s)
break
}
}
}
return r
}
// 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 (q Query) HasAnyIn(fn func(s *Statement) bool) Query {
r := Query{g: q.g}
for _, s := range q.terms {
it := q.g.To(s.ID())
for it.Next() {
if ConnectedByAny(q.g.Edge(it.Node().ID(), s.ID()), fn) {
r.terms = append(r.terms, s)
break
}
}
}
return r
}
// not returns the negation of fn.
func not(fn func(s *Statement) bool) func(s *Statement) bool {
return func(s *Statement) bool { return !fn(s) }
}
// And returns a query that holds the conjunction of q and p.
func (q Query) And(p Query) Query {
if q.g != p.g {
panic("rdf: binary query operation parameters from distinct graphs")
}
sortByID(q.terms)
sortByID(p.terms)
r := Query{g: q.g}
var i, j int
for i < len(q.terms) && j < len(p.terms) {
qi := q.terms[i]
pj := p.terms[j]
switch {
case qi.ID() < pj.ID():
i++
case pj.ID() < qi.ID():
j++
default:
r.terms = append(r.terms, qi)
i++
j++
}
}
return r
}
// Or returns a query that holds the disjunction of q and p.
func (q Query) Or(p Query) Query {
if q.g != p.g {
panic("rdf: binary query operation parameters from distinct graphs")
}
sortByID(q.terms)
sortByID(p.terms)
r := Query{g: q.g}
var i, j int
for i < len(q.terms) && j < len(p.terms) {
qi := q.terms[i]
pj := p.terms[j]
switch {
case qi.ID() < pj.ID():
if len(r.terms) == 0 || r.terms[len(r.terms)-1].UID != qi.UID {
r.terms = append(r.terms, qi)
}
i++
case pj.ID() < qi.ID():
if len(r.terms) == 0 || r.terms[len(r.terms)-1].UID != pj.UID {
r.terms = append(r.terms, pj)
}
j++
default:
if len(r.terms) == 0 || r.terms[len(r.terms)-1].UID != qi.UID {
r.terms = append(r.terms, qi)
}
i++
j++
}
}
r.terms = append(r.terms, q.terms[i:]...)
r.terms = append(r.terms, p.terms[j:]...)
return r
}
// Not returns a query that holds q less p.
func (q Query) Not(p Query) Query {
if q.g != p.g {
panic("rdf: binary query operation parameters from distinct graphs")
}
sortByID(q.terms)
sortByID(p.terms)
r := Query{g: q.g}
var i, j int
for i < len(q.terms) && j < len(p.terms) {
qi := q.terms[i]
pj := p.terms[j]
switch {
case qi.ID() < pj.ID():
r.terms = append(r.terms, qi)
i++
case pj.ID() < qi.ID():
j++
default:
i++
}
}
if len(r.terms) < len(q.terms) {
r.terms = append(r.terms, q.terms[i:len(q.terms)+min(0, i-len(r.terms))]...)
}
return r
}
// 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 (q Query) Repeat(fn func(Query) (q Query, ok bool)) Query {
for {
var ok bool
q, ok = fn(q)
if !ok || len(q.terms) == 0 {
return q
}
}
}
// Unique returns a copy of the receiver that contains only one instance
// of each term.
func (q Query) Unique() Query {
sortByID(q.terms)
r := Query{g: q.g}
for i, t := range q.terms {
if i == 0 || t.UID != q.terms[i-1].UID {
r.terms = append(r.terms, t)
}
}
return r
}
// Len returns the number of terms held by the query.
func (q Query) Len() int {
return len(q.terms)
}
// Result returns the terms held by the query.
func (q Query) Result() []Term {
return q.terms
}
func sortByID(terms []Term) {
sort.Slice(terms, func(i, j int) bool { return terms[i].ID() < terms[j].ID() })
}