$pull not removing document from an Array in MongoDB - node.js

I have a MongoDB document that looks like this:
"_id": ObjectID(48kf4kblahblahblah),
"name": "A Person's Name",
"username": "apersonsemail#mail.com",
"password": "a hashed password",
"formulas": [
{
"f_id": ObjectID(4k8s9blahblahblah),
"name": "A Name",
"description": "A description",
"ingredients": [{object}, {object}]
}
]
}
I'm trying to query for a document based on _id and then remove the sub-document from the formulas array based on an item's f_id value using $pull.
However, it's not updating anything.
This is taking part in an Express/Node application, and my code looks like this:
router.delete('/formula-list/:index', function(req, res){
var db = req.db.collection('users');
var index = req.params.index;
db.updateOne({"_id": new ObjectID(req.user.id)}, { $pull: {"formulas": {"f_id": index}} }, function(err, r){
assert.equal(null, err);
assert.equal(1, r.modifiedCount);
req.db.close();
});
res.end();
});
And I get Assertion Error: 1 == 0
I've consoled req.params.index and I get the value
59683b480986823e10f39fba
And if I console the r object the matchedCount is 1, so I know it's finding the document as well. It's just not doing anything with it.
My questions seems very similar to this one, with the exception that I'm not looking to delete multiple items, but I don't see how this would affect the results of my query.
Thank you.

Related

Add new array element to JSON object in Node-Postgres

I have a json field in a postgres database that looks something like this :
{
"first_name": "John",
"last_name": "Doe",
"email": "johndoe#example.org",
"reviews" :[
{
"comment": "John gave us great service! Love this guy!",
"last_name": "Mariana",
"first_name": "Brown"
}
]
}
Using node, I'm trying to add a new array element (a new review) to the reviews. I want to be able to do this dynamically. The query I'm using ads in a array at index 2. That is not dynamic. I did find helpful material here but all the answers don't tell you how to update(add) to second level keys like the one in my situation. How can I do this?
Query
var text ="UPDATE users SET info = JSONB_SET(info, '{reviews,2}', '"+review+"') WHERE id=$1 RETURNING*";//works but not dynamic
var values = [userid];
pool.query(text, values, (err, res) => {
if (err) {
//log errors
console.log(err.stack);
//return error
io.to(socketid).emit('review-sent',{valid:'false'});
} else {
//success
console.log(res.rows );
var json = res.rows;
//success
io.to(socketid).emit('review-sent',{valid:'true'});
}
});
You can use jsonb_insert() if that is given a negative index it counts from the end. Passing true as the third parameter will then insert the new value after that, so jsonb_insert(..., '{reviews, -1}', true) will append the new value at the end:
update users
set info = jsonb_insert(info,
'{reviews,-1}',
'{"comment": "...", "last_name": "...", "first_name": "..."}',
true)
where id = 1;

UpdateOne Not Modifying Subdocument in MongoDB

I have a mongodb document structure that looks like this:
Document Structure:
{
"_id": ObjectID(59628aa0a6f87f2b68a4636a),
"name": "persons name",
"username": "persons username",
"password": "hashed password",
"formulas": [
{
"f_id": ObjectID(596bfdd758296f208859b9a8),
"name": "formula name",
"amount": "500",
"description": "a description",
"ingredients": [{...}, {...},...,]
}
]
}
I have a page in my express app that allows people to update the information in a formula that's stored inside the formulas array.
When I submit a post request to update the modified sub-document nothing is changed within the collection.
The code for this functionality is here, in NodeJS:
SAMPLE CODE:
router.post('/formula-list/:id', function(req, res){
var db = req.db.collection('users');
var f_id = new ObjectID(req.params.id);
var id = new ObjectID(req.user.id);
var formula = data;
db.updateOne({"_id": id, "formulas.f_id": f_id}, { $set: {
"formulas.$.name": req.body.name,
"formulas.$.description": req.body.description,
"formulas.$.formula": formula
}}, function(err, r){
assert.equal(null, err);
assert.equal(1, r.matchedCount);
assert.equal(1, r.modifiedCount);
req.db.close();
});
data.length = 0;
});
What I'm trying to do is grab the subdocument by its f_id so I can change the information for that particular subdocument, but this is apparently not working.
EDIT:
I realized my original issue is that my POST request wasn't actually hitting my server due to an AJAX request that I changed, but the issue now is that this query does not update the intended sub-document.
Per comment requests I've added in actual values for ObjectID_id and f_id fields.
I've also done a db.collection.find({}) command with both of the values used inside the snippet here and it correctly returned the document.

How to add to array in MongoDB document with mongoose?

I've got a collection of spellbooks in a mongodb. One of my API calls in express through mongoose is a PUT where the user provides the ID of the document and then a body that contains an array of spells. I want to update the array of spells in the spellbook document with the array brought in from the client.
Here's what the document looks like:
{
"_id": {
"$oid": "56f999342c2bdee43869c33b"
},
"name": "malagar22",
"username": "zak",
"spells": [
"56bd4da932c3b8e88e3c113a",
"56bd4da932c3b8e88e3c113b"
],
"__v": 0
}
Here's what my API call looks like:
app.put('/api/spellbook/:id', function (req, res) {
Spellbook.findById(req.params.id, function(err,spellbook) {
if (err) {
res.json({info:'error during find spellbook',error:err});
};
if (spellbook) {
_.merge(spellbook, req.body);
spellbook.save(function(err) {
if (err) {
res.json({info:'error updating spellbook',error:err});
};
res.json({info:'spellbook updated successfully'});
});
} else {
res.json({info:'spell not found'});
}
})
});
As you can see, I'm finding the spellbook that matches the ID. Then I'm doing a lodash merge with the request body, which I imagine would match the spells array in the document with the spells array in the body, which I'll post below. Then we update.
Here's my request:
{
"spells":[
"56bd4daa32c3b8e88e3c1148"
]
}
I'm getting the success callback, but when I check the database, the document isn't being updated. Sorry if this is really simple. I'm an MS SQL guy and this is my first time using Express and Mongo.

How to prevent pushing in the document with same attribute in Mongodb

I have the following structure. I would like to prevent pushing in the document with the same attribute.
E.g. Basically, i find the user object first. If i have another vid (with is already inside), it will not get pushed in. Try using $addToSet, but failed.
I am using Mongoose.
This is my Model Structure:
var User = mongoose.model('User', {
oauthID: Number,
name: String,
username: String,
email: String,
location: String,
birthday: String,
joindate: Date,
pvideos: Array
});
This is my code for pushing into Mongo
exports.pinkvideo = function(req, res) {
var vid = req.body.vid;
var oauthid = req.body.oauthid;
var User = require('../models/user.js');
var user = User.findOne({
oauthID: oauthid
}, function(err, obj) {
if (!err && obj != null) {
obj.pvideos.push({
vid: vid
});
obj.save(function(err) {
res.json({
status: 'success'
});
});
}
});
};
You want the .update() method rather than retrieving the document and using .save() after making your changes.
This not only gives you access to the $addToSet operator that was mentioned, and it's intent is to avoid duplicates in arrays it is a lot more efficient as you are only sending your changes to the database rather than the whole document back and forth:
User.update(
{ oauthID: oauthid },
{ "$addToSet": { "pVideos": vid } },
function( err, numAffected ) {
// check error
res.json({ status: "success" })
}
)
The only possible problem there is it does depend on what you are actually pushing onto the array and expecting it to be unique. So if your array already looked like this:
[ { "name": "A", "value": 1 } ]
And you sent and update with an array element like this:
{ "name": "A", "value": 2 }
Then that document would not be considered to exist purely on the value of "A" in "name" and would add an additional document rather than just replace the existing document.
So you need to be careful about what your intent is, and if this is the sort of logic you are looking for then you would need to find the document and test the existing array entries for the conditions that you want.
But for basic scenarios where you simply don't want to add a clear duplicate then $addToSet as shown is what you want.

Backbone Ajax request with array of json data

I'm making a REST api for NodeJS with MongoDB that's using Backbone on the front end. This is the demo model
var MenuItem = Backbone.Model.extend({
idAttribute: "_id",
urlRoot: '/sentences'
});
This is the view code that calls fetch on that model (note I don't show the creation of that model in this code). I've hardcoded the _id for one of the mongo documents
itemDetails: function (event){
this.menuItemModel.set('_id', '526c0e21977a67d6966dc763');
this.menuItemModel.fetch();
The url that's generated for the post is this when menuItem.fetch() is called
XHR finished loading: "http://localhost:8080/sentences/526c0e21977a67d6966dc763".
Below is the json data at localhost:8080/sentences, so the xhr request to the url with the Mongo object id returns nothing. If I do localhost:8080/sentences/1, however, it returns the first from the array of json data.
[
{
"_id": "526c0e21977a67d6966dc763",
"question": "1",
"uk": "I heard a bloke on the train say that tomorrow's trains will be delayed.",
"us": "I heard a guy on the train say that tomorrow's trains will be delayed."
},
{
"_id": "526c0e21977a67d6966dc764",
"question": "2",
"uk": "Tom went outside for a fag. I think he smokes too much!",
"us": "Tom went outside for a cigarette. I think he smokes too much!"
},
{
"_id": "526c0e21977a67d6966dc765",
"question": "3",
"uk": "Do you fancy going to the cinema on Friday?",
"us": "How about going to the movies on Friday"
}
]
Question: Why isn't Backbone automagically returning the record when I call fetch on the model?
Update
this is the node.js method/route in server.js that returns the sentences
app.get('/sentences', function (req, res){
db.collection('english', function(err, collection) {
collection.find().toArray(function(err, items) {
res.send(items);
});
});
})
Update
this is the function that searches for an individual record. It used to search by question (at which time /sentences/1 returned a record) but now that I've changed it to search by _id, this url (with the mongo id) is still not working "http://localhost:8080/sentences/526c0e21977a67d6966dc763"
app.get('/sentences/:id', function(req,res){
var query = { '_id' : req.params.id };
db.collection('english').findOne(query, function(err, doc) {
if(err) throw err;
console.dir(doc);
res.send(doc);
});
});

Resources