Add filter, add READMES

This commit is contained in:
Oxy8
2026-03-06 15:35:04 -03:00
parent b44867abfa
commit 3c487d088b
56 changed files with 2495 additions and 1424 deletions

View 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
}

View 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)
}

View 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
}

View 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"`
}

View 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)
}

View 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)
}

View 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)
}