Add filter, add READMES
This commit is contained in:
102
backend_go/selection_queries/helpers.go
Normal file
102
backend_go/selection_queries/helpers.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package selection_queries
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func nodeKey(termType, iri string) string {
|
||||
return termType + "\x00" + iri
|
||||
}
|
||||
|
||||
func valuesTerm(n NodeRef) string {
|
||||
if n.TermType == "uri" {
|
||||
if n.IRI == "" {
|
||||
return ""
|
||||
}
|
||||
return "<" + n.IRI + ">"
|
||||
}
|
||||
if n.TermType == "bnode" {
|
||||
if n.IRI == "" {
|
||||
return ""
|
||||
}
|
||||
if strings.HasPrefix(n.IRI, "_:") {
|
||||
return n.IRI
|
||||
}
|
||||
return "_:" + n.IRI
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func termKeyFromSparqlTerm(term sparqlTerm, includeBNodes bool) (string, bool) {
|
||||
if term.Type == "" || term.Value == "" {
|
||||
return "", false
|
||||
}
|
||||
if term.Type == "literal" {
|
||||
return "", false
|
||||
}
|
||||
if term.Type == "bnode" {
|
||||
if !includeBNodes {
|
||||
return "", false
|
||||
}
|
||||
return nodeKey("bnode", "_:"+term.Value), true
|
||||
}
|
||||
if term.Type == "uri" {
|
||||
return nodeKey("uri", term.Value), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func selectedNodesFromIDs(idx Index, selectedIDs []int, includeBNodes bool) ([]NodeRef, map[int]struct{}) {
|
||||
out := make([]NodeRef, 0, len(selectedIDs))
|
||||
set := make(map[int]struct{}, len(selectedIDs))
|
||||
for _, nid := range selectedIDs {
|
||||
n, ok := idx.IDToNode[nid]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if n.TermType == "bnode" && !includeBNodes {
|
||||
continue
|
||||
}
|
||||
out = append(out, n)
|
||||
set[nid] = struct{}{}
|
||||
}
|
||||
return out, set
|
||||
}
|
||||
|
||||
func idsFromBindings(raw []byte, varName string, idx Index, selectedSet map[int]struct{}, includeBNodes bool) ([]int, error) {
|
||||
var res sparqlResponse
|
||||
if err := json.Unmarshal(raw, &res); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse SPARQL JSON: %w", err)
|
||||
}
|
||||
|
||||
neighborSet := make(map[int]struct{})
|
||||
for _, b := range res.Results.Bindings {
|
||||
term, ok := b[varName]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
key, ok := termKeyFromSparqlTerm(term, includeBNodes)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
nid, ok := idx.KeyToID[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, sel := selectedSet[nid]; sel {
|
||||
continue
|
||||
}
|
||||
neighborSet[nid] = struct{}{}
|
||||
}
|
||||
|
||||
ids := make([]int, 0, len(neighborSet))
|
||||
for nid := range neighborSet {
|
||||
ids = append(ids, nid)
|
||||
}
|
||||
sort.Ints(ids)
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
77
backend_go/selection_queries/neighbors.go
Normal file
77
backend_go/selection_queries/neighbors.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package selection_queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func neighborsQuery(selectedNodes []NodeRef, includeBNodes bool) string {
|
||||
valuesTerms := make([]string, 0, len(selectedNodes))
|
||||
for _, n := range selectedNodes {
|
||||
t := valuesTerm(n)
|
||||
if t == "" {
|
||||
continue
|
||||
}
|
||||
valuesTerms = append(valuesTerms, t)
|
||||
}
|
||||
|
||||
if len(valuesTerms) == 0 {
|
||||
return "SELECT ?nbr WHERE { FILTER(false) }"
|
||||
}
|
||||
|
||||
bnodeFilter := ""
|
||||
if !includeBNodes {
|
||||
bnodeFilter = "FILTER(!isBlank(?nbr))"
|
||||
}
|
||||
|
||||
values := strings.Join(valuesTerms, " ")
|
||||
return fmt.Sprintf(`
|
||||
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||
PREFIX owl: <http://www.w3.org/2002/07/owl#>
|
||||
|
||||
SELECT DISTINCT ?nbr
|
||||
WHERE {
|
||||
VALUES ?sel { %s }
|
||||
{
|
||||
?sel rdf:type ?o .
|
||||
?o rdf:type owl:Class .
|
||||
BIND(?o AS ?nbr)
|
||||
}
|
||||
UNION
|
||||
{
|
||||
?s rdf:type ?sel .
|
||||
?sel rdf:type owl:Class .
|
||||
BIND(?s AS ?nbr)
|
||||
}
|
||||
UNION
|
||||
{
|
||||
?sel rdfs:subClassOf ?o .
|
||||
BIND(?o AS ?nbr)
|
||||
}
|
||||
UNION
|
||||
{
|
||||
?s rdfs:subClassOf ?sel .
|
||||
BIND(?s AS ?nbr)
|
||||
}
|
||||
FILTER(!isLiteral(?nbr))
|
||||
FILTER(?nbr != ?sel)
|
||||
%s
|
||||
}
|
||||
`, values, bnodeFilter)
|
||||
}
|
||||
|
||||
func runNeighbors(ctx context.Context, q Querier, idx Index, selectedIDs []int, includeBNodes bool) ([]int, error) {
|
||||
selectedNodes, selectedSet := selectedNodesFromIDs(idx, selectedIDs, includeBNodes)
|
||||
if len(selectedNodes) == 0 {
|
||||
return []int{}, nil
|
||||
}
|
||||
|
||||
raw, err := q.Query(ctx, neighborsQuery(selectedNodes, includeBNodes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return idsFromBindings(raw, "nbr", idx, selectedSet, includeBNodes)
|
||||
}
|
||||
|
||||
33
backend_go/selection_queries/registry.go
Normal file
33
backend_go/selection_queries/registry.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package selection_queries
|
||||
|
||||
var definitions = []Definition{
|
||||
{
|
||||
Meta: Meta{ID: "neighbors", Label: "Neighbors"},
|
||||
Run: runNeighbors,
|
||||
},
|
||||
{
|
||||
Meta: Meta{ID: "superclasses", Label: "Superclasses"},
|
||||
Run: runSuperclasses,
|
||||
},
|
||||
{
|
||||
Meta: Meta{ID: "subclasses", Label: "Subclasses"},
|
||||
Run: runSubclasses,
|
||||
},
|
||||
}
|
||||
|
||||
func List() []Meta {
|
||||
out := make([]Meta, 0, len(definitions))
|
||||
for _, d := range definitions {
|
||||
out = append(out, d.Meta)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func Get(id string) (Definition, bool) {
|
||||
for _, d := range definitions {
|
||||
if d.Meta.ID == id {
|
||||
return d, true
|
||||
}
|
||||
}
|
||||
return Definition{}, false
|
||||
}
|
||||
14
backend_go/selection_queries/sparql_types.go
Normal file
14
backend_go/selection_queries/sparql_types.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package selection_queries
|
||||
|
||||
type sparqlTerm struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
Lang string `json:"xml:lang,omitempty"`
|
||||
}
|
||||
|
||||
type sparqlResponse struct {
|
||||
Results struct {
|
||||
Bindings []map[string]sparqlTerm `json:"bindings"`
|
||||
} `json:"results"`
|
||||
}
|
||||
|
||||
55
backend_go/selection_queries/subclasses.go
Normal file
55
backend_go/selection_queries/subclasses.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package selection_queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func subclassesQuery(selectedNodes []NodeRef, includeBNodes bool) string {
|
||||
valuesTerms := make([]string, 0, len(selectedNodes))
|
||||
for _, n := range selectedNodes {
|
||||
t := valuesTerm(n)
|
||||
if t == "" {
|
||||
continue
|
||||
}
|
||||
valuesTerms = append(valuesTerms, t)
|
||||
}
|
||||
|
||||
if len(valuesTerms) == 0 {
|
||||
return "SELECT ?nbr WHERE { FILTER(false) }"
|
||||
}
|
||||
|
||||
bnodeFilter := ""
|
||||
if !includeBNodes {
|
||||
bnodeFilter = "FILTER(!isBlank(?nbr))"
|
||||
}
|
||||
|
||||
values := strings.Join(valuesTerms, " ")
|
||||
return fmt.Sprintf(`
|
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||
|
||||
SELECT DISTINCT ?nbr
|
||||
WHERE {
|
||||
VALUES ?sel { %s }
|
||||
?nbr rdfs:subClassOf ?sel .
|
||||
FILTER(!isLiteral(?nbr))
|
||||
FILTER(?nbr != ?sel)
|
||||
%s
|
||||
}
|
||||
`, values, bnodeFilter)
|
||||
}
|
||||
|
||||
func runSubclasses(ctx context.Context, q Querier, idx Index, selectedIDs []int, includeBNodes bool) ([]int, error) {
|
||||
selectedNodes, selectedSet := selectedNodesFromIDs(idx, selectedIDs, includeBNodes)
|
||||
if len(selectedNodes) == 0 {
|
||||
return []int{}, nil
|
||||
}
|
||||
|
||||
raw, err := q.Query(ctx, subclassesQuery(selectedNodes, includeBNodes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return idsFromBindings(raw, "nbr", idx, selectedSet, includeBNodes)
|
||||
}
|
||||
|
||||
55
backend_go/selection_queries/superclasses.go
Normal file
55
backend_go/selection_queries/superclasses.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package selection_queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func superclassesQuery(selectedNodes []NodeRef, includeBNodes bool) string {
|
||||
valuesTerms := make([]string, 0, len(selectedNodes))
|
||||
for _, n := range selectedNodes {
|
||||
t := valuesTerm(n)
|
||||
if t == "" {
|
||||
continue
|
||||
}
|
||||
valuesTerms = append(valuesTerms, t)
|
||||
}
|
||||
|
||||
if len(valuesTerms) == 0 {
|
||||
return "SELECT ?nbr WHERE { FILTER(false) }"
|
||||
}
|
||||
|
||||
bnodeFilter := ""
|
||||
if !includeBNodes {
|
||||
bnodeFilter = "FILTER(!isBlank(?nbr))"
|
||||
}
|
||||
|
||||
values := strings.Join(valuesTerms, " ")
|
||||
return fmt.Sprintf(`
|
||||
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
||||
|
||||
SELECT DISTINCT ?nbr
|
||||
WHERE {
|
||||
VALUES ?sel { %s }
|
||||
?sel rdfs:subClassOf ?nbr .
|
||||
FILTER(!isLiteral(?nbr))
|
||||
FILTER(?nbr != ?sel)
|
||||
%s
|
||||
}
|
||||
`, values, bnodeFilter)
|
||||
}
|
||||
|
||||
func runSuperclasses(ctx context.Context, q Querier, idx Index, selectedIDs []int, includeBNodes bool) ([]int, error) {
|
||||
selectedNodes, selectedSet := selectedNodesFromIDs(idx, selectedIDs, includeBNodes)
|
||||
if len(selectedNodes) == 0 {
|
||||
return []int{}, nil
|
||||
}
|
||||
|
||||
raw, err := q.Query(ctx, superclassesQuery(selectedNodes, includeBNodes))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return idsFromBindings(raw, "nbr", idx, selectedSet, includeBNodes)
|
||||
}
|
||||
|
||||
29
backend_go/selection_queries/types.go
Normal file
29
backend_go/selection_queries/types.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package selection_queries
|
||||
|
||||
import "context"
|
||||
|
||||
type Querier interface {
|
||||
Query(ctx context.Context, query string) ([]byte, error)
|
||||
}
|
||||
|
||||
type NodeRef struct {
|
||||
ID int
|
||||
TermType string // "uri" | "bnode"
|
||||
IRI string
|
||||
}
|
||||
|
||||
type Index struct {
|
||||
IDToNode map[int]NodeRef
|
||||
KeyToID map[string]int
|
||||
}
|
||||
|
||||
type Meta struct {
|
||||
ID string `json:"id"`
|
||||
Label string `json:"label"`
|
||||
}
|
||||
|
||||
type Definition struct {
|
||||
Meta Meta
|
||||
Run func(ctx context.Context, q Querier, idx Index, selectedIDs []int, includeBNodes bool) ([]int, error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user