68 lines
1.8 KiB
Go
68 lines
1.8 KiB
Go
package graph
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/local/glpi-neural-brain/internal/model"
|
|
)
|
|
|
|
// NodeFilter limits knowledge-bearing nodes by the exact value stored in the
|
|
// KB document's source field. Values are compared case-sensitively after
|
|
// trimming surrounding whitespace. An empty Sources list means unrestricted.
|
|
// No category, origin, URI or taxonomy fallback participates in matching.
|
|
type NodeFilter struct {
|
|
Sources []string
|
|
}
|
|
|
|
func (f NodeFilter) Matches(node model.Node) bool {
|
|
if len(f.Sources) == 0 {
|
|
return true
|
|
}
|
|
source := strings.TrimSpace(NodeSource(node))
|
|
for _, wanted := range f.Sources {
|
|
wanted = strings.TrimSpace(wanted)
|
|
if wanted == "" {
|
|
continue
|
|
}
|
|
if source == wanted {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// NodeSource returns only the explicit source metadata copied from the KB
|
|
// file's top-level source field (or assigned explicitly to generated research
|
|
// evidence). Technical origins such as glpi-kb are deliberately ignored.
|
|
func NodeSource(node model.Node) string {
|
|
return metadataText(node.Metadata, "source")
|
|
}
|
|
|
|
// SourceFromURL derives the explicit source value used for learned web
|
|
// evidence. It is kept separate from NodeSource so ordinary nodes never fall
|
|
// back to their URI implicitly.
|
|
func SourceFromURL(raw string) string {
|
|
parsed, err := url.Parse(strings.TrimSpace(raw))
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return strings.ToLower(strings.TrimSpace(parsed.Hostname()))
|
|
}
|
|
|
|
func metadataText(metadata map[string]any, key string) string {
|
|
if metadata == nil {
|
|
return ""
|
|
}
|
|
value, ok := metadata[key]
|
|
if !ok || value == nil {
|
|
return ""
|
|
}
|
|
text := strings.TrimSpace(fmt.Sprint(value))
|
|
if text == "" || strings.EqualFold(text, "<nil>") || strings.EqualFold(text, "null") {
|
|
return ""
|
|
}
|
|
return text
|
|
}
|