'Use Schema inside another Schema not working. Error: CastError: Cast to [ObjectId] failed for value

What I'm trying to achieve: Use one Schema (Ingredient Schema) inside another one (Recipe Schema). My goal is that in the Recipe Schema, my ingredients key to be an array of my Ingredient Schema. Not sure why it's not working or what I am doing wrong. If you guys have any idea I would really appreciate it. Please see below my code. Thanks!

Error I get, when I call my seeds.js:

Error: Recipe validation failed: ingredients.0: Cast to [ObjectId] failed for value "[{"ingName":"tomato","ingQty":2,"ingQtyUnit":"piece","ingImageUrl":"https://upload.wikimedia.org/wikipedia/commons/8/89/Tomato_je.jpg"},{"ingName":"onion","ingQty":1,"ingQtyUnit":"piece","ingImageUrl":"https://www.seeds-gallery.shop/9430-large_default/onion-seeds-dutch-yellow.jpg"}]" (type string) at path "ingredients.0

'ingredients.0': CastError: Cast to [ObjectId] failed for value "[{"ingName":"tomato","ingQty":2,"ingQtyUnit":"piece","ingImageUrl":"https://upload.wikimedia.org/wikipedia/commons/8/89/Tomato_je.jpg"},{"ingName":"onion","ingQty":1,"ingQtyUnit":"piece","ingImageUrl":"https://www.seeds-gallery.shop/9430-large_default/onion-seeds-dutch-yellow.jpg"}]" (type string) at path "ingredients.0"

      stringValue: '"[{"ingName":"tomato","ingQty":2,"ingQtyUnit":"piece","ingImageUrl":"https://upload.wikimedia.org/wikipedia/commons/8/89/Tomato_je.jpg"}]"',
      messageFormat: undefined,
      kind: '[ObjectId]',
      value: '[{"ingName":"tomato","ingQty":2,"ingQtyUnit":"piece","ingImageUrl":"https://upload.wikimedia.org/wikipedia/commons/8/89/Tomato_je.jpg"}]',
      path: 'ingredients.0',
      reason: [CastError],
      valueType: 'string'
    }
  },
  _message: 'Recipe validation failed'

How I was trying to do it:

Recipe Model

const mongoose = require("mongoose");

const recipeSchema = mongoose.Schema({
    title: {
        type: String,
        required: true,
    },
    imageUrl: {
        type: String,
        required: true
    },
    calories: {
        type: Number, 
        required: false,
    }, 
    duration: {
        type: Number,
        required: true,
        min: 0,
    },
    ingredients:[{
        type: mongoose.Schema.Types.ObjectId, 
        ref: "Ingredient"
    }]
})

const Recipe = mongoose.model('Recipe', recipeSchema);

module.exports = Recipe;

Ingredient Model:

const mongoose = require("mongoose");

const ingredientSchema = mongoose.Schema({
    _id: Schema.Types.ObjectId,
    ingName: {
        type: String,
        required: true,
    },
    ingQty: {
        type: Number,
        required: true,
    },
    ingQtyUnit: {
        type: String,
        required: true,
    },
    ingImageUrl: {
        type: String,
        required: true,
    }
})

const Ingredient = mongoose.model('Ingredient', ingredientSchema);

module.exports = Ingredient;

seeds.js

const mongoose = require('mongoose');
const Recipe = require('./models/recipe');

mongoose.connect('mongodb://localhost:27017/foodApp', {useNewUrlParser: true, useUnifiedTopology: true})
    .then(() => {
        console.log("Mongo Connection open")
    })
    .catch((error) => {
        console.log("No, Mongo -> Connection Error " + error)
    })

const seedRecipes = [
    {
        title: "Spring vegetable pie",
        imageUrl: "https://recipes.lidl.co.uk/var/site/storage/images/1/9/4/3/243491-2-eng-GB/Spring-vegetable-pie.jpg",
        calories: 300,
        duration: 120,
        ingredients:[
            {
            ingName: "tomato",
            ingQty: 2,
            ingQtyUnit: "piece",
            ingImageUrl:"https://upload.wikimedia.org/wikipedia/commons/8/89/Tomato_je.jpg" 
        },
        {
            ingName: "onion",
            ingQty: 1,
            ingQtyUnit: "piece",
            ingImageUrl:"https://www.seeds-gallery.shop/9430-large_default/onion-seeds-dutch-yellow.jpg" 
        },
      ]
    },
]

Recipe.insertMany(seedRecipes)
.then(response => {
    console.log(response)
})
.catch( error => {
    console.log(error)
});


Solution 1:[1]

In case anyone is looking for an answer, this is what I've done. I created a new seeds folder with 2 files index.js seeds.js.

In my seeds.js I added my data recipes objects and exported it so I can use it somewhere else.

module.exports = [
    {
        title: "Mushroom and courgette fritter burgers",
        imageUrl: "https://recipes.lidl.co.uk/var/site/storage/images/2/5/4/2/252452-2-eng-GB/Mushroom-and-courgette-fritter-burgers.jpg",
        calories: 250,
        duration: 30,
        ingredients: [
            {
                ingName: "mushrooms",
                ingQty: 200,
                ingQtyUnit: "grams",
                ingImageUrl:"https://snaped.fns.usda.gov/sites/default/files/styles/crop_ratio_7_5/public/seasonal-produce/2018-05/mushrooms.jpg?h=b754914e&itok=Kldbq8Du" 
            },
            {
                ingName: "zucchini",
                ingQty: 2,
                ingQtyUnit: "piece",
                ingImageUrl:"https://healme.in/wp-content/uploads/2021/06/zucchini-2-1200.jpg" 
            }
        ]
    },    
]

Then, in my index.js from seeds folder

const mongoose = require('mongoose');
const Recipe = require('../models/recipe');
const Ingredient = require ('../models/ingredient')
const recipes = require('../seeds/seeds')

mongoose.connect('mongodb://localhost:27017/foodApp', {useNewUrlParser: true, useUnifiedTopology: true})
    .then(() => {
        console.log("Mongo Connection open from seeds")
    })
    .catch((error) => {
        console.log("No, Mongo -> Connection Error " + error)
    })

const seedDB = async () => {
    await Recipe.deleteMany({});
    let title;
    let imageUrl;
    let calories;
    let duration;
    let ingredients;
    for (let i = 0; i < recipes.length; i++) {
        title = recipes[i].title;
        imageUrl = recipes[i].imageUrl;
        calories = recipes[i].calories;
        duration = recipes[i].duration;
        ingredients = recipes[i].ingredients;
        for (let ing of ingredients) {
            ingredients = ing;
        }
    const newRecipe = new Recipe ({
        title: title,
        imageUrl: imageUrl,
        calories: calories,
        duration: duration,
        ingredients: new Ingredient({
            ingName: ingredients.ingName,
            ingQty: ingredients.ingQty,
            ingQtyUnit: ingredients.ingQtyUnit,
            ingImageUrl:ingredients.ingImageUrl,
        })
    })
        await newRecipe.save();
    }
}

seedDB();

Recipe Schema:

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const recipeSchema = new Schema({
    title: {
        type: String,
        required: true,
    },
    imageUrl: {
        type: String,
        required: true
    },
    calories: {
        type: Number, 
        required: false,
    }, 
    duration: {
        type: Number,
        required: true,
        min: 0,
    },
    ingredients: {
        _id: {
          type:mongoose.Schema.Types.ObjectId,
          ref:"Ingredient"
        }, 
        ingName: String,
        ingQty: Number, 
        ingQtyUnit: String,
        ingImageUrl: String,
    },
})

const Recipe = mongoose.model('Recipe', recipeSchema);

module.exports = Recipe;

Ingredient Schema:

const mongoose = require("mongoose");

const ingredientSchema = mongoose.Schema({
    ingName: {
        type: String,
        required: true,
    },
    ingQty: {
        type: Number,
        required: true,
    },
    ingQtyUnit: {
        type: String,
        required: true,
    },
    ingImageUrl: {
        type: String,
        required: true,
    },
})

const Ingredient = mongoose.model('Ingredient', ingredientSchema);

module.exports = Ingredient;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1