Storing data in MongoDB: Inserting into two collections with one collection referencing the other as a subdocument

As someone transitioning from a relational database background to Meteor and MongoDB, I'm facing the challenge of understanding how relationships work in MongoDB. Specifically, I am puzzled about how to handle relations between collections in MongoDB.

For instance, let's say I want to add a recipe to a collection of recipes, where each recipe is a comma-separated list of ingredients. However, I also want these ingredients to be added to a separate collection of ingredients simultaneously. Additionally, I want the recipe to reference the ingredients in the ingredient collection so that if there are any updates to an ingredient, it automatically reflects in all recipes.

One way to achieve this could be by including the ingredients collection as subdocuments within the recipes collection.

Despite this approach, I'm unsure about the implementation process. Below is a snippet of JavaScript code using Meteor:

Recipes = new Mongo.Collection("recipes");
Ingredients = new Mongo.Collection("ingredients");

Template.body.events({
    "submit .new-recipe": function(event) {
        // Avoid default browser form submission behavior
        event.preventDefault();

        // Get input value from form element
        var text = event.target.text.value;
        var splitText = text.split(",");
        var nInputs = splitText.length;
        var recipe = [];
        for (var i = 0; i < nInputs; i++) {
            // Add an ingredient to the ingredients collection
            Ingredients.insert({
                itemName: splitText[i].trim(),
                createdAt: new Date() 
            });
            recipe.push(splitText[i]);
        }
        // Insert the list of ingredients as a recipe into the recipes collection
        Recipes.insert({
            recipe: recipe,
            createdAt: new Date()
        });
        // Clear form content after submission
        event.target.text.value = "";
    }
});

The above code does not establish the desired relationship between ingredients and recipes. To maintain this connection, should I include the ingredient Ids in the recipe collection when adding ingredients? Or maybe insert the entire ingredient document as part of the recipe document in the recipes collection during ingredient insertion?

Answer №1

If you are looking to establish a simple relational model between two collections, one approach is to store the _id of one collection as a value in another collection. In your scenario, it would be beneficial to consider storing the ingredient ids as an array within the recipe. Here are some key points to address from your initial attempt:

  1. The process does not involve verifying the existence of an ingredient before insertion. Consequently, multiple recipes using the same ingredient may result in duplicating the ingredient documents - which probably contradicts your objective.

  2. The insert operation is performed on the client side; however, without publishing the entire ingredient collection, the client lacks the authority to confirm the presence of ingredients (as mentioned in point 1).

  3. By relying on the client's timestamp during the insert, there can be complications if their clock displays inaccurate timing. Although there is a package available for handling this issue, employing a method might streamline the process and resolve the concerns outlined above.


To enhance accuracy, consider dividing the text input on the client end and executing a

Meteor.call('recipes.insert', ingredientNames)
, with a method structured like this:

Meteor.methods({
  'recipes.insert': function(ingredientNames) {
    // Validate that the input comprises an array of strings.
    check(ingredientNames, [String]);

    // Utilize the same createdAt time for all inserted documents.
    var createdAt = new Date;

    // Formulate an array of ingredient ids based on the provided names.
    var ingredientIds = _.map(ingredientNames, function(ingredientName) {
      // Strip any excess spaces from the input—perhaps consider converting to lowercase too?
      var name = ingredientName.trim();

      // Verify whether an ingredient with this name already exists.
      var ingredient = Ingrediends.findOne({itemName: name});
      if (ingredient) {
        // If it does, utilize its id.
        return ingredient._id;
      } else {
        // Create a new document and obtain its id.
        return Ingrediends.insert({
          itemName: name,
          createdAt: createdAt
        });
      }
    });

    // Insert a fresh recipe incorporating the ingredient ids to establish linkage
     // between both collections.
    
    return Recipes.insert({
      ingredientIds: ingredientIds,
      createdAt: createdAt
    });
  }
});

For further insights, you may find the following resources helpful:

  • This stackoverflow discussion focusing on relational models in Meteor.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide on how to reload the page with its initial URL after the URL has been modified using history.pushState()

After utilizing history.pushState(), I encountered an issue where the page would refresh with the current URL instead of the original one when the user refreshed the page. I attempted to detect a page refresh using methods such as cookies and hidden field ...

A technique for simultaneously replacing two values in a single call to the replace function

Is there a way to replace two or more instances at once with a single replace call? I have not attempted anything yet, as I am unsure of how to proceed. let links = { _init: "https://%s.website.com/get/%s", } Here, you can see that I have a link wi ...

Mongoose: An unexpected error has occurred

Recently, I developed an express app with a nested app called users using Typescript. The structure of my app.js file is as follows: ///<reference path='d.ts/DefinitelyTyped/node/node.d.ts' /> ///<reference path='d.ts/DefinitelyTyp ...

Activate the element by simulating a button click on another element

I created a "copy-button" component that is utilized in various parts of the application, allowing users to copy information to their clipboard. This component has two bindings: buttonText and buttonClass, which can be used to customize the text displaye ...

Feeling uncertain about Node, NPM, Bower, and how to set them up for Bootstrap?

Looking to expand my knowledge in web development, I have a solid understanding of HTML, JS, CSS, and server-side programming. However, the concepts of Nodejs, npm, and Bower are still unclear to me. In order to begin a new project, I created a designated ...

Tips for incorporating CSS styling into imported joint js bpmn elements

Our dashboard now includes components from a company supplying us with Business Process Model And Notation (BPMN) json objects. We bring in the BPMN json object using "fromJSON()" onto the joint.dia.Paper. Everything is functioning properly. However, I ...

Slowly revealing sticky navigation with slideDown animation using JQuery

Here is the code for a .JS file: $(document).scroll(function (){ var menuheight= $('header').height(); var y = $(this).scrollTop(); if (y>(menuheight)) { $('.menu_nav_features').addClass ...

Creating a curved exponential data set with specific endpoints and a set number of data points

Struggling to create a function that involves math skills, I could really use some assistance. The task is to design a function that takes data points x and generates an array of size x with exponentially increasing values from 0 to 100. It would be ideal ...

How can we minimize the data contained in JSON HTML markup?

https://i.stack.imgur.com/mzuB0.png Currently, I am attempting to find a way to conceal the last 12 digits in the price shown on a button, as it is excessively long. The method I am utilizing involves a JSON api and insertAdjacentHTML markup. This snipp ...

Tips for finding a duplicate button on a webpage with Protractor

After searching extensively, I am still struggling to find a way to locate a specific button on a webpage that lacks a unique identifier in the HTML. There are three buttons on the page, and the one I need to identify is the second one. Despite trying vari ...

Creating a hierarchical JSON structure to populate a Handlebars template (HTML) for an iterative component, allowing for the display of three levels of interconnected

Currently, I am working on developing a mega menu component that involves three levels of content. Level 1 (L1): This level is displayed horizontally in a traditional navbar. When hovered over, it expands to reveal the mega menu. Level 2 (L2): These item ...

React: Oops! Looks like there's an issue - this.props.update is not defined as

Hello everyone, I'm diving into the world of programming for the first time and I might ask some silly questions along the way. Currently, I'm working on integrating a date picker into a search application built with React. The user should be ab ...

Switch the background color of the checkbox div or label with a single click

I have successfully implemented the following code, with one small issue. When I select the checkbox, the background color of the div changes from #fff to #ffe600 as expected. However, after submitting the form and refreshing the page, the background color ...

What is the best way to instantiate a service (injectable) with Angular within a class?

import { Store } from '@ngxs/store'; export class Service { constructor(private _store: Store) {} } export abstract class A { constructor( private _service: Service ) { } } export class B extends A { constructor( private _service: ...

Find the top two exam scores using MongoDB

Within my student database, I have a collection structured as follows: How can I identify the two highest exam scores for each student? [ { "_id" : ObjectId("61868aa03b2fe72b58c891a5"), "name" : "Max", ...

Having trouble persisting my login status in Selenium using Python

Has anyone experienced issues with logging into Instagram using an automate tab? Previously, I didn't have any problems, but now it seems that Instagram is not allowing users to log in through automation. An error message stating the following appears ...

I am interested in incorporating a condition to modify the image within this script or potentially generate a new one

I am looking to update an image based on the condition defined by val["id"]: if (val["id"] > 10) { //show the first image } else if (val["id"] > 20) { //show the second image } else { //show the third image }; I am struggling with some ...

Elements of Data Pagination in Vuetify Data Tables

My data-table is filled with thousands of data inputs, so I am using the default Vuetify pagination to display only 5, 10, or 25 items at a time on the table. However, I am in need of a way to determine which data is currently visible on the table. For ex ...

Class is still visible after the media query preview is looked at, despite attempts

Our print preview library is set up to display the final product to users, but we don't want the images to actually be printed since we are using branded paper. To address this, I have included a print media query in the print.css file and added all ...

In what ways can you shut down an electron application using JavaScript?

My Electron app is running an express server. Here is the main.js code: const electron = require("electron"), app = electron.app, BrowserWindow = electron.BrowserWindow; let mainWindow; function createWindow () { ma ...