Mongoose is only returning ID from MongoDB - node.js

I am currently trying to incorporate datatables with my MongoDB database. I am having some trouble accessing the returned object though. The main problem I am seeing is that I am only getting the _id returned from MongoDB, and no values of the object.
Heres the code I am using to pass the information to the datatables.
var itemsModel = require('./models/itemReturn');
exports.getItemList = function(req, res) {
var searchStr = req.body.search.value;
if (req.body.search.value) {
var regex = new RegExp(req.body.search.value, "i")
searchStr = { $or: [{ 'productName': regex }, { 'itemPrice': regex }, { 'Quantity': regex }, { 'Description': regex }, { 'seller': regex }] };
} else {
searchStr = {};
}
var recordsTotal = 0;
var recordsFiltered = 0;
itemsModel.count({}, function(err, c) {
recordsTotal = c;
console.log(c);
itemsModel.count(searchStr, function(err, c) {
recordsFiltered = c;
itemsModel.find(searchStr, 'productName itemPrice Quantity Description seller', { 'skip': Number(req.body.start), 'limit': Number(req.body.length) }, function(err, results) {
if (err) {
console.log('error while getting results' + err);
return;
}
var data = JSON.stringify({
"draw": req.body.draw,
"recordsFiltered": recordsFiltered,
"recordsTotal": recordsTotal,
"data": results
});
console.log(data);
res.send(data);
});
});
});
};
This is the model
// app/models/itemsReturn.js
// load the things we need
var mongoose = require('mongoose');
var schemaOptions = {
timestamps: true,
toJSON: {
virtuals: true
},
toObject: {
virtuals: true
}
};
// define the schema for our item model
var itemsReturned = mongoose.Schema({
productName: String,
itemPrice: String,
Quantity: String,
Description: String,
seller: String
}, schemaOptions);
// create the model for users and expose it to our app
var items = mongoose.model('items', itemsReturned);
module.exports = items;
The thing is that I know its not a data table issue as I can make the _id appear in the tables. I just need to know how to return the entire object instead of just the _ID so that I can access the values of the object.
If it helps this is the tutorial I am following.
UPDATE: Okay so I figured out why my MongoDB collections were only returning the item ID. The issue was that I had stored everything in the local database (oops).

Related

MongoDB mongoose subdocuments created twice

I am using a simple form that can be used to register an article to a website.
the back-end looks like this:
// Post new article
app.post("/articles", function(req, res){
var newArticle = {};
newArticle.title = req.body.title;
newArticle.description = req.body.description;
var date = req.body.date;
var split = date.split("/");
newArticle.date = split[1]+'/'+split[0]+'/'+split[2];
newArticle.link = req.body.link;
newArticle.body = req.body.body;
var platforms = req.body.platforms;
console.log(platforms);
Article.create(newArticle, function(err, createdArticle){
if(err){
console.log(err.message);
} else {
var counter=0;
platforms.forEach(function(platform){
var platformed=mongoose.mongo.ObjectID(platform);
Platform.findById(platformed, function(err, foundPlatform){
if(err){
console.log(err);
} else {
counter++;
foundPlatform.articles.push(createdArticle);
foundPlatform.save();
createdArticle.platforms.push(foundPlatform);
createdArticle.save();
if(counter==platforms.length){
res.redirect('articles/' + createdArticle._id);
}
}
});
});
}
});
});
The platforms field is passed to the back-end as an array of strings, one string being one objectID. When platforms only contains 1 string i.e. 1 platform to be linked to, everything works fine. When platforms contains multiple string. the created article has duplicates of each platform. Or sometimes only duplicates of some platforms
Any ideas?
UPDATE 1:
Article Schema:
var mongoose = require("mongoose");
var articleSchema = new mongoose.Schema({
title : String,
description : String,
link : String,
date : String,
body : String,
platforms : [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Platform"
}
]
})
module.exports = mongoose.model("Article", articleSchema);
Platform Schema:
var mongoose = require("mongoose");
var platformSchema = new mongoose.Schema({
name : String,
category : String,
contacts : [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Contact"
}
],
website : String,
country : String,
contactInformation : String,
businessModelNotes : String,
source : String,
generalNotes : String,
projects : [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Project"
}
],
articles : [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Article"
}
],
privacy : String,
comments : [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
module.exports = mongoose.model("Platform", platformSchema);
The forEach loop in your attempt does not recognise the callback completion of the findById() async method before the next iteration. You need to use any of async library methods async.each, async.whilst, or async.until which are equivalent to a for loop, and will wait until async's callback is invoked before moving on to the next iteration (in other words, a for loop that will yield).
For example:
var platform_docs = [];
async.each(platforms, function(id, callback) {
Platform.findById(id, function(err, platform) {
if (platform)
platform_docs.push(platform);
callback(err);
});
}, function(err) {
// code to run on completion or err
console.log(platform_docs);
});
For the whole operation, you could use the async.waterfall() method which allows each function to pass its results on to the next function.
The first function in the method creates the new article.
The second function uses the async.each() utility function to iterate over the platforms list, perform an asynchronous task for each id to update the platform using findByIdAndUpdate(), and when they're all done return the results of the update query in an object variable to the next function.
The final function will update the newly created article with the platform ids from the previous pipeline.
Something like the following example:
var newArticle = {},
platforms = req.body.platforms,
date = req.body.date,
split = date.split("/");
newArticle.title = req.body.title;
newArticle.description = req.body.description;
newArticle.date = split[2]+'/'+split[0]+'/'+split[2];
newArticle.link = req.body.link;
newArticle.body = req.body.body;
console.log(platforms);
async.waterfall([
// Create the article
function(callback) {
var article = new Article(newArticle);
article.save(function(err, article){
if (err) return callback(err);
callback(null, article);
});
},
// Query and update the platforms
function(articleData, callback) {
var platform_ids = [];
async.each(platforms, function(id, callback) {
Platform.findByIdAndUpdate(id,
{ "$push": { "articles": articleData._id } },
{ "new": true },
function(err, platform) {
if (platform)
platform_ids.push(platform._id);
callback(err);
}
);
}, function(err) {
// code to run on completion or err
if (err) return callback(err);
console.log(platform_ids);
callback(null, {
"article": articleData,
"platform_ids": platform_ids
});
});
},
// Update the article
function(obj, callback) {
var article = obj.article;
obj.platform_ids.forEach(function(id){ article.platforms.push(id); });
article.save(function(err, article){
if (err) return callback(err);
callback(null, article);
});
}
], function(err, result) {
/*
This function gets called after the above tasks
have called their "task callbacks"
*/
if (err) return next(err);
console.log(result);
res.redirect('articles/' + result._id);
});
Move your save function
if(counter==platforms.length){
createdArticle.save(function(err, savedObject){
if(err || !savedObject) console.log(err || "not saved");
else {
res.redirect('articles/' + savedObject._id.toString());
}
});
}
============= EDIT
Its because you have to call article.save only one time, and not at each loop. In addition you use save() as a sync function but it's async.
I thinks you should use directly update function :
} else {
var counter=0;
// map plateform array id with ObjectID
var idarray = platforms.map(function(e){return mongoose.mongo.ObjectID(e);});
// update all plateform with article id
Platform.update({_id:{$in: idarray}}, {$push:{articles: createdArticle}}, {multi:true, upsert:false}, function(err, raw){
if(err)
{
// error case
return res.status(403).json({});
}
// retrieve plateform
Platform.find({_id:{$in: idarray}}, function(err, results){
if(err || !results)
{
// error case
return res.status(403).json({});
}
Article.update({_id: createdArticle._id.toString()}, {$push:{platforms:{$each: results}}}, {multi:false, upsert:false}, function(err, saved){
if(err || !saved)
{
// error
return res.status(403).json({});
}
res.redirect('articles/' + savedObject._id.toString());
});
});
});
But it's a bad idea to store full objects, why not storing only id ??

How to convert multiple Mongoose documents?

Each of my schemas have a method, called toItem() which converts the doc to a more verbose / human-readable form. How can I create a toItems() method to do the same thing for an array of documents?
My example schema:
var mongoose = require('mongoose');
var membershipSchema = new mongoose.Schema({
m : { type: mongoose.Schema.ObjectId, ref: 'member' },
b : { type: Date, required: true },
e : { type: Date },
a : { type: Boolean, required: true }
});
var accountSchema = new mongoose.Schema({
n : { type: String, trim: true },
m : [ membershipSchema ]
});
accountSchema.methods.toItem = function (callback) {
var item = {
id : this._id.toString(),
name : this.n,
members : []
};
(this.m || []).forEach(function(obj){
item.members.push({
id : obj.m.toString(),
dateBegin : obj.b,
dateEnd : obj.e,
isAdmin : obj.a
});
});
return callback(null, item);
};
var accountModel = mongoose.model('account', accountSchema);
module.exports = accountModel;
I've tried using statics, methods, and third-party libraries, but nothing clean works. I would like to keep this as simple / clean as possible and have the toItems() function contained within my model file.
Thank you, in advance.
Your toItem() method is specific to the schema / model. Your toItems() method sounds more like a utility method which can / will be used by all of your models. If so, I would move create the toItems() method inside a utility file. You would simply pass in the array of documents and the utility method would call the individual toItem() method on each document.
For example:
var async = require('async');
var toItems = function (models, callback) {
models = models || [];
if (models.length < 1) { return callback(); }
var count = -1,
items = [],
errors = [];
async.forEach(models, function (model, next) {
count++;
model.toItem(function (err, item) {
if (err) {
errors.push(new Error('Error on item #' + count + ': ' + err.message));
}
else {
items.push(item);
}
next();
});
}, function (err) {
if (err) {
return callback(err);
}
if (errors.length > 0) {
return callback(errors[0]);
}
return callback(null, items);
});
};
module.exports.toItems = toItems;

How to patch array fields in subdocumented schema structure in mongodb,nodejs taking values from user

Please dont give a solution which doesn't match requirement.
I am writing REST api, GET,POST,PUT,DELETE are working awesome but i am having problem with PATCH, if you can please correct it. I am attaching required code, if you need full code please comment so with your contact details.
Modelfile1: TaskInfo Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var taskInfo = mongoose.Schema({
isactive:{type:Boolean},
taskobject:{type:String},
taskdetails:{type:String},
iscompleted:{type:Boolean}
});
var TaskInfo = mongoose.model('TaskInfo', taskInfo);
module.exports.TaskInfo = TaskInfo;
module.exports.TaskInfoSchema = taskInfo;
Model file 2 Task Schema
var mongoose = require('mongoose');
var TaskInfoSchema = require("./taskinfo.js").TaskInfoSchema
var Schema = mongoose.Schema;
// Task schema
var taskSchema = mongoose.Schema({
tasktype : {type: String},
createdon : {type: Date, default: Date.now},
//createdby : {type: Schema.Types.ObjectId,ref: 'User'},
//visitedby : [{type: Schema.Types.ObjectId,ref: 'User'}],
taskinfo : [TaskInfoSchema]
});
module.exports = mongoose.model('Task', taskSchema);
Route file api.js-- You have to correct this file ---
var TaskInfo = require('../models/taskinfo.js').TaskInfo;
var Task = require('../models/task.js');
var config = require('../../config');
module.exports = function (app, express) {
var api = express.Router();
//GET method is for fetching all the tasks from the database,
api.get('/taskdb', function (req, res) {
//console.log("____");
Task.find({}, function (err, taskdb) {
if (err) {
res.send(err);
return;
}
res.json(taskdb);
});
});
//POST method is for saving all the tasks to the database,
api.post('/tasks', function (req, res) {
var task = {};
task.tasktype = req.body.tasktype;
task.taskinfo = [];
for (var i = 0; i < req.body.taskInfo.length; i++) {
console.log(i);
var taskInfo = new TaskInfo(req.body.taskInfo[i]);
task.taskinfo.push(taskInfo);
}
var taskObj = new Task(task);
taskObj.save(function (err) {
if (err) {
res.send(err);
return;
}
res.json({
message: 'Task has been created'
})
});
});
//middleware
api.use('/tasks/:taskId',function(req, res, next){
Task.findById(req.params.taskId, function(err, task){
if(err){
res.send(err);
return;
}
else if(task)
{
req.task = task;
next();
}
else
{
res.status(404).send ('no task found with such details');
}
});
});
!-- // PLEASE CORRECT THIS PATCH METHOD --! want to partial update array fields
//patch method--- wrong implementation (google how to patch array fields)
api.patch ('/tasks/:taskId',function(req,res){
// if(req.body._id)
// delete req.body._id;
if(req.body.tasktype)
{
req.task.tasktype = req.body.tasktype;
}
req.task.save(function(err){
if(err){
res.send(err);
return;
}
res.json({message:'Task edited & updated '})
});
});
// update error put method code isn't working for boolean fields -- have to input 0 or 1 (then works correct)
//put method
api.put ('/tasks/:taskId',function(req,res){
//var task = {};
req.task.tasktype = req.body.tasktype;
req.task.taskinfo = [];
for (var i = 0; i < req.body.taskInfo.length; i++) {
console.log(i);
var taskInfo = new TaskInfo(req.body.taskInfo[i]);
req.task.taskinfo.push(taskInfo);
}
req.task.save(function(err){
if(err){
res.send(err);
return;
}
res.json({message:'User info updated successfully'})
});
});
//put ends here
//Task deletion is working fine
api.delete ('/tasks/:taskId',function(req,res){
req.task.remove(function(err){
if(err){
res.send(err);
return;
}
res.json({message:'Task has been deleted'})
});
});
api.get('/tasks/:taskId',function(req, res){
res.json(req.task);
});
return api
}
Json file:
[
{
"_id": "55f7a31c83dc2aa80b549516",
"__v": 13,
"tasktype": "Extreme",
"taskinfo": [
{
"_id": "55f84a6e110db2bc0d5159a2",
"iscompleted": false,
"taskdetails": "This is task 1",
"taskobject": "paid",
"isactive": false
},
{
"_id": "55f84a6e110db2bc0d5159a3",
"iscompleted": false,
"taskdetails": "This is task 2",
"taskobject": "paid",
"isactive": false
},
{
"_id": "55f84a6e110db2bc0d5159a4",
"iscompleted": true,
"taskdetails": "This is task 3",
"taskobject": "paid",
"isactive": true
},
{
"_id": "55f84a6e110db2bc0d5159a5",
"iscompleted": false,
"taskdetails": "This is task 4",
"taskobject": "paid",
"isactive": false
}
],
"createdon": "2015-09-15T04:48:28.631Z"
}
]
Do you have this working not using your above code, but directly from mongo shell ? Its handy to battle these syntax issues from there before implementing in code. I am also using mongoose and my PATCH uses the mongo update as per
DB.update(
myQuery,
{
$set: req.body
}
)
see doc at http://docs.mongodb.org/manual/reference/method/db.collection.update/
You can see it has built in a query to perform the find. Example myQuery :
{ id: '4' }
In my case I feed in the actual update in my req.body . Example req.body :
{ surname: 'Beethoven' }
This works.

Mongoose cascading deletes in same model

This is different than this and this. But they are very helpful.
Basically, I have a Topic schema. If one Topic get's deleted, I want to delete other topics. Think of a graph where deleting a node means deleting the edges.
var schema = new Schema({
title: { type: String, required: true, trim: true },
srcId: { type: Schema.Types.ObjectId, validate: [edgeValidator, 'Set both srcId and destId, or neither'] },
destId: Schema.Types.ObjectId,
});
I want the 2nd mongo delete to run in the schema.pre('remove', ...)
But I don't have a model at this point. So calling .find() or .remove() doesn't work. What's the best way?
schema.pre('remove', function(next) {
var query = '';
var self = this;
if (this.isEdge) {
query = 'MATCH ()-[r:CONNECTION { mongoId: {_id} }]-() DELETE r;';
} else {
query = 'MATCH (n:Topic { mongoId: {_id} })-[r]-() DELETE n,r;';
}
// This is another database.
neo.query(query, this, function(err, data) {
if (err) return next(err);
if (self.isEdge) {
return next();
} else {
// Now we're back to mongoose and mongodb
// Find and remove edges from mongo
schema.find({ mongoId: { // <------ .find() is undefined
$or: [
{ srcId: self._id },
{ destId: self._id }
]
}}, function(err, edges) {
edges.remove(next);
});
}
});
});
This turned out to be pretty easy.
var Model = null;
var schema = ...
module.exports = Model = mongoose.model('Topic', schema);
Then just use Model in the pre-remove. Piece of pie.

Mongoose nested document update failing?

If I have a nested document, how can I update a field in that nested document in Mongoose?
I carefully researched this problem using everything available I could find, and even changed my test code to match a similar answered question about this here on Stackoverflow, but I am still unable to figure this out. Here are is my Schema and Models, the code, and the Mongoose debug output. I am unable to understand what I am doing wrong, here.
var mongoose = require('mongoose')
, db = mongoose.createConnection('localhost', 'test')
, assert = require("node-assert-extras");
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
db.once('open', function () {
// yay!
});
mongoose.set('debug', true);
var PDFSchema = new Schema({
title : { type: String, required: true, trim: true }
})
var docsSchema = new Schema({
PDFs : [PDFSchema]
});
var A = db.model('pdf', PDFSchema);
var B = db.model('docs', docsSchema);
function reset(cb) {
B.find().remove();
// create some data with a nested document A
var newA = new A( { title : "my title" })
var newB = new B( { PDFs: newA});
newB.save();
cb();
}
function test1( ) {
reset(function() {
B.findOne({}, 'PDFs', function(e,o)
{
console.log(o);
pdf_id = o.PDFs[0]._id;
console.log("ID " + pdf_id);
B.update(
{ 'pdfs.pdf_id': pdf_id },
{ $set: {
'pdfs.$.title': 'new title'
}}, function (err, numAffected) {
if(err) throw err;
assert.equal(numAffected,1); //KA Boom!
}
);
});
});
}
test1();
/*
$ node test2.js
Mongoose: docs.remove({}) {}
Mongoose: docs.findOne({}) { fields: { PDFs: 1 }, safe: true }
Mongoose: docs.insert({ __v: 0, PDFs: [ { _id: ObjectId("50930e3d0a39ad162b000002"), title: 'my title' } ], _id: ObjectId("50930e3d0a39ad162b000003") }) { safe: true }
{ _id: 50930e3d0a39ad162b000003,
PDFs: [ { _id: 50930e3d0a39ad162b000002, title: 'my title' } ] }
ID 50930e3d0a39ad162b000002
assert.js:102
throw new assert.AssertionError({
^
AssertionError: 0 == 1
*/
You're not using the correct field names in your B.update call. It should be this instead:
B.update(
{ 'PDFs._id': pdf_id }, // <== here
{ $set: {
'PDFs.$.title': 'new title' // <== and here
}}, function (err, numAffected) {
if(err) throw err;
assert.equal(numAffected,1);
}
);
You should also fix your reset function to not call its callback until the save completes:
function reset(cb) {
B.find().remove();
// create some data with a nested document A
var newA = new A( { title : "my title" })
var newB = new B( { PDFs: newA});
newB.save(cb); // <== call cb when the document is saved
}

Resources