Preview
Preview Feature — This feature is currently in preview and under active development. APIs and functionality may change. We recommend testing thoroughly before using in production.
Graph Actions
Create reusable graph query templates that can be executed with different input parameters. Graph actions allow you to define graph operations once and reuse them across your application with variable interpolation.
Quick Example
- TypeScript
- Java
- Go
- .NET
// Create a graph action template
await ductape.graph.action.create({
name: 'Find User Friends',
tag: 'social-graph:find-user-friends',
operation: GraphActionTypes.FIND_RELATIONSHIPS,
description: 'Get all friends of a user with pagination',
template: {
startNodeId: '{{userId}}',
type: 'FRIENDS_WITH',
direction: 'OUTGOING',
limit: '{{limit}}',
skip: '{{offset}}',
},
});
// Execute the action with different inputs
const friends = await ductape.graph.execute({
graph: 'social-graph',
action: 'find-user-friends',
input: {
userId: 'user-123',
limit: 20,
offset: 0,
},
});
// Create a graph action template
ductape.graph.action.create(Map.of(
"name", "Find User Friends",
"tag", "social-graph:find-user-friends",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
"description", "Get all friends of a user with pagination",
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
"type", "FRIENDS_WITH",
"direction", "OUTGOING",
"limit", "Map.of(Map.of(limit))",
"skip", "Map.of(Map.of(offset))"
)
));
// Execute the action with different inputs
Map<String, Object> friends = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "find-user-friends",
input: Map.of(
"userId", "user-123",
"limit", 20,
"offset", 0
)
));
// Create a graph action template
client.graph.action.create({
"name": "Find User Friends",
"tag": "social-graph:find-user-friends",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
"description": "Get all friends of a user with pagination",
template: {
"startNodeId": "{{userId}}",
"type": "FRIENDS_WITH",
"direction": "OUTGOING",
"limit": "{{limit}}",
"skip": "{{offset}}",
},
});
// Execute the action with different inputs
friends := client.graph.execute({
"graph": "social-graph",
"action": "find-user-friends",
input: {
"userId": "user-123",
"limit": 20,
"offset": 0,
},
});
// Create a graph action template
await ductape.graph.action.create({
["name"] = "Find User Friends",
["tag"] = "social-graph:find-user-friends",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
["description"] = "Get all friends of a user with pagination",
template: {
["startNodeId"] = "{{userId}}",
["type"] = "FRIENDS_WITH",
["direction"] = "OUTGOING",
["limit"] = "{{limit}}",
["skip"] = "{{offset}}",
},
});
// Execute the action with different inputs
var friends = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "find-user-friends",
input: {
["userId"] = "user-123",
["limit"] = 20,
["offset"] = 0,
},
});
Why Use Graph Actions?
Benefits:
- Reusability - Define graph operations once, use everywhere
- Type Safety - Template validation at creation time
- Variable Interpolation - Dynamic queries with
{{placeholder}}syntax - Maintainability - Update logic in one place
- Consistency - Standardize graph access patterns
- Testing - Easy to test query templates
Action Types
Node Operations
CREATE_NODE - Create Nodes
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Create User Node',
tag: 'social-graph:create-user',
operation: GraphActionTypes.CREATE_NODE,
template: {
labels: ['User', 'Person'],
properties: {
name: '{{name}}',
email: '{{email}}',
age: '{{age}}',
status: 'active',
createdAt: '{{createdAt}}',
},
},
});
// Execute
const user = await ductape.graph.execute({
graph: 'social-graph',
action: 'create-user',
input: {
name: 'Alice Johnson',
email: 'alice@example.com',
age: 28,
createdAt: new Date().toISOString(),
},
});
ductape.graph.action.create(Map.of(
"name", "Create User Node",
"tag", "social-graph:create-user",
operation: GraphActionTypes.CREATE_NODE,
template: Map.of(
labels: ['User', 'Person'],
properties: Map.of(
"name", "Map.of(Map.of(name))",
"email", "Map.of(Map.of(email))",
"age", "Map.of(Map.of(age))",
"status", "active",
"createdAt", "Map.of(Map.of(createdAt))"
)
)
));
// Execute
Map<String, Object> user = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "create-user",
input: Map.of(
"name", "Alice Johnson",
"email", "alice@example.com",
"age", 28,
createdAt: Instant.now().toISOString()
)
));
client.graph.action.create({
"name": "Create User Node",
"tag": "social-graph:create-user",
operation: GraphActionTypes.CREATE_NODE,
template: {
labels: ['User', 'Person'],
properties: {
"name": "{{name}}",
"email": "{{email}}",
"age": "{{age}}",
"status": "active",
"createdAt": "{{createdAt}}",
},
},
});
// Execute
user := client.graph.execute({
"graph": "social-graph",
"action": "create-user",
input: {
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 28,
createdAt: new Date().toISOString(),
},
});
await ductape.graph.action.create({
["name"] = "Create User Node",
["tag"] = "social-graph:create-user",
operation: GraphActionTypes.CREATE_NODE,
template: {
labels: ['User', 'Person'],
properties: {
["name"] = "{{name}}",
["email"] = "{{email}}",
["age"] = "{{age}}",
["status"] = "active",
["createdAt"] = "{{createdAt}}",
},
},
});
// Execute
var user = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "create-user",
input: {
["name"] = "Alice Johnson",
["email"] = "alice@example.com",
["age"] = 28,
createdAt: DateTime.UtcNow.toISOString(),
},
});
FIND_NODES - Query Nodes
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Find Users by City',
tag: 'social-graph:find-users-by-city',
operation: GraphActionTypes.FIND_NODES,
template: {
labels: ['User'],
where: {
city: '{{city}}',
age: { $GTE: '{{minAge}}' },
status: 'active',
},
limit: '{{limit}}',
skip: '{{offset}}',
},
});
// Execute
const users = await ductape.graph.execute({
graph: 'social-graph',
action: 'find-users-by-city',
input: {
city: 'New York',
minAge: 18,
limit: 50,
offset: 0,
},
});
ductape.graph.action.create(Map.of(
"name", "Find Users by City",
"tag", "social-graph:find-users-by-city",
operation: GraphActionTypes.FIND_NODES,
template: Map.of(
labels: ['User'],
where: Map.of(
"city", "Map.of(Map.of(city))",
age: Map.of( $"GTE", "Map.of(Map.of(minAge))" ),
"status", "active"
),
"limit", "Map.of(Map.of(limit))",
"skip", "Map.of(Map.of(offset))"
)
));
// Execute
Map<String, Object> users = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "find-users-by-city",
input: Map.of(
"city", "New York",
"minAge", 18,
"limit", 50,
"offset", 0
)
));
client.graph.action.create({
"name": "Find Users by City",
"tag": "social-graph:find-users-by-city",
operation: GraphActionTypes.FIND_NODES,
template: {
labels: ['User'],
where: {
"city": "{{city}}",
age: { $"GTE": "{{minAge}}" },
"status": "active",
},
"limit": "{{limit}}",
"skip": "{{offset}}",
},
});
// Execute
users := client.graph.execute({
"graph": "social-graph",
"action": "find-users-by-city",
input: {
"city": "New York",
"minAge": 18,
"limit": 50,
"offset": 0,
},
});
await ductape.graph.action.create({
["name"] = "Find Users by City",
["tag"] = "social-graph:find-users-by-city",
operation: GraphActionTypes.FIND_NODES,
template: {
labels: ['User'],
where: {
["city"] = "{{city}}",
age: { $["GTE"] = "{{minAge}}" },
["status"] = "active",
},
["limit"] = "{{limit}}",
["skip"] = "{{offset}}",
},
});
// Execute
var users = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "find-users-by-city",
input: {
["city"] = "New York",
["minAge"] = 18,
["limit"] = 50,
["offset"] = 0,
},
});
FIND_NODE_BY_ID - Get Single Node
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Get User by ID',
tag: 'social-graph:get-user-by-id',
operation: GraphActionTypes.FIND_NODE_BY_ID,
template: {
id: '{{userId}}',
},
});
// Execute
const user = await ductape.graph.execute({
graph: 'social-graph',
action: 'get-user-by-id',
input: {
userId: 'user-123',
},
});
ductape.graph.action.create(Map.of(
"name", "Get User by ID",
"tag", "social-graph:get-user-by-id",
operation: GraphActionTypes.FIND_NODE_BY_ID,
template: Map.of(
"id", "Map.of(Map.of(userId))"
)
));
// Execute
Map<String, Object> user = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "get-user-by-id",
input: Map.of(
"userId", "user-123"
)
));
client.graph.action.create({
"name": "Get User by ID",
"tag": "social-graph:get-user-by-id",
operation: GraphActionTypes.FIND_NODE_BY_ID,
template: {
"id": "{{userId}}",
},
});
// Execute
user := client.graph.execute({
"graph": "social-graph",
"action": "get-user-by-id",
input: {
"userId": "user-123",
},
});
await ductape.graph.action.create({
["name"] = "Get User by ID",
["tag"] = "social-graph:get-user-by-id",
operation: GraphActionTypes.FIND_NODE_BY_ID,
template: {
["id"] = "{{userId}}",
},
});
// Execute
var user = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "get-user-by-id",
input: {
["userId"] = "user-123",
},
});
UPDATE_NODE - Modify Nodes
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Update User Profile',
tag: 'social-graph:update-user-profile',
operation: GraphActionTypes.UPDATE_NODE,
template: {
id: '{{userId}}',
properties: {
name: '{{name}}',
bio: '{{bio}}',
avatar: '{{avatar}}',
updatedAt: '{{updatedAt}}',
},
},
});
// Execute
await ductape.graph.execute({
graph: 'social-graph',
action: 'update-user-profile',
input: {
userId: 'user-123',
name: 'Alice Smith',
bio: 'Software Engineer',
avatar: 'https://...',
updatedAt: new Date().toISOString(),
},
});
ductape.graph.action.create(Map.of(
"name", "Update User Profile",
"tag", "social-graph:update-user-profile",
operation: GraphActionTypes.UPDATE_NODE,
template: Map.of(
"id", "Map.of(Map.of(userId))",
properties: Map.of(
"name", "Map.of(Map.of(name))",
"bio", "Map.of(Map.of(bio))",
"avatar", "Map.of(Map.of(avatar))",
"updatedAt", "Map.of(Map.of(updatedAt))"
)
)
));
// Execute
ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "update-user-profile",
input: Map.of(
"userId", "user-123",
"name", "Alice Smith",
"bio", "Software Engineer",
"avatar", "https://...",
updatedAt: Instant.now().toISOString()
)
));
client.graph.action.create({
"name": "Update User Profile",
"tag": "social-graph:update-user-profile",
operation: GraphActionTypes.UPDATE_NODE,
template: {
"id": "{{userId}}",
properties: {
"name": "{{name}}",
"bio": "{{bio}}",
"avatar": "{{avatar}}",
"updatedAt": "{{updatedAt}}",
},
},
});
// Execute
client.graph.execute({
"graph": "social-graph",
"action": "update-user-profile",
input: {
"userId": "user-123",
"name": "Alice Smith",
"bio": "Software Engineer",
"avatar": "https://...",
updatedAt: new Date().toISOString(),
},
});
await ductape.graph.action.create({
["name"] = "Update User Profile",
["tag"] = "social-graph:update-user-profile",
operation: GraphActionTypes.UPDATE_NODE,
template: {
["id"] = "{{userId}}",
properties: {
["name"] = "{{name}}",
["bio"] = "{{bio}}",
["avatar"] = "{{avatar}}",
["updatedAt"] = "{{updatedAt}}",
},
},
});
// Execute
await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "update-user-profile",
input: {
["userId"] = "user-123",
["name"] = "Alice Smith",
["bio"] = "Software Engineer",
["avatar"] = "https://...",
updatedAt: DateTime.UtcNow.toISOString(),
},
});
DELETE_NODE - Remove Nodes
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Delete User',
tag: 'social-graph:delete-user',
operation: GraphActionTypes.DELETE_NODE,
template: {
id: '{{userId}}',
detach: true, // Also delete relationships
},
});
// Execute
await ductape.graph.execute({
graph: 'social-graph',
action: 'delete-user',
input: {
userId: 'user-123',
},
});
ductape.graph.action.create(Map.of(
"name", "Delete User",
"tag", "social-graph:delete-user",
operation: GraphActionTypes.DELETE_NODE,
template: Map.of(
"id", "Map.of(Map.of(userId))",
"detach", true, // Also delete relationships
)
));
// Execute
ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "delete-user",
input: Map.of(
"userId", "user-123"
)
));
client.graph.action.create({
"name": "Delete User",
"tag": "social-graph:delete-user",
operation: GraphActionTypes.DELETE_NODE,
template: {
"id": "{{userId}}",
"detach": true, // Also delete relationships
},
});
// Execute
client.graph.execute({
"graph": "social-graph",
"action": "delete-user",
input: {
"userId": "user-123",
},
});
await ductape.graph.action.create({
["name"] = "Delete User",
["tag"] = "social-graph:delete-user",
operation: GraphActionTypes.DELETE_NODE,
template: {
["id"] = "{{userId}}",
["detach"] = true, // Also delete relationships
},
});
// Execute
await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "delete-user",
input: {
["userId"] = "user-123",
},
});
MERGE_NODE - Upsert Nodes
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Merge User',
tag: 'social-graph:merge-user',
operation: GraphActionTypes.MERGE_NODE,
template: {
labels: ['User'],
matchProperties: {
email: '{{email}}',
},
onCreate: {
name: '{{name}}',
email: '{{email}}',
createdAt: '{{createdAt}}',
},
onMatch: {
lastSeen: '{{lastSeen}}',
},
},
});
// Execute
await ductape.graph.execute({
graph: 'social-graph',
action: 'merge-user',
input: {
email: 'alice@example.com',
name: 'Alice Johnson',
createdAt: new Date().toISOString(),
lastSeen: new Date().toISOString(),
},
});
ductape.graph.action.create(Map.of(
"name", "Merge User",
"tag", "social-graph:merge-user",
operation: GraphActionTypes.MERGE_NODE,
template: Map.of(
labels: ['User'],
matchProperties: Map.of(
"email", "Map.of(Map.of(email))"
),
onCreate: Map.of(
"name", "Map.of(Map.of(name))",
"email", "Map.of(Map.of(email))",
"createdAt", "Map.of(Map.of(createdAt))"
),
onMatch: Map.of(
"lastSeen", "Map.of(Map.of(lastSeen))"
)
)
));
// Execute
ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "merge-user",
input: Map.of(
"email", "alice@example.com",
"name", "Alice Johnson",
createdAt: Instant.now().toISOString(),
lastSeen: Instant.now().toISOString()
)
));
client.graph.action.create({
"name": "Merge User",
"tag": "social-graph:merge-user",
operation: GraphActionTypes.MERGE_NODE,
template: {
labels: ['User'],
matchProperties: {
"email": "{{email}}",
},
onCreate: {
"name": "{{name}}",
"email": "{{email}}",
"createdAt": "{{createdAt}}",
},
onMatch: {
"lastSeen": "{{lastSeen}}",
},
},
});
// Execute
client.graph.execute({
"graph": "social-graph",
"action": "merge-user",
input: {
"email": "alice@example.com",
"name": "Alice Johnson",
createdAt: new Date().toISOString(),
lastSeen: new Date().toISOString(),
},
});
await ductape.graph.action.create({
["name"] = "Merge User",
["tag"] = "social-graph:merge-user",
operation: GraphActionTypes.MERGE_NODE,
template: {
labels: ['User'],
matchProperties: {
["email"] = "{{email}}",
},
onCreate: {
["name"] = "{{name}}",
["email"] = "{{email}}",
["createdAt"] = "{{createdAt}}",
},
onMatch: {
["lastSeen"] = "{{lastSeen}}",
},
},
});
// Execute
await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "merge-user",
input: {
["email"] = "alice@example.com",
["name"] = "Alice Johnson",
createdAt: DateTime.UtcNow.toISOString(),
lastSeen: DateTime.UtcNow.toISOString(),
},
});
Relationship Operations
CREATE_RELATIONSHIP - Create Connections
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Create Friendship',
tag: 'social-graph:create-friendship',
operation: GraphActionTypes.CREATE_RELATIONSHIP,
template: {
type: 'FRIENDS_WITH',
startNodeId: '{{userId1}}',
endNodeId: '{{userId2}}',
properties: {
since: '{{since}}',
closeness: '{{closeness}}',
},
},
});
// Execute
await ductape.graph.execute({
graph: 'social-graph',
action: 'create-friendship',
input: {
userId1: 'user-123',
userId2: 'user-456',
since: '2024-01-15',
closeness: 'high',
},
});
ductape.graph.action.create(Map.of(
"name", "Create Friendship",
"tag", "social-graph:create-friendship",
operation: GraphActionTypes.CREATE_RELATIONSHIP,
template: Map.of(
"type", "FRIENDS_WITH",
"startNodeId", "Map.of(Map.of(userId1))",
"endNodeId", "Map.of(Map.of(userId2))",
properties: Map.of(
"since", "Map.of(Map.of(since))",
"closeness", "Map.of(Map.of(closeness))"
)
)
));
// Execute
ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "create-friendship",
input: Map.of(
"userId1", "user-123",
"userId2", "user-456",
"since", "2024-01-15",
"closeness", "high"
)
));
client.graph.action.create({
"name": "Create Friendship",
"tag": "social-graph:create-friendship",
operation: GraphActionTypes.CREATE_RELATIONSHIP,
template: {
"type": "FRIENDS_WITH",
"startNodeId": "{{userId1}}",
"endNodeId": "{{userId2}}",
properties: {
"since": "{{since}}",
"closeness": "{{closeness}}",
},
},
});
// Execute
client.graph.execute({
"graph": "social-graph",
"action": "create-friendship",
input: {
"userId1": "user-123",
"userId2": "user-456",
"since": "2024-01-15",
"closeness": "high",
},
});
await ductape.graph.action.create({
["name"] = "Create Friendship",
["tag"] = "social-graph:create-friendship",
operation: GraphActionTypes.CREATE_RELATIONSHIP,
template: {
["type"] = "FRIENDS_WITH",
["startNodeId"] = "{{userId1}}",
["endNodeId"] = "{{userId2}}",
properties: {
["since"] = "{{since}}",
["closeness"] = "{{closeness}}",
},
},
});
// Execute
await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "create-friendship",
input: {
["userId1"] = "user-123",
["userId2"] = "user-456",
["since"] = "2024-01-15",
["closeness"] = "high",
},
});
FIND_RELATIONSHIPS - Query Connections
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Get User Followers',
tag: 'social-graph:get-user-followers',
operation: GraphActionTypes.FIND_RELATIONSHIPS,
template: {
endNodeId: '{{userId}}',
type: 'FOLLOWS',
direction: 'INCOMING',
where: {
active: true,
},
limit: '{{limit}}',
},
});
// Execute
const followers = await ductape.graph.execute({
graph: 'social-graph',
action: 'get-user-followers',
input: {
userId: 'user-123',
limit: 100,
},
});
ductape.graph.action.create(Map.of(
"name", "Get User Followers",
"tag", "social-graph:get-user-followers",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
template: Map.of(
"endNodeId", "Map.of(Map.of(userId))",
"type", "FOLLOWS",
"direction", "INCOMING",
where: Map.of(
"active", true
),
"limit", "Map.of(Map.of(limit))"
)
));
// Execute
Map<String, Object> followers = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "get-user-followers",
input: Map.of(
"userId", "user-123",
"limit", 100
)
));
client.graph.action.create({
"name": "Get User Followers",
"tag": "social-graph:get-user-followers",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
template: {
"endNodeId": "{{userId}}",
"type": "FOLLOWS",
"direction": "INCOMING",
where: {
"active": true,
},
"limit": "{{limit}}",
},
});
// Execute
followers := client.graph.execute({
"graph": "social-graph",
"action": "get-user-followers",
input: {
"userId": "user-123",
"limit": 100,
},
});
await ductape.graph.action.create({
["name"] = "Get User Followers",
["tag"] = "social-graph:get-user-followers",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
template: {
["endNodeId"] = "{{userId}}",
["type"] = "FOLLOWS",
["direction"] = "INCOMING",
where: {
["active"] = true,
},
["limit"] = "{{limit}}",
},
});
// Execute
var followers = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "get-user-followers",
input: {
["userId"] = "user-123",
["limit"] = 100,
},
});
UPDATE_RELATIONSHIP - Modify Connections
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Update Friendship',
tag: 'social-graph:update-friendship',
operation: GraphActionTypes.UPDATE_RELATIONSHIP,
template: {
id: '{{relationshipId}}',
properties: {
closeness: '{{closeness}}',
lastInteraction: '{{lastInteraction}}',
},
},
});
ductape.graph.action.create(Map.of(
"name", "Update Friendship",
"tag", "social-graph:update-friendship",
operation: GraphActionTypes.UPDATE_RELATIONSHIP,
template: Map.of(
"id", "Map.of(Map.of(relationshipId))",
properties: Map.of(
"closeness", "Map.of(Map.of(closeness))",
"lastInteraction", "Map.of(Map.of(lastInteraction))"
)
)
));
client.graph.action.create({
"name": "Update Friendship",
"tag": "social-graph:update-friendship",
operation: GraphActionTypes.UPDATE_RELATIONSHIP,
template: {
"id": "{{relationshipId}}",
properties: {
"closeness": "{{closeness}}",
"lastInteraction": "{{lastInteraction}}",
},
},
});
await ductape.graph.action.create({
["name"] = "Update Friendship",
["tag"] = "social-graph:update-friendship",
operation: GraphActionTypes.UPDATE_RELATIONSHIP,
template: {
["id"] = "{{relationshipId}}",
properties: {
["closeness"] = "{{closeness}}",
["lastInteraction"] = "{{lastInteraction}}",
},
},
});
DELETE_RELATIONSHIP - Remove Connections
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Remove Friendship',
tag: 'social-graph:remove-friendship',
operation: GraphActionTypes.DELETE_RELATIONSHIP,
template: {
id: '{{relationshipId}}',
},
});
ductape.graph.action.create(Map.of(
"name", "Remove Friendship",
"tag", "social-graph:remove-friendship",
operation: GraphActionTypes.DELETE_RELATIONSHIP,
template: Map.of(
"id", "Map.of(Map.of(relationshipId))"
)
));
client.graph.action.create({
"name": "Remove Friendship",
"tag": "social-graph:remove-friendship",
operation: GraphActionTypes.DELETE_RELATIONSHIP,
template: {
"id": "{{relationshipId}}",
},
});
await ductape.graph.action.create({
["name"] = "Remove Friendship",
["tag"] = "social-graph:remove-friendship",
operation: GraphActionTypes.DELETE_RELATIONSHIP,
template: {
["id"] = "{{relationshipId}}",
},
});
Traversal Operations
TRAVERSE - Explore Graph
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Get User Network',
tag: 'social-graph:get-user-network',
operation: GraphActionTypes.TRAVERSE,
template: {
startNodeId: '{{userId}}',
direction: 'OUTGOING',
relationshipTypes: ['FRIENDS_WITH', 'WORKS_WITH'],
maxDepth: '{{maxDepth}}',
nodeFilter: {
labels: ['User'],
where: {
status: 'active',
},
},
},
});
// Execute
const network = await ductape.graph.execute({
graph: 'social-graph',
action: 'get-user-network',
input: {
userId: 'user-123',
maxDepth: 2,
},
});
ductape.graph.action.create(Map.of(
"name", "Get User Network",
"tag", "social-graph:get-user-network",
operation: GraphActionTypes.TRAVERSE,
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
"direction", "OUTGOING",
relationshipTypes: ['FRIENDS_WITH', 'WORKS_WITH'],
"maxDepth", "Map.of(Map.of(maxDepth))",
nodeFilter: Map.of(
labels: ['User'],
where: Map.of(
"status", "active"
)
)
)
));
// Execute
Map<String, Object> network = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "get-user-network",
input: Map.of(
"userId", "user-123",
"maxDepth", 2
)
));
client.graph.action.create({
"name": "Get User Network",
"tag": "social-graph:get-user-network",
operation: GraphActionTypes.TRAVERSE,
template: {
"startNodeId": "{{userId}}",
"direction": "OUTGOING",
relationshipTypes: ['FRIENDS_WITH', 'WORKS_WITH'],
"maxDepth": "{{maxDepth}}",
nodeFilter: {
labels: ['User'],
where: {
"status": "active",
},
},
},
});
// Execute
network := client.graph.execute({
"graph": "social-graph",
"action": "get-user-network",
input: {
"userId": "user-123",
"maxDepth": 2,
},
});
await ductape.graph.action.create({
["name"] = "Get User Network",
["tag"] = "social-graph:get-user-network",
operation: GraphActionTypes.TRAVERSE,
template: {
["startNodeId"] = "{{userId}}",
["direction"] = "OUTGOING",
relationshipTypes: ['FRIENDS_WITH', 'WORKS_WITH'],
["maxDepth"] = "{{maxDepth}}",
nodeFilter: {
labels: ['User'],
where: {
["status"] = "active",
},
},
},
});
// Execute
var network = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "get-user-network",
input: {
["userId"] = "user-123",
["maxDepth"] = 2,
},
});
SHORTEST_PATH - Find Paths
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Find Connection Path',
tag: 'social-graph:find-connection-path',
operation: GraphActionTypes.SHORTEST_PATH,
template: {
startNodeId: '{{userId1}}',
endNodeId: '{{userId2}}',
relationshipTypes: ['FRIENDS_WITH', 'KNOWS'],
maxDepth: 6,
},
});
// Execute
const path = await ductape.graph.execute({
graph: 'social-graph',
action: 'find-connection-path',
input: {
userId1: 'user-123',
userId2: 'user-789',
},
});
if (path.path) {
console.log(`${path.path.length} degrees of separation`);
}
ductape.graph.action.create(Map.of(
"name", "Find Connection Path",
"tag", "social-graph:find-connection-path",
operation: GraphActionTypes.SHORTEST_PATH,
template: Map.of(
"startNodeId", "Map.of(Map.of(userId1))",
"endNodeId", "Map.of(Map.of(userId2))",
relationshipTypes: ['FRIENDS_WITH', 'KNOWS'],
"maxDepth", 6
)
));
// Execute
Map<String, Object> path = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "find-connection-path",
input: Map.of(
"userId1", "user-123",
"userId2", "user-789"
)
));
if (path.path) Map.of(
System.out.println(`$Map.of(path.path.length) degrees of separation`);
)
client.graph.action.create({
"name": "Find Connection Path",
"tag": "social-graph:find-connection-path",
operation: GraphActionTypes.SHORTEST_PATH,
template: {
"startNodeId": "{{userId1}}",
"endNodeId": "{{userId2}}",
relationshipTypes: ['FRIENDS_WITH', 'KNOWS'],
"maxDepth": 6,
},
});
// Execute
path := client.graph.execute({
"graph": "social-graph",
"action": "find-connection-path",
input: {
"userId1": "user-123",
"userId2": "user-789",
},
});
if (path.path) {
fmt.Println(`${path.path.length} degrees of separation`);
}
await ductape.graph.action.create({
["name"] = "Find Connection Path",
["tag"] = "social-graph:find-connection-path",
operation: GraphActionTypes.SHORTEST_PATH,
template: {
["startNodeId"] = "{{userId1}}",
["endNodeId"] = "{{userId2}}",
relationshipTypes: ['FRIENDS_WITH', 'KNOWS'],
["maxDepth"] = 6,
},
});
// Execute
var path = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "find-connection-path",
input: {
["userId1"] = "user-123",
["userId2"] = "user-789",
},
});
if (path.path) {
Console.WriteLine(`${path.path.length} degrees of separation`);
}
Variable Interpolation
Basic Placeholders
Use {{variableName}} syntax:
- TypeScript
- Java
- Go
- .NET
template: {
labels: ['User'],
properties: {
name: '{{name}}',
email: '{{email}}',
age: '{{age}}',
},
}
template: Map.of(
labels: ['User'],
properties: Map.of(
"name", "Map.of(Map.of(name))",
"email", "Map.of(Map.of(email))",
"age", "Map.of(Map.of(age))"
)
)
template: {
labels: ['User'],
properties: {
"name": "{{name}}",
"email": "{{email}}",
"age": "{{age}}",
},
}
template: {
labels: ['User'],
properties: {
["name"] = "{{name}}",
["email"] = "{{email}}",
["age"] = "{{age}}",
},
}
Nested Placeholders
Work in nested structures:
- TypeScript
- Java
- Go
- .NET
template: {
where: {
$AND: {
city: '{{city}}',
age: { $GTE: '{{minAge}}', $LTE: '{{maxAge}}' },
status: { $IN: ['{{status1}}', '{{status2}}'] },
},
},
}
template: Map.of(
where: Map.of(
$AND: Map.of(
"city", "Map.of(Map.of(city))",
age: Map.of( $"GTE", "Map.of(Map.of(minAge))", $"LTE", "Map.of(Map.of(maxAge))" ),
status: Map.of( $IN: ['Map.of(Map.of(status1))', 'Map.of(Map.of(status2))'] )
)
)
)
template: {
where: {
$AND: {
"city": "{{city}}",
age: { $"GTE": "{{minAge}}", $"LTE": "{{maxAge}}" },
status: { $IN: ['{{status1}}', '{{status2}}'] },
},
},
}
template: {
where: {
$AND: {
["city"] = "{{city}}",
age: { $["GTE"] = "{{minAge}}", $["LTE"] = "{{maxAge}}" },
status: { $IN: ['{{status1}}', '{{status2}}'] },
},
},
}
Filter Placeholders
Use in complex filters:
- TypeScript
- Java
- Go
- .NET
template: {
startNodeId: '{{userId}}',
relationshipTypes: ['{{relType1}}', '{{relType2}}'],
nodeFilter: {
where: {
city: '{{city}}',
age: { $GT: '{{minAge}}' },
},
},
}
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
relationshipTypes: ['Map.of(Map.of(relType1))', 'Map.of(Map.of(relType2))'],
nodeFilter: Map.of(
where: Map.of(
"city", "Map.of(Map.of(city))",
age: Map.of( $"GT", "Map.of(Map.of(minAge))" )
)
)
)
template: {
"startNodeId": "{{userId}}",
relationshipTypes: ['{{relType1}}', '{{relType2}}'],
nodeFilter: {
where: {
"city": "{{city}}",
age: { $"GT": "{{minAge}}" },
},
},
}
template: {
["startNodeId"] = "{{userId}}",
relationshipTypes: ['{{relType1}}', '{{relType2}}'],
nodeFilter: {
where: {
["city"] = "{{city}}",
age: { $["GT"] = "{{minAge}}" },
},
},
}
Managing Actions
Create Action
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Action Name',
tag: 'graph-tag:action-tag', // Format: graph:action
operation: GraphActionTypes.FIND_NODES,
description: 'Optional description',
template: {
// Your operation template
},
});
ductape.graph.action.create(Map.of(
"name", "Action Name",
"tag", "graph-tag:action-tag", // Format: graph:action
operation: GraphActionTypes.FIND_NODES,
"description", "Optional description",
template: Map.of(
// Your operation template
)
));
client.graph.action.create({
"name": "Action Name",
"tag": "graph-tag:action-tag", // Format: graph:action
operation: GraphActionTypes.FIND_NODES,
"description": "Optional description",
template: {
// Your operation template
},
});
await ductape.graph.action.create({
["name"] = "Action Name",
["tag"] = "graph-tag:action-tag", // Format: graph:action
operation: GraphActionTypes.FIND_NODES,
["description"] = "Optional description",
template: {
// Your operation template
},
});
Required Fields:
| Field | Type | Description |
|---|---|---|
name | string | Display name for the action |
tag | string | Unique identifier (format: graph:action) |
operation | GraphActionTypes | Action operation (FIND_NODES, etc.) |
template | object | Operation template with placeholders |
Optional Fields:
| Field | Type | Description |
|---|---|---|
description | string | Action description |
filterTemplate | object | Additional filter criteria |
Update Action
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.update({
tag: 'social-graph:find-users',
template: {
// Updated template
labels: ['User'],
where: {
status: '{{status}}',
},
},
});
ductape.graph.action.update(Map.of(
"tag", "social-graph:find-users",
template: Map.of(
// Updated template
labels: ['User'],
where: Map.of(
"status", "Map.of(Map.of(status))"
)
)
));
client.graph.action.update({
"tag": "social-graph:find-users",
template: {
// Updated template
labels: ['User'],
where: {
"status": "{{status}}",
},
},
});
await ductape.graph.action.update({
["tag"] = "social-graph:find-users",
template: {
// Updated template
labels: ['User'],
where: {
["status"] = "{{status}}",
},
},
});
Fetch Action
- TypeScript
- Java
- Go
- .NET
const action = await ductape.graph.action.fetch('social-graph:find-users');
console.log('Action:', action);
Map<String, Object> action = ductape.graph.action.fetch('social-graph:find-users');
System.out.println('Action:', action);
action := client.graph.action.fetch('social-graph:find-users');
fmt.Println('Action:', action);
var action = await ductape.graph.action.fetch('social-graph:find-users');
Console.WriteLine('Action:', action);
List Actions for Graph
- TypeScript
- Java
- Go
- .NET
const actions = await ductape.graph.action.fetchAll('social-graph');
console.log(`Found ${actions.length} actions`);
actions.forEach(action => {
console.log(`${action.tag}: ${action.name} (${action.type})`);
});
Map<String, Object> actions = ductape.graph.action.fetchAll('social-graph');
System.out.println(`Found $Map.of(actions.length) actions`);
actions.forEach(action => Map.of(
System.out.println(`$Map.of(action.tag): $Map.of(action.name) ($Map.of(action.type))`);
));
actions := client.graph.action.fetchAll('social-graph');
fmt.Println(`Found ${actions.length} actions`);
actions.forEach(action => {
fmt.Println(`${action.tag}: ${action.name} (${action.type})`);
});
var actions = await ductape.graph.action.fetchAll('social-graph');
Console.WriteLine(`Found ${actions.length} actions`);
actions.forEach(action => {
Console.WriteLine(`${action.tag}: ${action.name} (${action.type})`);
});
Execute Actions
Basic Execution
- TypeScript
- Java
- Go
- .NET
const result = await ductape.graph.execute({
graph: 'social-graph',
action: 'find-users',
input: {
city: 'New York',
limit: 50,
},
});
Map<String, Object> result = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "find-users",
input: Map.of(
"city", "New York",
"limit", 50
)
));
result := client.graph.execute({
"graph": "social-graph",
"action": "find-users",
input: {
"city": "New York",
"limit": 50,
},
});
var result = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "find-users",
input: {
["city"] = "New York",
["limit"] = 50,
},
});
With Type Safety
- TypeScript
- Java
- Go
- .NET
interface User {
id: string;
name: string;
email: string;
city: string;
}
const users = await ductape.graph.execute<User[]>({
graph: 'social-graph',
action: 'find-users',
input: { city: 'New York' },
});
// TypeScript knows users is User[]
users.forEach(user => {
console.log(`${user.name} - ${user.city}`);
});
interface User Map.of(
id: string;
name: string;
email: string;
city: string;
)
Map<String, Object> users = ductape.graph.execute<User[]>(Map.of(
"graph", "social-graph",
"action", "find-users",
input: Map.of( "city", "New York" )
));
// TypeScript knows users is User[]
users.forEach(user => Map.of(
System.out.println(`$Map.of(user.name) - $Map.of(user.city)`);
));
interface User {
id: string;
name: string;
email: string;
city: string;
}
users := client.graph.execute<User[]>({
"graph": "social-graph",
"action": "find-users",
input: { "city": "New York" },
});
// TypeScript knows users is User[]
users.forEach(user => {
fmt.Println(`${user.name} - ${user.city}`);
});
interface User {
id: string;
name: string;
email: string;
city: string;
}
var users = await ductape.graph.execute<User[]>({
["graph"] = "social-graph",
["action"] = "find-users",
input: { ["city"] = "New York" },
});
// TypeScript knows users is User[]
users.forEach(user => {
Console.WriteLine(`${user.name} - ${user.city}`);
});
Error Handling
- TypeScript
- Java
- Go
- .NET
try {
const users = await ductape.graph.execute({
graph: 'social-graph',
action: 'find-users',
input: { city: 'New York' },
});
} catch (error) {
if (error.message.includes('not found')) {
console.error('Action not found');
} else if (error.message.includes('validation')) {
console.error('Invalid input parameters');
} else {
console.error('Execution failed:', error.message);
}
}
try Map.of(
Map<String, Object> users = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "find-users",
input: Map.of( "city", "New York" )
));
) catch (error) Map.of(
if (error.message.includes('not found')) Map.of(
console.error('Action not found');
) else if (error.message.includes('validation')) Map.of(
console.error('Invalid input parameters');
) else Map.of(
console.error('Execution failed:', error.message);
)
)
try {
users := client.graph.execute({
"graph": "social-graph",
"action": "find-users",
input: { "city": "New York" },
});
} catch (error) {
if (error.message.includes('not found')) {
console.error('Action not found');
} else if (error.message.includes('validation')) {
console.error('Invalid input parameters');
} else {
console.error('Execution failed:', error.message);
}
}
try {
var users = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "find-users",
input: { ["city"] = "New York" },
});
} catch (error) {
if (error.message.includes('not found')) {
console.error('Action not found');
} else if (error.message.includes('validation')) {
console.error('Invalid input parameters');
} else {
console.error('Execution failed:', error.message);
}
}
Common Patterns
Social Network - Friend Suggestions
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Get Friend Suggestions',
tag: 'social-graph:friend-suggestions',
operation: GraphActionTypes.TRAVERSE,
description: 'Find friends of friends who are not already friends',
template: {
startNodeId: '{{userId}}',
relationshipTypes: ['FRIENDS_WITH'],
maxDepth: 2,
nodeFilter: {
labels: ['User'],
where: {
status: 'active',
},
},
},
});
// Execute and filter
const suggestions = await ductape.graph.execute({
graph: 'social-graph',
action: 'friend-suggestions',
input: { userId: 'user-123' },
});
ductape.graph.action.create(Map.of(
"name", "Get Friend Suggestions",
"tag", "social-graph:friend-suggestions",
operation: GraphActionTypes.TRAVERSE,
"description", "Find friends of friends who are not already friends",
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
relationshipTypes: ['FRIENDS_WITH'],
"maxDepth", 2,
nodeFilter: Map.of(
labels: ['User'],
where: Map.of(
"status", "active"
)
)
)
));
// Execute and filter
Map<String, Object> suggestions = ductape.graph.execute(Map.of(
"graph", "social-graph",
"action", "friend-suggestions",
input: Map.of( "userId", "user-123" )
));
client.graph.action.create({
"name": "Get Friend Suggestions",
"tag": "social-graph:friend-suggestions",
operation: GraphActionTypes.TRAVERSE,
"description": "Find friends of friends who are not already friends",
template: {
"startNodeId": "{{userId}}",
relationshipTypes: ['FRIENDS_WITH'],
"maxDepth": 2,
nodeFilter: {
labels: ['User'],
where: {
"status": "active",
},
},
},
});
// Execute and filter
suggestions := client.graph.execute({
"graph": "social-graph",
"action": "friend-suggestions",
input: { "userId": "user-123" },
});
await ductape.graph.action.create({
["name"] = "Get Friend Suggestions",
["tag"] = "social-graph:friend-suggestions",
operation: GraphActionTypes.TRAVERSE,
["description"] = "Find friends of friends who are not already friends",
template: {
["startNodeId"] = "{{userId}}",
relationshipTypes: ['FRIENDS_WITH'],
["maxDepth"] = 2,
nodeFilter: {
labels: ['User'],
where: {
["status"] = "active",
},
},
},
});
// Execute and filter
var suggestions = await ductape.graph.execute({
["graph"] = "social-graph",
["action"] = "friend-suggestions",
input: { ["userId"] = "user-123" },
});
E-Commerce - Product Recommendations
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Get Product Recommendations',
tag: 'commerce-graph:product-recommendations',
operation: GraphActionTypes.TRAVERSE,
template: {
startNodeId: '{{userId}}',
direction: 'OUTGOING',
relationshipTypes: ['PURCHASED', 'VIEWED'],
maxDepth: 3,
nodeFilter: {
labels: ['Product'],
where: {
in_stock: true,
category: '{{category}}',
},
},
limit: '{{limit}}',
},
});
ductape.graph.action.create(Map.of(
"name", "Get Product Recommendations",
"tag", "commerce-graph:product-recommendations",
operation: GraphActionTypes.TRAVERSE,
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
"direction", "OUTGOING",
relationshipTypes: ['PURCHASED', 'VIEWED'],
"maxDepth", 3,
nodeFilter: Map.of(
labels: ['Product'],
where: Map.of(
"in_stock", true,
"category", "Map.of(Map.of(category))"
)
),
"limit", "Map.of(Map.of(limit))"
)
));
client.graph.action.create({
"name": "Get Product Recommendations",
"tag": "commerce-graph:product-recommendations",
operation: GraphActionTypes.TRAVERSE,
template: {
"startNodeId": "{{userId}}",
"direction": "OUTGOING",
relationshipTypes: ['PURCHASED', 'VIEWED'],
"maxDepth": 3,
nodeFilter: {
labels: ['Product'],
where: {
"in_stock": true,
"category": "{{category}}",
},
},
"limit": "{{limit}}",
},
});
await ductape.graph.action.create({
["name"] = "Get Product Recommendations",
["tag"] = "commerce-graph:product-recommendations",
operation: GraphActionTypes.TRAVERSE,
template: {
["startNodeId"] = "{{userId}}",
["direction"] = "OUTGOING",
relationshipTypes: ['PURCHASED', 'VIEWED'],
["maxDepth"] = 3,
nodeFilter: {
labels: ['Product'],
where: {
["in_stock"] = true,
["category"] = "{{category}}",
},
},
["limit"] = "{{limit}}",
},
});
Knowledge Graph - Related Articles
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Find Related Articles',
tag: 'knowledge-graph:related-articles',
operation: GraphActionTypes.TRAVERSE,
template: {
startNodeId: '{{articleId}}',
direction: 'BOTH',
relationshipTypes: ['HAS_TOPIC', 'CITES', 'WRITTEN_BY'],
maxDepth: 2,
nodeFilter: {
labels: ['Article'],
where: {
status: 'published',
},
},
limit: '{{limit}}',
},
});
ductape.graph.action.create(Map.of(
"name", "Find Related Articles",
"tag", "knowledge-graph:related-articles",
operation: GraphActionTypes.TRAVERSE,
template: Map.of(
"startNodeId", "Map.of(Map.of(articleId))",
"direction", "BOTH",
relationshipTypes: ['HAS_TOPIC', 'CITES', 'WRITTEN_BY'],
"maxDepth", 2,
nodeFilter: Map.of(
labels: ['Article'],
where: Map.of(
"status", "published"
)
),
"limit", "Map.of(Map.of(limit))"
)
));
client.graph.action.create({
"name": "Find Related Articles",
"tag": "knowledge-graph:related-articles",
operation: GraphActionTypes.TRAVERSE,
template: {
"startNodeId": "{{articleId}}",
"direction": "BOTH",
relationshipTypes: ['HAS_TOPIC', 'CITES', 'WRITTEN_BY'],
"maxDepth": 2,
nodeFilter: {
labels: ['Article'],
where: {
"status": "published",
},
},
"limit": "{{limit}}",
},
});
await ductape.graph.action.create({
["name"] = "Find Related Articles",
["tag"] = "knowledge-graph:related-articles",
operation: GraphActionTypes.TRAVERSE,
template: {
["startNodeId"] = "{{articleId}}",
["direction"] = "BOTH",
relationshipTypes: ['HAS_TOPIC', 'CITES', 'WRITTEN_BY'],
["maxDepth"] = 2,
nodeFilter: {
labels: ['Article'],
where: {
["status"] = "published",
},
},
["limit"] = "{{limit}}",
},
});
Organization - Reporting Chain
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Get Reporting Chain',
tag: 'org-graph:reporting-chain',
operation: GraphActionTypes.TRAVERSE,
template: {
startNodeId: '{{employeeId}}',
direction: 'OUTGOING',
relationshipTypes: ['REPORTS_TO'],
maxDepth: 10,
},
});
ductape.graph.action.create(Map.of(
"name", "Get Reporting Chain",
"tag", "org-graph:reporting-chain",
operation: GraphActionTypes.TRAVERSE,
template: Map.of(
"startNodeId", "Map.of(Map.of(employeeId))",
"direction", "OUTGOING",
relationshipTypes: ['REPORTS_TO'],
"maxDepth", 10
)
));
client.graph.action.create({
"name": "Get Reporting Chain",
"tag": "org-graph:reporting-chain",
operation: GraphActionTypes.TRAVERSE,
template: {
"startNodeId": "{{employeeId}}",
"direction": "OUTGOING",
relationshipTypes: ['REPORTS_TO'],
"maxDepth": 10,
},
});
await ductape.graph.action.create({
["name"] = "Get Reporting Chain",
["tag"] = "org-graph:reporting-chain",
operation: GraphActionTypes.TRAVERSE,
template: {
["startNodeId"] = "{{employeeId}}",
["direction"] = "OUTGOING",
relationshipTypes: ['REPORTS_TO'],
["maxDepth"] = 10,
},
});
User Activity - Recent Interactions
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Get Recent Interactions',
tag: 'social-graph:recent-interactions',
operation: GraphActionTypes.FIND_RELATIONSHIPS,
template: {
startNodeId: '{{userId}}',
type: ['LIKED', 'COMMENTED', 'SHARED'],
direction: 'OUTGOING',
where: {
created_at: { $GTE: '{{sinceDate}}' },
},
limit: '{{limit}}',
},
});
ductape.graph.action.create(Map.of(
"name", "Get Recent Interactions",
"tag", "social-graph:recent-interactions",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
type: ['LIKED', 'COMMENTED', 'SHARED'],
"direction", "OUTGOING",
where: Map.of(
created_at: Map.of( $"GTE", "Map.of(Map.of(sinceDate))" )
),
"limit", "Map.of(Map.of(limit))"
)
));
client.graph.action.create({
"name": "Get Recent Interactions",
"tag": "social-graph:recent-interactions",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
template: {
"startNodeId": "{{userId}}",
type: ['LIKED', 'COMMENTED', 'SHARED'],
"direction": "OUTGOING",
where: {
created_at: { $"GTE": "{{sinceDate}}" },
},
"limit": "{{limit}}",
},
});
await ductape.graph.action.create({
["name"] = "Get Recent Interactions",
["tag"] = "social-graph:recent-interactions",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
template: {
["startNodeId"] = "{{userId}}",
type: ['LIKED', 'COMMENTED', 'SHARED'],
["direction"] = "OUTGOING",
where: {
created_at: { $["GTE"] = "{{sinceDate}}" },
},
["limit"] = "{{limit}}",
},
});
Best Practices
1. Use Descriptive Names
- TypeScript
- Java
- Go
- .NET
// Good - clear purpose
await ductape.graph.action.create({
name: 'Find Mutual Friends Between Users',
tag: 'social-graph:find-mutual-friends',
// ...
});
// ❌ Avoid - vague
await ductape.graph.action.create({
name: 'Find Friends',
tag: 'social-graph:friends',
// ...
});
// Good - clear purpose
ductape.graph.action.create(Map.of(
"name", "Find Mutual Friends Between Users",
"tag", "social-graph:find-mutual-friends",
// ...
));
// ❌ Avoid - vague
ductape.graph.action.create(Map.of(
"name", "Find Friends",
"tag", "social-graph:friends",
// ...
));
// Good - clear purpose
client.graph.action.create({
"name": "Find Mutual Friends Between Users",
"tag": "social-graph:find-mutual-friends",
// ...
});
// ❌ Avoid - vague
client.graph.action.create({
"name": "Find Friends",
"tag": "social-graph:friends",
// ...
});
// Good - clear purpose
await ductape.graph.action.create({
["name"] = "Find Mutual Friends Between Users",
["tag"] = "social-graph:find-mutual-friends",
// ...
});
// ❌ Avoid - vague
await ductape.graph.action.create({
["name"] = "Find Friends",
["tag"] = "social-graph:friends",
// ...
});
2. Add Descriptions
- TypeScript
- Java
- Go
- .NET
await ductape.graph.action.create({
name: 'Get User Influence Score',
tag: 'social-graph:user-influence',
description: 'Calculate influence score based on follower count, engagement, and network reach',
// ...
});
ductape.graph.action.create(Map.of(
"name", "Get User Influence Score",
"tag", "social-graph:user-influence",
"description", "Calculate influence score based on follower count, engagement, and network reach",
// ...
));
client.graph.action.create({
"name": "Get User Influence Score",
"tag": "social-graph:user-influence",
"description": "Calculate influence score based on follower count, engagement, and network reach",
// ...
});
await ductape.graph.action.create({
["name"] = "Get User Influence Score",
["tag"] = "social-graph:user-influence",
["description"] = "Calculate influence score based on follower count, engagement, and network reach",
// ...
});
3. Limit Traversal Depth
- TypeScript
- Java
- Go
- .NET
// Good - reasonable depth
template: {
startNodeId: '{{userId}}',
maxDepth: 3, // Controlled exploration
limit: '{{limit}}',
}
// ❌ Dangerous - unbounded
template: {
startNodeId: '{{userId}}',
maxDepth: 10, // Could explore millions
// No limit!
}
// Good - reasonable depth
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
"maxDepth", 3, // Controlled exploration
"limit", "Map.of(Map.of(limit))"
)
// ❌ Dangerous - unbounded
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
"maxDepth", 10, // Could explore millions
// No limit!
)
// Good - reasonable depth
template: {
"startNodeId": "{{userId}}",
"maxDepth": 3, // Controlled exploration
"limit": "{{limit}}",
}
// ❌ Dangerous - unbounded
template: {
"startNodeId": "{{userId}}",
"maxDepth": 10, // Could explore millions
// No limit!
}
// Good - reasonable depth
template: {
["startNodeId"] = "{{userId}}",
["maxDepth"] = 3, // Controlled exploration
["limit"] = "{{limit}}",
}
// ❌ Dangerous - unbounded
template: {
["startNodeId"] = "{{userId}}",
["maxDepth"] = 10, // Could explore millions
// No limit!
}
4. Use Specific Relationship Types
- TypeScript
- Java
- Go
- .NET
// Good - specific types
template: {
relationshipTypes: ['FRIENDS_WITH', 'WORKS_WITH'],
}
// ❌ Slower - all relationships
template: {
// No relationshipTypes specified
}
// Good - specific types
template: Map.of(
relationshipTypes: ['FRIENDS_WITH', 'WORKS_WITH']
)
// ❌ Slower - all relationships
template: Map.of(
// No relationshipTypes specified
)
// Good - specific types
template: {
relationshipTypes: ['FRIENDS_WITH', 'WORKS_WITH'],
}
// ❌ Slower - all relationships
template: {
// No relationshipTypes specified
}
// Good - specific types
template: {
relationshipTypes: ['FRIENDS_WITH', 'WORKS_WITH'],
}
// ❌ Slower - all relationships
template: {
// No relationshipTypes specified
}
5. Filter Early
- TypeScript
- Java
- Go
- .NET
// Good - filter in query
template: {
startNodeId: '{{userId}}',
relationshipTypes: ['FRIENDS_WITH'],
nodeFilter: {
where: {
status: 'active',
age: { $GTE: 18 },
},
},
}
// ❌ Bad - fetch all, filter later
template: {
startNodeId: '{{userId}}',
relationshipTypes: ['FRIENDS_WITH'],
// No filtering - filter in code
}
// Good - filter in query
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
relationshipTypes: ['FRIENDS_WITH'],
nodeFilter: Map.of(
where: Map.of(
"status", "active",
age: Map.of( $"GTE", 18 )
)
)
)
// ❌ Bad - fetch all, filter later
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
relationshipTypes: ['FRIENDS_WITH'],
// No filtering - filter in code
)
// Good - filter in query
template: {
"startNodeId": "{{userId}}",
relationshipTypes: ['FRIENDS_WITH'],
nodeFilter: {
where: {
"status": "active",
age: { $"GTE": 18 },
},
},
}
// ❌ Bad - fetch all, filter later
template: {
"startNodeId": "{{userId}}",
relationshipTypes: ['FRIENDS_WITH'],
// No filtering - filter in code
}
// Good - filter in query
template: {
["startNodeId"] = "{{userId}}",
relationshipTypes: ['FRIENDS_WITH'],
nodeFilter: {
where: {
["status"] = "active",
age: { $["GTE"] = 18 },
},
},
}
// ❌ Bad - fetch all, filter later
template: {
["startNodeId"] = "{{userId}}",
relationshipTypes: ['FRIENDS_WITH'],
// No filtering - filter in code
}
6. Keep Actions Focused
- TypeScript
- Java
- Go
- .NET
// Good - single purpose
await ductape.graph.action.create({
name: 'Get User Friends',
operation: GraphActionTypes.FIND_RELATIONSHIPS,
// ... only fetches friendships
});
// ❌ Avoid - too complex
await ductape.graph.action.create({
name: 'Get Complete User Social Graph',
// ... tries to fetch everything
});
// Good - single purpose
ductape.graph.action.create(Map.of(
"name", "Get User Friends",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
// ... only fetches friendships
));
// ❌ Avoid - too complex
ductape.graph.action.create(Map.of(
"name", "Get Complete User Social Graph",
// ... tries to fetch everything
));
// Good - single purpose
client.graph.action.create({
"name": "Get User Friends",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
// ... only fetches friendships
});
// ❌ Avoid - too complex
client.graph.action.create({
"name": "Get Complete User Social Graph",
// ... tries to fetch everything
});
// Good - single purpose
await ductape.graph.action.create({
["name"] = "Get User Friends",
operation: GraphActionTypes.FIND_RELATIONSHIPS,
// ... only fetches friendships
});
// ❌ Avoid - too complex
await ductape.graph.action.create({
["name"] = "Get Complete User Social Graph",
// ... tries to fetch everything
});
7. Use Consistent Naming
- TypeScript
- Java
- Go
- .NET
// Good - consistent pattern
'social-graph:get-user-friends'
'social-graph:create-friendship'
'social-graph:update-friendship'
'social-graph:delete-friendship'
// ❌ Avoid - inconsistent
'social-graph:getUserFriends'
'social-graph:friendship-create'
'social-graph:UpdateFriend'
'social-graph:del_friend'
// Good - consistent pattern
'social-graph:get-user-friends'
'social-graph:create-friendship'
'social-graph:update-friendship'
'social-graph:delete-friendship'
// ❌ Avoid - inconsistent
'social-graph:getUserFriends'
'social-graph:friendship-create'
'social-graph:UpdateFriend'
'social-graph:del_friend'
// Good - consistent pattern
'social-graph:get-user-friends'
'social-graph:create-friendship'
'social-graph:update-friendship'
'social-graph:delete-friendship'
// ❌ Avoid - inconsistent
'social-graph:getUserFriends'
'social-graph:friendship-create'
'social-graph:UpdateFriend'
'social-graph:del_friend'
// Good - consistent pattern
'social-graph:get-user-friends'
'social-graph:create-friendship'
'social-graph:update-friendship'
'social-graph:delete-friendship'
// ❌ Avoid - inconsistent
'social-graph:getUserFriends'
'social-graph:friendship-create'
'social-graph:UpdateFriend'
'social-graph:del_friend'
8. Test Actions
- TypeScript
- Java
- Go
- .NET
describe('find-user-friends', () => {
it('should fetch user friends', async () => {
const friends = await ductape.graph.execute({
graph: 'test-graph',
action: 'find-user-friends',
input: { userId: 'test-user-1', limit: 10 },
});
expect(friends).toBeDefined();
expect(friends.length).toBeLessThanOrEqual(10);
});
it('should respect direction', async () => {
const outgoing = await ductape.graph.execute({
graph: 'test-graph',
action: 'find-user-friends',
input: { userId: 'test-user-1', direction: 'OUTGOING' },
});
expect(outgoing.every(f => f.startNodeId === 'test-user-1')).toBe(true);
});
});
describe('find-user-friends', () => Map.of(
it('should fetch user friends', async () => Map.of(
Map<String, Object> friends = ductape.graph.execute(Map.of(
"graph", "test-graph",
"action", "find-user-friends",
input: Map.of( "userId", "test-user-1", "limit", 10 )
));
expect(friends).toBeDefined();
expect(friends.length).toBeLessThanOrEqual(10);
));
it('should respect direction', async () => Map.of(
Map<String, Object> outgoing = ductape.graph.execute(Map.of(
"graph", "test-graph",
"action", "find-user-friends",
input: Map.of( "userId", "test-user-1", "direction", "OUTGOING" )
));
expect(outgoing.every(f => f.startNodeId === 'test-user-1')).toBe(true);
));
));
describe('find-user-friends', () => {
it('should fetch user friends', async () => {
friends := client.graph.execute({
"graph": "test-graph",
"action": "find-user-friends",
input: { "userId": "test-user-1", "limit": 10 },
});
expect(friends).toBeDefined();
expect(friends.length).toBeLessThanOrEqual(10);
});
it('should respect direction', async () => {
outgoing := client.graph.execute({
"graph": "test-graph",
"action": "find-user-friends",
input: { "userId": "test-user-1", "direction": "OUTGOING" },
});
expect(outgoing.every(f => f.startNodeId === 'test-user-1')).toBe(true);
});
});
describe('find-user-friends', () => {
it('should fetch user friends', async () => {
var friends = await ductape.graph.execute({
["graph"] = "test-graph",
["action"] = "find-user-friends",
input: { ["userId"] = "test-user-1", ["limit"] = 10 },
});
expect(friends).toBeDefined();
expect(friends.length).toBeLessThanOrEqual(10);
});
it('should respect direction', async () => {
var outgoing = await ductape.graph.execute({
["graph"] = "test-graph",
["action"] = "find-user-friends",
input: { ["userId"] = "test-user-1", ["direction"] = "OUTGOING" },
});
expect(outgoing.every(f => f.startNodeId === 'test-user-1')).toBe(true);
});
});
Performance Tips
1. Use Indexes
Ensure indexed properties are used in filters:
- TypeScript
- Java
- Go
- .NET
// Make sure 'status' is indexed on User label
template: {
labels: ['User'],
where: {
status: '{{status}}', // Uses index
},
}
// Make sure 'status' is indexed on User label
template: Map.of(
labels: ['User'],
where: Map.of(
"status", "Map.of(Map.of(status))", // Uses index
)
)
// Make sure 'status' is indexed on User label
template: {
labels: ['User'],
where: {
"status": "{{status}}", // Uses index
},
}
// Make sure 'status' is indexed on User label
template: {
labels: ['User'],
where: {
["status"] = "{{status}}", // Uses index
},
}
2. Limit Result Sets
Always include limits:
- TypeScript
- Java
- Go
- .NET
template: {
startNodeId: '{{userId}}',
relationshipTypes: ['FRIENDS_WITH'],
limit: '{{limit}}', // Prevent unbounded results
}
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
relationshipTypes: ['FRIENDS_WITH'],
"limit", "Map.of(Map.of(limit))", // Prevent unbounded results
)
template: {
"startNodeId": "{{userId}}",
relationshipTypes: ['FRIENDS_WITH'],
"limit": "{{limit}}", // Prevent unbounded results
}
template: {
["startNodeId"] = "{{userId}}",
relationshipTypes: ['FRIENDS_WITH'],
["limit"] = "{{limit}}", // Prevent unbounded results
}
3. Use Direction
Specify direction to reduce search space:
- TypeScript
- Java
- Go
- .NET
template: {
startNodeId: '{{userId}}',
direction: 'OUTGOING', // Only follow outgoing relationships
relationshipTypes: ['FOLLOWS'],
}
template: Map.of(
"startNodeId", "Map.of(Map.of(userId))",
"direction", "OUTGOING", // Only follow outgoing relationships
relationshipTypes: ['FOLLOWS']
)
template: {
"startNodeId": "{{userId}}",
"direction": "OUTGOING", // Only follow outgoing relationships
relationshipTypes: ['FOLLOWS'],
}
template: {
["startNodeId"] = "{{userId}}",
["direction"] = "OUTGOING", // Only follow outgoing relationships
relationshipTypes: ['FOLLOWS'],
}
4. Filter at Query Level
Filter in the query, not in code:
- TypeScript
- Java
- Go
- .NET
// Good - filtered in query
template: {
nodeFilter: {
where: { status: 'active' },
},
}
// ❌ Bad - fetch all, filter in code
// (Requires transferring and processing more data)
// Good - filtered in query
template: Map.of(
nodeFilter: Map.of(
where: Map.of( "status", "active" )
)
)
// ❌ Bad - fetch all, filter in code
// (Requires transferring and processing more data)
// Good - filtered in query
template: {
nodeFilter: {
where: { "status": "active" },
},
}
// ❌ Bad - fetch all, filter in code
// (Requires transferring and processing more data)
// Good - filtered in query
template: {
nodeFilter: {
where: { ["status"] = "active" },
},
}
// ❌ Bad - fetch all, filter in code
// (Requires transferring and processing more data)
Next Steps
- Traversals & Pathfinding - Graph exploration patterns
- Nodes - Working with graph nodes
- Relationships - Managing connections
- Best Practices - Optimization patterns
See Also
- Graph Overview - Full API reference
- Indexes & Constraints - Performance optimization
- Transactions - Data consistency