mongoose update with push operations on array and set operation on object - arrays

I have this mongoose schema
var ContactSchema = module.exports = new mongoose.Schema({
name: {
type: String,
required: true
},
phone: {
type: Number,
required: true,
},
messages: [
{
title: {type: String, required: true},
msg: {type: String, required: true}
}],
address:{ city:String,
state:String
}
});
I have initially the collection set with name and phone field. I need to update the collection with new messages into messages array and new address into address object. the function must also need to handle any single operation, ie in some case i have only update to messages array or updates to both name and address. so how i can i do all operations in a single function.
var messages= {
title: req.body.title,
msg: req.body.msg
}
Model.findOneAndUpdate({'_id': req.body.id,},{$push: {messages:message}},{upsert: true}, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});

You could try use both the $set and $push operators in your update object. Suppose, for example, you want to update both name and address fields in one single operation, use the $set on the name field and a $push operation to the address array:
var messages= {
title: req.body.title,
msg: req.body.msg
},
query = {'_id': req.body.id},
update = {
$set: {name: req.body.name},
$push: {messages: message}
},
options = {upsert: true};
Model.findOneAndUpdate(query, update, options, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});

Related

fetch values from a loop inside .then in nodejs out in allemails array

// importing required builtin modules
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/mydb');
// schema for email
var emailSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
html: String,
text: String,
headers: {},
subject: String,
references: [String],
messageId: String,
inReplyTo: [String],
priority: String,
from: [],
replyto: [String],
to: [],
date: Date,
receivedDate: Date,
attachments: [],
read: { type: Boolean, default: 0 },
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
active: { type: Boolean, default: 1 },
labels: [String]
});
// schema for thread
var threadSchema = mongoose.Schema({
threadedEmails: [{ type: String, ref: 'Email' }],
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
});
// defining models
var Email = mongoose.model('Email', emailSchema);
var Thread = mongoose.model('Thread', threadSchema);
module.exports = Email;
module.exports = Thread;
// function to return an array which contains yet anohter array of emails each representing a thread
function doCalls() {
threads = [];
// a promise that always resolves
return new Promise(function (resolve, reject) {
resolve(1);
})
// this returns the threads as expected
.then(function (result) {
return Promise.resolve(
Thread.find({}, { threadedEmails: 1, _id: 0 }).then(
(_threads) => { return _threads }, //resolve
(err) => { reject(err); } //reject
)
)
})
// this does not returns the emails array as i expect
.then(function (threads) {
allEmails = [];
threads.forEach(thread => {
// Start off with a promise that always resolves
var sequence = Promise.resolve();
sequence = sequence.then(function (result) {
console.log('then of first foreach');
//query to make a database call to get all the emails whoes messageId's matchs
query = Email.find({ messageId: { "$in": thread.threadedEmails } });
query.exec((err, result) => {
if (err) throw err;
allEmails.push(result); //but this does not works because the code execution moves ahead
console.log(result); //this console log returns the value
});
})
})
//----------------- this is the problematic code here this array returns empty ----------------//
console.log(allEmails);
})
}
doCalls()
.then(function (allEmails) {
// console.log(allEmails);
});
I have written comments where ever i feel is required in the code, even though let me explain that context of what I am trying
I am fetching from a collection named threads all the threads which goes successfully
after that I am trying to fetch all the emails from a collection named email using a database query the output of which i am trying to store inside an array called allEmails
if i console.log() it in .then() just after the database call it does gives me the output,
my question is how do I get this out ?
I want an array which contains yet another array each having a collection of emails(each representing a thread) from emails collection
hope I made it clear, if there is anything else I require to provide please be my guide and tell me.
The forEach block finishes before any of the async operations inside it return. Thus , your allEmails array will still be empty when you console.log it.
What you need to do is build an array of promises that resolve with the "result" and then feed this array into Promise.all(arrayOfPromises) which will resolve when all your promises are resolved.
Promise.all is kind enough to resolve with the array of results for you.
Your last then would look something like:
.then(function (threads) {
const promiseArray = [];
threads.forEach(thread => promiseArray.push(
Promise.resolve(Email.find({ messageId: { "$in": thread.threadedEmails } }))
)
);
return Promise.all(promiseArray);
}
That being said, for what you're trying to achieve, Model.aggregate() would be a much more elegant solution.
Hope this helps

$push to an array in MongoDB with mongoose doesn't work

errmsg: 'The field \'weight\' must be an array but is of type int in
document
My Schema:
weight: [{
type: Number
}]
and my post request:
app.post('/edit', function(req, res){
var update = { $push: {"weight": req.body.weight}};
User.findOneAndUpdate(conditions, update, options, function (err)
{
if (err)
{
console.log(err);
}
else
{
console.log('yep');
}
})
});
If there are multiple documents in the collection that match your conditions, you can update only suitable one by adding { weight: { $type: 4 } } to your conditions.
Otherwise your application's schema doesn't match data in the database.
This might work.
//Schema
weight: [Number]
http://mongoosejs.com/docs/schematypes.html
//Or this way too if pushing objects into array
//Schema
weight: [{
weight: {
type: Number
}
}]
//Then in API
var update = { $push: {"weight": { "weight": req.body.weight }}};

Custom Validate an object Push to an array

I am using something similar to the following schema.
By visiting the Item page I can add related items to the Item's Related Items array field.
I would like to custom validate the object I am pushing to the Item's Related Items field, to test if a similar object exists in the array already - so that I do not get a duplicate.
In my code below, the custom validation does not fire. I expect this may be because custom validation cannot be applied to a type: [object], and should be applied to the properties of the object - but then I am unable to test the object as a whole.
const ItemsSchema = new SimpleSchema({
name: {
type: String,
label: 'Name',
},
related: {
type: [Object],
label: 'Related Items',
optional:true,
custom: function () {
let queryData = { docId: this.docId, related: this.value }
if (Meteor.isClient && this.isSet) {
Meteor.call("relatedObjectIsUniqueForThisItem", queryData,
function (error, result) {
if(!result){
console.log("not unique");
return "Invalid";
}
else{
return true;
}
});
}
}
},
'related.$.name':{
type: String,
label:'Name',
},
'related.$.code':{
type:String,
label:'Code',
min:5,
},
});
I figured out the way to handle this.
The custom validation should not be on the [object], but rather one of the properties of the object - in this case 'source' or 'code'.
Inside one of the object properties you can call this.siblingField(otherField); But it means you have to rebuild the object.
In my case :-
const ItemsSchema = new SimpleSchema({
name: {
type: String,
label: 'Name',
},
related: {
type: [Object],
label: 'Related Items',
optional:true,
},
'related.$.name':{
type: String,
label:'Name',
custom: function () {
//---------------------------
//this is the important bit
//---------------------------
let queryData = {
docId: this.docId,
related: {
name:this.value,
code:this.siblingField('code').value,
}
}
//---------------------------
//end of important bit
//---------------------------
if (Meteor.isClient && this.isSet) {
Meteor.call("relatedObjectIsUniqueForThisItem", queryData,
function (error, result) {
if(!result){
console.log("not unique");
return "Invalid";
}
else{
return true;
}
});
}
}
},
'related.$.code':{
type:String,
label:'Code',
min:5,
},
});

MongoDB Reference Issue

I have two collections set up at the moment. One collection lists all of the products in my store. The other collection stores the ratings on a scale of 1-5. The ratings store in the ratings collection successfully, and the products are stored and listed successfully. However, I am attempting to reference the appropriate rating for the individual product that is listed. I am using an ng-repeat to list all of the products in my product database. I'm not sure what is going on, but my reference to the ratings is returning an empty array.
How can I get the ratings to show for each product?
Product Schema:
var mongoose = require('mongoose');
var productSchema = new mongoose.Schema({
title: {
type: String,
unique: true,
required: true,
index: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true,
min: 0,
},
rating: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Rating'
}],
image: {
type: String,
required: true
}
});
module.exports = mongoose.model('Product', productSchema);
Product Controller (just the read portion):
var Product = require('../models/Product');
module.exports = {
read: function (req, res) {
Product.find(req.query)
.populate('Rating')
.exec(function (err, result) {
if (err) { return res.status(500).send(err);}
console.log("this is in the product ctrl", result);
{res.send(result);}
});
},
};
Rating Schema:
var mongoose = require('mongoose');
var ratingSchema = new mongoose.Schema({
rating: {
type: Number,
enum: [1, 2, 3, 4, 5]
}
});
module.exports = mongoose.model('Rating', ratingSchema);
Rating Controller (just read and create shown):
var Rating = require('../models/Rating');
module.exports = {
create: function (req, res) {
var newRating = new Rating(req.body);
newRating.save(function (err, result) {
if (err) return res.status(500).send(err);
else res.send(result);
});
},
read: function (req, res) {
Rating.find(req.query)
.populate('type')
.exec(function (err, result) {
if (err) return res.status(500).send(err);
else res.send(result);
});
}
};
Screenshot of view:
If more information is needed please let me know. I thought that something may be wrong with my .populate, but after reading this documentation I think everything is good. I'm stumped.
http://mongoosejs.com/docs/populate.html

Properties with default values in Mongoose schema are not persisting

I have the following schema I've written using Mongoose:
var querySchema = mongoose.Schema({
quoteId: { type: String, default: '' },
zipcode: { type: String, default: '' },
email: { type: String, default: '' },
type: {type: String, default: ''},
isEmailChecked: { type: Boolean, default: true },
});
I provide values for only 3 properties in the querySchema assuming that the result of the fields will take default values when a new instance of query object is persisted:
var query = {};
query.quoteId = "1414775421426";
query.email = "myself#somewhere.com";
query.type = "Foo";
But following document is what I see as the result in the collection:
{
"_id" : ObjectId("5453c27d0e4c3f2837071856"),
"email" : "myself#somewhere.com",
"type" : "Foo",
"quoteId" : "1414775421426",
"__v" : 0
}
Should isEmailChecked and zipcode not be assigned their default values when a new instance of query object is persisted to the MongoDB database?
Following is how I am persisting an instance of the query object using ExpressJS/NodeJS:
app.post('/api/queries', function (req, res) {
QuoteQuery.create({
quoteId: req.body.query.quoteId,
type: req.body.query.type,
zipcode: req.body.query.zipcode,
email: req.body.query.email,
isEmailChecked: req.body.query.isEmailChecked,
}, function (err, query) {
if (err) {
res.send(err);
}
res.json(query);
});
});
Could somebody help me understand that why I got the isEmailChecked and zipcode properties in the resulting document in the MongoDB database?
I am using NodeJS, AngularJS and ExpressJS in my application along with MongoDB.
When you set mongoose model field it not use default value.
As workaround you can use underscore to extend mongoose model object with keys which exists in your query object like this:
_.extend(dbQueryObject, query);
Here is complete example:
var mongoose = require('mongoose');
var querySchema = mongoose.Schema({
quoteId: { type: String, default: '' },
zipcode: { type: String, default: '' },
email: { type: String, default: '' },
type: {type: String, default: ''},
isEmailChecked: { type: Boolean, default: true }
});
var db = mongoose.createConnection('mongodb://localhost:27017/stackoverflow',
{ server: { auto_reconnect: true } },
function(err) {
var QuerySchema = db.model('test', querySchema);
var query = {};
query.quoteId = "1414775421426";
query.email = "myself#somewhere.com";
query.type = "Foo";
QuerySchema.create({
quoteId: query.quoteId,
type: query.type,
zipcode: query.zipcode,
email: query.email,
isEmailChecked: query.isEmailChecked
}, function (err, query) {
process.exit(0);
});
});
Here is what in db:
{
"_id" : ObjectId("5453ce3c9f7e0d13c52abf61"),
"type" : "Foo",
"email" : "myself#somewhere.com",
"quoteId" : "1414775421426",
"__v" : 0
}

Resources