← Back to Playground

Arrays & Embedded Documents vs PostgreSQL JOINs

Natural Data Relationships vs Normalized Complexity

🚀 Why This Matters: The Natural Data Advantage

Most applications deal with complex, nested data - users with multiple skills, orders with line items, blog posts with tags. Relational databases force you to break these natural relationships into separate tables, requiring complex JOINs that hurt performance and complicate your code.

MongoDB's differentiator: Store data the way your application thinks about it. Arrays and embedded documents let you model real-world relationships naturally, eliminate JOINs, and retrieve complete objects in a single query.

Real Impact: 50-80% fewer database queries, simpler application code, and blazing-fast performance for complex data relationships that would require multiple JOINs in SQL.

📋 What This Demo Shows

MongoDB Approach

📊 Native Array Support
// Blog post with tags (array of strings) { "_id": ObjectId("..."), "title": "MongoDB Best Practices", "tags": ["mongodb", "database", "nosql", "performance"], "categories": ["tech", "tutorial"] } // User with multiple skills (array of objects) { "_id": ObjectId("..."), "name": "Jane Developer", "skills": [ {"name": "JavaScript", "level": "expert"}, {"name": "Python", "level": "intermediate"}, {"name": "MongoDB", "level": "advanced"} ] }

🔍 Querying Arrays

// Find posts with specific tag db.posts.find({"tags": "mongodb"}) // Find users with JavaScript skills db.users.find({"skills.name": "JavaScript"}) // Find expert level skills db.users.find({"skills.level": "expert"})
🗂️ Embedded Documents
// E-commerce order with embedded data { "_id": ObjectId("..."), "order_id": "ORD-001", "customer": { "name": "John Smith", "email": "john@example.com", "address": { "street": "123 Main St", "city": "San Francisco", "zip": "94105" } }, "items": [ { "product_id": "PROD-1", "name": "Laptop", "price": 999.99, "quantity": 1 }, { "product_id": "PROD-2", "name": "Mouse", "price": 29.99, "quantity": 2 } ], "total": 1059.97, "status": "shipped" }

🔍 Querying Embedded Documents

// Find orders by customer city db.orders.find({"customer.address.city": "San Francisco"}) // Find orders with specific product db.orders.find({"items.product_id": "PROD-1"}) // Find high-value orders db.orders.find({"total": {$gt: 1000}})

🎮 Interactive Demo

✅ Benefits

  • Single query retrieves complete objects
  • 🎯 Natural array operations ($push, $pull, $addToSet)
  • 🔍 Rich query operators for arrays and nested docs
  • 📦 Data locality improves performance
  • 🚀 No complex JOINs required

PostgreSQL Approach

🔗 Junction Tables for Arrays
-- Posts table CREATE TABLE posts ( id SERIAL PRIMARY KEY, title VARCHAR(255), content TEXT ); -- Tags table CREATE TABLE tags ( id SERIAL PRIMARY KEY, name VARCHAR(100) UNIQUE ); -- Junction table for many-to-many CREATE TABLE post_tags ( post_id INTEGER REFERENCES posts(id), tag_id INTEGER REFERENCES tags(id), PRIMARY KEY (post_id, tag_id) ); -- User skills require separate table CREATE TABLE user_skills ( user_id INTEGER REFERENCES users(id), skill_name VARCHAR(100), skill_level VARCHAR(50), PRIMARY KEY (user_id, skill_name) );

🔍 Complex JOINs Required

-- Find posts with specific tag SELECT p.* FROM posts p JOIN post_tags pt ON p.id = pt.post_id JOIN tags t ON pt.tag_id = t.id WHERE t.name = 'mongodb'; -- Find users with JavaScript skills SELECT u.* FROM users u JOIN user_skills us ON u.id = us.user_id WHERE us.skill_name = 'JavaScript';
📋 Normalized Tables
-- Orders table CREATE TABLE orders ( id SERIAL PRIMARY KEY, order_id VARCHAR(50), customer_id INTEGER REFERENCES customers(id), total DECIMAL(10,2), status VARCHAR(50) ); -- Customers table CREATE TABLE customers ( id SERIAL PRIMARY KEY, name VARCHAR(255), email VARCHAR(255) ); -- Addresses table CREATE TABLE addresses ( id SERIAL PRIMARY KEY, customer_id INTEGER REFERENCES customers(id), street VARCHAR(255), city VARCHAR(100), zip VARCHAR(20) ); -- Order items table CREATE TABLE order_items ( order_id INTEGER REFERENCES orders(id), product_id VARCHAR(50), name VARCHAR(255), price DECIMAL(8,2), quantity INTEGER );

🔍 Multi-table JOINs

-- Get complete order information SELECT o.order_id, c.name, a.city, oi.name as item_name, oi.quantity FROM orders o JOIN customers c ON o.customer_id = c.id JOIN addresses a ON c.id = a.customer_id JOIN order_items oi ON o.id = oi.order_id WHERE o.order_id = 'ORD-001';

🎮 Interactive Demo

⚠️ Complexity

  • 🔗 Multiple tables for simple relationships
  • 🔄 Complex JOINs for data retrieval
  • 📊 Additional foreign key management
  • Potential performance overhead
  • 🔧 More maintenance and schema complexity

Performance & Complexity Comparison

🍃 MongoDB: Atomic Operations

  • Single Query: Retrieve complete nested objects
  • Array Operations: $push, $pull, $addToSet directly on arrays
  • Data Locality: Related data stored together

🗄️ SQL: Normalized Approach

  • Multiple Queries: JOINs across several tables
  • Junction Tables: Extra tables for simple arrays
  • Referential Integrity: Strong consistency guarantees