156 lines
3.5 KiB
Go
156 lines
3.5 KiB
Go
package selection_queries
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"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 []uint32, includeBNodes bool) ([]NodeRef, map[uint32]struct{}) {
|
|
out := make([]NodeRef, 0, len(selectedIDs))
|
|
set := make(map[uint32]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 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 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 {
|
|
sTerm, okS := b["s"]
|
|
pTerm, okP := b["p"]
|
|
oTerm, okO := b["o"]
|
|
if !okS || !okP || !okO {
|
|
continue
|
|
}
|
|
|
|
triple := Triple{
|
|
S: tripleTermFromSparqlTerm(sTerm),
|
|
P: tripleTermFromSparqlTerm(pTerm),
|
|
O: tripleTermFromSparqlTerm(oTerm),
|
|
}
|
|
|
|
subjID, subjOK := idFromSparqlTerm(sTerm, idx, includeBNodes)
|
|
if subjOK {
|
|
id := subjID
|
|
triple.SubjectID = &id
|
|
}
|
|
objID, objOK := idFromSparqlTerm(oTerm, idx, includeBNodes)
|
|
if objOK {
|
|
id := objID
|
|
triple.ObjectID = &id
|
|
}
|
|
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))
|
|
for nid := range neighborSet {
|
|
ids = append(ids, nid)
|
|
}
|
|
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
|
return Result{NeighborIDs: ids, Triples: triples}, nil
|
|
}
|