CREATE Clause in GQL

The CREATE clause is the primary mechanism for adding new data to your Geode graph database. It allows you to create nodes, relationships, and complete patterns with properties and labels in a single operation. Geode implements the CREATE clause according to the ISO/IEC 39075:2024 GQL standard.

Introduction to CREATE

In graph databases, data is represented as nodes (entities) and relationships (connections between entities). The CREATE clause enables you to build your graph structure by inserting these elements along with their properties and labels.

Key characteristics of CREATE:

  • Atomic Operations: Each CREATE statement executes as a single atomic operation
  • Pattern Support: Create entire patterns (multiple nodes and relationships) in one statement
  • Property Assignment: Set properties during creation
  • Label Assignment: Assign one or more labels to nodes
  • Relationship Types: Define relationship types and directions
  • Returning Created Elements: Access created elements for further operations

Creating Nodes

Basic Node Creation

Create a simple node without labels or properties.

-- Create an empty node
CREATE ();

-- Create a node and return it
CREATE (n)
RETURN n;

Nodes with Labels

Labels categorize nodes and enable efficient querying.

-- Create a node with a single label
CREATE (p:Person);

-- Create a node with multiple labels
CREATE (e:Person:Employee:Manager);

-- Create and return
CREATE (u:User)
RETURN u;

Nodes with Properties

Properties store data on nodes as key-value pairs.

-- Create a node with properties
CREATE (p:Person {
    name: 'Alice',
    age: 30,
    email: 'alice@example.com'
});

-- Create with various data types
CREATE (p:Product {
    name: 'Laptop',
    price: 999.99,
    in_stock: true,
    tags: ['electronics', 'computers'],
    specs: {cpu: 'M3', ram: '16GB'}
});

Creating Multiple Nodes

Create several nodes in a single statement.

-- Create multiple nodes
CREATE
    (a:Person {name: 'Alice'}),
    (b:Person {name: 'Bob'}),
    (c:Person {name: 'Charlie'});

-- Create with UNWIND for dynamic data
UNWIND $users AS user_data
CREATE (u:User {
    name: user_data.name,
    email: user_data.email
});

Creating Relationships

Basic Relationship Creation

Relationships connect nodes and have a type and direction.

-- Create a relationship between existing nodes
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[:KNOWS]->(b);

-- Create with relationship variable
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[r:FRIENDS_WITH]->(b)
RETURN r;

Relationships with Properties

Relationships can store properties just like nodes.

-- Create relationship with properties
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[:FRIENDS_WITH {
    since: date('2020-01-15'),
    closeness: 'best_friend'
}]->(b);

-- E-commerce example
MATCH (c:Customer {id: $customer_id}), (p:Product {id: $product_id})
CREATE (c)-[:PURCHASED {
    date: datetime(),
    quantity: $qty,
    price: $price
}]->(p);

Relationship Directions

GQL relationships are always directed, but can be traversed in any direction.

-- Outgoing relationship
CREATE (a)-[:FOLLOWS]->(b);

-- Incoming relationship (conceptually)
CREATE (a)<-[:FOLLOWED_BY]-(b);

-- Note: In GQL, relationships are stored with direction
-- Query traversal can ignore direction using -[:TYPE]-

Creating Patterns

Complete Pattern Creation

Create nodes and relationships together in a single pattern.

-- Create a complete pattern
CREATE (a:Person {name: 'Alice'})-[:KNOWS]->(b:Person {name: 'Bob'});

-- Create a chain
CREATE (a:User {name: 'Alice'})
    -[:POSTED]->(p:Post {title: 'Hello World'})
    -[:HAS_COMMENT]->(c:Comment {text: 'Great post!'});

Complex Patterns

Build intricate graph structures in one operation.

-- Create a small social network
CREATE
    (alice:Person {name: 'Alice', age: 30}),
    (bob:Person {name: 'Bob', age: 25}),
    (charlie:Person {name: 'Charlie', age: 35}),
    (alice)-[:FRIENDS_WITH {since: 2020}]->(bob),
    (bob)-[:FRIENDS_WITH {since: 2021}]->(charlie),
    (charlie)-[:FRIENDS_WITH {since: 2019}]->(alice);

-- Create an order with items
CREATE (o:Order {
    id: $order_id,
    date: datetime(),
    status: 'pending'
})
WITH o
UNWIND $items AS item
MATCH (p:Product {id: item.product_id})
CREATE (o)-[:CONTAINS {
    quantity: item.quantity,
    price: item.price
}]->(p);

Pattern with Existing Nodes

Combine MATCH and CREATE for patterns involving existing data.

-- Connect existing user to new post
MATCH (u:User {id: $user_id})
CREATE (u)-[:AUTHORED]->(p:Post {
    title: $title,
    content: $content,
    created_at: datetime()
})
RETURN p;

-- Create relationships between existing nodes
MATCH (p:Product), (c:Category)
WHERE p.category_name = c.name
CREATE (p)-[:IN_CATEGORY]->(c);

CREATE with Other Clauses

CREATE with MATCH

Use MATCH to find existing nodes before creating.

-- Find and connect
MATCH (u:User {email: $email})
MATCH (g:Group {name: $group_name})
CREATE (u)-[:MEMBER_OF {joined: datetime()}]->(g)
RETURN u, g;

-- Conditional pattern creation
MATCH (a:Person), (b:Person)
WHERE a.city = b.city AND a <> b AND NOT (a)-[:LIVES_NEAR]-(b)
CREATE (a)-[:LIVES_NEAR]->(b);

CREATE with WITH

Chain operations using WITH.

-- Create node then create relationships
CREATE (p:Project {name: $project_name})
WITH p
MATCH (u:User {id: $owner_id})
CREATE (u)-[:OWNS]->(p)
WITH p, u
MATCH (t:Team {id: $team_id})
CREATE (t)-[:WORKS_ON]->(p)
RETURN p, u, t;

CREATE with RETURN

Access created elements in the result.

-- Return created node
CREATE (u:User {
    name: $name,
    email: $email,
    created_at: datetime()
})
RETURN u.name AS name, elementId(u) AS id;

-- Return created relationship
MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
CREATE (a)-[r:KNOWS {since: date()}]->(b)
RETURN type(r) AS relationship_type, r.since AS since;

CREATE with Parameters

Use parameters for safe, efficient creation.

-- Parameterized node creation
CREATE (u:User {
    name: $name,
    email: $email,
    age: $age
});

-- Parameterized pattern
MATCH (author:User {id: $author_id})
CREATE (author)-[:WROTE]->(post:Post {
    title: $title,
    content: $content,
    tags: $tags
});

Bulk Data Creation

Using UNWIND

Create multiple elements from a list.

-- Create many nodes
UNWIND $users AS user_data
CREATE (u:User {
    name: user_data.name,
    email: user_data.email,
    department: user_data.department
});

-- Create nodes with relationships
UNWIND $friendships AS f
MATCH (a:User {id: f.user_id}), (b:User {id: f.friend_id})
CREATE (a)-[:FRIENDS_WITH {since: f.since}]->(b);

Batch Processing

Efficiently create large amounts of data.

-- Process in batches (application-side pattern)
-- Execute this query for each batch of 1000 records
UNWIND $batch AS record
CREATE (n:DataPoint {
    timestamp: record.ts,
    value: record.val,
    sensor_id: record.sensor
});

Importing from External Data

Create graph structure from imported data.

-- From CSV-like structure
UNWIND $rows AS row
MERGE (source:Location {name: row.from})
MERGE (target:Location {name: row.to})
CREATE (source)-[:CONNECTED_TO {
    distance: row.distance,
    transport: row.mode
}]->(target);

CREATE vs MERGE

Understanding when to use CREATE vs MERGE.

CREATE Behavior

CREATE always creates new elements, potentially causing duplicates.

-- This creates duplicates if run twice
CREATE (u:User {email: 'alice@example.com', name: 'Alice'});
CREATE (u:User {email: 'alice@example.com', name: 'Alice'});
-- Result: Two separate User nodes

MERGE for Idempotent Operations

MERGE creates only if the pattern does not exist.

-- This is idempotent - safe to run multiple times
MERGE (u:User {email: 'alice@example.com'})
ON CREATE SET u.name = 'Alice', u.created_at = datetime()
ON MATCH SET u.last_seen = datetime();

When to Use Each

ScenarioRecommendation
Initial data loadCREATE (faster)
Incremental updatesMERGE (prevents duplicates)
Event loggingCREATE (each event is unique)
Entity creationMERGE with unique constraint
Relationship creationDepends on domain semantics

Best Practices

Data Integrity

  1. Use Constraints: Define uniqueness constraints before bulk creation
-- Create constraint first
CREATE CONSTRAINT user_email_unique ON (u:User) ASSERT u.email IS UNIQUE;

-- Then use MERGE for safety
MERGE (u:User {email: $email})
ON CREATE SET u.name = $name;
  1. Validate Data: Check data before creating
-- Create only if valid
MATCH (u:User {id: $user_id})
WHERE u.active = true
CREATE (u)-[:PERFORMED]->(a:Action {type: $action_type, timestamp: datetime()});

Performance Optimization

  1. Batch Creations: Group multiple creates into transactions
-- More efficient than individual queries
UNWIND range(1, 1000) AS i
CREATE (n:TestNode {index: i});
  1. Index Before Bulk Load: Create indexes before loading data
CREATE INDEX user_email ON User(email);
CREATE INDEX product_sku ON Product(sku);
-- Then perform bulk load
  1. Use Parameters: Parameterized queries are cached and reused
-- Query plan is cached
CREATE (u:User {name: $name, email: $email});

Modeling Best Practices

  1. Meaningful Labels: Use descriptive, singular label names
-- Good
CREATE (p:Product), (c:Customer), (o:Order);

-- Avoid
CREATE (p:Products), (c:Cust), (o:Ord);
  1. Descriptive Relationship Types: Use verb phrases in UPPER_CASE
-- Good
CREATE (a)-[:PURCHASED]->(b);
CREATE (a)-[:REPORTS_TO]->(b);
CREATE (a)-[:AUTHORED]->(b);

-- Avoid
CREATE (a)-[:rel]->(b);
CREATE (a)-[:connection]->(b);
  1. Property Naming: Use consistent naming conventions
-- Consistent snake_case
CREATE (p:Product {
    product_name: 'Widget',
    unit_price: 29.99,
    created_at: datetime()
});

Error Handling

Common Errors

-- Constraint violation
-- Error: Node already exists with label 'User' and property 'email' = 'alice@example.com'
CREATE (u:User {email: 'alice@example.com'});  -- When constraint exists and node exists

-- Invalid property type
-- Error: Type mismatch: expected Integer but was String
CREATE (n:Node {count: 'five'});  -- If schema requires integer

Handling Errors in Applications

# Python example
from geode_client import Client, ConstraintViolationError

async def create_user(name, email):
    client = Client(host="localhost", port=3141)
    async with client.connection() as conn:
        try:
            result, _ = await conn.query("""
                CREATE (u:User {name: $name, email: $email})
                RETURN u
            """, {"name": name, "email": email})
            return result.bindings[0]['u']
        except ConstraintViolationError:
            # Handle duplicate
            return await get_user_by_email(email)

Further Reading

  • GQL Tutorial - Creating Data: /docs/gql-tutorial/creating-data/
  • Data Modeling Guide: /docs/guides/graph-modeling/
  • Bulk Import Guide: /docs/operations/bulk-import/
  • Constraint Reference: /docs/reference/constraints/
  • Transaction Management: /docs/transactions/overview/

Related Articles

No articles found with this tag yet.

Back to Home