Mongoose can't push new obejct to parent array - node.js

I've got the following mongoose models:
Place.js
const mongoose = require("mongoose")
const Schema = mongoose.Schema
const placeSchema = new Schema({
title: { type: String, require: true },
filename: { type: String, require: true },
lociSets: [{ type: Schema.Types.ObjectId, ref: 'LociSet'}]
})
module.exports = mongoose.model("places", placeSchema)
LociSet.js
const mongoose = require("mongoose")
const Schema = mongoose.Schema
const LociSchema = require('./Locus')
const lociSetSchema = new Schema({
title: { type: String, require: true },
creator: { type: Schema.Types.ObjectId, ref: 'User' },
public: { type: Boolean, default: true },
loci: [LociSchema]
})
module.exports = mongoose.model("lociSets", lociSetSchema)
Locus.js
const mongoose = require("mongoose")
const Schema = mongoose.Schema
const locusSchema = new Schema({
position: {
x: { type: Number, require: true },
y: { type: Number, require: true },
z: { type: Number, require: true }
}
})
module.exports = locusSchema
Problem:
I try to insert a new LociSet into the lociSet array of Place like so:
exports.createOne = async (req, res) => {
const {
title,
public = true,
loci = []
} = req.body
console.log(title,public,loci,req.user.id)
const lociSet = new LociSet({
title,
public,
loci,
creator: req.user.id
})
try {
const place = await Place.findOne({
"title": req.params.title.toLowerCase()
})
console.log(lociSet)
await lociSet.save()
await place.lociSets.push(lociSet)
await place.save()
} catch (err) {
res.status(500).send({
message: "Some error occurred while creating the loci set.", err
});
}
}
But then I get an error message saying "Cast to [undefined] failed for value \"[{\"title\":\"Test set\",\"creator\":\"5a7898c403999200c4ee3ae5\",\"public\":\"true\"}]\" at path \"lociSets\""
The LociSet model is created without problems, but it seems to break when I try to save the place model

Because lociSets is an array of ObjectId references, you may want to try the following approach:
exports.createOne = async (req, res) => {
const { title, public = true, loci = [] } = req.body
const lociSet = new LociSet({
title,
public,
loci,
creator: req.user.id
})
try {
const newLociSet = await lociSet.save()
const place = await Place.findOneAndUpdate(
{ "title": req.params.title.toLowerCase() },
{ "$push": { "lociSets" : newLociSet._id } },
{ "new": true}
)
res.status(200).json(place)
} catch (err) {
res.status(500).send({
message: "Some error occurred while creating the loci set.", err
})
}
}

Related

Mongodb unlimited nested category, sub-category and sub-sub-category and so on

I created an e-commerce website but want to change a couple of features of it, I want to allow users to create unlimited categories and subcategories. what is the best approach for it? I used Nodejs (express) and MongoDB (mongoose).
Currently, the DB schema is this one:
category.js
const mongoose = require('mongoose');
const { s, rs, n, ref } = require('../utils/mongo');
var schema = new mongoose.Schema(
{
user: ref('user'),
name: { ...rs, unique: true },
description: s,
image: s,
view: {
...n,
default: 0,
},
},
{ timestamps: true }
);
module.exports = mongoose.model('category', schema);
subcategory.js
const mongoose = require('mongoose');
const { s, rs, ref } = require('../utils/mongo');
var schema = new mongoose.Schema(
{
category: ref('category'),
name: { ...rs, unique: true },
description: s,
},
{ timestamps: true }
);
module.exports = mongoose.model('subcategory', schema);
Appreciate in advance
just use one model
import mongoose from "mongoose";
const CategorySchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
},
parent_id: {
type: mongoose.Schema.Types.ObjectId,
default: null
},
description: {
type: String,
default: null
},
image: {
url: String,
public_id: String
},
});
export mongoose.model('Category', CategorySchema);
// Create category
import { Category } from "./model/category.model"
const createCategory = async (data) => {
try {
return await new Category(data).save()
} catch (err) {
console.log(err)
}
}
// Get all category
const getAllCategory = async () => {
try {
const categories = await Category.find({});
if (!categories) return [];
return nestedCategories(categories);
} catch (err) {
console.log(err);
}
}
function nestedCategories(categories, parentId = null) {
const categoryList = [];
let category;
if (parentId == null) {
category = categories.filter(cat => cat.parent_id == null);
} else {
category = categories.filter(cat => String(cat.parent_id) == String(parentId));
}
for (let cate of category) {
categoryList.push({
_id: cate._id,
name: cate.name,
slug: cate.slug,
children: nestedCategories(categories, cate._id)
})
}
return categoryList;
}
If you want unlimited nested category you will only need one single category schema.
let schema = new mongoose.Schema(
{
user: ref('user'),
name: { ...rs, unique: true },
description: s,
image: s,
parent: { type: mongoose.Schema.Types.ObjectId }, //the parent category
view: {
...n,
default: 0,
},
},
{ timestamps: true }
);
module.exports = mongoose.model('category', schema);
Unfortunately, you cannot use populate and $lookup without specifying a specific level, so you need build the tree yourself if you need unlimited nested.

How to delete referenced data using mongoose

I have two schemas as below.
MirFile Schema
const mongoose = require("mongoose");
const {ObjectId} = mongoose.Schema
// Creating a Schema for uploaded files
const MirFileSchema = new mongoose.Schema({
createdAt: {
type: Date,
default: Date.now,
},
name: {
type: String,
required: [true, "Uploaded file must have a name"],
},
image:{
type:String
},
belongsTo:{
type:ObjectId,
ref:"MirFolder"
}
});
const File = mongoose.model("MirFile", MirFileSchema);
MirFileSchema.pre('remove', function(next) {
MirSubFile.remove({belongsTo: this._id}).exec();
next();
});
module.exports = File;
MirSubFileSchema
const mongoose = require("mongoose");
const {ObjectId} = mongoose.Schema
// Creating a Schema for uploaded subfiles
const MirSubFileSchema = new mongoose.Schema({
createdAt: {
type: Date,
default: Date.now,
},
text: {
type: String,
required: [true, "Uploaded file must have a text"],
},
image:{
type:String
},
belongsTo:{
type:ObjectId,
ref:"MirFile"
}
});
const File = mongoose.model("MirSubFile", MirSubFileSchema);
module.exports = File;
When I delete MirFile the document from MirSubFile which holds the _id of MirFile in belongsTo field must be deleted.I have used prehook but its not working.Any guide.?
Why don't you simply add your own 'deleteOne' or 'remove' Mongoose middleware on the MirFileSchema to deleteOne or remove all other documents
exports.DeleteMirFileSchema = (req, res) => {
MirFileSchema.deleteOne({ _id: req.query._id }, { new: true }, (err, data) => {
if (err) {
console.log(err);
let errorKeyArray = Object.keys(err.errors);
let msgArray = errorKeyArray.map((obj) => {
return err.errors[obj];
});
ResponseObj.errorResponse(res, { status: 400, msg: msgArray.join(", ") });
} else ResponseObj.successResponse(res, data);
});
};

Why this code is creating two similar object in mongodb?

Node js
The method used to post the comment data
const { type, movieId, userId, review, timestamp } = req.body;
const movie = await Movie.findOneAndUpdate(
{ movieId: movieId, type: type },
{
$push: {
movieReview: { ... },
},
},
{ upsert: true, new: true },
(err, info) => {
...
}
);
Reactjs
The method used to submit the comment
const submit_comment = async () => {
....
const file = {
movieId: id,
type: type,
userId: userInfo._id,
comment: comment,
};
if (!commentFlag) {
const { data } = await axios.post("/api/movie/add-comment", file, config);
...
};
Mongoose Schema
const movieSchema = new mongoose.Schema({
movieId: String,
type: String,
...
comment: [{ type: mongoose.Schema.Types.ObjectId, ref: "Comment" }],
});
After I run my submit function it posts two objects with the same object _id in mongoDB

Feathersjs-Mongoose populate data

When using find method, how can populate data from other collection. The join operation that we do with sql databases. Right now i am using something like :
code:
async find(data, params) {
let records = await super.find(data, params);
let newrecords = records.data.map(async (user) => {
let professordetails = await this.app
.service("users")
.get(user.professorId);
professordetails.password = undefined;
user.professorId = professordetails;
return user;
});
return await Promise.all(newrecords).then((completed) => {
return completed;
});
}
This is course service and its model :
module.exports = function (app) {
const modelName = "courses";
const mongooseClient = app.get("mongooseClient");
const { Schema } = mongooseClient;
const { ObjectId } = Schema;
const schema = new Schema(
{
name: { type: String, required: true },
details: { type: String, required: true },
professorId: { type: ObjectId, ref: "users", required: true },
enrolledStudents: [{ type: ObjectId, ref: "users" }],
},
{
timestamps: true,
}
);
// This is necessary to avoid model compilation errors in watch mode
// see https://mongoosejs.com/docs/api/connection.html#connection_Connection-deleteModel
if (mongooseClient.modelNames().includes(modelName)) {
mongooseClient.deleteModel(modelName);
}
return mongooseClient.model(modelName, schema);
};
This is something like a unwanted operation as we are having populate. But i couldn't do it with populate.

Cast to ObjectID failed for value "5e54c953031de84cd86b22a0 " at path "author"

I want to insert my book to Collection Book. In book, i have field author is a part of another Collection called Author. I try to insert the book but i got Mongoose casterror. This is my code:
const mongoose = require('mongoose')
const path = require('path')
const coverImageBasePath = 'uploads/bookCovers'
const bookSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
description: {
type: String
},
publishDate: {
type: Date,
required: true
},
pageCount: {
type: Number,
required: true
},
createdAt: {
type: Date,
required: true,
default: Date.now
},
coverImageName: {
type: String,
required: true
},
author: {
type: mongoose.Types.ObjectId,
required: true,
ref: 'Author'
}
})
module.exports = mongoose.model('Book', bookSchema)
And this is post router to create a book
router.post('/', upload.single('cover'), async (req, res) => {
const fileName = req.file != null ? req.file.filename : null
const book = new Book({
title: req.body.title,
author: req.body.author,
publishDate: new Date(req.body.publishDate),
pageCount: req.body.pageCount,
coverImageName: fileName,
description: req.body.description
})
try {
const newBook = await book.save()
// res.redirect(`books/${newBook.id}`)
res.redirect(`books`)
} catch (err) {
console.log(err)
if (book.coverImageName != null) {
removeBookCover(book.coverImageName)
}
renderNewPage(res, book, true)
}
})
async function renderNewPage(res, book, hasError = false) {
try {
const authors = await Author.find({})
const params = {
authors: authors,
book: book
}
if (hasError) params.errorMessage = 'Error Creating Book'
res.render('books/new', params)
} catch {
res.redirect('/books')
}
}
I tried some solution and googled but it did not work, when i insert straightly author by String, it works so i think the bug is in the mongoose Schema but i can not find out the way to solve it. Please help me.

Resources