I have a user class with two types of users - customers, and vendors. There is a column (GeoPoint) called vendorLocation in user table which has coordinates of the vendor's shop but is left blank (null) for the customers.
When a customer places an order, a new object is created in the Order Class in which I store the address location as a GeoPoint. I want to find the vendor closest to him. I tried to write a cloud code but kept facing the same error over and over.
Parse.Cloud.define("assignVendor", function(request, response){
var orderObjectId = request.params.orderObjectId;
var query= new Parse.Query("Order")
query.equalTo("objectId", orderObjectId);
query.first({
success: function(order){
//order is the newly created parse order object
//console.log("Order object found"+ order.get("orderNumber"));
var userGeoPoint=new Parse.GeoPoint();
//This is the problematic line
userGeoPoint = order.get("customerLocation");
var Query = new Parse.Query(Parse.User);
Query.exists("vendorLocation");
Query.near("vendorLocation", userGeoPoint);
console.log("Reached here");
Query.find({
success: function(results){
var chemist= results[0];
response.success("found "+vendor.get("profileName"));
order.put("vendorLocation", chemist);
order.save(null, {
success: function(result) {response.success("Saved")},
error: function(error) {console.log("Failed at save")} });
},
error:function(error){console.log("Cant find a suitable vendor")}
});
}, error:function(error){console.log("cant find the ");}
});
});
The error shown in cloud code console.
E2015-10-06T11:23:40.858Z]v13 Ran cloud function assignVendor for user 4otr3l7YwG with:
Input: {"orderObjectId":"OS5siGRXYW"}
Result: TypeError: Cannot call method 'get' of undefined
at e.Query.find.success (main.js:25:53)
at e.<anonymous> (Parse.js:14:27927)
at e.s (Parse.js:14:26859)
at e.n.value (Parse.js:14:26278)
at e.s (Parse.js:14:26987)
at e.n.value (Parse.js:14:26278)
at e.s (Parse.js:14:26987)
at e.n.value (Parse.js:14:26278)
at e.<anonymous> (Parse.js:14:26931)
at e.s (Parse.js:14:26859)
I2015-10-06T11:23:40.924Z]Reached here
The android code
HashMap atMap=new HashMap<>();
atMap.put("orderObjectId", "OS5siGRXYW");
ParseCloud.callFunctionInBackground("assignVendor", atMap, new FunctionCallback<String>() {
#Override
public void done(String value, ParseException e) {
if (e!=null){Log.d("failed ", +e.getCode()+e.getMessage());} else {Log.d("s", value);}
}
});
Error showed in android code is exactly the same as console, so I havnt posted it again. Error number is '141'
Here's some help tracking down the cause of your problem.
make absolutely sure that the order number is being passed correctly
and that the order with the object Id you are passing exists.
you may run into problems when directly assigning nested callbacks
to the first() function. It is better to use then() to string promises together.
it is better to use get() than to use first() when trying to
find a single object, as you should get an object not found error
if get() returns nothing, preventing the cannot call method get
error.
Let's clean up your code and see if it helps:
Parse.Cloud.define("assignVendor", function(request, response){
var fetchedOrder;
var orderObjectId = request.params.orderObjectId;
console.log("orderObjectId = " + orderObjectId);
var orderQuery= new Parse.Query("Order")
orderQuery.get(orderObjectId)
.then(function(order){
fetchedOrder = order;
console.log("Order object found"+ fetchedOrder.get("orderNumber"));
userGeoPoint = fetchedOrder.get("customerLocation");
var userQuery = new Parse.Query(Parse.User);
userQuery.near("vendorLocation", userGeoPoint);
return userQuery.first();
})
.then(function(chemist){
order.put("vendorLocation", chemist);
return order.save();
})
.then(function(){
response.success("Success!");
}, function (error){
console.error(JSON.stringify(error));
response.error(error);
});
});
Notes:
You dont need to define a new Parse.Geopoint before getting
customerLocation. You are just reassigning a variable there.
You dont need to check if the vendorLocation exists before
comparing it to the userGeoPoint. If it doesnt exist, it simply
wont get returned when using near
I would advise using response.success only at the end all your
promise thread
After userQuery.find(), I believe that order does not exist in that
context so I've set it as a function variable.
There is no need to use results[0], simply use first()
Related
In a DocDb stored procedure, as the first step in a process retrieving data that I'm mutating, I read and then use the data iff it matches the etag like so:
collection.readDocument(reqSelf, function(err, doc) {
if (doc._etag == requestEtag) {
// Success - want to update
} else {
// CURRENTLY: Discard the read result I just paid lots of RUs to read
// IDEALLY: check whether response `options` or similar indicates retrieval
was skipped due to doc not being present with that etag anymore
...
// ... Continue with an alternate strategy
}
});
Is there a way to pass an options to the readDocument call such that the callback will be informed "It's changed so we didn't get it, as you requested" ?
(My real problem here is that I can't find any documentation other than the readDocument undocumentation in the js-server docs)
Technically you can do that by creating a responseOptions object and passing it to the call.
function sample(selfLink, requestEtag) {
var collection = getContext().getCollection();
var responseOptions = { accessCondition: { type: "IfMatch", condition: requestEtag } };
var isAccepted = collection.readDocument(selfLink, responseOptions, function(err, doc, options) {
if(err){
throw new Error('Error thrown. Check the status code for PreconditionFailed errors');
}
var response = getContext().getResponse();
response.setBody(doc);
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
However, even if the etag you provide is not the one that the document has, you won't get an error and you will properly get the document itself back. It's just not supposed to work with that using the readDocument function in a stored procedure.
Thanks to some pushing from #Nick Chapsas, and this self-answer from #Redman I worked out that in my case I can achieve my goal (either read the current document via the self-link, or the newer one that has replaced it bearing the same id) by instead generating an Alt link within the stored procedure like so:
var docId = collection.getAltLink() + "/docs/"+req.id;
var isAccepted = collection.readDocument(docId, {}, function (err, doc, options) {
if (err) throw err;
// Will be null or not depending on whether it exists
executeUpsert(doc);
});
if (!isAccepted) throw new Error("readDocument not Accepted");
I am attempting to perform an update to a MongoDB document (using mongoose) by first using .findById to get the document, then updating the fields in that document with new values. I am still a bit new to this so I used a tutorial to figure out how to get it working, then I have been updating my code for my needs. Here is the tutorial: MEAN App Tutorial with Angular 4. The original code had a schema defined, but my requirement is for a generic MongoDB interface that will simply take whatever payload is sent to it and send it along to MongoDB. The original tutorial had something like this:
exports.updateTodo = async function(todo){
var id = todo.id
try{
//Find the old Todo Object by the Id
var oldTodo = await ToDo.findById(id);
}catch(e){
throw Error("Error occured while Finding the Todo")
}
// If no old Todo Object exists return false
if(!oldTodo){
return false;
}
console.log(oldTodo)
//Edit the Todo Object
oldTodo.title = todo.title
oldTodo.description = todo.description
oldTodo.status = todo.status
console.log(oldTodo)
try{
var savedTodo = await oldTodo.save()
return savedTodo;
}catch(e){
throw Error("And Error occured while updating the Todo");
}
}
However, since I don't want a schema and want to allow anything through, I don't want to assign static values to specific field names like, title, description, status, etc. So, I came up with this:
exports.updateData = async function(update){
var id = update.id
// Check the existence of the query parameters, If they don't exist then assign a default value
var dbName = update.dbName ? update.dbName : 'test'
var collection = update.collection ? update.collection : 'testing';
const Test = mongoose.model(dbName, TestSchema, collection);
try{
//Find the existing Test object by the Id
var existingData = await Test.findById(id);
}catch(e){
throw Error("Error occurred while finding the Test document - " + e)
}
// If no existing Test object exists return false
if(!existingData){
return false;
}
console.log("Existing document is " + existingData)
//Edit the Test object
existingData = JSON.parse(JSON.stringify(update))
//This was another way to overwrite existing field values, but
//performs a "shallow copy" so it's not desireable
//existingData = Object.assign({}, existingData, update)
//existingData.title = update.title
//existingData.description = update.description
//existingData.status = update.status
console.log("New data is " + existingData)
try{
var savedOutput = await existingData.save()
return savedOutput;
}catch(e){
throw Error("An error occurred while updating the Test document - " + e);
}
}
My original problem with this was that I had a lot of issues getting the new values to overwrite the old ones. Now that that's been solved, I am getting the error of "TypeError: existingData.save is not a function". I am thinking the data type changed or something, and now it is not being accepted. When I uncomment the static values that were in the old tutorial code, it works. This is further supported by my console logging before and after I join the objects, because the first one prints the actual data and the second one prints [object Object]. However, I can't seem to figure out what it's expecting. Any help would be greatly appreciated.
EDIT: I figured it out. Apparently Mongoose has its own data type of "Model" which gets changed if you do anything crazy to the underlying data by using things like JSON.stringify. I used Object.prototype.constructor to figure out the actual object type like so:
console.log("THIS IS BEFORE: " + existingData.constructor);
existingData = JSON.parse(JSON.stringify(update));
console.log("THIS IS AFTER: " + existingData.constructor);
And I got this:
THIS IS BEFORE: function model(doc, fields, skipId) {
model.hooks.execPreSync('createModel', doc);
if (!(this instanceof model)) {
return new model(doc, fields, skipId);
}
Model.call(this, doc, fields, skipId);
}
THIS IS AFTER: function Object() { [native code] }
Which showed me what was actually going on. I added this to fix it:
existingData = new Test(JSON.parse(JSON.stringify(update)));
On a related note, I should probably just use the native MongoDB driver at this point, but it's working, so I'll just put it on my to do list for now.
You've now found a solution but I would suggest using the MongoDB driver which would make your code look something along the lines of this and would make the origional issue disappear:
// MongoDB Settings
const MongoClient = require(`mongodb`).MongoClient;
const mongodb_uri = `mongodb+srv://${REPLACE_mongodb_username}:${REPLACE_mongodb_password}#url-here.gcp.mongodb.net/test`;
const db_name = `test`;
let db; // allows us to reuse the database connection once it is opened
// Open MongoDB Connection
const open_database_connection = async () => {
try {
client = await MongoClient.connect(mongodb_uri);
} catch (err) { throw new Error(err); }
db = client.db(db_name);
};
exports.updateData = async update => {
// open database connection if it isn't already open
try {
if (!db) await open_database_connection();
} catch (err) { throw new Error(err); }
// update document
let savedOutput;
try {
savedOutput = await db.collection(`testing`).updateOne( // .save() is being depreciated
{ // filter
_id: update.id // the '_id' might need to be 'id' depending on how you have set your collection up, usually it is '_id'
},
$set: { // I've assumed that you are overwriting the fields you are updating hence the '$set' operator
update // update here - this is assuming that the update object only contains fields that should be updated
}
// If you want to add a new document if the id isn't found add the below line
// ,{ upsert: true }
);
} catch (err) { throw new Error(`An error occurred while updating the Test document - ${err}`); }
if (savedOutput.matchedCount !== 1) return false; // if you add in '{ upsert: true }' above, then remove this line as it will create a new document
return savedOutput;
}
The collection testing would need to be created before this code but this is only a one-time thing and is very easy - if you are using MongoDB Atlas then you can use MongoDB Compass / go in your online admin to create the collection without a single line of code...
As far as I can see you should need to duplicate the update object. The above reduces the database calls from 2 to one and allows you to reuse the database connection, potentially anywhere else in the application which would help to speed things up. Also don't store your MongoDB credentials directly in the code.
I'm using parse.com as the backend to my project and am creating a web page using javascript.
I'm extending PFObject as follow:
var Match = Parse.Object.extend("Match");
On the match object i have a couple properties, let say the first one is "player1"
My question is how can i make it so that when i try to get a property of my match object it succeeds
ie:
var matchQuery = new Parse.Query("Match");
matchQuery.find({
success: function (results) {
_.each(results, function (element, index, list) {
//
var test = element.player1 <<<< here player1 is undefined
})
},
error: function (error) {
alert("Error: " + error.code + " " + error.message);
}
});
Thanks for any tips!
Setting values on the backbone object works like regular JS. The value is retained for as long as the object is in memory, but no longer.
match.memoryOnlyAttribute = "I'll be gone soon";
If match is released and then queried again, memoryOnlyAttribute will be null, as you have observed.
To get a value for a property that persists, it must first be a property on the object. This is typically done in the data browser with the "+ Col" button. (It can also be done in code if your CLP permits).
With that done, the object can only be assigned persistent property values via the set() method...
var Match = Parse.Object.extend("Match");
var match = new Match();
match.set("player1", /* an object here that is of the right type */);
match.save();
Once the object is retrieved, the property in the parse data can be retrieved with the get() method...
matchQuery.first().then(function(matchResult) {
var player1 = match.get("player1");
// player1 will have a value
});
I'm still struggling to understand how to access Meteor.users as a foreign key from another collection query. I understand that only the current user is published by default so I have a publication on the server as
Meteor.publish('itemOwner', function(userId) {
check(userId, String);
var user = Meteor.users.find({id: userId});
return user;
// return Meteor.users.find({id: userId}, {
// fields: {'profile': 1} });
});
I then have a Deps.autorun on the client..
Deps.autorun(function () {
var itemOwnerId = Session.get("itemOwnerID");
if (itemOwnerId) {
debugger
var ID = Session.get("itemOwnerID");
Meteor.subscribe('itemOwner', Session.get("itemOwnerID"));
}
});
I set the session ID on a modal form load, and display it in the template by calling the ownerProfile helper (or try to)
Template.showQuoteModalInner.helpers({
getQuote: function () {
// Get the quote ID from the session var
var quote = Session.get("quoteID");
if(quote) {
debugger;
var ID = quote.user._id;
Session.set("itemOwnerID", quote.user._id);
return quote;
}
},
ownerProfile: function() {
debugger;
var quote = Session.get("quoteID");
if(quote) {
var ID = quote.user._id;
var theUser = Meteor.users.find({_id: quote.user._id});
return theUser;
};
}
});
Now, I can trace the user ID at each stage and see it getting correctly passed to the autorun and the helpers. If I stop the program at the debugger in the ownerProfile helper and in the console put in Meteor.user.fetch({_id: "the id here"}).fetch() I get the correct user back.. but, in the handler itself the Meteor.users.find returns null??? What am I missing?
Two possibilities I noticed.
First, you are missing an underscore in the find in your publish function.
.find({id: userId}) should be .find({_id: userId}).
But this probably isn't the issue if you are seeing the user (other than the logged in user) in the console.
Second, if you are not seeing the user from your Template.showQuoteModalInner.ownerProfile helper, it is probably because you are returning a find() instead of a findOne().
find() returns a cursor whereas findOne() returns the record. Try findOne() if you want to display that single user's attributes.
UPDATE: In a nutshell, I would like to use the Master key, because I need to write an other user object with my current user, but I don't want to override all security, I just wanna use it in one function. The accepted answer in this question gave a very nice starting point, however I couldn't make it to work. It's the last code block in this question.
I have two separated functions. The first is pure objective-c, it deletes users from the currentUser's firstRelation. It worked well without any problems until i added a different CloudCode function into a different view controller. The CloudCode function uses the master key and adds currentUser to otherUser's sampleRelation & adds otherUser to currentUser's sampleRelation (firstRelation and sampleRelation is two different column inside the User class).
So the problem is when I delete a user from currentUser's firstRelation (with current user) my app crashes, because the user must be authenticated via logIn or signUp. Actually i don't understand this, because in this case I'm writing the currentUser with the currentUser instead of another user, so it must work without any problems (and worked before the CloudCode).
I'm almost sure that it's because I'm using the master key with the CloudCode, but have no idea how can I avoid it. Everything else is still working, for example I can upload images with currentUser.
Here is the code that I'm using for the CloudCode, JavaScript is totally unknown for me, maybe somebody will see what causes the problem.
Parse.Cloud.define('editUser', function(request, response) {
Parse.Cloud.useMasterKey();
var userQuery = new Parse.Query(Parse.User);
userQuery.get(request.params.userId)
.then(function (user) {
var relation = user.relation("sampleRelation");
relation.add(request.user);
// chain the promise
return user.save();
}).then(function (user) {
var currentUser = request.user;
var relation = currentUser.relation("sampleRelation");
relation.add(user);
// chain the new promise
return currentUser.save();
}).then(function () {
response.success();
}, function (error) {
response.error(error);
});
});
It crashes when i try to remove the object:
PFUser *user = [self.friends objectAtIndex:indexPath.row];
PFRelation *myFriendsRel = [self.currentUser relationForKey:#"simpleRelation"];
if ([self isFriend:user]) {
for (PFUser *friendName in self.friends) {
if ([friendName.objectId isEqualToString:user.objectId]){
[self.friends removeObject:friendName];
break; // to exit a loop
}
}
// remove from parse
[myFriendsRel removeObject:user];
NSLog(#"deleted: %#", user.username);
}
[self.currentUser saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error){
NSLog(#"Error %# %#", error, [error userInfo]);
}
}];
This is the newest attempt, that based Fosco's answer from the other question. It works, but the same way as the earlier versions.
Parse.Cloud.define('editUser', function(request, response) {
var userId = request.params.userId;
var User = Parse.Object.extend('_User'),
user = new User({ objectId: userId });
var currentUser = request.user;
var relation = user.relation("friendsRelation");
relation.add(currentUser);
user.save(null, { useMasterKey:true}).then(function(user) {
response.success(user);
}, function(error) {
response.error(error)
});
});
At a quick glance it looks like its failing because you're trying to remove an object from an array whilst it is being iterated. I know this causes a crash in Objective C regardless of whether you're using Parse objects or not.
Try re-writing this segment:
for (PFUser *friendName in self.friends) {
if ([friendName.objectId isEqualToString:user.objectId]){
[self.friends removeObject:friendName];
break; // to exit a loop
}
}
To something like this:
NSMutableArray *tempArray = [[NSMutableArray alloc]init];
for (PFUser *friendName in self.friends) {
if (![friendName.objectId isEqualToString:user.objectId]) {
[tempArray addObject:friendName];
}
self.friends = [NSArray arrayWithArray:tempArray];
Again, only had a quick glance so not 100% if that is your problem but it looks like it, let me know if it helps