I need to write a background job in Cloud Code.
It should query the 'User' class for each user, get the array of skills in the "offer" column. Then it should query the 'User' class again for each user comparing the skills from the first user on a match with all the other users.
Here is what I currently have..
Parse.Cloud.job("backgroundJob", function(request, status) {
// Set up to modify user data
Parse.Cloud.useMasterKey();
var counter = 0;
// Query for all users
var users_query = new Parse.Query(Parse.User);
users_query.find(function(user) {
for(var i = 0; i < user.length; i++){
var searchSkills = user[i].get("search");
var query = new Parse.Query(Parse.User);
query.containedIn("offer", searchSkills);
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.matchesQuery('user', query);
}
Parse.Push.send({
where: pushQuery,
data: {
alert: "Found someone close to you!"
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
status.message(counter + " users processed.");
counter++;
}).then(function() {
// Set the job's success status
status.success("Success");
}, function(error) {
// Set the job's error status
status.error("Error has been encountered");
});
});
I get following error in the Push Notification saying:
Cannot perform operation on non-existing column "offer"
Apparently it creates a new empty 'User' Class for my second user query.
Any help would be very much appreciated.
UPDATE! Thats how my user class looks like:
Here's the link for the containedIn method: https://parse.com/docs/js/api/classes/Parse.Query.html#methods_containedIn
So you need to have an "offer" column on your user, that contains a value that would be found in the array searchSkills. Sounds like you don't have an "offer" column. Maybe you have "Offer"? These are case sensitive.
For the second part, do you have a "user" field on your installations? That's something you'd have to set manually, I believe. So it sounds like you never set that, and it's creating a blank user field for the query.
Also, Query.find can only return 1000 results. If you want to go through all of your users, you'll have to use Query.each instead.
Related
Building a Twitter bot to:
search for tweets with keywords like "Net Neutrality"
Return the tweet IDs and usernames for those tweets
Publish a tweet in response to that user (via in_reply_to_status_id, as described in Twitter Docs)
Here is my current code:
const Twitter = new twit(config);
let tweet = function() {
let params = {
q: '#netneutrality, #savethenet, Net Neutrality',
result_type: 'mixed',
lang: 'en'
}
// search through all tweets using our params and execute a function:
Twitter.get('search/tweets', params, function(err, data) {
// if there is no error
if (!err) {
// loop through the first 4 returned tweets
for (let i = 0; i < 4; i++) {
// iterate through those first four defining a rtId that is equal to the value of each of those tweets' ids
let rtId = data.statuses[i].id_str;
let username = data.statuses[i].username;
// the post action
Twitter.post('statuses/update', {
// setting the id equal to the rtId variable
in_reply_to_status_id: rtId
, status: `#${username} Send single-click #SaveTheNet tweets to key politicians at fliplist.app. Take 60 seconds to protect our Internet, once and for all.`
// log response and log error
}, function(err, response) {
if (response) {
console.log('Successfully tweeted');
}
if (err) {
console.log(err);
}
});
}
}
else {
// catch all log if the search could not be executed
console.log('Could not search tweets.');
}
});
}
tweet();
setInterval(tweet, 600000);
When I ran it, there was a successful output ("Succesfully tweeted" 4 times in terminal), and there were indeed 4 new tweets published from my account.
However, the username in all of those tweets was #undefined.
So I imagine I'm either failing to collect those usernames from the relevant tweets or failing to add them to the status string appropriately.
Any suggestions for how I can fix this?
For reference, here is a link to one of the #undefined tweets: https://twitter.com/ProoveTweets/status/1075855455958765569
And a screenshot is attached here
Two things to note. Firstly, Twitter's usernames are called "Screen Names". So you code should probably read:
let username = data.statuses[i].user.screen_name;
Take a look at the search documentation for an example of the JSON being returned. https://developer.twitter.com/en/docs/tweets/search/api-reference/get-search-tweets.html
Secondly - please don't do this! Automatically replying to keywords is against the developer terms of service and is really annoying to users.
https://help.twitter.com/en/rules-and-policies/twitter-automation
On our website I want people to be able to report if a video is uploaded on Youtube so we have 3 fields on our website. A Username, password and video Id field. When the user clicks the Html button we have it call a javascript function.
function reportUpload() {
Parse.Cloud.run('report_upload',
{
username: "testUser",
password: "testPassword",
videoId: "VQv9xfOfLOY"
},{
success: function(result) {
//Do Neat Stuff
},
error: function(e) {
//error
}
});
}
That then calls our cloud code function. We need the cloud code function to pull the usernames from the User class and test if any of them match. If any do match they will then need to check if the passwords match. if all of that doesn't fail it will send a push notification to one of the users of the app.
So far in the cloud code funtion I have figured out how to query all the usernames.
Parse.Cloud.define("report_upload", function(request, response) {
console.log(request.params);
var query = new Parse.Query(Parse.User);
query.find({
success: function(results){
var users = [];
//extract out user names from results
for(var i = 0; i < results.length; ++i){
users.push(results[i].get("username"));
}
response.success(users);
console.log(JSON.stringify(users));
}, error: function(error){
response.error("Error");
}
});
});
Thanks
Seems like you want to check if a particular user has made an account already with a particular username.
You will have to query the User class. The user class is secured by default.
In the cloud code, you should write Parse.useMasterKey(); to have a full access (Careful, read note below).
var myquery = new Parse.Query(Parse.User);
myquery.equalTo("username",yourUsernameHere);
myquery.find({
success: function(results){
if(results.length === 0)
{
// User not found
}
else
{
// found
}
});
Note: Using the master key overrides all individual access privileges for your data. Be absolutely sure what you are doing.
I have an object called Group, which stores an array of Users (default Parse user) as a column. I am trying to list all of these users' display names (column called "displayName") in a certain group, but for some reason, when I try to use the .get function on a user in the retrieved array, it only gives me the information for the current user. I checked my permissions and ACL and it says for each user Public Read, and the User class has public read and write permissions. Here is the code I am using:
var groupObject = Parse.Object.extend("Group");
var users = [];
var groupQuery = new Parse.Query(groupObject);
groupQuery.get(groupId,{
success: function(group)
{
users = group.get("Users");
for(var i=0;i<users.length;i++)
{
var user = users[i];
console.log("display name: " + user.get("displayName") + "username: " + user.get("username") + "id: " + user.id);
}
doneCallback(users);
},
error: function(object, error)
{
errorCallback(error);
}
});
I am able to console.log all of their ids, and the query is successful, but the only thing I can use get("column") on is the current user (which is part of the group users array).
I think you should use a Relation instead of an Array to store your users in the Group object. It will be way easier to add, remove, fetch and access your user objects via the relation. You also avoid some headache when the list of your users becomes large. Then you should be able to fetch all the users in any given group like this:
var groupObject = new Parse.Object("Group");
groupObject.set("objectId", yourGroupId);
var query = groupObject.relation("Users").query();
Parse.Cloud.useMasterKey();
query.find().then( function(users) {
_.each(users, function(user) {
console.log(user.get("displayName"));
});
});
Here is a question for parse.com gurus.
I am using Parse Javascript API and trying to execute a query over 2 Pointers and cannot get it to work.
So i have following classes: Posts, Users, Groups. Posts has Pointer to Users. Users has a Pointer to Groups.
I need to get all POSTS, where a USER belongs to GROUP, which name starts with "Admin". Here is my code that doesn't work:
var Posts = Parse.Object.extend("Posts");
var Users = Parse.Object.extend("Users");
var Groups = Parse.Object.extend("Groups");
var query = new Parse.Query(Posts);
var innerQueryUsers = new Parse.Query(Users);
var innerQueryGroups = new Parse.Query(Groups);
innerQueryGroups.startsWith("name", "Admin");
innerQueryUsers.matchesQuery("group", innerQueryGroups);
query.matchesQuery("user", innerQueryUsers);
query.find({
success: function(data) {
},
error: function(error){
// here i get error: {code: 102, message: "bad type for $inQuery"}
}
});
Anybody have an idea how to do it right?
Edit - This can be done in one (untested) query by combining a group query and a post query with the same user query...
function postsInGroup(name) {
var groupQuery = new Parse.Query("Group");
groupQuery.equalTo("name", name);
var userQuery = new Parse.Query(Parse.User);
userQuery.matchesQuery("group", groupQuery);
var postQuery = new Parse.Query("Post");
postQuery.matchesQuery("user", userQuery);
return postQuery.find();
}
Call it like this...
postsInGroup("Admin").then(function(posts) {
console.log(JSON.stringify(posts));
}, function(error) {
console.log(JSON.stringify(error));
});
Its not clear what savings there is between this approach and first querying the group. It's likely that parse.com runs the inner queries much as you would. The difference in readability is a matter of taste.
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