I'm using mongoose, and I'm getting an error while trying to push some data into an embedded array for one of my already established documents. My app is basically like a forum where a user post a topic question, someone can answer it, and then someone can post a comment on that answer. The problem is that, I've set up my database so that the topics and answers are referenced to one another in separate models, but the comments are embedded within the answer schema. With every method I try I get an error saying either:
message: 'The field \'comment\' must be an array but is of type Object in document {_id: ObjectId(\'5669acad9b68142c1258b472\')}',
driver: true,
index: 0,
code: 16837,
errmsg: 'The field \'comment\' must be an array but is of type Object in document {_id: ObjectId(\'5669acad9b68142c1258b472\')}' }
or:
{ [ValidationError: Answer validation failed]
message: 'Answer validation failed',
name: 'ValidationError',
errors:
{ 'comment.1._id':
{ [CastError: Cast to ObjectID failed for value "[object Object]" at path
"_id"]
message: 'Cast to ObjectID failed for value "[object Object]" at path "_
id"',
name: 'CastError',
kind: 'ObjectID',
value: [Object],
path: '_id',
reason: undefined } } }
Answer.js: model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var AnswerSchema = new Schema(
{
_topic: {type: Schema.Types.ObjectId, ref: 'Topic'},
_user: {type: Schema.Types.String, ref: 'User'},
likes: Number,
dislikes: Number,
content: String,
created_at: {type: Date, default: new Date},
comment: [{
_user: {type: Schema.Types.String, ref: 'User'},
content: String
}]
});
var Answer = mongoose.model('Answer', AnswerSchema);
topics.js: controller
add_comment: function(req,res)
{
Answer.findOne({_id: req.body.answerid}, function(err, answer)
{
if(err)
{
console.log(err)
}
else
{
console.log(answer);
answer.comment.push([{_user: req.body.currentUser, content: req.body.content}]);
answer.save(function(err)
{
if(err)
{
console.log(err);
}
else
{
res.redirect('/');
}
})
}
})
}
if i simply update the data (to replace other comments) it will process it with no err, if I use an operator like $push I'll receive the same errors.
Thanks for any help.
Related
What is the proper way to save an array of data in mongodb using node/mongoose?
I am able to all my data as an array, but everything is saved as the first value in the array (see img)
this is my current code:
const channel = new Channel({
// other fields
fields:req.body.fields,
creator: req.userData.userId //fix: creator not being passed
});
channel
.save()
.then(createdChannel => {
res.status(201).json({
message: "Channel added successfully",
channel: {
...createdChannel,
id: createdChannel._id
}
});
})
.catch(error => {
console.log('error',error)
res.status(500).json({
message: "Creating a channel failed!"
});
});
here is my data model:
const mongoose = require("mongoose");
const channelSchema = mongoose.Schema({
// other fields
fields: { type: [String], required: true },
creator: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }
});
module.exports = mongoose.model("Channel", channelSchema);
I didn't see a similar issue online.
Any idea on how I am suppose to save an array properly?
Looks like your req.body.fields is stringified. So, you should parse it back to JSON:
const channel = new Channel({
fields: JSON.parse(req.body.fields),
creator: req.userData.userId,
});
This is because you are setting fields value as an array of strings, regarding to this question you should define your schema as:
fields : [{foo: String, bar: string}]
or if you don't know what attributes you will have in the object:
fields: {"type": Array, "default": []}
Hope this can help you
Context: I am trying to simply console log a value from a document that I queried for. I am successfully finding the document. I know because I print out the doc before I print out the value of the doc I am looking for. The doc prints find, the field on the other hand comes out as undefined.
Here is the code:
Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let TextSchema = new Schema({
text_ID: {type: Number, required: true},
title: {type: String, required: true, max: 100},
body: {type: String, required: true},
});
// Export the model
module.exports = mongoose.model('texts', TextSchema);
I know that Mongo creates its own IDs for everything but bear with me on my own ID's for now.
Here is the problematic code:
textModel.find({text_ID: currentTextLocation}, function(err, doc){
if(err){
console.log('error: ' + err);
}
console.log(doc);
console.log(doc.body);
currentTextBody = doc.body;
});
Here is the output it gives me:
Database connection established!
[
{
_id: 5dc2915f1c9d440000ed7077,
text_ID: 1,
title: 'Kevin Abstract',
body: 'Kevin Abstract was a famous american rap artist.'
}
]
undefined
Finally here is what it should look like:
Database connection established!
[
{
_id: 5dc2915f1c9d440000ed7077,
text_ID: 1,
title: 'Kevin Abstract',
body: 'Kevin Abstract was a famous american rap artist.'
}
]
Kevin Abstract was a famous american rap artist.
I have made sure that the schema relates to the correct collection. I tried printing the type of doc, that returned an object.
hi im currently building something for fun that allows users to post anything. and im experincing some problems here is my code.
return details.findOne({'data': {$elemMatch: {'_id':req.params.id}}}).then((a) => {
return res.render('post', { a });
}).catch(err => console.log(err));
i only want this to return one post i thought by using the objectId id would be able todo that but it seems to return everything in the data array anybody have any ideas below is my schema.
i need this to only return the single object whos objId is in the url
var schemaMode = new mongoose.Schema({
email: {type: String, required: true},
password: {type: String, required: true},
username: {type: String,required:true},
data: [{
author: String,
title: String,
comments: [String],
article: String,
}]
});
Details.findById(req.params.id, function(err, foundObject){
//foundObject contains the object with the ._id matching req.params.id
});
if you only want certain fields back, like you want the object to only have its data field for example, then you do:
Details.findById(req.params.id, "data",
function(err, foundObject){
//foundObject contains the object with the ._id matching req.params.id
});
OR
Details.findById(req.params.id).select("data")
.exec(function(err, foundObject){
//foundObject contains the object with the ._id matching req.params.id
});
to be clear, in the code above, Details is the imported (with require) schema (in your case, the one named schemaMode)
Is "data" your "post"? If so, I think you need a projection.
return details.findOne({'data': {$elemMatch: {'_id':req.params.id}}},{'data.$'}).then((a) => {
return res.render('post', { a.data[0] });
}).catch(err => console.log(err));
'data.$' will project you the whole model, filled only with the desired "data"/"post"
I have a document 'Collection':
{
name: {type: String, required: true, default: "Untitled Collection"},
movies: [
{ the_id: {type: String},
movie_id: {type: String},
tmdb_id: {type: String},
original_title: {type: String},
release_date: {type:Date}
}],
author: {type: String},}
I need to find and remove a specific item from movies []. This is what I have currently, and it does not work.
req.body is an object passed through the data of a POST request and has all the information necessary to be able to match one in the movies[] array
Collection.findOne({_id : req.params.id}, (err, collection) =>{
if(err){res.status(400).json(err);}
if(!collection)
{
res.status(404).json({message: 'No collection found'});
}else{
collection.movies.pop(req.body);
collection.save();
res.json(collection);
}
});
All it currently does is pop off the front object in the array, but I need it to remove the object in the array that is equal to req.body
Look into the $pull operator
collection.movies.pull(req.body);
You can remove an item in an array with $pull
Collection.update({
_id: req.params.id
}, {
$pull: {
'movies': req.body
}
}, (err, collection) => {
if (err) {
res.status(400).json(err);
}
console.log(res);
res.status(200);
});
I got 3 database models in mongoose that looks like this:
//profile.js
var ProfileSchema = new Schema({
username: { type: String, required: true },
password: { type: String, required: true },
matches: [{ type: Schema.Types.ObjectId, ref: 'Match' }]
});
//match.js
var MatchSchema = new Schema({
scores: [{ type: Schema.Types.ObjectId, ref: 'Score', required: true }],
});
//score.js
var ScoreSchema = new Schema({
score: {type: Number, required: true},
achivement: [{type: String, required: true}],
});
And I try to populate a profile with
Profile.findOne({ _id: mongoose.Types.ObjectId(profile_id) })
.populate('matches')
.populate('matches.scores')
.exec(function(err, profile) {
if (err) {...}
if (profile) {
console.log(profile);
}
});
The matches get populated but I dont get the scores in matches to populate. Is this not supported in mongoose or do I do something wrong? Populate gives me this:
{
user_token: "539b07397c045fc00efc8b84"
username: "username002"
sex: 0
country: "SE"
friends: []
-matches: [
-{
__v: 1
_id: "539eddf9eac17bb8185b950c"
-scores: [
"539ee1c876f274701e17c068"
"539ee1c876f274701e17c069"
"539ee1c876f274701e17c06a"
]
}
]
}
But I want to populate the score array in the match array. Can I do this?
Yes, you are right. I tried using Chaining of populate I got same output.
For your query please use async.js and then populate by the method mentioned below.
For more details please have a look at this code snippet. It is a working, tested, sample code according to your query. Please go through the commented code for better understanding in the code below and the link of the snippet provided.
//Find the profile and populate all matches related to that profile
Profile.findOne({
_id: mongoose.Types.ObjectId(profile_id)
})
.populate('matches')
.exec(function(err, profile) {
if (err) throw err;
//We got the profile and populated matches in Array Form
if (profile) {
// Now there are multiple Matches
// We want to fetch score of each Match
// for that particular profile
// For each match related to that profile
async.forEach(profile.matches, function(match) {
console.log(match, 'match')
// For each match related to that profile
// Populate score achieved by that person
Match.find({
_id:match.id
})
.populate('scores', 'score')
.exec(function (err, score) {
if (err) throw err;
// here is score of all the matches
// played by the person whose profile id
// is passed
console.log(score);
})
})
}
});
Profile.findOne({ _id: mongoose.Types.ObjectId(profile_id) })
.populate('matches.scores')
.exec(function(err, profile) {
if (err) {...}
if (profile) {
console.log(profile);
}
});