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 }