# Graph_D Tutorial: Building a Social Network
This tutorial walks through building a complete social network application using Graph_D, demonstrating all major features.
## What We'll Build
A social network with:
- **Users** with profiles and preferences
- **Friendships** and social connections
- **Posts** with likes and comments
- **Groups** and memberships
- **Analytics** and reporting features
## Prerequisites
- Rust 1.70+ installed
- Basic familiarity with Rust syntax
- Understanding of graph concepts (nodes, edges)
## Setup
Add Graph_D to your `Cargo.toml`:
```toml
[dependencies]
graph_d = "0.1.0"
serde_json = "1.0"
```
## Chapter 1: User Management
Let's start by creating user accounts and profiles.
```rust
use graph_d::{Graph, Result};
use graph_d::query::QueryBuilder;
use serde_json::json;
use std::collections::HashMap;
fn main() -> Result<()> {
let mut social_network = Graph::new()?;
// Create user profiles
let alice_id = create_user(&mut social_network, UserProfile {
username: "alice_dev".to_string(),
email: "alice@example.com".to_string(),
display_name: "Alice Johnson".to_string(),
bio: "Software engineer passionate about Rust".to_string(),
location: "San Francisco, CA".to_string(),
interests: vec!["rust", "programming", "hiking", "photography"],
privacy_settings: PrivacySettings {
profile_public: true,
show_email: false,
allow_friend_requests: true,
},
})?;
let bob_id = create_user(&mut social_network, UserProfile {
username: "bob_designer".to_string(),
email: "bob@example.com".to_string(),
display_name: "Bob Smith".to_string(),
bio: "UX Designer crafting beautiful experiences".to_string(),
location: "Seattle, WA".to_string(),
interests: vec!["design", "art", "music", "travel"],
privacy_settings: PrivacySettings {
profile_public: true,
show_email: false,
allow_friend_requests: true,
},
})?;
println!("Created users: Alice ({}), Bob ({})", alice_id, bob_id);
Ok(())
}
struct UserProfile {
username: String,
email: String,
display_name: String,
bio: String,
location: String,
interests: Vec<&'static str>,
privacy_settings: PrivacySettings,
}
struct PrivacySettings {
profile_public: bool,
show_email: bool,
allow_friend_requests: bool,
}
fn create_user(graph: &mut Graph, profile: UserProfile) -> Result<u64> {
let user_id = graph.create_node([
("type".to_string(), json!("user")),
("username".to_string(), json!(profile.username)),
("email".to_string(), json!(profile.email)),
("display_name".to_string(), json!(profile.display_name)),
("bio".to_string(), json!(profile.bio)),
("location".to_string(), json!(profile.location)),
("interests".to_string(), json!(profile.interests)),
("privacy_settings".to_string(), json!({
"profile_public": profile.privacy_settings.profile_public,
"show_email": profile.privacy_settings.show_email,
"allow_friend_requests": profile.privacy_settings.allow_friend_requests
})),
("created_at".to_string(), json!("2024-01-15T10:00:00Z")),
("last_active".to_string(), json!("2024-01-19T15:30:00Z")),
("stats".to_string(), json!({
"posts_count": 0,
"friends_count": 0,
"groups_count": 0
})),
].into())?;
Ok(user_id)
}
```
## Chapter 2: Building Social Connections
Now let's add friendship functionality.
```rust
fn create_friendship(
graph: &mut Graph,
user1_id: u64,
user2_id: u64
) -> Result<u64> {
// Create friendship relationship
let friendship_id = graph.create_relationship(
user1_id,
user2_id,
"FRIENDS_WITH".to_string(),
[
("status".to_string(), json!("confirmed")),
("created_at".to_string(), json!("2024-01-16T12:00:00Z")),
("interaction_score".to_string(), json!(0.8)),
].into(),
)?;
// Update friend counts (in a real app, this would be done atomically)
update_friend_count(graph, user1_id)?;
update_friend_count(graph, user2_id)?;
Ok(friendship_id)
}
fn update_friend_count(graph: &mut Graph, user_id: u64) -> Result<()> {
// Get current friend count
let friends = QueryBuilder::from_node(graph, user_id)
.outgoing("FRIENDS_WITH")?
.count();
// In a real implementation, you'd update the node's stats
// This is simplified for the tutorial
println!("User {} now has {} friends", user_id, friends);
Ok(())
}
// Usage
fn demo_friendships(graph: &mut Graph) -> Result<()> {
// Assume we have user IDs from previous chapter
let alice_id = 1;
let bob_id = 2;
let charlie_id = 3; // Created similarly
// Create friendships
create_friendship(graph, alice_id, bob_id)?;
create_friendship(graph, bob_id, charlie_id)?;
// Find mutual friends
let mutual_friends = find_mutual_friends(graph, alice_id, charlie_id)?;
println!("Mutual friends between Alice and Charlie: {}", mutual_friends.len());
Ok(())
}
fn find_mutual_friends(graph: &Graph, user1_id: u64, user2_id: u64) -> Result<Vec<u64>> {
// Get friends of user1
let user1_friends: std::collections::HashSet<u64> = QueryBuilder::from_node(graph, user1_id)
.outgoing("FRIENDS_WITH")?
.node_ids()
.iter()
.copied()
.collect();
// Get friends of user2
let user2_friends: std::collections::HashSet<u64> = QueryBuilder::from_node(graph, user2_id)
.outgoing("FRIENDS_WITH")?
.node_ids()
.iter()
.copied()
.collect();
// Find intersection
let mutual: Vec<u64> = user1_friends
.intersection(&user2_friends)
.copied()
.collect();
Ok(mutual)
}
```
## Chapter 3: Content Creation (Posts)
Let's add posts and content sharing.
```rust
fn create_post(
graph: &mut Graph,
author_id: u64,
content: &str,
post_type: &str,
) -> Result<u64> {
let post_id = graph.create_node([
("type".to_string(), json!("post")),
("content".to_string(), json!(content)),
("post_type".to_string(), json!(post_type)), // "text", "image", "link"
("author_id".to_string(), json!(author_id)),
("created_at".to_string(), json!("2024-01-17T09:30:00Z")),
("stats".to_string(), json!({
"likes": 0,
"comments": 0,
"shares": 0
})),
("visibility".to_string(), json!("public")), // "public", "friends", "private"
].into())?;
// Create AUTHORED relationship
graph.create_relationship(
author_id,
post_id,
"AUTHORED".to_string(),
[("created_at".to_string(), json!("2024-01-17T09:30:00Z"))].into(),
)?;
Ok(post_id)
}
fn like_post(graph: &mut Graph, user_id: u64, post_id: u64) -> Result<()> {
// Create LIKES relationship
graph.create_relationship(
user_id,
post_id,
"LIKES".to_string(),
[
("created_at".to_string(), json!("2024-01-17T14:45:00Z")),
("reaction_type".to_string(), json!("like")), // Could be "like", "love", "laugh", etc.
].into(),
)?;
Ok(())
}
fn create_comment(
graph: &mut Graph,
user_id: u64,
post_id: u64,
comment_text: &str,
) -> Result<u64> {
let comment_id = graph.create_node([
("type".to_string(), json!("comment")),
("content".to_string(), json!(comment_text)),
("author_id".to_string(), json!(user_id)),
("created_at".to_string(), json!("2024-01-17T16:20:00Z")),
].into())?;
// Link comment to post
graph.create_relationship(
comment_id,
post_id,
"COMMENT_ON".to_string(),
HashMap::new(),
)?;
// Link user to comment
graph.create_relationship(
user_id,
comment_id,
"AUTHORED".to_string(),
HashMap::new(),
)?;
Ok(comment_id)
}
```
## Chapter 4: Groups and Communities
Add group functionality for communities.
```rust
fn create_group(
graph: &mut Graph,
creator_id: u64,
name: &str,
description: &str,
group_type: &str,
) -> Result<u64> {
let group_id = graph.create_node([
("type".to_string(), json!("group")),
("name".to_string(), json!(name)),
("description".to_string(), json!(description)),
("group_type".to_string(), json!(group_type)), // "public", "private", "secret"
("creator_id".to_string(), json!(creator_id)),
("created_at".to_string(), json!("2024-01-18T11:00:00Z")),
("stats".to_string(), json!({
"member_count": 1,
"post_count": 0,
"activity_score": 0.0
})),
("settings".to_string(), json!({
"allow_posts": true,
"moderated": false,
"invite_only": false
})),
].into())?;
// Creator becomes admin
graph.create_relationship(
creator_id,
group_id,
"MEMBER_OF".to_string(),
[
("role".to_string(), json!("admin")),
("joined_at".to_string(), json!("2024-01-18T11:00:00Z")),
("permissions".to_string(), json!(["post", "invite", "moderate", "admin"])),
].into(),
)?;
Ok(group_id)
}
fn join_group(
graph: &mut Graph,
user_id: u64,
group_id: u64,
role: &str,
) -> Result<()> {
graph.create_relationship(
user_id,
group_id,
"MEMBER_OF".to_string(),
[
("role".to_string(), json!(role)), // "member", "moderator", "admin"
("joined_at".to_string(), json!("2024-01-18T15:30:00Z")),
("permissions".to_string(), json!(["post"])),
].into(),
)?;
Ok(())
}
```
## Chapter 5: Advanced Analytics
Now let's add analytics and insights.
```rust
use graph_d::query::{AggregateFunction, SortCriteria, Pagination};
fn analyze_user_engagement(graph: &Graph) -> Result<()> {
// Find all users
let all_users: Vec<u64> = QueryBuilder::new(graph, (1..=100).collect())
.filter_by_property("type", &json!("user"))?
.node_ids()
.to_vec();
println!("=== User Engagement Analytics ===");
// Most active users by post count
let query = QueryBuilder::new(graph, all_users.clone());
// Get user statistics
let stats = query.statistics("stats.posts_count")?;
println!("Post Statistics:");
println!(" Average posts per user: {:.1}", stats.mean);
println!(" Most active user has: {:.0} posts", stats.max);
// Top users by friend count
let top_connected = query.sorted_page(
vec![SortCriteria::desc("stats.friends_count")],
Pagination::new(0, 5),
)?;
println!("\nTop 5 Most Connected Users:");
for (i, user) in top_connected.items.iter().enumerate() {
let name = user.get_property("display_name")
.and_then(|v| v.as_str())
.unwrap_or("Unknown");
let friend_count = user.get_property("stats")
.and_then(|v| v.get("friends_count"))
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
println!(" {}. {}: {:.0} friends", i + 1, name, friend_count);
}
Ok(())
}
fn analyze_content_trends(graph: &Graph) -> Result<()> {
// Find all posts
let all_posts: Vec<u64> = QueryBuilder::new(graph, (1..=1000).collect())
.filter_by_property("type", &json!("post"))?
.node_ids()
.to_vec();
println!("\n=== Content Trends ===");
let query = QueryBuilder::new(graph, all_posts);
// Group posts by type
let post_types = query.aggregate(AggregateFunction::GroupBy("post_type".to_string()))?;
if let graph_d::query::AggregateResult::Groups(groups) = post_types {
println!("Posts by type:");
for (post_type, posts) in groups {
println!(" {}: {} posts", post_type, posts.len());
}
}
// Most liked posts
let popular_posts = query.sorted_page(
vec![SortCriteria::desc("stats.likes")],
Pagination::new(0, 3),
)?;
println!("\nMost Popular Posts:");
for (i, post) in popular_posts.items.iter().enumerate() {
let content = post.get_property("content")
.and_then(|v| v.as_str())
.unwrap_or("No content");
let likes = post.get_property("stats")
.and_then(|v| v.get("likes"))
.and_then(|v| v.as_f64())
.unwrap_or(0.0);
// Truncate long content
let display_content = if content.len() > 50 {
format!("{}...", &content[..47])
} else {
content.to_string()
};
println!(" {}. \"{}\" - {:.0} likes", i + 1, display_content, likes);
}
Ok(())
}
fn recommend_friends(graph: &Graph, user_id: u64) -> Result<Vec<u64>> {
// Friend recommendation algorithm:
// 1. Find friends of friends who aren't already friends
// 2. Score by mutual connections and shared interests
// Get current friends
let current_friends: std::collections::HashSet<u64> = QueryBuilder::from_node(graph, user_id)
.outgoing("FRIENDS_WITH")?
.node_ids()
.iter()
.copied()
.collect();
// Get friends of friends
let friends_of_friends: Vec<u64> = QueryBuilder::from_node(graph, user_id)
.outgoing("FRIENDS_WITH")?
.outgoing("FRIENDS_WITH")?
.node_ids()
.iter()
.copied()
.filter(|&id| id != user_id && !current_friends.contains(&id))
.collect();
// Score and sort by mutual connections
let mut recommendations: Vec<(u64, usize)> = friends_of_friends
.into_iter()
.map(|candidate_id| {
let mutual_count = find_mutual_friends(graph, user_id, candidate_id)
.unwrap_or_default()
.len();
(candidate_id, mutual_count)
})
.collect();
recommendations.sort_by(|a, b| b.1.cmp(&a.1));
Ok(recommendations.into_iter().take(5).map(|(id, _)| id).collect())
}
```
## Chapter 6: Performance Optimization
Best practices for optimal performance.
```rust
use graph_d::transaction::{TransactionManager, IsolationLevel, LockableResource};
fn batch_create_users(graph: &mut Graph, users: Vec<UserProfile>) -> Result<Vec<u64>> {
let tx_manager = TransactionManager::new(IsolationLevel::ReadCommitted);
let mut tx = tx_manager.begin_concurrent();
// Acquire a write lock on the schema for batch operations
tx.write_lock(LockableResource::Schema)?;
let mut user_ids = Vec::new();
for user in users {
let user_id = create_user(graph, user)?;
user_ids.push(user_id);
// Process in batches to avoid holding locks too long
if user_ids.len() % 100 == 0 {
tx.commit()?;
tx = tx_manager.begin_concurrent();
tx.write_lock(LockableResource::Schema)?;
}
}
tx.commit()?;
Ok(user_ids)
}
fn efficient_friend_suggestions(graph: &Graph, user_id: u64, limit: usize) -> Result<Vec<u64>> {
// Use pagination for large result sets
let friends_of_friends = QueryBuilder::from_node(graph, user_id)
.outgoing("FRIENDS_WITH")?
.outgoing("FRIENDS_WITH")?
.paginate(Pagination::new(0, limit * 2))? // Get more than needed for filtering
.nodes()?;
// Filter and limit
let suggestions: Vec<u64> = friends_of_friends
.into_iter()
.filter(|node| node.id != user_id) // Not self
.map(|node| node.id)
.take(limit)
.collect();
Ok(suggestions)
}
```
## Chapter 7: Testing Your Social Network
```rust
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_user_creation() -> Result<()> {
let mut graph = Graph::new()?;
let user_id = create_user(&mut graph, UserProfile {
username: "test_user".to_string(),
email: "test@example.com".to_string(),
display_name: "Test User".to_string(),
bio: "Test bio".to_string(),
location: "Test City".to_string(),
interests: vec!["testing"],
privacy_settings: PrivacySettings {
profile_public: true,
show_email: false,
allow_friend_requests: true,
},
})?;
let user = graph.get_node(user_id)?.unwrap();
assert_eq!(user.get_property("username").unwrap(), &json!("test_user"));
Ok(())
}
#[test]
fn test_friendship_creation() -> Result<()> {
let mut graph = Graph::new()?;
// Create test users
let alice_id = 1; // Assume created
let bob_id = 2; // Assume created
create_friendship(&mut graph, alice_id, bob_id)?;
// Verify friendship exists
let alice_friends = QueryBuilder::from_node(&graph, alice_id)
.outgoing("FRIENDS_WITH")?
.node_ids();
assert!(alice_friends.contains(&bob_id));
Ok(())
}
#[test]
fn test_mutual_friends() -> Result<()> {
let mut graph = Graph::new()?;
let alice_id = 1;
let bob_id = 2;
let charlie_id = 3;
// Create friendship chain: Alice -> Bob -> Charlie
create_friendship(&mut graph, alice_id, bob_id)?;
create_friendship(&mut graph, bob_id, charlie_id)?;
// Add mutual friend
let diana_id = 4;
create_friendship(&mut graph, alice_id, diana_id)?;
create_friendship(&mut graph, charlie_id, diana_id)?;
let mutual = find_mutual_friends(&graph, alice_id, charlie_id)?;
assert!(mutual.contains(&diana_id));
Ok(())
}
}
```
## Conclusion
You've now built a complete social network with Graph_D! The tutorial covered:
- ✅ **User management** with rich profiles
- ✅ **Social connections** and friendship networks
- ✅ **Content creation** with posts and comments
- ✅ **Group functionality** for communities
- ✅ **Analytics** and insights
- ✅ **Performance optimization** techniques
- ✅ **Testing strategies**
## Next Steps
1. **Add real-time features** using async/await
2. **Implement caching** for frequently accessed data
3. **Add full-text search** for posts and users
4. **Build a REST API** using your favorite web framework
5. **Add monitoring** and observability
6. **Scale horizontally** with multiple graph instances
## Additional Resources
- [API Reference](API_REFERENCE.md) - Complete API documentation
- [Performance Guide](PERFORMANCE.md) - Optimization tips
- [Examples](../examples/) - More code examples
- [GitHub Repository](https://github.com/your-org/graph_d) - Source code and issues
Happy graph building! 🦀📊