The INSERT statement allows you to add new nodes and edges into the graph using node and edge patterns.
Syntax<insert statement> ::= "INSERT" <insert path pattern> [ { "," <insert path pattern> } ... ] <insert path pattern> ::= <insert node pattern> [ { <insert edge pattern> <insert node pattern> } ... ] <insert node pattern> ::= "(" [ <node variable declaration> ] [ <label expression> ] [ <property specification> ] ")" <insert edge pattern> ::= "-" [ <edge variable declaration> ] [ <label expression> ] [ <property specification> ] "->" | "<-" [ <edge variable declaration> ] [ <label expression> ] [ <property specification> ] "-"
Details
<insert path pattern> supports single node pattern or simple concatenation of node and edge patterns.<insert path pattern> does not support WHERE clause or undirected edges.Ultipa supports both closed graphs and open graphs. Their data insertion syntax is similar, but with important differences in requirements.
For an open graph, you can directly insert nodes and edges, and the labels and properties are created on the fly.
30 is stored as INT, "hello" as STRING, [1,2] as LIST). Unlike closed graphs where types are explicitly defined (e.g., UINT32 vs INT64), open graphs have no fine-grained type control.Learn more about open graphs →
For a closed graph, any node or edge inserted must conform to its defined node or edge type:
User has labels [User, Employee], you must insert with :User&Employee.STRING, UINT32, FLOAT), and inserted values are validated against these types. Properties not provided in the insert default to null, unless a NOT NULL constraint is defined. Inserting an undefined property name results in an error.Learn more about closed graphs →
Insert a single User node:
GQLINSERT (:User {name: "claire", gender: "female"})
Insert multiple nodes and return them:
GQLINSERT (n1:User&Employee {_id: "U2", name: "Quasar92"}), (n2:Club {_id: "C1"}), (n3:Club) RETURN n1, n2, n3
Insert an edge between existing nodes, first retrieve the nodes using MATCH:
GQLMATCH (n1:User {name: 'claire'}), (n2:Club {_id: 'C1'}) INSERT (n1)-[e:Joins {fee: 1200}]->(n2) RETURN e
Insert a Joins edge from an existing User node to a new Club node:
GQLMATCH (user:User {name: 'Quasar92'}) INSERT (user)-[:Joins]->(:Club {_id: "C2"})
Insert two User nodes and a Follows edge between them:
GQLINSERT (:User {name: 'rowlock'})-[:Follows {since: date('2024-01-05')}]->(:User {name: 'Brainy', gender: 'male'})
Insert the 4 nodes and 3 edges shown below, consider it as two paths intersecting at the Club node:

GQLINSERT (:User {name: 'waveBliss'})-[:Joins]->(c:Club {_id: 'C3'}), (:User {name: 'bella'})-[:Joins]->(c)<-[:Joins]-(:User {name: 'Roose'})
Alternatively, you can insert each node and edge individually:
GQLINSERT (waveBliss:User {name: 'waveBliss'}), (bella:User {name: 'bella'}), (Roose:User {name: 'Roose'}), (C3:Club {_id: 'C3'}), (waveBliss)-[:Joins]->(C3), (bella)-[:Joins]->(C3), (Roose)-[:Joins]->(C3)
Below are examples of property values for different types. See Property Value Types for the complete list.
GQLINSERT (:Person { // Numeric memberLevel: 2, score: 60.3, // Textual name: "John Doe", bio: 'A short bio', // Temporal Instance birthday: date('2000-01-15'), registered: local_datetime('2025-01-01 12:20:02'), lastLogin: 1762338059, meetingTime: time('14:30:00'), zonedAt: zoned_datetime('2025-01-01T12:20:02+08:00'), // Temporal Duration membership: duration('P2Y5M'), // Boolean isBlocked: FALSE, // Spatial location: point(125.6, 22.3), position: point3d(10, 3.4, 6.2), // Record bodyInfo: {height: 175, weight: 68, hairColor: "brown"}, // List tags: ["IT", "happy", "geek"], // Binary avatar: "data:image/png;base64,iVBORw0KGgo...", // Vector (using AI.VECTOR function) embedding: ai.vector([0.12, 0.45, 0.78, 0.33]) })