Working with Nodes
Learn how to create, query, update, and delete nodes in your graph database. This guide covers all node operations with practical examples and best practices.
Quick Example
- TypeScript
- Java
- Go
- .NET
// Create a node
const user = await ductape.graph.createNode({
labels: ['Person', 'User'],
properties: {
name: 'Alice Johnson',
email: 'alice@example.com',
age: 28,
},
});
// Find nodes
const adults = await ductape.graph.findNodes({
labels: ['Person'],
where: { age: { $gte: 18 } },
limit: 10,
});
// Update a node
await ductape.graph.updateNode({
id: user.node.id,
properties: { age: 29, lastLogin: new Date() },
});
// Create a node
Map<String, Object> user = ductape.graph.createNode(Map.of(
labels: ['Person', 'User'],
properties: Map.of(
"name", "Alice Johnson",
"email", "alice@example.com",
"age", 28
)
));
// Find nodes
Map<String, Object> adults = ductape.graph.findNodes(Map.of(
labels: ['Person'],
where: Map.of( age: Map.of( $"gte", 18 ) ),
"limit", 10
));
// Update a node
ductape.graph.updateNode(Map.of(
id: user.node.id,
properties: Map.of( "age", 29, lastLogin: Instant.now() )
));
// Create a node
user := client.graph.createNode({
labels: ['Person', 'User'],
properties: {
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 28,
},
});
// Find nodes
adults := client.graph.findNodes({
labels: ['Person'],
where: { age: { $"gte": 18 } },
"limit": 10,
});
// Update a node
client.graph.updateNode({
id: user.node.id,
properties: { "age": 29, lastLogin: new Date() },
});
// Create a node
var user = await ductape.graph.createNode({
labels: ['Person', 'User'],
properties: {
["name"] = "Alice Johnson",
["email"] = "alice@example.com",
["age"] = 28,
},
});
// Find nodes
var adults = await ductape.graph.findNodes({
labels: ['Person'],
where: { age: { $["gte"] = 18 } },
["limit"] = 10,
});
// Update a node
await ductape.graph.updateNode({
id: user.node.id,
properties: { ["age"] = 29, lastLogin: DateTime.UtcNow },
});
Creating Nodes
Basic Node Creation
Create a node with labels and properties:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.createNode({
labels: ['Person'],
properties: {
name: 'Bob Smith',
email: 'bob@example.com',
age: 32,
city: 'New York',
joined: new Date(),
},
});
console.log('Created node ID:', result.node.id);
console.log('Node properties:', result.node.properties);
Map<String, Object> result = ductape.graph.createNode(Map.of(
labels: ['Person'],
properties: Map.of(
"name", "Bob Smith",
"email", "bob@example.com",
"age", 32,
"city", "New York",
joined: Instant.now()
)
));
System.out.println('Created node "ID", ", result.node.id);
System.out.println("Node properties:', result.node.properties);
result := client.graph.createNode({
labels: ['Person'],
properties: {
"name": "Bob Smith",
"email": "bob@example.com",
"age": 32,
"city": "New York",
joined: new Date(),
},
});
fmt.Println('Created node "ID": ", result.node.id);
fmt.Println("Node properties:', result.node.properties);
var result = await ductape.graph.createNode({
labels: ['Person'],
properties: {
["name"] = "Bob Smith",
["email"] = "bob@example.com",
["age"] = 32,
["city"] = "New York",
joined: DateTime.UtcNow,
},
});
Console.WriteLine('Created node ["ID"] = ", result.node.id);
Console.WriteLine("Node properties:', result.node.properties);
Multiple Labels
Nodes can have multiple labels for classification:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.createNode({
labels: ['Person', 'Employee', 'Engineer'],
properties: {
name: 'Charlie Davis',
email: 'charlie@company.com',
role: 'Senior Engineer',
department: 'Engineering',
salary: 120000,
},
});
Map<String, Object> result = ductape.graph.createNode(Map.of(
labels: ['Person', 'Employee', 'Engineer'],
properties: Map.of(
"name", "Charlie Davis",
"email", "charlie@company.com",
"role", "Senior Engineer",
"department", "Engineering",
"salary", 120000
)
));
result := client.graph.createNode({
labels: ['Person', 'Employee', 'Engineer'],
properties: {
"name": "Charlie Davis",
"email": "charlie@company.com",
"role": "Senior Engineer",
"department": "Engineering",
"salary": 120000,
},
});
var result = await ductape.graph.createNode({
labels: ['Person', 'Employee', 'Engineer'],
properties: {
["name"] = "Charlie Davis",
["email"] = "charlie@company.com",
["role"] = "Senior Engineer",
["department"] = "Engineering",
["salary"] = 120000,
},
});
Complex Properties
Nodes support various property types:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.createNode({
labels: ['Product'],
properties: {
// Strings
name: 'Laptop Pro',
sku: 'LPT-2024-001',
// Numbers
price: 1299.99,
stock: 45,
// Booleans
inStock: true,
featured: false,
// Dates
releaseDate: new Date('2024-01-15'),
lastUpdated: new Date(),
// Arrays
tags: ['electronics', 'computers', 'premium'],
colors: ['silver', 'space gray'],
// Objects (stored as JSON strings in most graph DBs)
specs: {
cpu: 'M3 Pro',
ram: '16GB',
storage: '512GB SSD',
},
},
});
Map<String, Object> result = ductape.graph.createNode(Map.of(
labels: ['Product'],
properties: Map.of(
// Strings
"name", "Laptop Pro",
"sku", "LPT-2024-001",
// Numbers
"price", 1299.99,
"stock", 45,
// Booleans
"inStock", true,
"featured", false,
// Dates
releaseDate: new Date('2024-01-15'),
lastUpdated: Instant.now(),
// Arrays
tags: ['electronics', 'computers', 'premium'],
colors: ['silver', 'space gray'],
// Objects (stored as JSON strings in most graph DBs)
specs: Map.of(
"cpu", "M3 Pro",
"ram", "16GB",
"storage", "512GB SSD"
)
)
));
result := client.graph.createNode({
labels: ['Product'],
properties: {
// Strings
"name": "Laptop Pro",
"sku": "LPT-2024-001",
// Numbers
"price": 1299.99,
"stock": 45,
// Booleans
"inStock": true,
"featured": false,
// Dates
releaseDate: new Date('2024-01-15'),
lastUpdated: new Date(),
// Arrays
tags: ['electronics', 'computers', 'premium'],
colors: ['silver', 'space gray'],
// Objects (stored as JSON strings in most graph DBs)
specs: {
"cpu": "M3 Pro",
"ram": "16GB",
"storage": "512GB SSD",
},
},
});
var result = await ductape.graph.createNode({
labels: ['Product'],
properties: {
// Strings
["name"] = "Laptop Pro",
["sku"] = "LPT-2024-001",
// Numbers
["price"] = 1299.99,
["stock"] = 45,
// Booleans
["inStock"] = true,
["featured"] = false,
// Dates
releaseDate: new Date('2024-01-15'),
lastUpdated: DateTime.UtcNow,
// Arrays
tags: ['electronics', 'computers', 'premium'],
colors: ['silver', 'space gray'],
// Objects (stored as JSON strings in most graph DBs)
specs: {
["cpu"] = "M3 Pro",
["ram"] = "16GB",
["storage"] = "512GB SSD",
},
},
});
Finding Nodes
Find All Nodes with Label
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.findNodes({
labels: ['Person'],
});
console.log('Found nodes:', result.nodes.length);
result.nodes.forEach(node => {
console.log(`${node.properties.name} - ID: ${node.id}`);
});
Map<String, Object> result = ductape.graph.findNodes(Map.of(
labels: ['Person']
));
System.out.println('Found nodes:', result.nodes.length);
result.nodes.forEach(node => Map.of(
System.out.println(`$Map.of(node.properties.name) - ID: $Map.of(node.id)`);
));
result := client.graph.findNodes({
labels: ['Person'],
});
fmt.Println('Found nodes:', result.nodes.length);
result.nodes.forEach(node => {
fmt.Println(`${node.properties.name} - ID: ${node.id}`);
});
var result = await ductape.graph.findNodes({
labels: ['Person'],
});
Console.WriteLine('Found nodes:', result.nodes.length);
result.nodes.forEach(node => {
Console.WriteLine(`${node.properties.name} - ID: ${node.id}`);
});
Find with Filters
Use the where clause to filter nodes. Ductape uses lowercase operators following the Mongoose/MongoDB convention:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.findNodes({
labels: ['Person'],
where: {
city: 'San Francisco',
age: { $gt: 25 },
},
limit: 10,
});
Map<String, Object> result = ductape.graph.findNodes(Map.of(
labels: ['Person'],
where: Map.of(
"city", "San Francisco",
age: Map.of( $"gt", 25 )
),
"limit", 10
));
result := client.graph.findNodes({
labels: ['Person'],
where: {
"city": "San Francisco",
age: { $"gt": 25 },
},
"limit": 10,
});
var result = await ductape.graph.findNodes({
labels: ['Person'],
where: {
["city"] = "San Francisco",
age: { $["gt"] = 25 },
},
["limit"] = 10,
});
Available Filter Operators
| Operator | Description | Example |
|---|---|---|
$gt | Greater than | { age: { $gt: 18 } } |
$gte | Greater than or equal | { age: { $gte: 18 } } |
$lt | Less than | { price: { $lt: 100 } } |
$lte | Less than or equal | { price: { $lte: 100 } } |
$ne | Not equal | { status: { $ne: 'deleted' } } |
$in | In array | { role: { $in: ['admin', 'moderator'] } } |
$nin | Not in array | { status: { $nin: ['banned', 'suspended'] } } |
$contains | String contains | { email: { $contains: '@gmail.com' } } |
$startsWith | String starts with | { name: { $startsWith: 'Dr.' } } |
$endsWith | String ends with | { email: { $endsWith: '.edu' } } |
$exists | Property exists | { verified: { $exists: true } } |
$regex | Regex match | { email: { $regex: '^user.*@example.com$' } } |
Uppercase operators (e.g., $GT, $IN) are still supported for backwards compatibility, but lowercase is recommended.
Complex Filters
Combine multiple conditions using array syntax (Mongoose-style):
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.findNodes({
labels: ['Product'],
where: {
$and: [
{ price: { $gte: 50 } },
{ price: { $lte: 500 } },
{ inStock: true },
{ category: { $in: ['electronics', 'gadgets'] } },
],
},
limit: 20,
});
Map<String, Object> result = ductape.graph.findNodes(Map.of(
labels: ['Product'],
where: Map.of(
$and: [
Map.of( price: Map.of( $"gte", 50 ) ),
Map.of( price: Map.of( $"lte", 500 ) ),
Map.of( "inStock", true ),
Map.of( category: Map.of( $in: ['electronics', 'gadgets'] ) ),
]
),
"limit", 20
));
result := client.graph.findNodes({
labels: ['Product'],
where: {
$and: [
{ price: { $"gte": 50 } },
{ price: { $"lte": 500 } },
{ "inStock": true },
{ category: { $in: ['electronics', 'gadgets'] } },
],
},
"limit": 20,
});
var result = await ductape.graph.findNodes({
labels: ['Product'],
where: {
$and: [
{ price: { $["gte"] = 50 } },
{ price: { $["lte"] = 500 } },
{ ["inStock"] = true },
{ category: { $in: ['electronics', 'gadgets'] } },
],
},
["limit"] = 20,
});
OR Conditions
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.findNodes({
labels: ['Person'],
where: {
$or: [
{ role: 'admin' },
{ isSuperUser: true },
{ department: 'Security' },
],
},
});
Map<String, Object> result = ductape.graph.findNodes(Map.of(
labels: ['Person'],
where: Map.of(
$or: [
Map.of( "role", "admin" ),
Map.of( "isSuperUser", true ),
Map.of( "department", "Security" ),
]
)
));
result := client.graph.findNodes({
labels: ['Person'],
where: {
$or: [
{ "role": "admin" },
{ "isSuperUser": true },
{ "department": "Security" },
],
},
});
var result = await ductape.graph.findNodes({
labels: ['Person'],
where: {
$or: [
{ ["role"] = "admin" },
{ ["isSuperUser"] = true },
{ ["department"] = "Security" },
],
},
});
Nested AND/OR
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.findNodes({
labels: ['Order'],
where: {
$and: [
{ total: { $gt: 100 } },
{ status: { $in: ['pending', 'processing'] } },
{
$or: [
{ priority: 'high' },
{ expressShipping: true },
],
},
],
},
});
Map<String, Object> result = ductape.graph.findNodes(Map.of(
labels: ['Order'],
where: Map.of(
$and: [
Map.of( total: Map.of( $"gt", 100 ) ),
Map.of( status: Map.of( $in: ['pending', 'processing'] ) ),
Map.of(
$or: [
Map.of( "priority", "high" ),
Map.of( "expressShipping", true ),
]
),
]
)
));
result := client.graph.findNodes({
labels: ['Order'],
where: {
$and: [
{ total: { $"gt": 100 } },
{ status: { $in: ['pending', 'processing'] } },
{
$or: [
{ "priority": "high" },
{ "expressShipping": true },
],
},
],
},
});
var result = await ductape.graph.findNodes({
labels: ['Order'],
where: {
$and: [
{ total: { $["gt"] = 100 } },
{ status: { $in: ['pending', 'processing'] } },
{
$or: [
{ ["priority"] = "high" },
{ ["expressShipping"] = true },
],
},
],
},
});
Pattern Matching
- TypeScript
- Java
- Go
- .NET
// Find users with Gmail addresses
const result = await ductape.graph.findNodes({
labels: ['User'],
where: {
email: { $endsWith: '@gmail.com' },
name: { $startsWith: 'John' },
},
});
// Find users with Gmail addresses
Map<String, Object> result = ductape.graph.findNodes(Map.of(
labels: ['User'],
where: Map.of(
email: Map.of( $"endsWith", "@gmail.com" ),
name: Map.of( $"startsWith", "John" )
)
));
// Find users with Gmail addresses
result := client.graph.findNodes({
labels: ['User'],
where: {
email: { $"endsWith": "@gmail.com" },
name: { $"startsWith": "John" },
},
});
// Find users with Gmail addresses
var result = await ductape.graph.findNodes({
labels: ['User'],
where: {
email: { $["endsWith"] = "@gmail.com" },
name: { $["startsWith"] = "John" },
},
});
Finding Nodes by ID
Single Node Lookup
- TypeScript
- Java
- Go
- .NET
const node = await ductape.graph.findNodeById('node-id-123');
if (node) {
console.log('Found:', node.properties.name);
} else {
console.log('Node not found');
}
Map<String, Object> node = ductape.graph.findNodeById('node-id-123');
if (node) Map.of(
System.out.println('"Found", ", node.properties.name);
) else Map.of(
System.out.println("Node not found');
)
node := client.graph.findNodeById('node-id-123');
if (node) {
fmt.Println('"Found": ", node.properties.name);
} else {
fmt.Println("Node not found');
}
var node = await ductape.graph.findNodeById('node-id-123');
if (node) {
Console.WriteLine('["Found"] = ", node.properties.name);
} else {
Console.WriteLine("Node not found');
}
With Type Safety
- TypeScript
- Java
- Go
- .NET
interface PersonProperties {
name: string;
email: string;
age: number;
}
const person = await ductape.graph.findNodeById<PersonProperties>('node-id-123');
if (person) {
console.log(`${person.properties.name} is ${person.properties.age} years old`);
}
interface PersonProperties Map.of(
name: string;
email: string;
age: number;
)
Map<String, Object> person = ductape.graph.findNodeById<PersonProperties>('node-id-123');
if (person) Map.of(
System.out.println(`$Map.of(person.properties.name) is $Map.of(person.properties.age) years old`);
)
interface PersonProperties {
name: string;
email: string;
age: number;
}
person := client.graph.findNodeById<PersonProperties>('node-id-123');
if (person) {
fmt.Println(`${person.properties.name} is ${person.properties.age} years old`);
}
interface PersonProperties {
name: string;
email: string;
age: number;
}
var person = await ductape.graph.findNodeById<PersonProperties>('node-id-123');
if (person) {
Console.WriteLine(`${person.properties.name} is ${person.properties.age} years old`);
}
Updating Nodes
Update Properties
Replace or add properties to an existing node:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.updateNode({
id: 'node-id-123',
properties: {
age: 29,
lastLogin: new Date(),
status: 'active',
},
});
console.log('Updated node:', result.node.properties);
Map<String, Object> result = ductape.graph.updateNode(Map.of(
"id", "node-id-123",
properties: Map.of(
"age", 29,
lastLogin: Instant.now(),
"status", "active"
)
));
System.out.println('Updated node:', result.node.properties);
result := client.graph.updateNode({
"id": "node-id-123",
properties: {
"age": 29,
lastLogin: new Date(),
"status": "active",
},
});
fmt.Println('Updated node:', result.node.properties);
var result = await ductape.graph.updateNode({
["id"] = "node-id-123",
properties: {
["age"] = 29,
lastLogin: DateTime.UtcNow,
["status"] = "active",
},
});
Console.WriteLine('Updated node:', result.node.properties);
Partial Updates
Only specified properties are updated; others remain unchanged:
- TypeScript
- Java
- Go
- .NET
// Original node: { name: 'Alice', email: 'alice@example.com', age: 28 }
await ductape.graph.updateNode({
id: nodeId,
properties: { age: 29 },
});
// Result: { name: 'Alice', email: 'alice@example.com', age: 29 }
// Original node: Map.of( "name", "Alice", "email", "alice@example.com", "age", 28 )
ductape.graph.updateNode(Map.of(
id: nodeId,
properties: Map.of( "age", 29 )
));
// Result: Map.of( "name", "Alice", "email", "alice@example.com", "age", 29 )
// Original node: { "name": "Alice", "email": "alice@example.com", "age": 28 }
client.graph.updateNode({
id: nodeId,
properties: { "age": 29 },
});
// Result: { "name": "Alice", "email": "alice@example.com", "age": 29 }
// Original node: { ["name"] = "Alice", ["email"] = "alice@example.com", ["age"] = 28 }
await ductape.graph.updateNode({
id: nodeId,
properties: { ["age"] = 29 },
});
// Result: { ["name"] = "Alice", ["email"] = "alice@example.com", ["age"] = 29 }
Update by Filter
Update multiple nodes matching criteria:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.updateNode({
labels: ['User'],
where: { lastLogin: { $lt: new Date('2024-01-01') } },
properties: { status: 'inactive' },
});
console.log('Updated nodes:', result.updatedCount);
Map<String, Object> result = ductape.graph.updateNode(Map.of(
labels: ['User'],
where: Map.of( lastLogin: Map.of( $lt: new Date('2024-01-01') ) ),
properties: Map.of( "status", "inactive" )
));
System.out.println('Updated nodes:', result.updatedCount);
result := client.graph.updateNode({
labels: ['User'],
where: { lastLogin: { $lt: new Date('2024-01-01') } },
properties: { "status": "inactive" },
});
fmt.Println('Updated nodes:', result.updatedCount);
var result = await ductape.graph.updateNode({
labels: ['User'],
where: { lastLogin: { $lt: new Date('2024-01-01') } },
properties: { ["status"] = "inactive" },
});
Console.WriteLine('Updated nodes:', result.updatedCount);
Increment Values
- TypeScript
- Java
- Go
- .NET
// Increment a counter
await ductape.graph.updateNode({
id: nodeId,
properties: {
loginCount: { $INCREMENT: 1 },
reputation: { $INCREMENT: 10 },
},
});
// Increment a counter
ductape.graph.updateNode(Map.of(
id: nodeId,
properties: Map.of(
loginCount: Map.of( $"INCREMENT", 1 ),
reputation: Map.of( $"INCREMENT", 10 )
)
));
// Increment a counter
client.graph.updateNode({
id: nodeId,
properties: {
loginCount: { $"INCREMENT": 1 },
reputation: { $"INCREMENT": 10 },
},
});
// Increment a counter
await ductape.graph.updateNode({
id: nodeId,
properties: {
loginCount: { $["INCREMENT"] = 1 },
reputation: { $["INCREMENT"] = 10 },
},
});
Deleting Nodes
Delete by ID
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.deleteNode({
id: 'node-id-123',
detach: true, // Also delete connected relationships
});
console.log('Deleted:', result.deleted);
Map<String, Object> result = ductape.graph.deleteNode(Map.of(
"id", "node-id-123",
"detach", true, // Also delete connected relationships
));
System.out.println('Deleted:', result.deleted);
result := client.graph.deleteNode({
"id": "node-id-123",
"detach": true, // Also delete connected relationships
});
fmt.Println('Deleted:', result.deleted);
var result = await ductape.graph.deleteNode({
["id"] = "node-id-123",
["detach"] = true, // Also delete connected relationships
});
Console.WriteLine('Deleted:', result.deleted);
Delete by Filter
Delete multiple nodes matching criteria:
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.deleteNode({
labels: ['TempUser'],
where: {
createdAt: { $lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }, // Older than 30 days
},
detach: true,
});
console.log('Deleted nodes:', result.deletedCount);
Map<String, Object> result = ductape.graph.deleteNode(Map.of(
labels: ['TempUser'],
where: Map.of(
createdAt: Map.of( $lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) ), // Older than 30 days
),
"detach", true
));
System.out.println('Deleted nodes:', result.deletedCount);
result := client.graph.deleteNode({
labels: ['TempUser'],
where: {
createdAt: { $lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }, // Older than 30 days
},
"detach": true,
});
fmt.Println('Deleted nodes:', result.deletedCount);
var result = await ductape.graph.deleteNode({
labels: ['TempUser'],
where: {
createdAt: { $lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }, // Older than 30 days
},
["detach"] = true,
});
Console.WriteLine('Deleted nodes:', result.deletedCount);
Detach vs Non-Detach
Detach Delete (recommended):
- TypeScript
- Java
- Go
- .NET
// Deletes the node AND all its relationships
await ductape.graph.deleteNode({
id: nodeId,
detach: true,
});
// Deletes the node AND all its relationships
ductape.graph.deleteNode(Map.of(
id: nodeId,
"detach", true
));
// Deletes the node AND all its relationships
client.graph.deleteNode({
id: nodeId,
"detach": true,
});
// Deletes the node AND all its relationships
await ductape.graph.deleteNode({
id: nodeId,
["detach"] = true,
});
Non-Detach Delete:
- TypeScript
- Java
- Go
- .NET
// Fails if node has relationships (ensures referential integrity)
await ductape.graph.deleteNode({
id: nodeId,
detach: false,
});
// Fails if node has relationships (ensures referential integrity)
ductape.graph.deleteNode(Map.of(
id: nodeId,
"detach", false
));
// Fails if node has relationships (ensures referential integrity)
client.graph.deleteNode({
id: nodeId,
"detach": false,
});
// Fails if node has relationships (ensures referential integrity)
await ductape.graph.deleteNode({
id: nodeId,
["detach"] = false,
});
Merge Operations
Merge creates a node if it doesn't exist, or updates it if it does (upsert operation).
Basic Merge
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.mergeNode({
labels: ['Person'],
matchProperties: { email: 'alice@example.com' },
onCreate: {
name: 'Alice Johnson',
email: 'alice@example.com',
createdAt: new Date(),
},
onMatch: {
lastSeen: new Date(),
},
});
if (result.created) {
console.log('Created new node:', result.node.id);
} else {
console.log('Updated existing node:', result.node.id);
}
Map<String, Object> result = ductape.graph.mergeNode(Map.of(
labels: ['Person'],
matchProperties: Map.of( "email", "alice@example.com" ),
onCreate: Map.of(
"name", "Alice Johnson",
"email", "alice@example.com",
createdAt: Instant.now()
),
onMatch: Map.of(
lastSeen: Instant.now()
)
));
if (result.created) Map.of(
System.out.println('Created new "node", ", result.node.id);
) else Map.of(
System.out.println("Updated existing node:', result.node.id);
)
result := client.graph.mergeNode({
labels: ['Person'],
matchProperties: { "email": "alice@example.com" },
onCreate: {
"name": "Alice Johnson",
"email": "alice@example.com",
createdAt: new Date(),
},
onMatch: {
lastSeen: new Date(),
},
});
if (result.created) {
fmt.Println('Created new "node": ", result.node.id);
} else {
fmt.Println("Updated existing node:', result.node.id);
}
var result = await ductape.graph.mergeNode({
labels: ['Person'],
matchProperties: { ["email"] = "alice@example.com" },
onCreate: {
["name"] = "Alice Johnson",
["email"] = "alice@example.com",
createdAt: DateTime.UtcNow,
},
onMatch: {
lastSeen: DateTime.UtcNow,
},
});
if (result.created) {
Console.WriteLine('Created new ["node"] = ", result.node.id);
} else {
Console.WriteLine("Updated existing node:', result.node.id);
}
Match on Multiple Properties
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.mergeNode({
labels: ['Product'],
matchProperties: {
sku: 'LPT-2024-001',
vendor: 'TechCorp',
},
onCreate: {
name: 'Laptop Pro',
sku: 'LPT-2024-001',
vendor: 'TechCorp',
price: 1299.99,
stock: 100,
createdAt: new Date(),
},
onMatch: {
stock: { $INCREMENT: 50 },
lastRestocked: new Date(),
},
});
Map<String, Object> result = ductape.graph.mergeNode(Map.of(
labels: ['Product'],
matchProperties: Map.of(
"sku", "LPT-2024-001",
"vendor", "TechCorp"
),
onCreate: Map.of(
"name", "Laptop Pro",
"sku", "LPT-2024-001",
"vendor", "TechCorp",
"price", 1299.99,
"stock", 100,
createdAt: Instant.now()
),
onMatch: Map.of(
stock: Map.of( $"INCREMENT", 50 ),
lastRestocked: Instant.now()
)
));
result := client.graph.mergeNode({
labels: ['Product'],
matchProperties: {
"sku": "LPT-2024-001",
"vendor": "TechCorp",
},
onCreate: {
"name": "Laptop Pro",
"sku": "LPT-2024-001",
"vendor": "TechCorp",
"price": 1299.99,
"stock": 100,
createdAt: new Date(),
},
onMatch: {
stock: { $"INCREMENT": 50 },
lastRestocked: new Date(),
},
});
var result = await ductape.graph.mergeNode({
labels: ['Product'],
matchProperties: {
["sku"] = "LPT-2024-001",
["vendor"] = "TechCorp",
},
onCreate: {
["name"] = "Laptop Pro",
["sku"] = "LPT-2024-001",
["vendor"] = "TechCorp",
["price"] = 1299.99,
["stock"] = 100,
createdAt: DateTime.UtcNow,
},
onMatch: {
stock: { $["INCREMENT"] = 50 },
lastRestocked: DateTime.UtcNow,
},
});
Conditional Properties
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.mergeNode({
labels: ['User'],
matchProperties: { email: 'bob@example.com' },
onCreate: {
name: 'Bob Smith',
email: 'bob@example.com',
role: 'user',
createdAt: new Date(),
loginCount: 1,
},
onMatch: {
loginCount: { $INCREMENT: 1 },
lastLogin: new Date(),
},
});
Map<String, Object> result = ductape.graph.mergeNode(Map.of(
labels: ['User'],
matchProperties: Map.of( "email", "bob@example.com" ),
onCreate: Map.of(
"name", "Bob Smith",
"email", "bob@example.com",
"role", "user",
createdAt: Instant.now(),
"loginCount", 1
),
onMatch: Map.of(
loginCount: Map.of( $"INCREMENT", 1 ),
lastLogin: Instant.now()
)
));
result := client.graph.mergeNode({
labels: ['User'],
matchProperties: { "email": "bob@example.com" },
onCreate: {
"name": "Bob Smith",
"email": "bob@example.com",
"role": "user",
createdAt: new Date(),
"loginCount": 1,
},
onMatch: {
loginCount: { $"INCREMENT": 1 },
lastLogin: new Date(),
},
});
var result = await ductape.graph.mergeNode({
labels: ['User'],
matchProperties: { ["email"] = "bob@example.com" },
onCreate: {
["name"] = "Bob Smith",
["email"] = "bob@example.com",
["role"] = "user",
createdAt: DateTime.UtcNow,
["loginCount"] = 1,
},
onMatch: {
loginCount: { $["INCREMENT"] = 1 },
lastLogin: DateTime.UtcNow,
},
});
Batch Operations
Create Multiple Nodes
- TypeScript
- Java
- Go
- .NET
const people = [
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
{ name: 'Charlie', email: 'charlie@example.com' },
];
for (const person of people) {
await ductape.graph.createNode({
labels: ['Person'],
properties: person,
});
}
Map<String, Object> people = [
Map.of( "name", "Alice", "email", "alice@example.com" ),
Map.of( "name", "Bob", "email", "bob@example.com" ),
Map.of( "name", "Charlie", "email", "charlie@example.com" ),
];
for (Map<String, Object> person of people) Map.of(
ductape.graph.createNode(Map.of(
labels: ['Person'],
properties: person
));
)
people := [
{ "name": "Alice", "email": "alice@example.com" },
{ "name": "Bob", "email": "bob@example.com" },
{ "name": "Charlie", "email": "charlie@example.com" },
];
for (const person of people) {
client.graph.createNode({
labels: ['Person'],
properties: person,
});
}
var people = [
{ ["name"] = "Alice", ["email"] = "alice@example.com" },
{ ["name"] = "Bob", ["email"] = "bob@example.com" },
{ ["name"] = "Charlie", ["email"] = "charlie@example.com" },
];
for (var person of people) {
await ductape.graph.createNode({
labels: ['Person'],
properties: person,
});
}
Efficient Batch with Transaction
- TypeScript
- Java
- Go
- .NET
await ductape.graph.executeTransaction(async (tx) => {
for (const person of people) {
await ductape.graph.createNode({
labels: ['Person'],
properties: person,
}, tx);
}
});
ductape.graph.executeTransaction(async (tx) => Map.of(
for (Map<String, Object> person of people) Map.of(
ductape.graph.createNode(Map.of(
labels: ['Person'],
properties: person
), tx);
)
));
client.graph.executeTransaction(async (tx) => {
for (const person of people) {
client.graph.createNode({
labels: ['Person'],
properties: person,
}, tx);
}
});
await ductape.graph.executeTransaction(async (tx) => {
for (var person of people) {
await ductape.graph.createNode({
labels: ['Person'],
properties: person,
}, tx);
}
});
Node Structure
Node Object
interface INode<T = NodeProperties> {
id: string | number; // Database-specific ID
labels: string[]; // Node labels/types
properties: T; // Node properties
elementId?: string; // Neo4j 5.x element ID
}
Result Types
Create Result:
interface ICreateNodeResult<T> {
node: INode<T>; // The created node
created: boolean; // Always true for create
}
Find Result:
interface IFindNodesResult<T> {
nodes: INode<T>[]; // Array of matching nodes
count: number; // Total count (for pagination)
}
Update Result:
interface IUpdateNodeResult<T> {
node?: INode<T>; // Updated node (if single update)
nodes?: INode<T>[]; // Updated nodes (if batch update)
updatedCount: number; // Number of nodes updated
}
Delete Result:
interface IDeleteNodeResult {
deleted: boolean; // Whether deletion succeeded
deletedCount: number; // Number of nodes deleted
}
Merge Result:
interface IMergeNodeResult<T> {
node: INode<T>; // The resulting node
created: boolean; // true if created, false if matched
}
Use Case Examples
User Registration
- TypeScript
- Java
- Go
- .NET
async function registerUser(email: string, name: string, password: string) {
// Check if user exists
const existing = await ductape.graph.findNodes({
labels: ['User'],
where: { email },
limit: 1,
});
if (existing.nodes.length > 0) {
throw new Error('User already exists');
}
// Create new user
const user = await ductape.graph.createNode({
labels: ['User'],
properties: {
email,
name,
passwordHash: hashPassword(password),
createdAt: new Date(),
status: 'active',
emailVerified: false,
},
});
return user.node;
}
async function registerUser(email: string, name: string, password: string) Map.of(
// Check if user exists
Map<String, Object> existing = ductape.graph.findNodes(Map.of(
labels: ['User'],
where: Map.of( email ),
"limit", 1
));
if (existing.nodes.length > 0) Map.of(
throw new Error('User already exists');
)
// Create new user
Map<String, Object> user = ductape.graph.createNode(Map.of(
labels: ['User'],
properties: Map.of(
email,
name,
passwordHash: hashPassword(password),
createdAt: Instant.now(),
"status", "active",
"emailVerified", false
)
));
return user.node;
)
async function registerUser(email: string, name: string, password: string) {
// Check if user exists
existing := client.graph.findNodes({
labels: ['User'],
where: { email },
"limit": 1,
});
if (existing.nodes.length > 0) {
throw new Error('User already exists');
}
// Create new user
user := client.graph.createNode({
labels: ['User'],
properties: {
email,
name,
passwordHash: hashPassword(password),
createdAt: new Date(),
"status": "active",
"emailVerified": false,
},
});
return user.node;
}
async function registerUser(email: string, name: string, password: string) {
// Check if user exists
var existing = await ductape.graph.findNodes({
labels: ['User'],
where: { email },
["limit"] = 1,
});
if (existing.nodes.length > 0) {
throw new Error('User already exists');
}
// Create new user
var user = await ductape.graph.createNode({
labels: ['User'],
properties: {
email,
name,
passwordHash: hashPassword(password),
createdAt: DateTime.UtcNow,
["status"] = "active",
["emailVerified"] = false,
},
});
return user.node;
}
Product Catalog
- TypeScript
- Java
- Go
- .NET
async function addOrUpdateProduct(sku: string, productData: any) {
const result = await ductape.graph.mergeNode({
labels: ['Product'],
matchProperties: { sku },
onCreate: {
...productData,
sku,
createdAt: new Date(),
views: 0,
},
onMatch: {
...productData,
updatedAt: new Date(),
},
});
return result;
}
async function addOrUpdateProduct(sku: string, productData: any) Map.of(
Map<String, Object> result = ductape.graph.mergeNode(Map.of(
labels: ['Product'],
matchProperties: Map.of( sku ),
onCreate: Map.of(
...productData,
sku,
createdAt: Instant.now(),
"views", 0
),
onMatch: Map.of(
...productData,
updatedAt: Instant.now()
)
));
return result;
)
async function addOrUpdateProduct(sku: string, productData: any) {
result := client.graph.mergeNode({
labels: ['Product'],
matchProperties: { sku },
onCreate: {
...productData,
sku,
createdAt: new Date(),
"views": 0,
},
onMatch: {
...productData,
updatedAt: new Date(),
},
});
return result;
}
async function addOrUpdateProduct(sku: string, productData: any) {
var result = await ductape.graph.mergeNode({
labels: ['Product'],
matchProperties: { sku },
onCreate: {
...productData,
sku,
createdAt: DateTime.UtcNow,
["views"] = 0,
},
onMatch: {
...productData,
updatedAt: DateTime.UtcNow,
},
});
return result;
}
Activity Tracking
- TypeScript
- Java
- Go
- .NET
async function trackUserActivity(userId: string) {
await ductape.graph.updateNode({
id: userId,
properties: {
lastActive: new Date(),
activityCount: { $INCREMENT: 1 },
},
});
}
async function trackUserActivity(userId: string) Map.of(
ductape.graph.updateNode(Map.of(
id: userId,
properties: Map.of(
lastActive: Instant.now(),
activityCount: Map.of( $"INCREMENT", 1 )
)
));
)
async function trackUserActivity(userId: string) {
client.graph.updateNode({
id: userId,
properties: {
lastActive: new Date(),
activityCount: { $"INCREMENT": 1 },
},
});
}
async function trackUserActivity(userId: string) {
await ductape.graph.updateNode({
id: userId,
properties: {
lastActive: DateTime.UtcNow,
activityCount: { $["INCREMENT"] = 1 },
},
});
}
Soft Delete Pattern
- TypeScript
- Java
- Go
- .NET
async function softDeleteUser(userId: string) {
await ductape.graph.updateNode({
id: userId,
properties: {
status: 'deleted',
deletedAt: new Date(),
},
});
}
async function getActiveUsers() {
return ductape.graph.findNodes({
labels: ['User'],
where: {
status: { $ne: 'deleted' },
},
});
}
async function softDeleteUser(userId: string) Map.of(
ductape.graph.updateNode(Map.of(
id: userId,
properties: Map.of(
"status", "deleted",
deletedAt: Instant.now()
)
));
)
async function getActiveUsers() Map.of(
return ductape.graph.findNodes(Map.of(
labels: ['User'],
where: Map.of(
status: Map.of( $"ne", "deleted" )
)
));
)
async function softDeleteUser(userId: string) {
client.graph.updateNode({
id: userId,
properties: {
"status": "deleted",
deletedAt: new Date(),
},
});
}
async function getActiveUsers() {
return client.graph.findNodes({
labels: ['User'],
where: {
status: { $"ne": "deleted" },
},
});
}
async function softDeleteUser(userId: string) {
await ductape.graph.updateNode({
id: userId,
properties: {
["status"] = "deleted",
deletedAt: DateTime.UtcNow,
},
});
}
async function getActiveUsers() {
return ductape.graph.findNodes({
labels: ['User'],
where: {
status: { $["ne"] = "deleted" },
},
});
}
Best Practices
1. Use Meaningful Labels
- TypeScript
- Java
- Go
- .NET
// Good - descriptive and hierarchical
await ductape.graph.createNode({
labels: ['Person', 'Employee', 'Engineer'],
properties: { name: 'Alice' },
});
// Avoid - too generic
await ductape.graph.createNode({
labels: ['Node'],
properties: { name: 'Alice' },
});
// Good - descriptive and hierarchical
ductape.graph.createNode(Map.of(
labels: ['Person', 'Employee', 'Engineer'],
properties: Map.of( "name", "Alice" )
));
// Avoid - too generic
ductape.graph.createNode(Map.of(
labels: ['Node'],
properties: Map.of( "name", "Alice" )
));
// Good - descriptive and hierarchical
client.graph.createNode({
labels: ['Person', 'Employee', 'Engineer'],
properties: { "name": "Alice" },
});
// Avoid - too generic
client.graph.createNode({
labels: ['Node'],
properties: { "name": "Alice" },
});
// Good - descriptive and hierarchical
await ductape.graph.createNode({
labels: ['Person', 'Employee', 'Engineer'],
properties: { ["name"] = "Alice" },
});
// Avoid - too generic
await ductape.graph.createNode({
labels: ['Node'],
properties: { ["name"] = "Alice" },
});
2. Index Frequently Queried Properties
- TypeScript
- Java
- Go
- .NET
// Create an index for faster lookups
await ductape.graph.createNodeIndex({
name: 'idx_user_email',
label: 'User',
properties: ['email'],
unique: true,
});
// Create an index for faster lookups
ductape.graph.createNodeIndex(Map.of(
"name", "idx_user_email",
"label", "User",
properties: ['email'],
"unique", true
));
// Create an index for faster lookups
client.graph.createNodeIndex({
"name": "idx_user_email",
"label": "User",
properties: ['email'],
"unique": true,
});
// Create an index for faster lookups
await ductape.graph.createNodeIndex({
["name"] = "idx_user_email",
["label"] = "User",
properties: ['email'],
["unique"] = true,
});
3. Use Merge for Idempotent Operations
- TypeScript
- Java
- Go
- .NET
// Merge ensures the operation can be retried safely
const result = await ductape.graph.mergeNode({
labels: ['User'],
matchProperties: { email },
onCreate: userData,
onMatch: { lastSeen: new Date() },
});
// Merge ensures the operation can be retried safely
Map<String, Object> result = ductape.graph.mergeNode(Map.of(
labels: ['User'],
matchProperties: Map.of( email ),
onCreate: userData,
onMatch: Map.of( lastSeen: Instant.now() )
));
// Merge ensures the operation can be retried safely
result := client.graph.mergeNode({
labels: ['User'],
matchProperties: { email },
onCreate: userData,
onMatch: { lastSeen: new Date() },
});
// Merge ensures the operation can be retried safely
var result = await ductape.graph.mergeNode({
labels: ['User'],
matchProperties: { email },
onCreate: userData,
onMatch: { lastSeen: DateTime.UtcNow },
});
4. Always Use Detach When Deleting
- TypeScript
- Java
- Go
- .NET
// Prevents orphaned relationships
await ductape.graph.deleteNode({
id: nodeId,
detach: true,
});
// Prevents orphaned relationships
ductape.graph.deleteNode(Map.of(
id: nodeId,
"detach", true
));
// Prevents orphaned relationships
client.graph.deleteNode({
id: nodeId,
"detach": true,
});
// Prevents orphaned relationships
await ductape.graph.deleteNode({
id: nodeId,
["detach"] = true,
});
5. Validate Properties Before Creation
- TypeScript
- Java
- Go
- .NET
function validateEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
async function createUser(data: any) {
if (!validateEmail(data.email)) {
throw new Error('Invalid email');
}
return ductape.graph.createNode({
labels: ['User'],
properties: data,
});
}
function validateEmail(email: string): boolean Map.of(
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
)
async function createUser(data: any) Map.of(
if (!validateEmail(data.email)) Map.of(
throw new Error('Invalid email');
)
return ductape.graph.createNode(Map.of(
labels: ['User'],
properties: data
));
)
function validateEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
async function createUser(data: any) {
if (!validateEmail(data.email)) {
throw new Error('Invalid email');
}
return client.graph.createNode({
labels: ['User'],
properties: data,
});
}
function validateEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
async function createUser(data: any) {
if (!validateEmail(data.email)) {
throw new Error('Invalid email');
}
return ductape.graph.createNode({
labels: ['User'],
properties: data,
});
}
Next Steps
- Manage Relationships - Connect nodes with relationships
- Traverse Graphs - Find paths and explore neighborhoods
- Advanced Querying - Complex patterns and full-text search
- Use Transactions - Ensure data consistency
See Also
- Graph Overview - Full API reference
- Best Practices - Performance optimization