Mongo $addToSet with multiple values correct syntax - javascript

I have this mongoose schema:
var listingSchema = new Schema({
street : String,
buildingNumber : Number,
apartmentNumber : Number,
UsersAndQuestions: [{
userID: String,
questionID: [String]
}]
});
And I just want to update it with a new entry to UsersAndQuestions which will consist of a userID which is a String, and a questionID which is also a String (but needs to be inserted into an array).
I am using this PUT request:
app.put('/api/listing/:street/:buildingNumber/:apartmentNumber/addUserInput/:userid/:listingid/:questionid')
So I have all the necessary parameters in hand.
Usually, when I wanted to update a field in a schema I used this code that I wrote:
app.put('/api/listing/:street/:buildingNumber/:apartmentNumber/addReportedUser/:userid/:listingid', function (req, res) {
var listingToUpdate = req.params.listingid;
var idToAdd = req.params.userid;
Listing.update({_id: ObjectId(listingToUpdate)},
{$addToSet: {reportedUsersIDs: ObjectId(idToAdd)}}
, function (err) {
if (err) {
res.send("There was a problem adding the reportedUserID to the listing" + err);
}
else {
console.log("Success adding reportedUserID to listing!");
}
})
});
You can see I used $addToSet and it worked well. But now I want to add two parameters to a field which is an array. I thought about doing something like this:
app.put('/api/listing/:street/:buildingNumber/:apartmentNumber/addUserInput/:userid/:listingid/:questionid', function(req,res){
var listingToUpdate = req.params.listingid;
var idToAdd = req.params.userid;
var questionToAdd = req.params.questionid;
Listing.update({_id: ObjectId(listingToUpdate)},
{$addToSet: {UsersAndQuestions.userID : ObjectId(idToAdd), UsersAndQuestions.questionID : ObjectId(questionToAdd)}}
, function (err) {
if (err) {
res.send("There was a problem adding the user and question to the listing" + err);
}
else{
console.log("Success adding user and question to the listing!");
}
})
});
But I'm obviously getting a SyntaxError.
What is the correct syntax for doing what I tried to do?
Thanks a lot! :)

You need to add object to set UsersAndQuestions:
{$addToSet: {UsersAndQuestions: { userID: idToAdd, questionID: questionToAdd } }}
UPDATE.
I would do it with two queries:
Listing.update({_id: ObjectId(listingToUpdate), 'UsersAndQuestions.userID': idToAdd},
{"$addToSet": {"UsersAndQuestions.$.questionID": questionToAdd}}
, function (err, result) {
if(result.n === 0){
//we haven't found document with the userId - idToAdd
//we need to insert to UsersAndQuestions document with this user
Listing.update({_id: ObjectId(listingToUpdate)},
{$addToSet: {UsersAndQuestions: { userID: idToAdd, questionID: questionToAdd } }},
function(err, res){
})
}
})

Related

await with .then behavior [duplicate]

I have an issue I've not seen before with the Mongoose findByIdAndUpdate not returning the correct model in the callback.
Here's the code:
var id = args._id;
var updateObj = {updatedDate: Date.now()};
_.extend(updateObj, args);
Model.findByIdAndUpdate(id, updateObj, function(err, model) {
if (err) {
logger.error(modelString +':edit' + modelString +' - ' + err.message);
self.emit('item:failure', 'Failed to edit ' + modelString);
return;
}
self.emit('item:success', model);
});
The original document in the db looks like this:
{
_id: 1234
descriptors: Array[2],
name: 'Test Name 1'
}
The updateObj going in looks like this:
{
_id: 1234
descriptors: Array[2],
name: 'Test Name 2'
}
The model returned from the callback is identical to the original model, not the updatedObj.
If I query the db, it has been updated correctly. It's just not being returned from the database.
This feels like a 'stupid-user' error, but I can't see it. Any ideas greatly appreciated.
In Mongoose 4.0, the default value for the new option of findByIdAndUpdate (and findOneAndUpdate) has changed to false, which means returning the old doc (see #2262 of the release notes). So you need to explicitly set the option to true to get the new version of the doc, after the update is applied:
Model.findByIdAndUpdate(id, updateObj, {new: true}, function(err, model) {...
app.put("/vendor/:id",async (req,res)=>{
res.send(req.params)
await ModelName.findByIdAndUpdate(id, {type: change}, function(err, docs){
if(err){
conslole.log(err)
}else{
console.log(docs)
}
})
})
Example:
app.put("/vendor/:id",async (req,res)=>{
res.send(req.params)
const data = await userModel.findByIdAndUpdate(req.params.id, {isVendor: true},
function(err, docs){
if(err){
conslole.log(err)
}else{
console.log(docs)
}
})
})

Mongoose unable to get data from MongoDB database

I have a model (AccountModel.js) as below and the controller for it. i tried to change one document using postman but i am getting an empty array from the database event though the data is present.
let mongoose = require('mongoose')
let Schema = mongoose.Schema
var ObjectId = Schema.ObjectId
let mySchema = mongoose.Schema({
account_id:ObjectId,
account_key:String,
message:String,
created_at:Date,
updated_at:Date
})
let MySchema = module.exports =
mongoose.model('account',mySchema);
module.exports.get = function(callback,limit){
MySchema.find(callback).limit(limit)
}
and AccountController as below to manage account db. i have consoled the query and the output from the database.
var mongoose = require('mongoose')
var Account = require('../models/AccountModel')
var ObjectId = mongoose.Types.ObjectId;
exports.setMessage = function(req,res){
query = {account_id:new ObjectId(req.body.acnt_id)}
console.log(query,"...")
Account.find(query,function(err,account_data){
if(err){
res.send(err)
}
else{
try{
console.log(account_data,'setWelcomeMessage')
account_data.message =
req.body.welcomeMessage
account_data.updated_at = new Date()
account_data.save((err,data)=>{
if(err){
console.log(err)
res.send(err)
}
res.send({"Status":"Success"})
})
res.send({"Status":"Success"})
}
catch(e){
//console.log(e)
res.send({"Status":"Failed"})
}
}
})
}
below is the database
> db.account.find().pretty()
{
"_id" : ObjectId("5c18fea5c5a6a4ebf7999c0b"),
"account_id" : ObjectId("5c18fbefc5a6a4ebf7999c08"),
"account_key" : "UDS1500",
"message" : "testing message",
"created_at" : ISODate("2018-12-18T14:05:25.637Z"),
"updated_at" : ISODate("2018-12-18T14:05:25.637Z")
}
{
"_id" : ObjectId("5c18feffc5a6a4ebf7999c0c"),
"account_id" : ObjectId("5c18fbaac5a6a4ebf7999c07"),
"account_key" : "UDS1299",
"message" : "testing message2",
"created_at" : ISODate("2018-12-18T14:06:55.968Z"),
"updated_at" : ISODate("2018-12-18T14:06:55.968Z")
}
after calling from POSTMAN i am getting an empty array
Below is the request format
{
"acnt_id":"5c18fbaac5a6a4ebf7999c07",
"welcomeMessage":"test message 3!!"
}
console is as below
{ account_id: 5c18fbaac5a6a4ebf7999c07 } '...'
[] 'setWelcomeMessage'
what might be the problem in getting empty data? i have wasted a lot of time on this.
The culprit is this line
query = {account_id:new ObjectId(req.body.acnt_id)}
where the statement new ObjectId(req.body.acnt_id) creates a new id (regardless of what you pass in the constructor) thus your query fails as there won't be any match in the db. You don't necessarily need to cast the acnt_id string to ObjectId as Mongoose does this for you under the hood, but if need be use
query = {account_id:mongoose.Types.ObjectId(req.body.acnt_id)}
otherwise
query = {account_id:req.body.acnt_id}
will suffice.
A better way to do the update would be to use the findOneAndUpdate method which does an atomic update of your model and mostly used when you want to update a single document in the db and return it to your application, so you can refactor your controller method to:
exports.setMessage = (req, res) => {
const query = { 'account_id': req.body.acnt_id };
const update = {
'$set': {
'message': req.body.welcomeMessage,
'updated_at': new Date(),
}
};
const options = { 'new': true };
Account.findOneAndUpdate(query, update, options, (err, account_data) => {
if (err){
res.send(err)
}
else {
console.log(account_data); // logs the updated account document
res.send({"Status":"Success"})
}
});
}
Also, you can set timestamps in your schema where mongoose assigns createdAt and updatedAt fields to your schema and the type assigned is Date i.e.
let mySchema = mongoose.Schema({
account_id: ObjectId,
account_key: String,
message: String,
}, { timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } });
.find() returns an array, not a single element. Therefore i'd recommend using the .findOne() method instead.
Thank you for you response. I found answer for my problem.
The reason is that the mongoose has created a model with the plural name. Which means, here i have named the model "account". But here in database it will create/connect to a collection with the name "accounts". I dont know the reason for mongoose not creating/connecting to a collection named "accounts". Since there is no collection named with "accounts" it is always giving me the empty result.
At last, i have changed the collection name to "accounts". Now it working fine.
Please comment the reason, mongoose creating/connecting to plural name of the given model.
//getting from postman .... give the account id in postman
query = {account_id:req.body.acnt_id};
//there is match in DB--- getting the data
Account.find(query,function(err,account_data){
if(err){
res.send(err)
}
u wana update
else{
Accoun.update({account_id:req.body.acnt_id},req.body
};
//on body what you enter in postman that will update and store on DB
IN MY KNOWLEDGE
// just example
Model.update
Updates all documents matching conditions using the update clause. All update values are casted to their appropriate types before being sent.
var conditions = { name: 'bourne' }
, update = { $inc: { visits: 1 }}
, options = { multi: true };
Model.update(conditions, update, options, callback);
function callback (err, numAffected) {
// numAffected is the number of updated documents
})

How can I find a property of a mongoose model by another property?

I have a mongoose schema as below:
var personnelSchema = new Schema({
fullName: String,
dob: String,
cNumber: String,
address: String,
wCard: String,
dLic: Number,
hrate: Number,
});
How can i find the property "hrate" by having only "fullName". I don't have access to ID otherwise I would find it by id.
You can see the mongoDB docs on find here: https://docs.mongodb.com/manual/reference/method/db.collection.find/
On mongoose here: http://mongoosejs.com/docs/2.7.x/docs/finding-documents.html/
But finding documents by field is usually done via:
Model.findOne({ fullName: 'someName'}, function (err, doc){
// doc is a Document
});
If you just want to hrate from fullname property then you can use projection.
Use find for fetch all record and findOne for first match record.
var projection = 'hrate'
UserModel.findOne({
fullName: "Some String"
}, projection, function (err, data) {
if (!data) {
callback('No data found', null)
} else {
callback(err, data)
}
})
// Query would be in MongoDB shell:
> db.getCollection('test').findOne({fullName : "Hardik Shah"}, {'hrate' : 1})
If you want full document along with hrate then don't use projection
UserModel.findOne({
fullName: "Some String"
}, function (err, data) {
if (!data) {
callback('No data found', null)
} else {
callback(err, data)
}
})
// Query would be in MongoDB shell:
> db.getCollection('test').findOne({fullName : "Hardik Shah"})

mongoose forEach loop and update documents one by one

I want to query a collection and update each document using some value that i will get from another query which is gonna be built with some info from the returned document.
const mongoose = require('mongoose');
const userModel = {
country: { type: String }
newField: { type: String }
};
const myUsersModel = mongoose.model('user',userModel);
myUsersModel.find({country:"USA"}).forEach(function (doc) {
// another query here into a relation Database:
let anotherQuery = 'SELECT * FROM myTable WHERE name=' + doc.name;
mySQLConnection.query(
anotherQuery,
function selectCb(err, results, fields) {
if (err) {
console.log("ERROR: " + err.message);
throw err;
}
console.log("Got "+results.length+" Rows:");
let updatedInfo = results.SomeField;
// update the mongoose doc:
doc.newField = updatedInfo;
myUsersModel.save(doc);
});
mySQLConnection.end(function(err) {
console.log("connection ended.");
});
mongoose.connection.close();
});
I am getting the following error:
TypeError: myUsersModel.find(...).forEach is not a function
myUsersModel.find({country:"USA"})
.then(users=>users.forEach //users might be null here btw
Or if you want to keep your callback style
myUsersModel.find({country:"USA"}, function(err, users) {
if (err) throw err;
users.forEach
If a callback is not provided, Model.find returns an instance of Query and not an instance of Array.
Hence, you can not use forEach as Query is not an Array.

mongoose save fails without error

As the title suggests I'm having problems with mongoose save method, which fails but does not produce an error.
I actually know why it fails, which is down to the userId field being marked as required but not being provided... but I don't know why it doesn't throw an error. I've exhausted google and stackoverflow looking at similar suggestions with no luck, so throwing it open to anyone who can help me!
Here's the code...
Model.js
var mongoose = require('mongoose');
var TimeSchema = new mongoose.Schema({
client: String,
matter: String,
activity: String,
tags: String,
description: String,
comments: [String],
startTime: Date,
startTimeUTC: Number,
endTime: Date,
endTimeUTC: Number,
duration: Number,
durationRnd: Number,
durationUnits: Number,
billable: Boolean,
rate: Number,
total: Number,
user: String,
userId: { type: mongoose.Schema.ObjectId, required: true }
}, {safe: true});
mongoose.model("Time", TimeSchema);
Controller.js
exports.addTime = function (req, res) {
console.log('Adding time: ' + JSON.stringify(req.body));
var time = new Time(req.body);
time.save(function (err) {
if (err) { res.send({'error' : err}); }
res.send(time);
});
}
EDIT - To clarify the callback is being called, take the following code for example.
exports.addTime = function (req, res) {
console.log('Adding time: ' + JSON.stringify(req.body));
var time = new Time(req.body);
console.log("time = " + time);
// TODO user
time.save(function (err) {
if (err) { handleError(res, err); }
console.log("ok");
Time.findById(time._id, function (err, found) {
console.log("found = " + found);
});
res.send(time);
});
}
and here's the console output
Adding time: {"description":"test","client":"","matter":"","activity":"","rate":
"","startTime":"2013-11-30T19:58:43.000Z","startTimeUTC":"1385841523000","endTim
e":"2013-11-30T19:58:45.000Z","endTimeUTC":"1385841525000","startLocale":"19:58"
,"startTimeLocale":"19:58:43","endLocale":"19:58","endTimeLocale":"19:58:45"}
time = { description: 'test',
client: '',
matter: '',
activity: '',
rate: null,
startTime: Sat Nov 30 2013 19:58:43 GMT+0000 (GMT Standard Time),
startTimeUTC: 1385841523000,
endTime: Sat Nov 30 2013 19:58:45 GMT+0000 (GMT Standard Time),
endTimeUTC: 1385841525000,
startTimeLocale: '19:58:43',
endTimeLocale: '19:58:45',
_id: 529a43750a366b6419000001,
comments: [] }
ok
POST /api/times 200 14ms - 313b
found = null
It is very possible to run into this error by naively not connecting to the database. Has happened several times to me. Make sure your mongoose.connect() is in place.
Problem solved, thanks to robertkelp.
Here's my revised code in case if ever helps anyone, but it appears the error was being thrown I just wasn't handling it correctly.
exports.addTime = function (req, res) {
console.log('Adding time: ' + JSON.stringify(req.body));
var time = new Time(req.body);
time.save(function (err) {
if (err) {
handleError(res, err);
}
else {
res.send(time);
}
});
}
My Problem was not solved by using findOne, it was solved by defining the fields i updated , in the model schema. so my code was like that:
User.findOne({email:data.userData.email}, function (err, user) {
if (!err && user!=undefined){
console.log(user);
var userDiscounts = user.discounts;
for(var i=0;i<userDiscounts.length;i++){
if (userDiscounts[i]!=undefined && userDiscounts[i].code=="XXXXXX"){
userDiscounts[i].claimed = true;
console.log('discount claimed');
}
}
user.discounts = userDiscounts;
user.fbDiscountClaimed = true;
user.save(function(err) {
if (err) console.log(err);
console.log('Saved Hashve-FB as claimed');
});
}
});
}
}
But the schema of the discount and user model was missing the definition of types of user.discounts so i added the following:
var UserSchema = new Schema({
name: String,
email: { type: String, lowercase: true },
role: {
type: String,
default: 'user'
},
hashedPassword: String,
provider: String,
salt: String,
messages:[],
discounts:[{
"name": String,
"description": String,
"created_at": String,
"updated_at": String,
"code": String,
"claimed": Boolean
}
],
facebook: {},
fbDiscountClaimed:false,
twitter: {},
google: {},
github: {}
});
UserSchema.pre('save',function(next){
var currentDate=new Date();
this.updatedAt=currentDate;
if(!this.createdAt){
this.createdAt=currentDate;
};
next();
});
When I use the .pre('save',fn) to create time, I forgot the next(), causing the data store to fail without error,I hope can help somebody!
For anyone can face a similar issue, I found that when populating a mongoose schema object from a json object that actually owns its “_id” property (even set to “null”), insert fails with no error.
Example:
var json = JSON.parse(req.body.time);
var time = new Time(json);
assuming that json._id is defined, no matter it's declared as a new "Time", when you try to insert with:
time.save(function (error) {
if (error) { res.send({'error' : error}); }
res.send(time);
});
error variabile is null but item was never inserted.
In this case I solved by deleting "_id" property before populating mongoose object so final code as follows:
var json = JSON.parse(req.body.time);
delete json._id;
var time = new Time(json);
time.save(function (error) {
if (error) { res.send({'error' : error}); }
res.send(time);
});
Regards
Angelo
Not directly related to the scenario described in the post, but I was getting the same error.
The issue was that I was not calling next(); at the end of the pre('save') function on the model definition.
For example:
cartSchema.pre('save', function(next) {
const currentDate = new Date();
this.modified = currentDate;
if (this.isNew) {
this.created = currentDate;
this.cartid = uuidv4();
}
next(); // this was missing :'(
});
I had a situation where the save callback was getting called, but the document wasn't changing. I haven't yet tracked down the root cause (some sort of validation thing probably) but the problem had something to do with one of the changes I made. So I just tried the changes one by one until I found the culprit.
(Feel free to edit this answer if you figure out more of this phenomenon)

Categories