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;
Related
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.
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.
In mongodb there is a document like below,
{
"_id": ObjectId("57443657ee5b5ccc30c4e6f8"),
"name": "Kevin",
"email": "kevinwarn#gmail.com",
"password": "$2a$13$iZ0PhuY6VlBF6qC9rUredrG39Fw5pEKkIh.VCLiGsLPZMKYveyzey",
"mobile": "9980896745",
"__v": NumberInt(0),
"ocassionTypes": [
{
"occasiontype": "Anniversary",
"date": "2016-05-30T18:30:00.000Z"
},
{
"occasiontype": "Donation",
"date": "2016-07-24T18:30:00.000Z"
},
{
"occasiontype": "House Warming",
"date": "2016-09-21T18:30:00.000Z"
}
]
}
So I have written a query in Nodejs to search occasiontype element in ocassionTypes array like below,
router.post('/find-registry', function(req, res){
var uEmail = req.body.email;
var uocType = req.body.userOccasion;
var findUserId = function(db, callback) {
var cursor =db.collection('users').find({email:uEmail, ocassionTypes: {$elemMatch: {occasiontype:uocType}}}).toArray(function(err, docs1){
if(err){
callback(new Error("Some problem"));
} else {
callback(null,docs1);
}
});
};
MongoClient.connect(config.database, function(err, db) {
assert.equal(null, err);
findUserId(db, function(err,docs1) {
db.close();
if(err) return res.json({result:null})
else
return res.json({result1:docs1});
});
});
});
Using this query I am getting 0th index element, but if I give 1st and 2nd element it always shows only 0th index in the output.
In front end I have given input as shown in the picture below.
file.html
Is there any wrong in my query? please help me to fix this.
your query is right but it will give matched document with full array
just add projection in your query
db.collection('users').find({email:uEmail, ocassionTypes: {$elemMatch: {occasiontype:uocType}}},{email:1, ocassionTypes: {$elemMatch: {occasiontype:uocType}}})
If you are searching in sub document, mongodb returns all sub-document instead of matched sub-document. You can limit no. of sub-document using following code.
var cursor =db.collection('users').find({email:uEmail, ocassionTypes: {$elemMatch: {occasiontype:uocType}}},{email: 1, ocassionTypes: {$elemMatch: {occasiontype: uocType}}}).toArray(function(err, docs1){
if(err){
callback(new Error("Some problem"));
} else {
callback(null,docs1);
}
});
It is not tested but it should work.
A piece of my Mongo document structure is:
{ "_id": ObjectId("xxxxxx..."),
"Country" : "UNITED KINGDOM",
"Line" : "something",
"Records" : [
{"rdata" : "foo", "rtype" : "X", "name" : "John"},
{"rdata" : "bar", "rtype" : "Y", "name" : "Bill"}
], ...
I'm using Mongoose to access the data via the following model:
var Record = new Schema({
rdata: String,
rtype: String,
name: String
}, {_id: false});
var ThingSchema = new Schema({
Country: String,
Line : String,
Records : [Record],
Let's say I want to update the "Line" property of one of my documents, from being "Line" : "something" to "Line" : "way more interesting" by sending a PUT request to the appropriate API URL. I can see that the data being sent is all right. This is what the API does:
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Thing.findById(req.params.id, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) { return res.send(404); }
var updated = _.merge(thing, req.body);
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, updated);
});
});
};
The API comes back with 200/OK - but I see the following updated data:
{ "_id": ObjectId("xxxxxx..."),
"Country" : "UNITED KINGDOM",
"Line" : "way more interesting", <-- updated correctly
"Records" : [
{"rdata" : "foo", "rtype" : "X", "name" : "John"},
{"rdata" : "foo", "rtype" : "X", "name" : "John"}
], ...
Notice, how the Records array got messed up by overwriting my second record by duplicating the first one. (If I switch on the automatic addition of '_id' to the subdocument by Mongoose, then even the "_id" fields will be the same on the two records within the array).
It may be relevant, that originally the Records were not added via Mongoose - but by importing a JSON document. Any suggestion as to how to start finding out why this is happening would be fantastic.
Try changing _.merge to _.extend, then call save directly on the thing document returned by the findById() method instead of the merged object updated:
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Thing.findById(req.params.id, function (err, thing) {
if (err) { return handleError(res, err); }
if(!thing) { return res.send(404); }
_.extend(thing, req.body);
thing.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, thing);
});
});
}
Another option is using the set method on the entity i.e. thing.set(req.body) before calling the save method on the thing object.
This answer by ShitalShah highlights the differences between merge and extend that is causing duplicates in your resulting object with merge but essentially:
Here's how extend/assign works: For each property in source, copy its
value as-is to destination. if property values themselves are objects,
there is no recursive traversal of their properties. Entire object
would be taken from source and set in to destination.
Here's how merge works: For each property in source, check if that
property is object itself. If it is then go down recursively and try
to map child object properties from source to destination. So
essentially we merge object hierarchy from source to destination.
While for extend/assign, it's simple one level copy of properties from
source to destination.
JSBin to illustrate the differences
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);
});
});