package heartwood import ( "context" "fmt" "github.com/neo4j/neo4j-go-driver/v6/neo4j" ) // ---------------------------------------------------------------------------- // Schema Match Keys // ---------------------------------------------------------------------------- func NewSimpleMatchKeys() *SimpleMatchKeys { return &SimpleMatchKeys{ Keys: map[string][]string{ "User": {"pubkey"}, "Event": {"id"}, "Tag": {"name", "value"}, "ReplacementKey": {"pubkey", "kind"}, }, } } // ---------------------------------------------------------------------------- // Node Constructors // ---------------------------------------------------------------------------- func NewUserNode(pubkey string) *Node { return NewNode("User", Properties{"pubkey": pubkey}) } 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 NewReplacementKeyNode(pubkey string, kind int) *Node { return NewNode("ReplacementKey", Properties{ "pubkey": pubkey, "kind": kind, }) } // ---------------------------------------------------------------------------- // Relationship Constructors // ---------------------------------------------------------------------------- func NewSignedRel( start *Node, end *Node, props Properties) *Relationship { return NewRelationshipWithValidation( "SIGNED", "User", "Event", start, end, props) } func NewTaggedRel( start *Node, end *Node, props Properties) *Relationship { return NewRelationshipWithValidation( "TAGGED", "Event", "Tag", start, end, props) } func NewReferencesEventRel( start *Node, end *Node, props Properties) *Relationship { return NewRelationshipWithValidation( "REFERENCES", "Tag", "Event", start, end, props) } func NewReferencesUserRel( start *Node, end *Node, props Properties) *Relationship { return NewRelationshipWithValidation( "REFERENCES", "Tag", "User", start, end, props) } func NewIsReplaceableRel( start *Node, end *Node, props Properties) *Relationship { return NewRelationshipWithValidation( "IS_REPLACEABLE", "Event", "ReplacementKey", start, end, props) } func NewForUserRel( start *Node, end *Node, props Properties) *Relationship { return NewRelationshipWithValidation( "FOR_USER", "ReplacementKey", "User", start, end, props) } func NewWithDTagRel( start *Node, end *Node, props Properties) *Relationship { return NewRelationshipWithValidation( "WITH_D_TAG", "ReplacementKey", "Tag", start, end, props) } func NewReferencesReplacementKeyRel( start *Node, end *Node, props Properties) *Relationship { return NewRelationshipWithValidation( "REFERENCES", "Tag", "ReplacementKey", start, end, props) } // ---------------------------------------------------------------------------- // Relationship Constructor Helpers // ---------------------------------------------------------------------------- func validateNodeLabel(node *Node, role string, expectedLabel string) { if !node.Labels.Contains(expectedLabel) { panic(fmt.Errorf( "expected %s node to have label %q. got %v", role, expectedLabel, node.Labels.AsSortedArray(), )) } } func NewRelationshipWithValidation( rtype string, startLabel string, endLabel string, start *Node, end *Node, props Properties) *Relationship { validateNodeLabel(start, "start", startLabel) validateNodeLabel(end, "end", endLabel) return NewRelationship(rtype, start, end, props) } // ---------------------------------------------------------------------------- // Indexes and Constraints // ---------------------------------------------------------------------------- // SetNeo4jSchema ensures that the necessary indexes and constraints exist in // the database func SetNeo4jSchema(ctx context.Context, driver neo4j.Driver) error { schemaQueries := []string{ // Constraints // User.pubkey `CREATE CONSTRAINT user IF NOT EXISTS FOR (n:User) REQUIRE n.pubkey IS UNIQUE`, // Event.id `CREATE CONSTRAINT event IF NOT EXISTS FOR (n:Event) REQUIRE n.id IS UNIQUE`, // Tag.(name, value) `CREATE CONSTRAINT tag IF NOT EXISTS FOR (n:Tag) REQUIRE (n.name, n.value) IS UNIQUE`, // ReplacementKey.(pubkey, kind) `CREATE CONSTRAINT replacement_key IF NOT EXISTS FOR (n:ReplacementKey) REQUIRE (n.pubkey, n.kind) IS UNIQUE`, // Indexes // Event.kind `CREATE INDEX event_kind IF NOT EXISTS FOR (n:Event) ON (n.kind)`, // Event.created_at `CREATE INDEX event_created_at IF NOT EXISTS FOR (n:Event) ON (n.created_at)`, // ReplacementKey.kind `CREATE INDEX replacement_key_kind IF NOT EXISTS FOR (n:ReplacementKey) ON (n.kind)`, } // Create indexes and constraints for _, query := range schemaQueries { _, err := neo4j.ExecuteQuery(ctx, driver, query, nil, neo4j.EagerResultTransformer, neo4j.ExecuteQueryWithDatabase("neo4j")) if err != nil { return err } } return nil }