radial sugiyama positioning integration

This commit is contained in:
Oxy8
2026-03-23 11:13:27 -03:00
parent 6b9115e43b
commit 696844f341
51 changed files with 10089 additions and 364 deletions

View File

@@ -3,6 +3,7 @@ package selection_queries
import (
"encoding/json"
"fmt"
"log"
"sort"
"strings"
)
@@ -66,30 +67,83 @@ func selectedNodesFromIDs(idx Index, selectedIDs []uint32, includeBNodes bool) (
return out, set
}
func idsFromBindings(raw []byte, varName string, idx Index, selectedSet map[uint32]struct{}, includeBNodes bool) ([]uint32, error) {
func idFromSparqlTerm(term sparqlTerm, idx Index, includeBNodes bool) (uint32, bool) {
key, ok := termKeyFromSparqlTerm(term, includeBNodes)
if !ok {
return 0, false
}
nid, ok := idx.KeyToID[key]
return nid, ok
}
func tripleTermFromSparqlTerm(term sparqlTerm) TripleTerm {
return TripleTerm{
Type: term.Type,
Value: term.Value,
Lang: term.Lang,
}
}
func logQueryExecutionFailure(queryName string, selectedIDs []uint32, includeBNodes bool, sparql string, err error) {
log.Printf(
"%s: SPARQL execution failed selected_ids=%v include_bnodes=%t err=%v\nSPARQL:\n%s",
queryName,
selectedIDs,
includeBNodes,
err,
strings.TrimSpace(sparql),
)
}
func resultFromTripleBindings(raw []byte, idx Index, selectedSet map[uint32]struct{}, includeBNodes bool) (Result, error) {
var res sparqlResponse
if err := json.Unmarshal(raw, &res); err != nil {
return nil, fmt.Errorf("failed to parse SPARQL JSON: %w", err)
return Result{}, fmt.Errorf("failed to parse SPARQL JSON: %w", err)
}
neighborSet := make(map[uint32]struct{})
triples := make([]Triple, 0, len(res.Results.Bindings))
for _, b := range res.Results.Bindings {
term, ok := b[varName]
if !ok {
sTerm, okS := b["s"]
pTerm, okP := b["p"]
oTerm, okO := b["o"]
if !okS || !okP || !okO {
continue
}
key, ok := termKeyFromSparqlTerm(term, includeBNodes)
if !ok {
continue
triple := Triple{
S: tripleTermFromSparqlTerm(sTerm),
P: tripleTermFromSparqlTerm(pTerm),
O: tripleTermFromSparqlTerm(oTerm),
}
nid, ok := idx.KeyToID[key]
if !ok {
continue
subjID, subjOK := idFromSparqlTerm(sTerm, idx, includeBNodes)
if subjOK {
id := subjID
triple.SubjectID = &id
}
if _, sel := selectedSet[nid]; sel {
continue
objID, objOK := idFromSparqlTerm(oTerm, idx, includeBNodes)
if objOK {
id := objID
triple.ObjectID = &id
}
neighborSet[nid] = struct{}{}
if pTerm.Type == "uri" {
if predID, ok := idx.PredicateIDByIRI[pTerm.Value]; ok {
id := predID
triple.PredicateID = &id
}
}
_, subjSelected := selectedSet[subjID]
_, objSelected := selectedSet[objID]
if subjOK && subjSelected && objOK && !objSelected {
neighborSet[objID] = struct{}{}
}
if objOK && objSelected && subjOK && !subjSelected {
neighborSet[subjID] = struct{}{}
}
triples = append(triples, triple)
}
ids := make([]uint32, 0, len(neighborSet))
@@ -97,5 +151,5 @@ func idsFromBindings(raw []byte, varName string, idx Index, selectedSet map[uint
ids = append(ids, nid)
}
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
return ids, nil
return Result{NeighborIDs: ids, Triples: triples}, nil
}

View File

@@ -17,12 +17,12 @@ func neighborsQuery(selectedNodes []NodeRef, includeBNodes bool) string {
}
if len(valuesTerms) == 0 {
return "SELECT ?nbr WHERE { FILTER(false) }"
return "SELECT ?s ?p ?o WHERE { FILTER(false) }"
}
bnodeFilter := ""
if !includeBNodes {
bnodeFilter = "FILTER(!isBlank(?nbr))"
bnodeFilter = "FILTER(!isBlank(?s) && !isBlank(?o))"
}
values := strings.Join(valuesTerms, " ")
@@ -31,46 +31,55 @@ 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
SELECT DISTINCT ?s ?p ?o
WHERE {
VALUES ?sel { %s }
{
?sel rdf:type ?o .
VALUES ?sel { %s }
BIND(?sel AS ?s)
VALUES ?p { rdf:type }
?s ?p ?o .
?o rdf:type owl:Class .
BIND(?o AS ?nbr)
}
UNION
{
?s rdf:type ?sel .
VALUES ?sel { %s }
VALUES ?p { rdf:type }
?s ?p ?sel .
?sel rdf:type owl:Class .
BIND(?s AS ?nbr)
BIND(?sel AS ?o)
}
UNION
{
?sel rdfs:subClassOf ?o .
BIND(?o AS ?nbr)
VALUES ?sel { %s }
BIND(?sel AS ?s)
VALUES ?p { rdfs:subClassOf }
?s ?p ?o .
}
UNION
{
?s rdfs:subClassOf ?sel .
BIND(?s AS ?nbr)
VALUES ?sel { %s }
VALUES ?p { rdfs:subClassOf }
?s ?p ?sel .
BIND(?sel AS ?o)
}
FILTER(!isLiteral(?nbr))
FILTER(?nbr != ?sel)
FILTER(!isLiteral(?o))
FILTER(?s != ?o)
%s
}
`, values, bnodeFilter)
`, values, values, values, values, bnodeFilter)
}
func runNeighbors(ctx context.Context, q Querier, idx Index, selectedIDs []uint32, includeBNodes bool) ([]uint32, error) {
func runNeighbors(ctx context.Context, q Querier, idx Index, selectedIDs []uint32, includeBNodes bool) (Result, error) {
selectedNodes, selectedSet := selectedNodesFromIDs(idx, selectedIDs, includeBNodes)
if len(selectedNodes) == 0 {
return []uint32{}, nil
return Result{NeighborIDs: []uint32{}, Triples: []Triple{}}, nil
}
raw, err := q.Query(ctx, neighborsQuery(selectedNodes, includeBNodes))
query := neighborsQuery(selectedNodes, includeBNodes)
raw, err := q.Query(ctx, query)
if err != nil {
return nil, err
logQueryExecutionFailure("neighbors", selectedIDs, includeBNodes, query, err)
return Result{}, err
}
return idsFromBindings(raw, "nbr", idx, selectedSet, includeBNodes)
return resultFromTripleBindings(raw, idx, selectedSet, includeBNodes)
}

View File

@@ -17,38 +17,42 @@ func subclassesQuery(selectedNodes []NodeRef, includeBNodes bool) string {
}
if len(valuesTerms) == 0 {
return "SELECT ?nbr WHERE { FILTER(false) }"
return "SELECT ?s ?p ?o WHERE { FILTER(false) }"
}
bnodeFilter := ""
if !includeBNodes {
bnodeFilter = "FILTER(!isBlank(?nbr))"
bnodeFilter = "FILTER(!isBlank(?s) && !isBlank(?o))"
}
values := strings.Join(valuesTerms, " ")
return fmt.Sprintf(`
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT DISTINCT ?nbr
SELECT DISTINCT ?s ?p ?o
WHERE {
VALUES ?sel { %s }
?nbr rdfs:subClassOf ?sel .
FILTER(!isLiteral(?nbr))
FILTER(?nbr != ?sel)
VALUES ?p { rdfs:subClassOf }
?s ?p ?sel .
BIND(?sel AS ?o)
FILTER(!isLiteral(?o))
FILTER(?s != ?o)
%s
}
`, values, bnodeFilter)
}
func runSubclasses(ctx context.Context, q Querier, idx Index, selectedIDs []uint32, includeBNodes bool) ([]uint32, error) {
func runSubclasses(ctx context.Context, q Querier, idx Index, selectedIDs []uint32, includeBNodes bool) (Result, error) {
selectedNodes, selectedSet := selectedNodesFromIDs(idx, selectedIDs, includeBNodes)
if len(selectedNodes) == 0 {
return []uint32{}, nil
return Result{NeighborIDs: []uint32{}, Triples: []Triple{}}, nil
}
raw, err := q.Query(ctx, subclassesQuery(selectedNodes, includeBNodes))
query := subclassesQuery(selectedNodes, includeBNodes)
raw, err := q.Query(ctx, query)
if err != nil {
return nil, err
logQueryExecutionFailure("subclasses", selectedIDs, includeBNodes, query, err)
return Result{}, err
}
return idsFromBindings(raw, "nbr", idx, selectedSet, includeBNodes)
return resultFromTripleBindings(raw, idx, selectedSet, includeBNodes)
}

View File

@@ -17,38 +17,42 @@ func superclassesQuery(selectedNodes []NodeRef, includeBNodes bool) string {
}
if len(valuesTerms) == 0 {
return "SELECT ?nbr WHERE { FILTER(false) }"
return "SELECT ?s ?p ?o WHERE { FILTER(false) }"
}
bnodeFilter := ""
if !includeBNodes {
bnodeFilter = "FILTER(!isBlank(?nbr))"
bnodeFilter = "FILTER(!isBlank(?s) && !isBlank(?o))"
}
values := strings.Join(valuesTerms, " ")
return fmt.Sprintf(`
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
SELECT DISTINCT ?nbr
SELECT DISTINCT ?s ?p ?o
WHERE {
VALUES ?sel { %s }
?sel rdfs:subClassOf ?nbr .
FILTER(!isLiteral(?nbr))
FILTER(?nbr != ?sel)
BIND(?sel AS ?s)
VALUES ?p { rdfs:subClassOf }
?s ?p ?o .
FILTER(!isLiteral(?o))
FILTER(?s != ?o)
%s
}
`, values, bnodeFilter)
}
func runSuperclasses(ctx context.Context, q Querier, idx Index, selectedIDs []uint32, includeBNodes bool) ([]uint32, error) {
func runSuperclasses(ctx context.Context, q Querier, idx Index, selectedIDs []uint32, includeBNodes bool) (Result, error) {
selectedNodes, selectedSet := selectedNodesFromIDs(idx, selectedIDs, includeBNodes)
if len(selectedNodes) == 0 {
return []uint32{}, nil
return Result{NeighborIDs: []uint32{}, Triples: []Triple{}}, nil
}
raw, err := q.Query(ctx, superclassesQuery(selectedNodes, includeBNodes))
query := superclassesQuery(selectedNodes, includeBNodes)
raw, err := q.Query(ctx, query)
if err != nil {
return nil, err
logQueryExecutionFailure("superclasses", selectedIDs, includeBNodes, query, err)
return Result{}, err
}
return idsFromBindings(raw, "nbr", idx, selectedSet, includeBNodes)
return resultFromTripleBindings(raw, idx, selectedSet, includeBNodes)
}

View File

@@ -13,8 +13,9 @@ type NodeRef struct {
}
type Index struct {
IDToNode map[uint32]NodeRef
KeyToID map[string]uint32
IDToNode map[uint32]NodeRef
KeyToID map[string]uint32
PredicateIDByIRI map[string]uint32
}
type Meta struct {
@@ -22,7 +23,27 @@ type Meta struct {
Label string `json:"label"`
}
type TripleTerm struct {
Type string `json:"type"`
Value string `json:"value"`
Lang string `json:"lang,omitempty"`
}
type Triple struct {
S TripleTerm `json:"s"`
P TripleTerm `json:"p"`
O TripleTerm `json:"o"`
SubjectID *uint32 `json:"subject_id,omitempty"`
ObjectID *uint32 `json:"object_id,omitempty"`
PredicateID *uint32 `json:"predicate_id,omitempty"`
}
type Result struct {
NeighborIDs []uint32 `json:"neighbor_ids"`
Triples []Triple `json:"triples"`
}
type Definition struct {
Meta Meta
Run func(ctx context.Context, q Querier, idx Index, selectedIDs []uint32, includeBNodes bool) ([]uint32, error)
Run func(ctx context.Context, q Querier, idx Index, selectedIDs []uint32, includeBNodes bool) (Result, error)
}