Updating a field within a document in MongoDB / Meteor - mongodb

I am attempting to update a few different fields within a document in a collection, and seem to be having some trouble. I don't seem to be able to access any of the specific attributes I am looking to set (which are subject, standard, price). Would anyone be able to help? Here is my code so far, and the newAttributes don't seem to be making it there.
I have confirmed the doc._id is correctly populating.
Template.AppAdmin.events({
'click #editApp': function() {
let newAttributes = {};
let subject = $('#subject').val();
let standard = $('#standard').val();
let price = $('#price').val();
console.log(subject);
console.log(standard);
console.log(price);
newAttributes = { subject: subject, standard: standard, price: price };
var doc = Session.get('appId');
console.log(doc);
console.log(newAttributes);
Apps.update(doc._id, { $set: { newAttributes }});
console.log('app has been updated!');
}
});
Thank you.
UPDATE of course I figured it out, I wasn't correctly grabbing the document by ID...
This worked:
Apps.update({_id: doc}, { $set: newAttributes });

You need to remove the braces around newAttributes, because newAttributes is already an object. So your updated code will be like
Apps.update(doc._id, { $set: newAttributes });
Or
Apps.update({'_id':doc._id}, { $set: newAttributes });

Related

mongodb, express.js. Add new doc to array of documents selector is id

I want to add a new document to an array of documents. So I pass in my param which is the _id of the document I want to add to. Then I need to just add it to the array. I thought I had it working but it was actually adding a nested array to that array. I realized this because I am also trying to sort it so newly added documents are at top. So I ended up having to go back and try and fix my add query. As of now it basically just says cannot add values. This is why I have been using mongodb client, express, await.
I have been looking at mongodb manual and trying what they have but cannot get it to work, obviously something wrong with my adding of new document. Anyone see the issue or show me an example? Thanks!
app.post("/addComment/:id", async (request, response) => {
let mongoClient = new MongoClient(URL, { useUnifiedTopology: true });
try {
await mongoClient.connect();
let id = new ObjectId(request.sanitize(request.params.id));
request.body.comments = { $push: {"comments.author": "myTestPOSTMAN - 1", "comments.comment":
"myTestCommPostMan - 1"}};
let selector = { "_id":id };
//let newValues = {$push: {"comments.comment": "myTestCommPostMan - 1", "comments.author":
"myTestPOSTMAN - 1"}};
let newValues = request.body.comments;
let result = await mongoClient.db(DB_NAME).collection("photos").updateOne(selector,
newValues);
if (JSON.parse(result).n <= 0) {
response.status(404);
response.send({error: "No documents found with ID"});
mongoClient.close();
return;
}
response.status(200);
response.send(result);
} catch (error) {
response.status(500);
response.send({error: error.message});
throw error;
} finally {
mongoClient.close();
}
});
Using post man this is what my json looks like and what the array of documents looks like I am trying to add to.
{"comments": [
{
"comment": "pm - test3",
"author": "pm - test4"
}
]
}
do the mongodb connection outside the function, no need to connect and disconnect everytime when function call, don't create unusual variables too much.
for push object you need to provide main key name and assign object to it.
let mongoClient = new MongoClient(URL, { useUnifiedTopology: true });
await mongoClient.connect();
app.post("/addComment/:id", async (request, response) => {
try {
let result = await mongoClient.db(DB_NAME).collection("photos").updateOne(
{ "_id": new ObjectId(request.sanitize(request.params.id)) },
{ $push: { comments: request.body.comments } }
);
if (JSON.parse(result).n <= 0) {
response.status(404).send({ error: "No documents found with ID" });
return;
}
response.status(200).send(result);
} catch (error) {
response.status(500).send({ error: error.message });
}
});

Circular Reference Issue in Mongoose pre-hook

In my MongoDB/Node backend environment I am using Mongoose pre and post hook middleware to check what's changed on the document, in order to create some system notes as a result.
One problem I'm running into is that when I try and lookup the record for the document in question I get a "Customer.findOne()" is not a function error. This is ONLY a problem when I'm looking up a record from the same collection from which the model just launched this pre and post hook triggers file. In other words, if my "Customer" model kicks off functions in a pre hook function in an external file, then I get an error if I then try and lookup a Customer with a standard findOne():
My customer model looks something like this:
module.exports = mongoose.model(
"Customer",
mongoose
.Schema(
{
__v: {
type: Number,
select: false
},
deleted: {
type: Boolean,
default: false
},
// Other props
searchResults: [
{
matchKey: String,
matchValue: String
}
]
},
{
timestamps: true
}
)
.pre("save", function(next) {
const doc = this;
trigger.preSave(doc);
next();
})
.post("save", function(doc) {
trigger.postSave(doc);
})
.post("update", function(doc) {
trigger.postSave(doc);
})
.post("findOneAndUpdate", function(doc) {
trigger.postSave(doc);
})
);
... the problematic findOne() function in the triggers file being called from the model looks like this:
const Customer = require("../../models/customer");
exports.preSave = async function(doc) {
this.preSaveDoc = await Customer.findOne({
_id: doc._id
}).exec();
};
To clarify, this is NOT a problem if I'm using a findOne() to lookup a record from a different collection in this same triggers file. Then it works fine. See below when finding a Contact -- no problem here:
const Contact = require("../../models/contact");
exports.preSave = async function(doc) {
this.preSaveDoc = await Contact.findOne({
_id: doc._id
}).exec();
};
The workaround I've found is to use Mongo instead of Mongoose, like so:
exports.preSave = async function(doc) {
let MongoClient = await require("../../config/database")();
let db = MongoClient.connection.db;
db.collection("customers")
.findOne({ _id: doc._id })
.then(doc => {
this.preSaveDoc = doc;
});
}
... but I'd prefer to use Mongoose syntax here. How can I use a findOne() in a pre-hook function being called from the same model/collection as the lookup type?
I have ran similar issue few days ago.
Effectively it is a circular dependency problem. When you call .findOne() on your customer model it doesn't exist as it is not exported yet.
You should probably try something like that :
const customerSchema = mongoose.Schema(...);
customerSchema.pre("save", async function(next) {
const customer = await Customer.findOne({
_id: this._id
}).exec();
trigger.setPreSaveDoc(customer);
next();
})
const Customer = mongoose.model("Customer", customerSchema)
module.export Customer;
Here customer will be defined because it is not called (the pre hook) before its creation.
As an easier way (I am not sure about it) but you could try to move the Contact import in your Trigger file under the save function export. That way I think the decencies may works.
Did it helps ?

Trouble updating a Simple Schema sub document

I'm trying to update a sub document on an existing collection. I'm getting a MongoDB error message.
"MongoError: The positional operator did not find the match needed from the query. Unexpanded update: articleWords.$ [409]"
From my Articles Simple Schema
"articleWords.$": {
type: Object
},
"articleWords.$.wordId": {
type: String,
label: 'Word ID'
},
"articleWords.$.word": {
type: String,
label: 'Word'
},
Update Function
function updateArticle(_id,wordArr) {
_.each(wordArr,function(elem) {
var ret = Articles.update(
{'_id': _id},
{ $set: { 'articleWords.$': { 'wordId': elem.wordId, 'word': elem.word } }
});
});
return true;
}
As you can see I am passing an array of objects. Is there a better way to do this than _.each ?
CLARIFICATION
Thank you to #corvid for the answer. I think I didn't make my question clear enough. There does exist an article record, but there is no data added to the articleWords attribute. Essentially we are updating a record but insert into the articleWords array.
A second attempt, is also not working
_.each(wordArr,function(elem) {
var ret = Articles.update(
{'_id': _id},
{ $set: { 'articleWords.$.wordId': elem.wordId, 'articleWords.$.word': elem.word } }
);
});
Yes, you need your selector to match something within the subdocument. For example,
Articles.update({
'_id': <someid>,
'words.wordId': <somewordid>
}, {
$set: {
'words.$.word': elem.word,
'words.$.wordId': elem.wordId
}
});
If the array doesn't exist yet then you're going about this in the hardest way possible. You can just set the entire array at one go:
var ret = Articles.update(
{'_id': _id},
{ $set: { articleWords: wordArr }}
);
I can see that wordArr already has the id and string. This will work as long as it doesn't have more content. If it does then you can just make a second version with the parts you want to keep.

Mongoose findByIdAndUpdate not working

I have a fairly straight forward method below to update a document based on its ObjectId. It does not return an error but it fails to make the required updates to the document. I think it is failing because, according to my research, findByIdAndUpdate() takes only plain Javascript whereas job._id is an ObjectId from the document that I want to update. Can someone tell me how to make this work correctly?
function handleEncoderResponse(xmlResponse, job) {
var r = et.parse(xmlResponse);
var mediaID = r.findtext('./MediaID');
var message = r.findtext('./message');
EncodingJob = mongoose.model('EncodingJob');
EncodingJob.findByIdAndUpdate( job._id, {
"MediaID": mediaID,
"Status": message
}, function(err, result) {
if (err) console.log(err);
console.log(result);
});
}
Edit: Per this question Mongoose update document Fail with findByIdAndUpdate
I also tried the following code to no avail.
job.MediaID = mediaID;
job.Status = message;
job.save(function(err, res) {
if(err) console.log(err);
});
This approach yields the issue. It does not update the document and it does not return an error.
As it turns out, my mistake was forgetting to define MediaID and Status in the Schema as follows:
var encodingJobSchema = new mongoose.Schema({
...
MediaID: String,
Status: String
});

Update model with Mongoose, Express, NodeJS

I'm trying to update an instantiated model ('Place' - I know it works from other routes) in a MongoDB and have spent a while trying to properly do so. I'm also trying to redirect back to the page that views the 'place' to view the updated properties.
Node v0.4.0, Express v1.0.7, Mongoose 1.10.0
Schema:
var PlaceSchema = new Schema({
name :String
, capital: String
, continent: String
});
Controller/route:
app.put('/places/:name', function(req, res) {
var name = req.body.name;
var capital = req.body.capital;
var continent = req.body.continent;
Place.update({ name: name, capital: capital, continent: continent}, function(name) {
res.redirect('/places/'+name)
});
});
I've tried a bunch of different ways but can't seem to get it.
Also, isn't how I declare the three {name, capital, and continent} variables blocking further operations? Thanks. General debugging help is also appreciated. Console.log(name) (right below the declaration) doesn't log anything.
Jade form:
h1 Editing #{place.name}
form(action='/places/'+place.name, method='POST')
input(type='hidden', name='_method', value='PUT')
p
label(for='place_name') Name:
p
input(type='text', id='place_name', name='place[name]', value=place.name)
p
label(for='place_capital') Capital:
p
input(type='text', id='place_capital', name='place[capital]', value=place.capital)
p
label(for='place_continent') Continent:
p
textarea(type='text', id='place_continent', name='place[continent]')=place.continent
p
input(type="submit")
You have to find the document before updating anything:
Place.findById(req.params.id, function(err, p) {
if (!p)
return next(new Error('Could not load Document'));
else {
// do your updates here
p.modified = new Date();
p.save(function(err) {
if (err)
console.log('error')
else
console.log('success')
});
}
});
works for me in production code using the same setup you have. Instead of findById you can use any other find method provided by mongoose. Just make sure you fetch the document before updating it.
Now, i think you can do this :
Place.findOneAndUpdate({name:req.params.name}, req.body, function (err, place) {
res.send(place);
});
You can find by id too :
Place.findOneAndUpdate({_id:req.params.id}, req.body, function (err, place) {
res.send(place);
});
So now you can find and update directly by id, this is for Mongoose v4
Place.findByIdAndUpdate(req.params.id, req.body, function (err, place) {
res.send(place);
});
Just to mention, if you needs updated object then you need to pass {new: true} like
Place.findByIdAndUpdate(req.params.id, req.body, {new: true}, function (err, place) {
res.send(place);
});
I think your problem is that you are using node 0.4.0 - try moving to 0.2.6 with an it should work. There is an issue logged on github with the bodyDecoder not populating the req.body.variable field in node >= 0.3.0.