I am using Node.js + Express to interact with mongodb.
I am trying to write a mongodb update function wrapper.
whenever I make a post request mongo reports "Invalid modifier specified: $set undefined".
Anyone know what's wrong with my funciton wrapper? or is there a better way to do this?
my update_personal_info.js looks like this:
exports.update_personal_info = function(req, res){
var critia = req.body.critia;
var data = req.body.data;
db.collection('accounts', function(err, collection) {
collection.update(critia, data, function (err, doc) {
if (err) {
res.send("There's problem updating the db");
}
});
});
}
The post data I am using to send via Chrome's extension "Advance Rest Client":
critia={"_id": "user_1234"}
data={$set: {"password": "44444444"}}
my app.js looks like this:
app.post('/1',update_personal_info);
accounts collection looks like this:
{
"_id": "user_1234",
"name": "John Chu",
"password": "111111",
"address": [
{"old": "123 seattle st. WA. 123456"},
{"new": "123 new york st. DC. 123456"}
]
}
Using express 3.4.4 and mongodb 1.3.20 it works just fine when I run it. Are you using a really old version of the mongodb driver?
One thing to me sure you are doing is setting the Content Type correctly in the Advanced REST Client. If you are using the Form tab in the Payload section, you need to set it to application/x-www-form-urlencoded.
Some general guidance:
You don't want to have the client send in the selector and the document. It opens you up to allow someone to send in values that could do some damage to your data.
I've solved my own problem by convert the post data string into an object using the eval() function:
var critia = eval("("+req.body.critia+")");
var data = eval("("+req.body.data+")");
Hope this answer will help others.
Cheers
Related
I think this has to do with my limited experience with javascript.
I am using the node.js client library by Google , found here - https://github.com/googlemaps/google-maps-services-js
The example shows how to create a client object
var googleMapsClient = require('#google/maps').createClient({
key: 'your API key here'
});
And then how to run a gecode request and print out the results :
// Geocode an address.
googleMapsClient.geocode({
address: '1600 Amphitheatre Parkway, Mountain View, CA'
}, function(err, response) {
if (!err) {
console.log(response.json.results);
}
});
What I need to do is read a list of addresses from a file and build an array of objects for all of them .
I want my code to do something along the lines of :
create address-objects array
create a client object
open the text files
for each line in text files
geocode the line(address)
add the address and the results into an object and add it to the address-objects
I don't understand whether the googleMapsClient.geocode returns something , if yes how do I access it ? should I set it to some variable along the lines of :
var gc_results = googleMapsClient.geocode(param , ...)
Hope I was clear , thanks in advance
Jonathan.
You can access the response inside the callback function.
So, if you want to have this function to be called for each address, you can first define an empty array:
var gc_results = [];
Then you shall call the function for each address you get out of the file, something similar to this:
addresses.forEach(function(){
googleMapsClient.geocode({
address:
}, function(err, res) {
gc_results.push(res.json.results);
})
})
after this, you will have the gc_results array full with the information you need
I need to develop an App that get some information by FB (user_posts and user_likes) I'm trying implement its using Passport-facebook.
I tried, (just 'likes' to test first)
newUser.facebook.likes = profile.likes[0].value;
and the error is:
TypeError: Cannot read property '0' of undefined
'likes' is on my profileFields.
I put my code that is working on my Github. (whitout likes and posts)
Other information like (id, name, email...) works very well, but I'm not be able to get the data user_likes and user_posts. I would like to enter this data in my MongoDB database.
The method that follow (in Javascript):
function getInfo() {
FB.api('me', {fields :'id,name,posts,likes'}, function(response) {
var dataStr = "data:application/octet-stream;charset=utf-8," + encodeURIComponent(JSON.stringify(response));
var dlAnchorElem = document.getElementById('downloadAnchorElem');
dlAnchorElem.setAttribute("href", dataStr);
dlAnchorElem.setAttribute("download", "feed.json");
dlAnchorElem.click();
});
};
Do what I want, but it writes data (.json) in a file, I want to record in the database and using NodeJS.
Any help?
Are you aware of Mongoose and how to define a schema in mongoose.
i suggest that it would be the simplest way for you to do.
you can use json-schema-to-mongoose (see link below for example)
https://www.npmjs.com/package/json-schema-to-mongoose
and them simply save the document to mongodb.
I'm trying to implement a Password reset. So I'm taking the phone number of the user, getting the document from the database using the phone number to find it, and I'm taking the new password and trying to update the corresponding document using a PUT request in my Cloudant database.
app.post('/pass_rst', function(req,response){
var log='';
//log is just for me to see what's happening
var phone= req.body.phone;
log+=phone+'\n';
db.find({selector:{'phone':phone}}, function(err, result){
if(err){
throw err;
}
if(result.docs.length==0){
response.send('User doesnt exist');
}else{
var existing_data=result.docs[0];
log+=JSON.stringify(existing_data)+'\n';
var upd_pswd= req.body.new_password;
log+=upd_pswd+'\n';
var new_data=existing_data;
new_data.password=upd_pswd;
log+=JSON.stringify(new_data)+'\n';
var id= result.docs[0]._id;
log+=id+'\n';
//make PUT request to db
var options={
host:dbCredentials.host,
port:dbCredentials.port,
path:'/'+dbCredentials.dbName+'/'+id,
//url: dbCredentials.url+'/'+dbCredentials.dbName+'/'+id,
method:'PUT',
json:new_data,
headers:{
'Content-Type':'application/json',
'accept':'*/*'
}
};
log+=JSON.stringify(options)+'\n';
var httpreq= http.request(options);
//log+=JSON.stringify(httpreq);
httpreq.on('error', function(e){
response.send('Error'+e.message);
});
response.send(log+'\n\n\nUpdated');
}
});
});
dbCredentials is defined above as follows:
dbCredentials.host = vcapServices.cloudantNoSQLDB[0].credentials.host;
dbCredentials.port = vcapServices.cloudantNoSQLDB[0].credentials.port;
dbCredentials.user = vcapServices.cloudantNoSQLDB[0].credentials.username;
dbCredentials.password = vcapServices.cloudantNoSQLDB[0].credentials.password;
dbCredentials.url = vcapServices.cloudantNoSQLDB[0].credentials.url;
I've tried tinkering around with it, but in the best case scenario, I don't get an error and I see "Updated" but nothing actually happens in the database. Sometimes I get an error saying : 502 Bad Gateway: Registered endpoint failed to handle the request.
If you see what's going wrong, please let me know. Thank you.
This is the documentation on how to update documents in cloudant
UPDATE
Updating a document
PUT /$DATABASE/$DOCUMENT_ID HTTP/1.1 { "_id": "apple", "_rev":
"1-2902191555", "item": "Malus domestica", "prices": {
"Fresh Mart": 1.59,
"Price Max": 5.99,
"Apples Express": 0.79,
"Gentlefop's Shackmart": 0.49 } }
To update (or create) a document, make a PUT request with the updated
JSON content and the latest _rev value (not needed for creating new
documents) to https://$USERNAME.cloudant.com/$DATABASE/$DOCUMENT_ID.
If you fail to provide the latest _rev, Cloudant responds with a 409
error. This error prevents you overwriting data changed by other
processes. If the write quorum cannot be met, a 202 response is
returned.
Example response: { "ok":true, "id":"apple",
"rev":"2-9176459034" }
The response contains the ID and the new revision of the document or
an error message in case the update failed.
I am using bluemix -nodejs and cloudant. The best way that worked for me to do the update is to use the nano package for db interaction from node.js.
You can refer to the post here:
The summary is - By making use of nano api, you can easily update the record. You need to make sure to use - the _id and the right _rev number, while you use nano. This inturn uses PUT Method underneath.
Updating and Deleting documents using NodeJS on Cloudant DB
When you are including nano, make sure to update the package.json to have the nano dependency added. Let me know if you have further questions on the update/delete
When using the cloudant node.js module there is no separate update function.
You need to use the db.insert function also for the update with the right doc revision, so you need to read the latest revision before the insert.
https://github.com/apache/couchdb-nano#document-functions
"and also used to update an existing document, by including the _rev token in the document being saved:"
// read existing document from db
db.get(key, function(error, existing) {
if (!error)
// use revision of existing doc for new doc to update
obj._rev = existing._rev;
// call db insert
db.insert(obj, key, cb);
});
I'm having difficulty in updating an existing metafield with Shopify API. Each time I receive an error, advising me that the variant already exists... so it must be thinking I'm trying to create a new one (and not update).
I thought this may be an issue with 'put' and 'post' - so changed my method to put, however the error persists. I've hardwired in all my variables to make it easier to test.
I'm working with Cloudinary. I'm using https://github.com/sinechris/shopify-node-api with Express.js
app.post('/upload', function(req, res){
// upload page... assume we have uploaded our image - but have hard-wired a local file in for now
cloudinary.uploader.upload('/Users/Rob/Pictures/testimg.jpg', function(savedImg) {
var imageURL = savedImg.url;
console.log(imageURL)
},
{
public_id: "testimg"
});
// the saved image is returned - so we add it to our updateMetafieldData json object
var updateMetafieldData = {
"variant": {
"id": '253818949',
"metafields": [
{
"key": "variant_image_0",
"value": 'testimg', // whatever the public id of our image is.
"value_type": "string",
"namespace": "variant_image"
}
]
}
}
// and post the result to shopify - then redirect to /getvariants
Shopify.put('/admin/variants/253818949.json', updateMetafieldData, function(data){
// res.redirect('/getvariants')
});
});
I actually created Shopify Node API and was just now happened upon this months later but thought I'd answer for anyone else coming along.
Take a look at the shopify API here: https://docs.shopify.com/api/metafield#update
You can update the metafield directly by performing a PUT request against the metafield resource instead of the variant like so:
/admin/metafields/#{id}.json
You would of course need to know the ID of the metafield first so that would require a call to the variant first or you could simply store the id in your local database for reference.
Pretty straightforward and without any sort of configuration ->
In the project directory I entered the command:
$ meteor mongo to access the mongodb
From there (mongo shell), I switched to db meteor using the command use meteor then entered some basic data to test:
j = { name: "mongo" }
k = { x: 3 }
db.testData.insert(j)
db.testData.insert(k)
I checked and got results by entering: db.testData.find()
Here's my meteor code provided that mongodb access is only required on the client:
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to test.";
};
Template.hello.events({
'click input' : function () {
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
});
Documents = new Meteor.Collection('testData');
var document = Documents.find();
console.log(document);
var documentCbResults = Documents.find(function(err, items) {
console.log(err);
console.log(items);
});
}
Upon checking on the browser and based on the logs, it says undefined. I was unsuccessful from retrieving data from mongodb and showing to the client console.
What am I missing?
For this answer I'm going to assume this is a newly created project with autopublish still on.
As Christian pointed out, you need to define Documents on both the client and the server. You can easily accomplish this by just putting the collection definition at the top of the file or in another file which isn't in either of the server or client directories.
An example which prints the first two test documents could look like this:
Documents = new Meteor.Collection('testData');
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to apui.";
};
Template.hello.events({
'click input' : function () {
var documents = Documents.find().fetch();
console.log(documents[0]);
console.log(documents[1]);
}
});
}
Note the following:
The find function returns a cursor. This is often all you want when writing template code. However, in this case we need direct access to the documents to print them so I used fetch on the cursor. See the documentation for more details.
When you first start the client, the server will read the contents of the defined collections and sync all documents (if you have autopublish on) to the client's local minimongo database. I placed the find inside of the click event to hide that sync time. In your code, the find would have executed the instant the client started and the data probably would not have arrived in time.
Your method of inserting initial items into the database works (you don't need the use meteor by the way), however mongo will default to using an ObjectId instead of a string as the _id. There are subtle ways that this can be annoying in a meteor project, so my recommendation is to let meteor insert your data if at all possible. Here is some code that will ensure the testData collection has some documents:
if (Meteor.isServer) {
Meteor.startup(function() {
if (Documents.find().count() === 0) {
console.log('inserting test data');
Documents.insert({name: "mongo"});
Documents.insert({x: 3});
}
});
}
Note this will only execute if the collection has no documents in it. If you ever want to clear out the collection you can do so via the mongo console. Alternatively you can drop the whole database with:
$ meteor reset
It's not enough to only define collections on the client side. Your mongo db lives on the server and your client needs to get its data from somewhere. It doesn't get it directly from mongodb (I think), but gets it via syncing with the collections on the server.
Just define the Documents collection in the joint scope of client and server. You may also need to wait for the subscription to Documents to complete before you can expect content. So safer is:
Meteor.subscribe('testData', function() {
var document = Documents.find();
console.log(document);
});