One-to-One Relationship example

  • Let's use MongoDB Shell to create a one-to-one relationship with both embedding and referencing approaches.
Example 1: Embedding Approach
  • Step 1: Insert Data (Embedding)
  • In this approach, we embed the Profile document directly inside the User document.
  • Create the Users collection with an embedded Profile field:


    use myDatabase   # Switch to or create the database

    db.users.insertOne({
      _id: 1,
      name: "John Doe",
      email: "john@example.com",
      profile: {
        age: 30,
        gender: "Male",
        address: "123 Main Street"
      }
    })

  • Here, the profile field is embedded directly into the users document. 
  • Step 2: Query Data (Embedding)
  • To retrieve a user with their embedded profile, simply query the users collection:


    db.users.findOne({ _id: 1 })

    // Output:
    {
      "_id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "profile": {
        "age": 30,
        "gender": "Male",
        "address": "123 Main Street"
      }
    }

Example 2: Referencing Approach

  • Step 1: Insert Data (Referencing)
  • In this approach, we store the Profile in a separate collection and use a reference (e.g., profile_id) in the Users collection.
  • Create the Profiles collection and insert the profile data:


  db.profiles.insertOne({
    _id: 101,
    age: 30,
    gender: "Male",
    address: "123 Main Street"
  })

  • Create the Users collection with a reference to the profile:


  db.users.insertOne({
    _id: 1,
    name: "John Doe",
    email: "john@example.com",
    profile_id: 101  # Reference to the profile
  })

  • Step 2: Query Data (Referencing)
  • To get a user and their profile, you need to perform two queries or use MongoDB’s $lookup (aggregation framework) to join the two collections.
Option 1: Simple Query with Two Separate Lookups
  • 1. Query the user to get their profile_id:


    var user = db.users.findOne({ _id: 1 })

  • 2. Query the profiles collection to get the related profile using the profile_id:


    db.profiles.findOne({ _id: user.profile_id })

Option 2: Using Aggregation with $lookup to Join the Data

  • You can use the $lookup stage to join users and profiles collections in a single query:


  db.users.aggregate([
    {
      $lookup: {
        from: "profiles",            // Collection to join
        localField: "profile_id",    // Field from the users collection
        foreignField: "_id",         // Field from the profiles collection
        as: "profile"                // Output array field
      }
    }
  ])

  // Output:
  [
    {
      "_id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "profile_id": 101,
      "profile": [
        {
          "_id": 101,
          "age": 30,
          "gender": "Male",
          "address": "123 Main Street"
        }
      ]
    }
  ]

Explanation:

  • Embedding is simple and effective when the data is tightly related and will be fetched together. In this case, everything related to a user is stored in one document.
  • Referencing helps keep collections normalized and is useful when the related data (e.g., profiles) might change often or when it’s large and you don’t always need to fetch the entire profile with every user query.
Conclusion:
  • For one-to-one relationships, you can choose between embedding and referencing based on how you want to manage your data.
  • Embedding is good for simpler, tightly coupled data, while referencing offers more flexibility and normalization.

Relationships in MongoDB

  • In MongoDB, relationships between documents are not as strictly defined as in SQL-based databases. Instead of using joins or foreign keys, MongoDB handles relationships by embedding documents or referencing documents. Here’s a detailed explanation of both types of relationships, with examples.
Types of Relationships in MongoDB:
  • One-to-One Relationship
  • One-to-Many Relationship
  • Many-to-Many Relationship
One-to-One Relationship
  • A one-to-one relationship exists when one document in a collection is related to one document in another collection. This can be handled by embedding one document inside another or referencing the related document.
Example (Embedding):
  • Suppose you have a Users collection, and each user has a unique profile stored in the Profiles collection. You can embed the Profile document directly inside the User document.


    {
        "_id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "profile": {
            "age": 29,
            "gender": "Male",
            "address": "1234 Elm Street"
        }
    }

Example (Referencing):

  • You could also keep the Profiles in a separate collection and reference the Profile document in the Users collection using the profile's ID.

    // Users Collection:
    {
        "_id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "profile_id": 101
    }

    // Profiles Collection:
    {
        "_id": 101,
        "age": 29,
        "gender": "Male",
        "address": "1234 Elm Street"
    }

  • Here, the profile_id is used to reference the corresponding profile in the Profiles collection.
One-to-Many Relationship
  • In a one-to-many relationship, a single document is related to many documents in another collection. This is commonly used when a single entity has multiple associated entities.
Example (Embedding):
  • Suppose you have an Authors collection, and each author has multiple books. You can embed the books inside the author document.

    {
        "_id": 1,
        "name": "J.K. Rowling",
        "books": [
            {
                "title": "Harry Potter and the Sorcerer's Stone",
                "year": 1997
            },
            {
                "title": "Harry Potter and the Chamber of Secrets",
                "year": 1998
            }
        ]
    }


Example (Referencing):

  • Alternatively, you can store the books in a separate collection and reference them in the author document.

    // Authors Collection:
    {
        "_id": 1,
        "name": "J.K. Rowling",
        "book_ids": [
            101,
            102
        ]
    }

    // Books Collection:
    {
        "_id": 101,
        "title": "Harry Potter and the Sorcerer's Stone",
        "year": 1997,
        "author_id": 1
    }

    {
        "_id": 102,
        "title": "Harry Potter and the Chamber of Secrets",
        "year": 1998,
        "author_id": 1
    }

  • This creates a one-to-many relationship where an author can have multiple books.
Many-to-Many Relationship
  • In a many-to-many relationship, multiple documents in one collection can relate to multiple documents in another collection. This is typically managed using references.
Example (Referencing):
  • Suppose you have a Students collection and a Courses collection, where students can enroll in multiple courses, and each course can have multiple students.

    // Students Collection:
    {
        "_id": 1,
        "name": "Alice",
        "course_ids": [
            101,
            102
        ]
    }

    {
        "_id": 2,
        "name": "Bob",
        "course_ids": [
            101
        ]
    }

    // Courses Collection:
    {
        "_id": 101,
        "course_name": "Math 101",
        "student_ids": [
            1,
            2
        ]
    }

    {
        "_id": 102,
        "course_name": "Physics 101",
        "student_ids": [
            1
        ]
    }

  • In this case, a student can enroll in many courses, and a course can have many students. The course_ids field in the Students collection and the student_ids field in the Courses collection establish the many-to-many relationship.
Choosing Between Embedding and Referencing
  • Embedding is preferred when:
    • The relationship is tightly coupled (e.g., profile data inside a user document).
    • The related data will always be retrieved along with the parent document.
    • The embedded document is relatively small and unlikely to change often.
  • Referencing is preferred when:
    • The relationship is loosely coupled (e.g., orders and customers).
    • The related data is large or frequently updated.
    • You need to maintain data consistency and avoid duplication.
Aggregation and Relationships
  • MongoDB provides the $lookup operator to join collections similar to SQL joins in referenced relationships.
Example of $lookup:
  • If you want to retrieve an author along with their books using the referencing approach, you can use the $lookup operator.

    db.authors.aggregate([
      {
        $lookup: {
          from: "books",           // The collection to join
          localField: "book_ids",   // Field from the authors collection
          foreignField: "_id",      // Field from the books collection
          as: "books"               // Output array field
        }
      }
    ])

  • This will return an author with an embedded array of books.
Conclusion
  • MongoDB provides flexibility in managing relationships between documents. Depending on your data model and use case, you can choose between embedding or referencing to design relationships efficiently. Understanding the trade-offs between the two approaches is key to building scalable and performant MongoDB applications.

Embedding and Referencing approaches in mongo db

  • In MongoDB, when dealing with relationships between data, you can choose between two main approaches: embedding and referencing. Each of these approaches has specific use cases and trade-offs. Let's explore both in detail.
Embedding in MongoDB
  • Embedding means that you nest related documents inside a parent document. The related data is stored within the same document as an array or sub-document.
Characteristics of Embedding:
  • Data is stored together: All related information is part of the same document.
  • Fast data retrieval: Since all the related data is contained in a single document, no additional queries are required to fetch it.
  • Simplicity: You avoid the complexity of managing multiple collections for related data.
  • Document size: MongoDB has a document size limit of 16 MB. If the embedded data grows too large, it can hit this limit.
Use Cases for Embedding:
  • When the related data is tightly coupled and frequently used together.
  • When the embedded data won’t grow indefinitely or change independently of the parent document.
  • For small, static datasets where duplicating information isn’t a concern.
Example of Embedding:
  • Let's assume we are modeling users and their profiles in MongoDB. Each user has only one profile, so we can store the profile data inside the user document.

    {
        "_id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "profile": {
            "age": 30,
            "gender": "Male",
            "address": "123 Main Street"
        }
    }


In this case:

  • User data and profile data are stored together.
  • The user document contains a profile field, which is itself an object containing age, gender, and address.
  • Since the profile data is specific to this user and always accessed along with the user, embedding is a good choice.
Advantages of Embedding:
  • Single Query: You only need one query to get the parent document and its related data.

    db.users.findOne({ _id: 1 })

  • No Joins: Joins or lookups are unnecessary, which improves read performance.
  • Atomicity: Since the entire document is stored together, updates to the document (including its embedded fields) are atomic, meaning they happen completely or not at all.
Disadvantages of Embedding:
  • Document Growth: If the embedded data grows too large, it could hit MongoDB’s document size limit (16 MB).
  • Data Duplication: If multiple documents contain similar embedded data, duplication can occur.
  • Difficulty in Independent Updates: If the embedded data needs to be updated independently of the parent document, it can become inefficient since you have to update the entire document.
Referencing in MongoDB
  • Referencing means that related data is stored in separate documents, and these documents reference each other through a shared key, typically using the _id field of the referenced document.
Characteristics of Referencing:
  • Data is stored separately: The related data lives in different collections (or different documents within the same collection).
  • More flexible: The related documents can grow independently, and there’s no strict size limit on either.
  • Requires multiple queries or joins: You often need multiple queries to retrieve the related data or use MongoDB’s $lookup operation for joins.
  • Normalization: Referencing allows you to normalize your data, reducing duplication and improving data consistency.
Use Cases for Referencing:
  • When the related data is loosely coupled or changes frequently, or when the data grows over time.
  • When related data is large and may not always be required.
  • When there’s potential for shared relationships (e.g., many-to-many relationships).
Example of Referencing:
  • Let’s consider the same example of users and profiles, but this time we’ll store the Profile in a separate collection and reference it from the User document.

    // Users Collection:
    {
        "_id": 1,
        "name": "John Doe",
        "email": "john@example.com",
        "profile_id": 101 // Reference to profile document
    }

    // Profiles Collection:
    {
        "_id": 101,
        "age": 30,
        "gender": "Male",
        "address": "123 Main Street"
    }


In this case:

  • The Users document contains a reference (profile_id) to the corresponding Profiles document.
  • The Profiles document holds the profile data in a separate collection.
Advantages of Referencing:
  • Avoids Data Duplication: The same referenced data can be shared by multiple documents.
  • Scalability: Documents can grow independently without being constrained by the 16 MB document size limit.
  • Modularity: Related data can be updated independently without affecting the parent document.
  • Data Normalization: You store a single version of data and reference it, which makes updates simpler and avoids inconsistency.
Disadvantages of Referencing:
  • Additional Queries: You need multiple queries to retrieve related data. For example, you first query the users collection to get the profile_id, and then query the profiles collection to get the profile.
  • Complexity: Managing and querying related data in separate collections can be more complex than embedding.
  • Joins/Lookups: When you want to combine data from different collections in a single query, you need to use the $lookup aggregation, which can be slower compared to embedding.
Performing Queries with Referencing:
  • In a referencing approach, if you want to get a user along with their profile, you can either:
  • 1. Use two separate queries (one for the user and another for the profile).
  • Query the User document:


    var user = db.users.findOne({ _id: 1 })

  • Query the Profile document using profile_id:

    db.profiles.findOne({ _id: user.profile_id })

  • 2. Use the $lookup operator to join the users and profiles collections:
  • Using Aggregation for Lookup:

  db.users.aggregate([
    {
      $lookup: {
        from: "profiles",            // The collection to join
        localField: "profile_id",    // Field from users
        foreignField: "_id",         // Field from profiles
        as: "profile"                // Output array field name
      }
    }
  ])

  // Result:
  [
    {
      "_id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "profile_id": 101,
      "profile": [
        {
          "_id": 101,
          "age": 30,
          "gender": "Male",
          "address": "123 Main Street"
        }
      ]
    }
  ]


Choosing Between Embedding and Referencing


When to Use Embedding:
  • Tightly Coupled Data: If the related data is always accessed together (e.g., user and profile), embedding makes sense.
  • Small Subdocuments: If the embedded data is small and doesn’t grow too large over time.
  • Atomic Updates: If you need to update the document and its embedded data in a single, atomic operation.
When to Use Referencing:
  • Loosely Coupled Data: If the related data is often accessed separately (e.g., orders and customers).
  • Data Growth: If the related data could grow large or change frequently.
  • Multiple Relationships: If the related data might be used by multiple documents (e.g., users and posts in a social media app).
  • Document Size Limits: If you are concerned about the 16 MB document size limit.
Summary of Embedding vs. Referencing:
  • Both approaches have their strengths, and the choice depends on your application's requirements. Generally, embedding works well for smaller, tightly related datasets, while referencing is better for larger, more loosely related or frequently changing data.

Debouncing and Throttling in JavaScript

Debouncing and Throttling - Made Simple! Think of these as traffic controllers for your functions: Debouncing = Wait until user stops, ...