function create_node_and_ensure_relationships
Creates a new node in a Neo4j graph database and establishes multiple relationships to existing nodes within a single atomic transaction.
/tf/active/vicechatdev/CDocs/db/db_operations.py
258 - 358
moderate
Purpose
This function provides a transactional way to create a node and its relationships simultaneously in Neo4j. It ensures data consistency by wrapping node creation and relationship establishment in a single transaction. If any operation fails, the entire transaction is rolled back. The function automatically generates a UID and creation timestamp if not provided, and supports both incoming and outgoing relationships with optional properties.
Source Code
def create_node_and_ensure_relationships(
label: str,
properties: Dict[str, Any],
relationships: Optional[List[Dict[str, Any]]] = None
) -> Optional[Dict[str, Any]]:
"""
Create a node and establish multiple relationships in one transaction.
Parameters
----------
label : str
The node label for the new node
properties : Dict[str, Any]
Properties for the new node
relationships : List[Dict[str, Any]], optional
List of relationship details dictionaries, each containing:
- to_node_uid: UID of target node
- relationship_type: Type of relationship
- direction: "OUTGOING" or "INCOMING"
- properties: Optional relationship properties
Returns
-------
Dict[str, Any]
The created node properties
"""
try:
# Generate a unique ID if not provided
if 'UID' not in properties:
properties['UID'] = str(uuid.uuid4())
# Add creation timestamp if not provided
if 'createdDate' not in properties:
properties['createdDate'] = datetime.now()
# If no relationships needed, just create the node
if not relationships:
return create_node(label, properties)
# Get driver and start transaction
driver = get_driver()
with driver.session() as session:
with session.begin_transaction() as tx:
# Create the node first
node_query = f"""
CREATE (n:{label} $node_props)
RETURN n
"""
node_result = tx.run(node_query, node_props=properties).single()
# If node creation failed, return None
if not node_result:
logger.error("Failed to create node")
return None
# Extract node data
node_data = dict(node_result['n'])
node_uid = node_data['UID']
# Create each relationship
for rel in relationships:
to_node_uid = rel.get('to_node_uid')
rel_type = rel.get('relationship_type')
direction = rel.get('direction', 'OUTGOING')
rel_props = rel.get('properties', {})
if not to_node_uid or not rel_type:
continue
# Set relationship direction in query
if direction == "OUTGOING":
# New node -> Existing node
rel_query = f"""
MATCH (n {{UID: $node_uid}}), (target {{UID: $to_node_uid}})
CREATE (n)-[r:{rel_type} $rel_props]->(target)
RETURN r
"""
else:
# Existing node -> New node
rel_query = f"""
MATCH (n {{UID: $node_uid}}), (target {{UID: $to_node_uid}})
CREATE (target)-[r:{rel_type} $rel_props]->(n)
RETURN r
"""
# Execute relationship creation query
rel_result = tx.run(
rel_query,
node_uid=node_uid,
to_node_uid=to_node_uid,
rel_props=rel_props
)
# Return the node data
return node_data
except Exception as e:
logger.error(f"Error creating node with relationships: {str(e)}")
import traceback
logger.error(traceback.format_exc())
return None
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
label |
str | - | positional_or_keyword |
properties |
Dict[str, Any] | - | positional_or_keyword |
relationships |
Optional[List[Dict[str, Any]]] | None | positional_or_keyword |
Parameter Details
label: The Neo4j node label (type) for the new node being created. This is a string that categorizes the node (e.g., 'Person', 'Document', 'Event'). Must be a valid Neo4j label name.
properties: A dictionary containing the properties (attributes) for the new node. Keys are property names and values can be any JSON-serializable type. If 'UID' is not provided, a UUID will be auto-generated. If 'createdDate' is not provided, the current datetime will be added automatically.
relationships: Optional list of dictionaries, where each dictionary defines a relationship to create. Each relationship dictionary must contain: 'to_node_uid' (UID of the target node), 'relationship_type' (the type/label of the relationship), 'direction' ('OUTGOING' for new_node->target or 'INCOMING' for target->new_node), and optionally 'properties' (dict of relationship properties). If None or empty list, only the node is created without relationships.
Return Value
Type: Optional[Dict[str, Any]]
Returns a dictionary containing all properties of the successfully created node, including the auto-generated UID and createdDate if they were added. Returns None if the node creation fails or if an exception occurs during the transaction. The returned dictionary mirrors the node's properties as stored in Neo4j.
Dependencies
neo4juuiddatetimeloggingtypingtraceback
Required Imports
import logging
import uuid
from typing import Dict, List, Any, Optional
from datetime import datetime
from neo4j import Driver
import traceback
Conditional/Optional Imports
These imports are only needed under specific conditions:
from CDocs.db import get_driver
Condition: Required to obtain the Neo4j driver instance. This is a custom module that must be available in the CDocs.db package.
Required (conditional)from CDocs.db import create_node
Condition: Required when no relationships are specified, as the function delegates to create_node for simple node creation.
Required (conditional)Usage Example
# Example 1: Create a node with outgoing relationships
properties = {
'name': 'John Doe',
'email': 'john@example.com',
'age': 30
}
relationships = [
{
'to_node_uid': 'existing-company-uid-123',
'relationship_type': 'WORKS_AT',
'direction': 'OUTGOING',
'properties': {'since': 2020, 'role': 'Engineer'}
},
{
'to_node_uid': 'existing-project-uid-456',
'relationship_type': 'CONTRIBUTES_TO',
'direction': 'OUTGOING',
'properties': {'hours_per_week': 40}
}
]
result = create_node_and_ensure_relationships(
label='Person',
properties=properties,
relationships=relationships
)
if result:
print(f"Created node with UID: {result['UID']}")
else:
print("Failed to create node")
# Example 2: Create a node without relationships
simple_node = create_node_and_ensure_relationships(
label='Document',
properties={'title': 'Report', 'content': 'Annual report data'}
)
# Example 3: Create a node with incoming relationship
incoming_rel = [
{
'to_node_uid': 'manager-uid-789',
'relationship_type': 'MANAGES',
'direction': 'INCOMING',
'properties': {}
}
]
employee = create_node_and_ensure_relationships(
label='Employee',
properties={'name': 'Jane Smith'},
relationships=incoming_rel
)
Best Practices
- Always ensure target nodes exist before creating relationships to them, otherwise the relationship creation will silently fail
- Use transactions for data consistency - this function handles it automatically
- Validate relationship dictionaries contain required keys ('to_node_uid', 'relationship_type') before calling
- Handle None return values to detect failures in node/relationship creation
- Use meaningful relationship types that follow Neo4j naming conventions (UPPERCASE_WITH_UNDERSCORES)
- Be aware that if any relationship creation fails, the entire transaction including node creation will be rolled back
- Consider the direction parameter carefully: 'OUTGOING' means new_node->target, 'INCOMING' means target->new_node
- Check logs for detailed error messages if the function returns None
- Ensure the logger is properly configured before using this function
- The function auto-generates UID and createdDate, but you can override them by including them in the properties dict
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function create_node_with_relationship 89.0% similar
-
function create_node 70.2% similar
-
function create_node_with_uid 67.4% similar
-
function batch_create_nodes 62.2% similar
-
function create_relationship 61.3% similar