One place for hosting & domains

      Document

      Accessing the Document Object Model with JavaScript


      The Document Object Model (DOM) is a language-agnostic interface that provides access to an HTML document’s structure and content. JavaScript is often the language used to access the DOM in order to generate dynamic and interactive web pages.

      This guide provides an overview of the Document Object Model and shows you how to interact with it using a series of JavaScript examples.

      Before You Begin

      The examples in this guide use a web browser’s developer tools to view the DOM and interact with a JavaScript console. To get the most out of the information in this guide, follow along in your own Chrome or Firefox browser.

      • On Chrome, refer to Google’s
        Open Chrome DevTools
        documentation to learn how to access their developer tools.

      • On Firefox, refer to Mozilla’s
        Open the Inspector
        documentation to learn how to access their developer tools.

      What Is the Document Object Model?

      The Document Object Model (DOM) is an interface that provides access to an HTML document’s structure and content. The DOM represents the elements and content of an HTML document as nodes and objects. The DOM representation can then be accessed and modified by JavaScript and other scripting languages.

      Essentially, the DOM is what allows web pages to become dynamic. Languages like JavaScript work with the nodes that make up the DOM to dynamically and interactively change a web page’s presentation.

      There are many ways of displaying the DOM. One of the most widely used ways is the HTML format. When you open your browser’s Inspect dashboard, you can view the HTML representation of a web page. The example below shows the HTML markup for a simple web page.

      <!DOCTYPE html>
      <html>
        <head>
          <title>Example Page</title>
        </head>
        <body>
          <p>Example page content.</p>
        </body>
      </html>
      

      How the DOM Differs from HTML Source Code

      The DOM itself is not equivalent to a web page’s HTML source code. Instead, the DOM is a representation of how a web page is displayed in the moment that it is accessed.

      To illustrate how the DOM and HTML source code can differ, the example below displays an HTML source file. The HTML file includes a JavaScript function that adds additional HTML elements to the page once the page loads.

      File: example-page.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      
      <!DOCTYPE html>
      <html>
        <head>
          <title>Example Page</title>
          <script>
              function addExampleList() {
                  const exampleList = document.createElement("ul");
      
                  const exampleListItem1 = document.createElement("li");
                  const exampleListItem1Text = document.createTextNode("First item");
                  exampleListItem1.appendChild(exampleListItem1Text);
      
                  const exampleListItem2 = document.createElement("li");
                  const exampleListItem2Text = document.createTextNode("second item");
                  exampleListItem2.appendChild(exampleListItem2Text);
      
                  exampleList.appendChild(exampleListItem1);
                  exampleList.appendChild(exampleListItem2);
      
                  document.body.appendChild(exampleList);
              }
          </script>
        </head>
        <body onload="addExampleList();">
          <p>Example page content.</p>
        </body>
      </html>

      Once the HTML page is loaded and the JavaScript runs, the DOM representation of the HTML source above resembles the code displayed below. The JavaScript has been left out to make the resulting HTML easier to read. The HTML now includes an unordered list (<ul>...</ul>) with two list items (<li>...</li>).

      <!DOCTYPE html>
      <html>
          <head>
              <title>Example Page</title>
              <script>[...]</script>
          </head>
          <body onload="addExampleList();">
              <p>Example page content.</p>
              <ul>
                  <li>First item</li>
                  <li>Second item</li>
              </ul>
          </body>
      </html>
      

      Since the DOM is concerned with displaying the current state of an HTML page, it now displays the new HTML elements that were added to the page by the JavaScript. The DOM always reflects any additions, subtractions, or other modifications that happen to a web page. This characteristic is what enables the DOM to make web pages dynamic.

      The Document Object Model and JavaScript

      Most often, JavaScript is how web developers interact with the DOM. JavaScript is able to access the DOM with the document object and the nodes nested under it.

      The next sections explain what the document object is and the parts that make it up.

      Document Object

      To work with the DOM, client-side JavaScript provides the document object. This object includes properties and methods to access and modify the DOM.

      The previous section included some examples of the document object in action. Below are two additional commands that show more of the document object’s features.

      1. The document object’s properties provide information about the HTML document or access to its nested nodes. They also allow you to modify characteristics of the DOM as shown in the example below:

        document.body.style.backgroundColor = "blue";
        

        The example JavaScript accesses the document object’s backgroundColor property and sets its value to "blue". The web page it modifies should now have a blue background. The DOM representation of the change looks as follows:

        <!DOCTYPE html>
        <html>
            <head>
                <title>Example Page</title>
                <script>[...]</script>
            </head>
            <body onload="addExampleList();" style="background-color: blue;">
                <p>Example page content.</p>
                <ul>
                    <li>First item</li>
                    <li>second item</li>
                </ul>
            </body>
        </html>
        

        The color blue is assigned to the <body> element using the style attribute.

      2. The document object has several methods that do everything from provide access to specific nodes to add new nodes to the DOM. In the example below, the getElementsByTagName() method grabs every HTML element with the tag name, <li>. The JavaScript loops through those elements, and then outputs each elements textContent attributes.

        for (item of document.getElementsByTagName("li")) {
            console.log(item.textContent);
        }
        

        Using the for loop above, the JavaScript console should display the following output:

        First item
        Second item

      Nodes and Elements

      The document object contains numerous other objects that all make up the DOM. These objects are called nodes. Nodes include everything from HTML elements, to attributes, to text.

      You are likely to work most frequently with element nodes. DOM element nodes correspond to a web page’s HTML elements. They allow you to access and manipulate the building blocks of a web page.

      The script used in the
      How the DOM Differs from HTML Source
      section added a <ul> element and <li> elements to the page. This added the following two kinds of nodes to the page:

      • Element nodes, which were created using the document.createElement method.
      • Text nodes, created with the document.createTextNode method.

      Each part of the document object is actually a node of some kind or other. Additionally, each node inherits common properties, like the appendChild method, which lets elements add text nodes.

      The document object does more than just let you extend the DOM. For instance, you can also use it to navigate the DOM and make precise modifications to it. The script below demonstrates how these modifications can be made to the DOM. Access the
      example-page.html
      page in your browser. Then, open your browser’s JavaScript console, and enter in the following JavaScript:

      const listItems = document.getElementsByTagName("li");
      
      for (item of listItems) {
          const newTextNode = document.createTextNode(item.textContent.replace("item", "thing"));
      
          item.innerHTML = "";
          item.appendChild(newTextNode);
      }
      

      As a result, the DOM is updated and the text, item, contained within the <li> tags is updated to thing.

      <!DOCTYPE html>
      <html>
          <head>
              <title>Example Page</title>
              <script>[...]</script>
            </head>
            <body onload="addExampleList();">
              <p>Example page content.</p>
              <ul>
                  <li>First thing</li>
                  <li>Second thing</li>
              </ul>
          </body>
      </html>
      

      See our guide
      Traversing the Document Object Model with JavaScript
      , to learn about other built-in document object methods.

      Conclusion

      The DOM provides an interface to an HTML web page. This enables you to manipulate the structure and content of a web page using scripting languages, like JavaScript. This guide introduced you to the DOM and demonstrated how JavaScript is used to add, modify, and remove HTML elements from a web page.

      More Information

      You may wish to consult the following resources for additional information
      on this topic. While these are provided in the hope that they will be
      useful, please note that we cannot vouch for the accuracy or timeliness of
      externally hosted materials.



      Source link

      Traversing the Document Object Model with JavaScript


      The Document Object Model (DOM) is an interface that gives scripting languages, like JavaScript, access to a web page’s structure and content. You can learn more about the DOM and how it represents HTML in our guide
      Introduction to the DOM
      .

      The DOM is organized as a tree of objects, called nodes, that give access to everything from HTML elements to the text displayed on a web page. Understanding how to navigate and access nodes on this tree is essential to working with the DOM. This guide explains the DOM tree, how to navigate it, and how to access its nodes using JavaScript.

      Before You Begin

      The examples in this guide use a web browser’s developer tools to view the DOM and interact with a JavaScript console. To get the most out of the information in this guide, follow along in your own Chrome or Firefox browser.

      • On Chrome, refer to Google’s
        Open Chrome DevTools
        documentation to learn how to access their developer tools.
      • On Firefox, refer to Mozilla’s
        Open the Inspector
        documentation to learn how to access their developer tools.

      Most of this guide’s examples are based on an example web page created from the HTML source code displayed below. To follow along with this guide’s example,
      view the rendered example web page
      in your browser.

      File: example-page.html
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      
      <!DOCTYPE html>
      <html>
          <head>
              <title>Example Page</title>
          </head>
          <body>
              <div id="first-div" class="content-div">
                  <p>Example page content.</p>
                  <ul>
                      <li><span class="numeral-name" style="color: green;">First</span> item</li>
                      <li><span class="numeral-name" style="color: red;">Second</span> item</li>
                  </ul>
              </div>
              <div id="second-div" class="content-div">
                  <p><a href="https://loremipsum.io/">Lorem ipsum</a> dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Tortor condimentum lacinia quis vel eros donec. Purus ut faucibus pulvinar elementum integer enim neque volutpat ac. Netus et malesuada fames ac turpis egestas sed tempus. Nulla posuere sollicitudin aliquam ultrices sagittis orci a scelerisque. Et netus et malesuada fames ac turpis egestas sed. Purus ut faucibus pulvinar elementum integer enim neque. <em>Amet consectetur adipiscing elit ut aliquam.</em></p>
              </div>
          </body>
      </html>

      Understanding the DOM Tree

      The DOM is organized as a tree, each branch of which is a node. Though many nodes represent HTML elements, they also represent attributes and text associated with elements.

      In the following sections, you learn more about the structure of the DOM tree. You also learn about the components used to identify nodes and sets of nodes, which is the basis for effectively accessing nodes.

      What Is the DOM Tree?

      The
      document object
      is the base of all of the DOM’s nodes. The nodes are arranged as a tree, with nodes nested under other nodes. Below, is an example of the DOM representation of a simple web page:

      <body>
          <div>
              <p style="color: purple;">Paragraph text</p>
          </div>
          <div>
              <ul>
                  <li>First item</li>
                  <li>Second item</li>
              </ul>
          </div>
      </body>
      

      In the example above, elements nest under other elements. The two div elements are nested under the body element. Text nodes are nested under the p and li elements, and the style attribute is considered a node under the p element, as well.

      Plotting the nesting structure out, the DOM resembles the following tree:

      body
       \_ div
       |   \_ p
       |      \_ [text]
       |      |
       |      \_ [attribute]
       \_ div
           \_ ul
               \_ li
               |   \_ [text]
               |
               \_ li
                   \_ [text]
      

      Knowing the arrangement of the DOM tree and its leaves, helps you understand how to access specific nodes when working with JavaScript. This is especially true when you are working with more complicated web pages. The
      Navigating the DOM Tree
      section of this guide includes a more in-depth discussion on moving around the nodes of the DOM tree.

      The diagram below provides a visualization of the DOM tree for this guide’s
      example web page
      . You can also view the example-page.html file in the
      Before You Begin
      section of this guide.

      A DOM tree for an example web page

      How Are Nodes Accessed?

      When working with JavaScript, you need to pinpoint a node or a particular set of nodes to access. Nodes can be identified by the three components listed below:

      • Tags, used to define HTML elements. Common examples include div for page components, p for paragraphs, and a for hyperlinks.
      • Classes that distinguish between similar elements. Classes are optional, but let you apply CSS styles and let you differentiate between a subset of the same type of element.
      • IDs, which are meant to identify particular elements. These are most useful when applied to elements that you want to be able to conveniently and consistently select individually from the DOM.

      Although this is not always the case, the arrangement of these components above generally reflects the components’ specificity, from least specific to most. For instance:

      • A tag can be used to identify every div element on a page.
      • A class can identify a smaller set of those div elements.
      • An ID can identify a specific div element.

      The
      Navigating the DOM Tree
      section below shows how these components can be used to access particular elements or set of elements.

      Query Selectors

      The popular JavaScript library
      jQuery
      introduced query selectors. These give you advanced ways to access DOM elements, using the above three components as well as attributes and other node features. Query selectors even let you combine all of these in a single command.

      JavaScript now directly implements its own query selectors as well. This gives you access to advanced DOM selections, which are covered in depth later on in this guide.

      To give you an idea, the list below includes some examples of where you can use query selectors to fetch elements. The query selectors can be based on elements:

      • matching a specific sequence of tag, class, and/or ID
      • with a given attribute and/or attribute value
      • with a matching parent element
      • that do not match a query

      Once you understand the DOM’s tree structure and how its nodes are identified, you can start using JavaScript to access and alter the DOM. The sections below show how you can use JavaScript to select specific elements from the DOM. This is divided into two sections:

      1. Using standard methods on DOM objects. These have been around for a longer time and provide some of the most straightforward selection methods.
      2. Using query selectors. These are relatively new features in standard JavaScript and provide advanced selection methods. They are ideal when you want to make complicated queries in a few commands.

      How to Access Elements via Object Methods

      Most often, you can start accessing the DOM elements you need using methods from the document object. These allow you to match elements based on tag, class, or ID.

      Moreover, these methods are inherited by any element object. This makes it possible to navigate the DOM tree from element to element, narrowing it down to the specific elements you need.

      • To fetch elements based on tag, use the getElementsByTagName method:

         document.getElementsByTagName("p");
        
      • To fetch elements based on class, use the getElementsByClassName method:

         document.getElementsByClassName("content-div");
        
      • To fetch an element based on ID, use the getElementById method:

         document.getElementById("first-div");
        

      With the exception of getElementById, all the listed methods return an array of elements, no matter how many elements actually match the query. The getElementById method only returns the first matching element, even if multiple elements on the page have the same ID.

      The following example shows how you can traverse the DOM using a combination of the inherited document object methods. It also shows how you can leverage the fact that every element inherits these methods, allowing you to narrow your search down the DOM tree.

      // Fetch the element with the `first-div` ID. This uses the `document` object,
      // so the search looks at all elements on the page.
      const first_div_element = document.getElementById("first-div");
      
      // Fetch all of the `ul` elements from under the `first-div` element. Remember,
      // this method returns an array, even if there is only one matching element.
      const ul_elements = first_div_element.getElementsByTagName("ul");
      
      // Fetch the elements with the `numeral-name` class from under the first
      // element in the array of `ul` elements.
      const numeral_elements = ul_elements[0].getElementsByClassName("numeral-name");
      
      // Grab and print the `style.color` value from each of the matching
      // `numeral-name` elements.
      for (const span_element of numeral_elements) {
          console.log(span_element.style.color);
      }
      

      If you run the above JavaScript in your web browser’s developer tools console, you see the following output.

      green
      red

      From the example above, you could also get to the numeral-name elements directly using the following code:

      const numeral_elements = document.getElementsByClassName("numeral-name");
      

      The approach of using particular elements’ methods to select lower elements on the tree can be extraordinarily helpful in some cases. For instance, if you want to select only the p elements from the second div, use the following JavaScript code:

      const second_div_element = document.getElementById("second-div");
      const p_elements = second_div_element.getElementsByTagName("p");
      

      How to Access Elements via Query Selectors

      Query selectors give you more advanced options for selecting elements from the DOM. They can be accessed via two methods from the document object — and they are also inherited on all other element objects. The two methods are the following:

      • querySelector fetches one element matching the query string. If multiple elements match, the method only returns the first one.
      • querySelectorAll fetches an array of elements matching the query string. Even if only one element matches, the result is an array.

      Like the methods covered in the previous section, these query selector methods let you select elements based on tag, class, and ID. However, the query selector syntax also lets you combine element selectors, and expands on how you can search for specific elements.

      The following examples display some key ways in which you can use query selectors to navigate and access elements from the DOM.

      • You can look for elements with a specific combination of tag, class, and ID. For instance, to search for a combination of the div tag, the content-div class, and the first-div ID, use the following code:

          document.querySelectorAll("div.content-div#first-div");
        

        The query selector syntax uses . to precede class names and # to precede IDs. It assumes labels without a prefix to be tag names.

      • You can look for elements nested under particular elements. These are called the descendants of an element. The following example finds em elements nested somewhere under any element with the content-div class:

          document.querySelectorAll(".content-div em");
        
      • You can look for elements based on associated attributes. The example below accesses the first element with an href attribute:

          document.querySelector("[href]");
        

        Alongside this, you can also specify the tag, class, and/or ID. This allows you to narrow down the search to elements whose attribute has a specific value:

          document.querySelector("a[href="https://loremipsum.io/"]");
        
      • You can look for elements based on their direct parent elements. The next command fetches all p elements that are immediate children of any div elements with the first-div ID:

          document.querySelectorAll("div#first-div > p");
        

        This selector is more specific than the descendant selector above. Where the descendant selector div em grabs an element from the example page, the child selector div > em does not.

        Why is this? The page’s em element is a direct child of a p element — that is, nested immediately under a p element — but not a div element. It is only a descendant of a div element, meaning it is nested somewhere, however deeply, beneath one.

      • You can look for elements that do not have a certain matching quality. For instance, the example below gets all p elements that are not a child of an element with the first-div ID:

          document.querySelectorAll("p:not(#first-div > p)")
        

      The above is, in fact, just a selection of some of the most commonly used features of the query selector. You can get more examples of query selector options in the
      More Information
      section of this guide.

      Conclusion

      This tutorial walked you through what the DOM tree looks like, how to navigate its parts, and how to start accessing them. The
      links below
      give you some resources to learn more about navigating the DOM, with more examples and coverage of advanced options and scenarios.

      More Information

      You may wish to consult the following resources for additional information
      on this topic. While these are provided in the hope that they will be
      useful, please note that we cannot vouch for the accuracy or timeliness of
      externally hosted materials.



      Source link

      How To Design a Document Schema in MongoDB


      The author selected the Open Internet/Free Speech Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      If you have a lot of experience working with relational databases, it can be difficult to move past the principles of the relational model, such as thinking in terms of tables and relationships. Document-oriented databases like MongoDB make it possible to break free from rigidity and limitations of the relational model. However, the flexibility and freedom that comes with being able to store self-descriptive documents in the database can lead to other pitfalls and difficulties.

      This conceptual article outlines five common guidelines related to schema design in a document-oriented database and highlights various considerations one should make when modeling relationships between data. It will also walk through several strategies one can employ to model such relationships, including embedding documents within arrays and using child and parent references, as well as when these strategies would be most appropriate to use.

      Guideline 1 — Storing Together What Needs to be Accessed Together

      In a typical relational database, data is kept in tables, and each table is constructed with a fixed list of columns representing various attributes that make up an entity, object, or event. For example, in a table representing students at a a university, you might find columns holding each student’s first name, last name, date of birth, and a unique identification number.

      Typically, each table represents a single subject. If you wanted to store information about a student’s current studies, scholarships, or prior education, it could make sense to keep that data in a separate table from the one holding their personal information. You could then connect these tables to signify that there is a relationship between the data in each one, indicating that the information they contain has a meaningful connection.

      For instance, a table describing each student’s scholarship status could refer to students by their student ID number, but it would not store the student’s name or address directly, avoiding data duplication. In such a case, to retrieve information about any student with all information on the student’s social media accounts, prior education, and scholarships, a query would need to access more than one table at a time and then compile the results from different tables into one.

      This method of describing relationships through references is known as a normalized data model. Storing data this way — using multiple separate, concise objects related to each other — is also possible in document-oriented databases. However, the flexibility of the document model and the freedom it gives to store embedded documents and arrays within a single document means that you can model data differently than you might in a relational database.

      The underlying concept for modeling data in a document-oriented database is to “store together what will be accessed together.”“ Digging further into the student example, say that most students at this school have more than one email address. Because of this, the university wants the ability to store multiple email addresses with each student’s contact information.

      In a case like this, an example document could have a structure like the following:

      {
          "_id": ObjectId("612d1e835ebee16872a109a4"),
          "first_name": "Sammy",
          "last_name": "Shark",
          "emails": [
              {
                  "email": "sammy@digitalocean.com",
                  "type": "work"
              },
              {
                  "email": "sammy@example.com",
                  "type": "home"
              }
          ]
      }
      

      Notice that this example document contains an embedded list of email addresses.

      Representing more than a single subject inside a single document characterizes a denormalized data model. It allows applications to retrieve and manipulate all the relevant data for a given object (here, a student) in one go without a need to access multiple separate objects and collections. Doing so also guarantees the atomicity of operations on such a document without having to use multi-document transactions to guarantee integrity.

      Storing together what needs to be accessed together using embedded documents is often the optimal way to represent data in a document-oriented database. In the following guidelines, you’ll learn how different relationships between objects, such as one-to-one or one-to-many relationships, can be best modeled in a document-oriented database.

      Guideline 2 — Modeling One-to-One Relationships with Embedded Documents

      A one-to-one relationship represents an association between two distinct objects where one object is connected with exactly one of another kind.

      Continuing with the student example from the previous section, each student has only one valid student ID card at any given point in time. One card never belongs to multiple students, and no student can have multiple identification cards. If you were to store all this data in a relational database, it would likely make sense to model the relationship between students and their ID cards by storing the student records and the ID card records in separate tables that are tied together through references.

      One common method for representing such relationships in a document database is by using embedded documents. As an example, the following document describes a student named Sammy and their student ID card:

      {
          "_id": ObjectId("612d1e835ebee16872a109a4"),
          "first_name": "Sammy",
          "last_name": "Shark",
          "id_card": {
              "number": "123-1234-123",
              "issued_on": ISODate("2020-01-23"),
              "expires_on": ISODate("2020-01-23")
          }
      }
      

      Notice that instead of a single value, this example document’s id_card field holds an embedded document representing the student’s identification card, described by an ID number, the card’s date of issue, and the card’s expiration date. The identity card essentially becomes a part of the document describing the student Sammy, even though it’s a separate object in real life. Usually, structuring the document schema like this so that you can retrieve all related information through a single query is a sound choice.

      Things become less straightforward if you encounter relationships connecting one object of a kind with many objects of another type, such as a student’s email addresses, the courses they attend, or the messages they post on the student council’s message board. In the next few guidelines, you’ll use these data examples to learn different approaches for working with one-to-many and many-to-many relationships.

      Guideline 3 — Modeling One-to-Few Relationships with Embedded Documents

      When an object of one type is related to multiple objects of another type, it can be described as a one-to-many relationship. A student can have multiple email addresses, a car can have numerous parts, or a shopping order can consist of multiple items. Each of these examples represents a one-to-many relationship.

      While the most common way to represent a one-to-one relationship in a document database is through an embedded document, there are several ways to model one-to-many relationships in a document schema. When considering your options for how to best model these, though, there are three properties of the given relationship you should consider:

      • Cardinality: Cardinality is the measure of the number of individual elements in a given set. For example, if a class has 30 students, you could say that class has a cardinality of 30. In a one-to-many relationship, the cardinality can be different in each case. A student could have one email address or multiple. They could be registered for just a few classes or they could have a completely full schedule. In a one-to-many relationship, the size of “many” will affect how you might model the data.
      • Independent access: Some related data will rarely, if ever, be accessed separately from the main object. For example, it might be uncommon to retrieve a single student’s email address without other student details. On the other hand, a university’s courses might need to be accessed and updated individually, regardless of the student or students that are registered to attend them. Whether or not you will ever access a related document alone will also affect how you might model the data.
      • Whether the relationship between data is strictly a one-to-many relationship: Consider the courses an example student attends at a university. From the student’s perspective, they can participate in multiple courses. On the surface, this may seem like a one-to-many relationship. However, university courses are rarely attended by a single student; more often, multiple students will attend the same class. In cases like this, the relationship in question is not really a one-to-many relationship, but a many-to-many relationship, and thus you’d take a different approach to model this relationship than you would a one-to-many relationship.

      Imagine you’re deciding how to store student email addresses. Each student can have multiple email addresses, such as one for work, one for personal use, and one provided by the university. A document representing a single email address might take a form like this:

      {
          "email": "sammy@digitalocean.com",
          "type": "work"
      }
      

      In terms of cardinality, there will be only a few email addresses for each student, since it’s unlikely that a student will have dozens — let alone hundreds — of email addresses. Thus, this relationship can be characterized as a one-to-few relationship, which is a compelling reason to embed email addresses directly into the student document and store them together. You don’t run any risk that the list of email addresses will grow indefinitely, which would make the document big and inefficient to use.

      Note: Be aware that there are certain pitfalls associated with storing data in arrays. For instance, a single MongoDB document cannot exceed 16MB in size. While it is possible and common to embed multiple documents using array fields, if the list of objects grows uncontrollably the document could quickly reach this size limit. Additionally, storing a large amount of data inside embedded arrays have a big impact on query performance.

      Embedding multiple documents in an array field will likely be suitable in many situations, but know that it also may not always be the best solution.

      Regarding independent access, email addresses will likely not be accessed separately from the student. As such, there is no clear incentive to store them as separate documents in a separate collection. This is another compelling reason to embed them inside the student’s document.

      The last thing to consider is whether this relationship is really a one-to-many relationship instead of a many-to-many relationship. Because an email address belongs to a single person, it’s reasonable to describe this relationship as a one-to-many relationship (or, perhaps more accurately, a one-to-few relationship) instead of a many-to-many relationship.

      These three assumptions suggest that embedding students’ various email addresses within the same documents that describe students themselves would be a good choice for storing this kind of data. A sample student’s document with email addresses embedded might take this shape:

      {
          "_id": ObjectId("612d1e835ebee16872a109a4"),
          "first_name": "Sammy",
          "last_name": "Shark",
          "emails": [
              {
                  "email": "sammy@digitalocean.com",
                  "type": "work"
              },
              {
                  "email": "sammy@example.com",
                  "type": "home"
              }
          ]
      }
      

      Using this structure, every time you retrieve a student’s document you will also retrieve the embedded email addresses in the same read operation.

      If you model a relationship of the one-to-few variety, where the related documents do not need to be accessed independently, embedding documents directly like this is usually desirable, as this can reduce the complexity of the schema.

      As mentioned previously, though, embedding documents like this isn’t always the optimal solution. The next section provides more details on why this might be the case in some scenarios, and outlines how to use child references as an alternative way to represent relationships in a document database.

      Guideline 4 — Modeling One-to-Many and Many-to-Many Relationships with Child References

      The nature of the relationship between students and their email addresses informed how that relationship could best be modeled in a document database. There are some differences between this and the relationship between students and the courses they attend, so the way you model the relationships between students and their courses will be different as well.

      A document describing a single course that a student attends could follow a structure like this:

      {
          "name": "Physics 101",
          "department": "Department of Physics",
          "points": 7
      }
      

      Say that you decided from the outset to use embedded documents to store information about each students’ courses, as in this example:

      {
          "_id": ObjectId("612d1e835ebee16872a109a4"),
          "first_name": "Sammy",
          "last_name": "Shark",
          "emails": [
              {
                  "email": "sammy@digitalocean.com",
                  "type": "work"
              },
              {
                  "email": "sammy@example.com",
                  "type": "home"
              }
          ],
          "courses": [
              {
                  "name": "Physics 101",
                  "department": "Department of Physics",
                  "points": 7
              },
              {
                  "name": "Introduction to Cloud Computing",
                  "department": "Department of Computer Science",
                  "points": 4
              }
          ]
      }
      

      This would be a perfectly valid MongoDB document and could well serve the purpose, but consider the three relationship properties you learned about in the previous guideline.

      The first one is cardinality. A student will likely only maintain a few email addresses, but they can attend multiple courses during their study. After several years of attendance, there could be dozens of courses the student took part in. Plus, they’d attend these courses along with many other students who are likewise attending their own set of courses over their years of attendance.

      If you decided to embed each course like the previous example, the student’s document would quickly get unwieldy. With a higher cardinality, the embedded document approach becomes less compelling.

      The second consideration is independent access. Unlike email addresses, it’s sound to assume there would be cases in which information about university courses would need to be retrieved on their own. For instance, say someone needs information about available courses to prepare a marketing brochure. Additionally, courses will likely need to be updated over time: the professor teaching the course might change, its schedule may fluctuate, or its prerequisites might need to be updated.

      If you were to store the courses as documents embedded within student documents, retrieving the list of all the courses offered by the university would become troublesome. Also, each time a course needs an update, you would need to go through all student records and update the course information everywhere. Both are good reasons to store courses separately and not embed them fully.

      The third thing to consider is whether the relationship between student and a university course is actually one-to-many or instead many-to-many. In this case, it’s the latter, as more than one student can attend each course. This relationship’s cardinality and independent access aspects suggest against embedding each course document, primarily for practical reasons like ease of access and update. Considering the many-to-many nature of the relationship between courses and students, it might make sense to store course documents in a separate collection with unique identifiers of their own.

      The documents representing classes in this separate collection might have a structure like these examples:

      {
          "_id": ObjectId("61741c9cbc9ec583c836170a"),
          "name": "Physics 101",
          "department": "Department of Physics",
          "points": 7
      },
      {
          "_id": ObjectId("61741c9cbc9ec583c836170b"),
          "name": "Introduction to Cloud Computing",
          "department": "Department of Computer Science",
          "points": 4
      }
      

      If you decide to store course information like this, you’ll need to find a way to connect students with these courses so that you will know which students attend which courses. In cases like this where the number of related objects isn’t excessively large, especially with many-to-many relationships, one common way of doing this is to use child references.

      With child references, a student’s document will reference the object identifiers of the courses that the student attends in an embedded array, as in this example:

      {
          "_id": ObjectId("612d1e835ebee16872a109a4"),
          "first_name": "Sammy",
          "last_name": "Shark",
          "emails": [
              {
                  "email": "sammy@digitalocean.com",
                  "type": "work"
              },
              {
                  "email": "sammy@example.com",
                  "type": "home"
              }
          ],
          "courses": [
              ObjectId("61741c9cbc9ec583c836170a"),
              ObjectId("61741c9cbc9ec583c836170b")
          ]
      }
      

      Notice that this example document still has a courses field which also is an array, but instead of embedding full course documents like in the earlier example, only the identifiers referencing the course documents in the separate collection are embedded. Now, when retrieving a student document, courses will not be immediately available and will need to be queried separately. On the other hand, it’s immediately known which courses to retrieve. Also, in case any course’s details need to be updated, only the course document itself needs to be altered. All references between students and their courses will remain valid.

      Note: There is no firm rule for when the cardinality of a relation is too great to embed child references in this manner. You might choose a different approach at either a lower or higher cardinality if it’s what best suits the application in question. After all, you will always want to structure your data to suit the manner in which your application queries and updates it.

      If you model a one-to-many relationship where the amount of related documents is within reasonable bounds and related documents need to be accessed independently, favor storing the related documents separately and embedding child references to connect to them.

      Now that you’ve learned how to use child references to signify relationships between different types of data, this guide will outline an inverse concept: parent references.

      Guideline 5 — Modeling Unbounded One-to-Many Relationships with Parent References

      Using child references works well when there are too many related objects to embed them directly inside the parent document, but the amount is still within known bounds. However, there are cases when the number of associated documents might be unbounded and will continue to grow with time.

      As an example, imagine that the university’s student council has a message board where any student can post whatever messages they want, including questions about courses, travel stories, job postings, study materials, or just a free chat. A sample message in this example consists of a subject and a message body:

      {
          "_id": ObjectId("61741c9cbc9ec583c836174c"),
          "subject": "Books on kinematics and dynamics",
          "message": "Hello! Could you recommend good introductory books covering the topics of kinematics and dynamics? Thanks!",
          "posted_on": ISODate("2021-07-23T16:03:21Z")
      }
      

      You could use either of the two approaches discussed previously — embedding and child references — to model this relationship. If you were to decide on embedding, the student’s document might take a shape like this:

      {
          "_id": ObjectId("612d1e835ebee16872a109a4"),
          "first_name": "Sammy",
          "last_name": "Shark",
          "emails": [
              {
                  "email": "sammy@digitalocean.com",
                  "type": "work"
              },
              {
                  "email": "sammy@example.com",
                  "type": "home"
              }
          ],
          "courses": [
              ObjectId("61741c9cbc9ec583c836170a"),
              ObjectId("61741c9cbc9ec583c836170b")
          ],
          "message_board_messages": [
              {
                  "subject": "Books on kinematics and dynamics",
                  "message": "Hello! Could you recommend good introductory books covering the topics of kinematics and dynamics? Thanks!",
                  "posted_on": ISODate("2021-07-23T16:03:21Z")
              },
              . . .
          ]
      }
      

      However, if a student is prolific with writing messages their document will quickly become incredibly long and could easily exceed the 16MB size limit, so the cardinality of this relation suggests against embedding. Additionally, the messages might need to be accessed separately from the student, as could be the case if the message board page is designed to show the latest messages posted by students. This also suggests that embedding is not the best choice for this scenario.

      Note: You should also consider whether the message board messages are frequently accessed when retrieving the student’s document. If not, having them all embedded inside that document would incur a performance penalty when retrieving and manipulating this document, even when the list of messages would not be used often. Infrequent access of related data is often another clue that you shouldn’t embed documents.

      Now consider using child references instead of embedding full documents as in the previous example. The individual messages would be stored in a separate collection, and the student’s document could then have the following structure:

      {
          "_id": ObjectId("612d1e835ebee16872a109a4"),
          "first_name": "Sammy",
          "last_name": "Shark",
          "emails": [
              {
                  "email": "sammy@digitalocean.com",
                  "type": "work"
              },
              {
                  "email": "sammy@example.com",
                  "type": "home"
              }
          ],
          "courses": [
              ObjectId("61741c9cbc9ec583c836170a"),
              ObjectId("61741c9cbc9ec583c836170b")
          ],
          "message_board_messages": [
              ObjectId("61741c9cbc9ec583c836174c"),
              . . .
          ]
      }
      

      In this example, the message_board_messages field now stores the child references to all messages written by Sammy. However, changing the approach solves only one of the issues mentioned before in that it would now be possible to access the messages independently. But although the student’s document size would grow more slowly using the child references approach, the collection of object identifiers could also become unwieldy given the unbounded cardinality of this relation. A student could easily write thousands of messages during their four years of study, after all.

      In such scenarios, a common way to connect one object to another is through parent references. Unlike the child references described previously, it’s now not the student document referring to individual messages, but rather a reference in the message’s document pointing towards the student that wrote it.

      To use parent references, you would need to modify the message document schema to contain a reference to the student who authored the message:

      {
          "_id": ObjectId("61741c9cbc9ec583c836174c"),
          "subject": "Books on kinematics and dynamics",
          "message": "Hello! Could you recommend a good introductory books covering the topics of kinematics and dynamics? Thanks!",
          "posted_on": ISODate("2021-07-23T16:03:21Z"),
          "posted_by": ObjectId("612d1e835ebee16872a109a4")
      }
      

      Notice the new posted_by field contains the object identifier of the student’s document. Now, the student’s document won’t contain any information about the messages they’ve posted:

      {
          "_id": ObjectId("612d1e835ebee16872a109a4"),
          "first_name": "Sammy",
          "last_name": "Shark",
          "emails": [
              {
                  "email": "sammy@digitalocean.com",
                  "type": "work"
              },
              {
                  "email": "sammy@example.com",
                  "type": "home"
              }
          ],
          "courses": [
              ObjectId("61741c9cbc9ec583c836170a"),
              ObjectId("61741c9cbc9ec583c836170b")
          ]
      }
      

      To retrieve the list of messages written by a student, you would use a query on the messages collection and filter against the posted_by field. Having them in a separate collection makes it safe to let the list of messages grow without affecting any of the student’s documents.

      Note: When using parent references, creating an index on the field referencing the parent document can significantly increase the query performance each time you filter against the parent document identifier.

      If you model a one-to-many relationship where the amount of related documents is unbounded, regardless of whether the documents need to be accessed independently, it’s generally advised that you store related documents separately and use parent references to connect them to the parent document.

      Conclusion

      Thanks to the flexibility of document-oriented databases, determining the best way to model relationships in a document databases is less of a strict science than it is in a relational database. By reading this article, you’ve acquainted yourself with embedding documents and using child and parent references to store related data. You’ve learned about considering the relationship cardinality and avoiding unbounded arrays, as well as taking into account whether the document will be accessed separately or frequently.

      These are just a few guidelines that can help you model typical relationships in MongoDB, but modeling database schema is not a one size fits all. Always take into account your application and how it uses and updates the data when designing the schema.

      To learn more about schema design and common patterns for storing different kinds of data in MongoDB, we encourage you to check the official MongoDB documentation on that topic.



      Source link