🔍 Code Extractor

function create_node_and_ensure_relationships

Maturity: 72

Creates a new node in a Neo4j graph database and establishes multiple relationships to existing nodes within a single atomic transaction.

File:
/tf/active/vicechatdev/CDocs/db/db_operations.py
Lines:
258 - 358
Complexity:
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

  • neo4j
  • uuid
  • datetime
  • logging
  • typing
  • traceback

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

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function create_node_with_relationship 89.0% similar

    Creates a new node in a Neo4j graph database and optionally establishes a relationship with an existing node in a single atomic operation.

    From: /tf/active/vicechatdev/CDocs/db/db_operations.py
  • function create_node 70.2% similar

    Creates a node in a Neo4j graph database with a specified label and properties, automatically generating a unique ID and timestamp if not provided.

    From: /tf/active/vicechatdev/CDocs/db/db_operations.py
  • function create_node_with_uid 67.4% similar

    Creates a new node in a Neo4j graph database with a specified UID, label, and properties, automatically adding a creation timestamp if not provided.

    From: /tf/active/vicechatdev/CDocs/db/db_operations.py
  • function batch_create_nodes 62.2% similar

    Creates multiple Neo4j graph database nodes in batches for improved performance, automatically generating UIDs and timestamps for each node.

    From: /tf/active/vicechatdev/CDocs/db/db_operations.py
  • function create_relationship 61.3% similar

    Creates a directed relationship between two Neo4j graph database nodes identified by their UIDs, with optional properties attached to the relationship.

    From: /tf/active/vicechatdev/CDocs/db/db_operations.py
← Back to Browse