Started transforming tags into relationships.

This commit is contained in:
Jay
2025-02-24 11:45:23 -05:00
parent 1bb73d7712
commit 13724ce56f
7 changed files with 244 additions and 47 deletions

View File

@@ -346,36 +346,3 @@ func DeserializeRelKey(sortKey string) (string, string, string) {
rtype, startLabel, endLabel := parts[0], parts[1], parts[2]
return rtype, startLabel, endLabel
}
// ========================================
// Cypher Formatting Functions
// ========================================
// ToCypherLabel converts a node label or relationship type into its Cypher
// format.
func ToCypherLabel(label string) string {
return fmt.Sprintf(":`%s`", label)
}
// ToCypherLabels converts a list of node labels into its Cypher format.
func ToCypherLabels(labels []string) string {
var cypherLabels []string
for _, label := range labels {
cypherLabels = append(cypherLabels, ToCypherLabel(label))
}
return strings.Join(cypherLabels, "")
}
func ToCypherProps(keys []string, prefix string) string {
if prefix == "" {
prefix = "$"
}
cypherPropsParts := []string{}
for _, key := range keys {
cypherPropsParts = append(
cypherPropsParts, fmt.Sprintf("%s: %s%s", key, prefix, key))
}
return strings.Join(cypherPropsParts, ", ")
}

View File

@@ -6,18 +6,19 @@ import (
"fmt"
"log"
"os"
"regexp"
"strings"
"sync"
"github.com/nbd-wtf/go-nostr"
"github.com/nbd-wtf/go-nostr/nip60"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
// Workers
func ImportEvents() {
data, err := os.ReadFile("./export.json")
data, err := os.ReadFile("./zaps.json")
if err != nil {
panic(err)
}
@@ -35,7 +36,7 @@ func ImportEvents() {
var event nostr.Event
for i, line := range strings.Split(string(data), "\n") {
if i > 10000 {
if i > 2000000 {
break
}
@@ -80,6 +81,12 @@ func ParseEvents(events chan nostr.Event) {
eventNode.Props["kind"] = event.Kind
eventNode.Props["content"] = event.Content
if event.Kind == nostr.KindZap {
// Event is a zap receipt
// Write the zap amount to the event
eventNode.Labels.Add("ZapReceiptEvent")
}
authorRel := NewSignedRel(userNode, eventNode, nil)
subgraph.AddNode(userNode)
@@ -91,13 +98,67 @@ func ParseEvents(events chan nostr.Event) {
if len(tag) >= 2 {
name := tag[0]
value := tag[1]
var rest []string
// Special cases
if len(name)+len(value) > 8192 {
// Skip tags that are too large for the neo4j indexer
continue
}
tagNode := NewTagNode(name, value)
tagRel := NewTaggedRel(eventNode, tagNode, nil)
subgraph.AddNode(tagNode)
subgraph.AddRel(tagRel)
if len(tag) > 2 {
rest = append([]string{}, tag[2:]...)
}
if event.Kind == nostr.KindZap && name == "bolt11" {
amount, err := nip60.GetSatoshisAmountFromBolt11(value)
if err == nil {
eventNode.Props["amount"] = amount
} else {
fmt.Println("Invalid bolt11 amount:", err)
}
}
if name == "e" &&
len(value) == 64 &&
regexp.MustCompile(`^[0-9a-f]{64}$`).MatchString(value) {
// Tag is an event reference
// Create a relationship to the referenced event
referencedEventNode := NewEventNode(value)
referencesRel := NewReferencesEventRel(
eventNode,
referencedEventNode,
map[string]any{
"name": name,
"value": value,
"rest": rest,
})
subgraph.AddNode(referencedEventNode)
subgraph.AddRel(referencesRel)
} else if name == "p" &&
len(value) == 64 &&
regexp.MustCompile(`^[0-9a-f]{64}$`).MatchString(value) {
// Tag is a user reference
// Create a relationship to the referenced user
referencedUserNode := NewUserNode(value)
referencesRel := NewReferencesUserRel(
eventNode,
referencedUserNode,
map[string]any{
"name": name,
"value": value,
"rest": rest,
})
subgraph.AddNode(referencedUserNode)
subgraph.AddRel(referencesRel)
} else {
// Generic Tag
tagNode := NewTagNode(name, value, rest)
tagRel := NewTaggedRel(eventNode, tagNode, nil)
subgraph.AddNode(tagNode)
subgraph.AddRel(tagRel)
}
}
}
@@ -308,7 +369,7 @@ func mergeRels(
MATCH (start%s { %s })
MATCH (end%s { %s })
CREATE (start)-[r%s]->(end)
MERGE (start)-[r%s]->(end)
SET r += rel.props
`,
startCypherLabel, startCypherProps,

View File

@@ -38,8 +38,11 @@ func NewEventNode(id string) *Node {
return NewNode("Event", Properties{"id": id})
}
func NewTagNode(name string, value string) *Node {
return NewNode("Tag", Properties{"name": name, "value": value})
func NewTagNode(name string, value string, rest []string) *Node {
return NewNode("Tag", Properties{
"name": name,
"value": value,
"rest": rest})
}
// ========================================
@@ -59,6 +62,18 @@ func NewTaggedRel(
"TAGGED", "Event", "Tag", start, end, props)
}
func NewReferencesEventRel(
start *Node, end *Node, props Properties) *Relationship {
return NewRelationshipWithValidation(
"REFERENCES", "Event", "Event", start, end, props)
}
func NewReferencesUserRel(
start *Node, end *Node, props Properties) *Relationship {
return NewRelationshipWithValidation(
"REFERENCES", "Event", "User", start, end, props)
}
// ========================================
// Relationship Constructor Helpers
// ========================================

39
lib/write.go Normal file
View File

@@ -0,0 +1,39 @@
package lib
import (
"fmt"
"strings"
)
// ========================================
// Cypher Formatting Functions
// ========================================
// ToCypherLabel converts a node label or relationship type into its Cypher
// format.
func ToCypherLabel(label string) string {
return fmt.Sprintf(":`%s`", label)
}
// ToCypherLabels converts a list of node labels into its Cypher format.
func ToCypherLabels(labels []string) string {
var cypherLabels []string
for _, label := range labels {
cypherLabels = append(cypherLabels, ToCypherLabel(label))
}
return strings.Join(cypherLabels, "")
}
func ToCypherProps(keys []string, prefix string) string {
if prefix == "" {
prefix = "$"
}
cypherPropsParts := []string{}
for _, key := range keys {
cypherPropsParts = append(
cypherPropsParts, fmt.Sprintf("%s: %s%s", key, prefix, key))
}
return strings.Join(cypherPropsParts, ", ")
}