How can I access a field of a Parse.com Object when I don't know the fieldname? - parse-platform

When I use the code below to access a class row for the current user?
Parse reports that the function returns a data set thus:
{"user":{"__type":"Pointer""className":"_User""objectId":"NFYHCP6Ftw"}
"known_fieldname":"Value"
"known_fn2":"value2"
"known_fn3":"value3"
"objectId":"obFbHtMW4E"
"createdAt":"2014-09-16T15:47:55.047Z"
"updatedAt":"2014-09-16T16:10:55.318Z"
"__type":"Object"
"className":"Answers"}
Parse CloudCode:
Parse.Cloud.define("Answers", function(request, response) {
var query = new Parse.Query("Answers");
query.equalTo("user", Parse.User.current());
query.first({
success: function(obj) {
// kick out "value"...
console.log("A field I know="+obj.get("known_fieldname"));
// this for loop doesn't loop thru the field "keys" and "values"
// what do I replace it with?
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
console.log("Answer="+obj.get(key));
// add to a dictionary to return to caller
}
}
response.success(obj);
},
error: function() {
response.error("Failed to get any answers.");
}
});
});

To get a list of keys for an Object one needs to do this:
var keys = Object.keys(obj.toJSON());
One can then loop thru keys thus:
for (var key in keys) { ...
I think the Parse developers assumed one would know the names of fields within a Parse class and so didn't provide (yet) a clean API to access them.

Related

Parse Cloud Right Query to retrieve Friendslist and not (like Instagram)

I have the class "Users" the default of Parse Dashboad. I have also the class "Friends", where I store the friendships between users like this:
There are two columns, "toUser" and "fromUser", which are showing who of the two users sent the friend request. These two columns are Pointers(Pointer<_User>) to the Users Class.
My concept is to retrieve two lists:
1. Friendlist for the logged in user
2. Userlist of users (who are not friends with the logged in user)
What would be the appropriate queries for that?
The first one logically should not scan all the class Users because it may slow down the perfomance of the app which is using Ionic 3 and Angular 4
Thank you
you don't need another class to do so all you need is a relation column in the default user class
to add new friend you just need the following code
var friendtoadd
//this is the friend object from parse user class you want to add you can get it by anyway you want
var currentUser = Parse.User.current();
var relation = currentUser.relation("friends");
relation.add(friendtoadd)
currentUser.save();
to retrieve array of friends of logged in user you can use the following code
var currentUser = Parse.User.current();
var friendRelation = currentUser.relation('friends');
var env = this
friendRelation.query().find({
success: function(users) {
for (var i = 0; i< users.length; i++) {
var object = users[i];
env.friends.push(object)
console.log(env.friends)
}
}
});
// you should define friends = []; in the class
if I understood your question right you want to find the friend requests you sent, or the ones you received. because I don't see where you made a relation between the user and his friends.
this is the code if you want to do this using cloud code:
First I validated the parameters of the friendRequest being saved :
Parse.Cloud.beforeSave("Friends", function(request, response){
var friendRequest = request.object;
if (!friendRequest.has("toUser") || !friendRequest.has("fromUser")) {
response.error("Invalid parameters");
return;
}
response.success();
});
then I created two cloud functions, one for retrieving the sentRequests:
Parse.Cloud.define("getSentRequests", function(request, response){
var query = new Parse.Query("Friends");
query.equalTo("fromUser", request.user);
if (!request.master) {
query.find({ sessionToken: request.user.getSessionToken() }).then(function(friends){
response.success(friends);
});
}else{
query.find({useMasterKey:true}).then(function(friends){
response.success(friends);
});
}
});
and you can call this either from a logged in user or using the masterKey if you want, and the other query is for the recievedRequests:
Parse.Cloud.define("getRecievedRequests", function(request, response){
var query = new Parse.Query("Friends");
query.equalTo("toUser", request.user);
if (!request.master) {
query.find({ sessionToken: request.user.getSessionToken() }).then(function(users){
response.success(users);
});
}else{
query.find({useMasterKey:true}).then(function(users){
response.success(users);
});
}
});

Why Parse server is creating new object instead of updating?

I am running parse server in NodeJS environment with express.
Generally, Parse automatically figures out which data has changed so only “dirty” fields will be sent to the Parse Cloud. So, I don’t need to worry about squashing data that I didn’t intend to update.
But why this following code is saving new data every time instead of updating the existing document data with name "Some Name".
// Parse code
Parse.initialize(keys.parseAppID);
Parse.serverURL = keys.parseServerURL;
var GameScore = Parse.Object.extend("GameScore");
var gameScore = new GameScore();
let data = {
playerName: "Some Name",
score: 2918,
cheatMode: true
};
gameScore.save(data, {
success: (gameScore) => {
// let q = new Parse.Query("GameScore");
// q.get(gameScore.id)
console.log("ID: " + gameScore.id)
},
error: function (gameScore, error) {
// Execute any logic that should take place if the save fails.
// error is a Parse.Error with an error code and message.
alert('Failed to create new object, with error code: ' + error.message);
}
});
// End of Parse code
The problem is that you're executing the query to find which object you want to update, but then you're not using the results when you go to save data.
query.first({ // This will result in just one object returned
success: (result) => {
// Check to make sure a result exists
if (result) {
result.save(data, {
// Rest of code
Note: You're treating playerName as a unique key. If multiple users can have the same playerName attribute, then there will be bugs. You can use id instead which is guaranteed to be unique. If you use id instead, you can utilize Parse.Query.get
Edit:
Since you want to update an existing object, you must specify its id.
var GameScore = Parse.Object.extend("GameScore");
var gameScore = new GameScore();
gameScore.id = "ID"; // This id should be the id of the object you want to update

Angular Meteor objects not acting as expected

I am working with Angular Meteor and am having an issue with my objects/arrays. I have this code:
angular.module("learn").controller("CurriculumDetailController", ['$scope', '$stateParams', '$meteor',
function($scope, $stateParams, $meteor){
$scope.curriculum = $meteor.object(CurriculumList, $stateParams.curriculumId);
$scope.resources = _.map($scope.curriculum.resources, function(obj) {
return ResourceList.findOne({_id:obj._id})
});
console.log($scope.resources)
}]);
I am attempting to iterate over 'resources', which is a nested array in the curriculum object, look up each value in the 'ResourceList' collection, and return the new array in the scope.
Problem is, sometimes it works, sometimes it doesnt. When I load up the page and access it through a UI-router link. I get the array as expected. But if the page is refreshed, $scope.resources is an empty array.
My thought is there is something going on with asynchronous calls but have not been able for find a solution. I still have the autopublish package installed. Any help would be appreciated.
What you're going to do is return a cursor containing all the information you want, then you can work with $meteor.object on the client side if you like. Normally, publishComposite would look something like this: (I don't know what your curriculum.resources looks like)
Use this method if the curriculum.resources has only ONE id:
// this takes the place of the publish method
Meteor.publishComposite('curriculum', function(id) {
return {
find: function() {
// Here you are getting the CurriculumList based on the id, or whatever you want
return CurriculumList.find({_id: id});
},
children: [
{
find: function(curr) {
// (curr) will be each of the CurriculumList's found from the parent query
// Normally you would do something like this:
return ResourceList.find(_id: curr.resources[0]._id);
}
}
]
}
})
This method if you have multiple resources:
However, since it looks like your curriculum is going to have a resources list with one or many objects with id's then we need to build the query before returning anything. Try something like:
// well use a function so we can send in an _id
Meteor.publishComposite('curriculum', function(id){
// we'll build our query before returning it.
var query = {
find: function() {
return CurriculumList.find({_id: id});
}
};
// now we'll fetch the curriculum so we can access the resources list
var curr = CurriculumList.find({_id: id}).fetch();
// this will pluck the ids from the resources and place them into an array
var rList = _.pluck(curr.resources, '_id');
// here we'll iterate over the resource ids and place a "find" object into the query.children array.
query.children = [];
_.each(rList, function(id) {
var childObj = {
find: function() {
return ResourceList.find({_id: id});
}
};
query.children.push(childObj)
})
return query;
});
So what should happen here (I didn't test) is with one publish function you will be getting the Curriculum you want, plus all of it's resourceslist children.
Now you will have access to these on the client side.
$scope.curriculum = $meteor.object(CurriculumList, $stateParams.curriculumId);
// collection if more than one, object if only one.
$scope.resources = $meteor.collection(ResoursesList, false);
This was thrown together somewhat quickly so I apologize if it doesn't work straight off, any trouble I'll help you fix.

Find objects not in a relation

I have a custom object Team in Parse with a relation field for the default User object. What I would like to do is retrieve all User objects which are not related to any Team object. Can anyone point me in the right direction on how to do this using the JavaScript SDK? I've been going over the documentation for the Query object but I can't find anything.
Perhaps another type of relation, or placing the relation at another place is a better solution. What I want to accomplish is the following: Each user is allowed to be in one team and one team only. In addition I need to be able to query the following information from Parse:
I want to retrieve the User objects of all the users assigned to a team
I want to retrieve the User objects of all the users who are not assigned to any team
I have tried using a join table with both the user and team object ids. Then I tried to following query to get all users not assigned to a team:
var teammember = Parse.Object.extend('TeamMember'),
query = new Parse.Query("User");
var innerQuery = new Parse.Query("TeamMember");
query.doesNotMatchQuery('user', innerQuery);
query.find({
success: function(results) {
response.success(results);
},
error : function(error) {
response.error(error);
}
})
But this just gets me the following response: error: "{"code":102,"message":"bad type for $notInQuery"}".
I like the Relation type as I can add or remove multiple members at once with a single call to the REST API. I also have no problems retrieving the information on team members when using the Relation type to connect the users to the teams. It is just getting the users which are not assigned to any team that is giving me problems.
It doesn't sound like you need a relation at all. Instead, add a Pointer column to User that points to Team. It ensures that a User can only belong to one team, and your other requirements can be captured as follows.
// All users assigned to a team
query = new Parse.Query('User');
query.exists('team');
// All users assigned to a specific team
query = new Parse.Query('User');
query.equalTo('team', specificTeam);
// All unassigned users
query = new Parse.Query('User');
query.doesNotExist('team');
Update: If you need to support multiple teams per User in the future, then I would suggest creating a Parse table called Membership with two columns: a Pointer to User and a Pointer to Team. This essentially gives you more control than relying on Parse relations, but it gets a little more complicated.
_ = require('underscore'); // Or lodash
// All users assigned to a team
query = new Parse.Query('Membership');
query.find().then(function (results) {
// http://underscorejs.org/#uniq
users = _.uniq(results, false, function (user) { return user.id; });
});
// All users assigned to a specific team
query = new Parse.Query('Membership');
query.equalTo('team', specificTeam);
// All unassigned users
var assignedUsers = []
var unassignedUsers = []
memberQuery = new Parse.Query('Membership');
userQuery = new Parse.Query('User');
memberQuery.find().then(function (memberResults) {
// http://underscorejs.org/#map
var ids = _.map(memberResults, function (user) { return user.id; });
// http://underscore.js.org/#uniq
assignedUsers = _.uniq(ids);
userQuery.find();
}).then(function (userResults) {
var users = _.map(userResults, function (user) { return user.id; });
// http://underscorejs.org/#difference
unassignedUsers = _.difference(users, assignedUsers);
});
To add and remove Users to/from Teams, you would create Membership objects and save API calls with Parse.Object.saveAll() and Parse.Object.destroyAll().
I ran into trouble with the answer provided by Seth. When retrieving the users not assigned to a team the difference between the two arrays would be incorrect. I am assuming this is due to the assignedUsers having object of type Membership and userResults being of type User. This would make it impossible for underscore to make a proper match.
I would up using this as my Cloud Code:
Parse.Cloud.define("getTeamlessUsers", function(request, response) {
var _ = require("underscore"),
assignedUsers = [],
companyUsers = [],
memberQuery = new Parse.Query("TeamMembers"),
userQuery = new Parse.Query("User"),
index,
ubound;
memberQuery.find().then(function(memberResults) {
// Make sure each User ID will appear just once
memberResults = _.unique(memberResults, false, function(item) { return item.get('user').id; });
// Loop over the unique team members and push the User ID into the array
for (index = 0, ubound = memberResults.length; index < ubound; index++) {
var user = memberResults[index].get("user");
assignedUsers.push(user.id);
}
// Get al the users
return userQuery.find();
}).then(function(userResults) {
// Loop over all the users and push the ID into the array
for (index = 0, ubound = userResults.length; index < ubound; index++) {
companyUsers.push(userResults[index].id);
}
// Create an array of user IDs which are not present in the assignedUsers array
var result = _.difference(companyUsers, assignedUsers);
// Return the IDs of user not assigned to any team
response.success(result);
}).fail(function(error) {
response.error(error);
});
});

Backbone.js + MVC3. Nested collection doesn't get populated

I have a backbone collection on the client.
Model of the collection has some properties along with another collection
When I do fetch() my action method on the server returns some data, collection gets populated, all the properties too, except that nested collection.
What could be the reason?
var Job = Backbone.Model.extend();
var Jobs = Backbone.Collection.extend({model: Job})
var Foo = Backbone.Model.extend({
initialize:function(){
this.jobs = new Jobs();
}})
var FooCollection = Backbone.Collection.extend({model: Foo})
var fooCol = new FooCollection()
fooCol.fetch();
fooCol.first().get('name') // => returns name
fooCol.first().jobs.toJSON() // returns nothing
// although this will
fooCol.first().get('jobs') //it will return an array
So somehow nested Backbone collection becomes just a regular property (Array)
OK - with your extra information, I can give you an answer.
First - "get" doesn't get a property off of the model. It gets a property off of the model's attributes property. So, the attributes probably look like:
{
name: 'blah',
jobs: [{name: 'job1'}, {name: 'job2'}]
}
Backbone doesn't automagically transform arrays into collections and models, and simply setting this.jobs isn't going to work. What you need to do is a little more complex.
var Foo = Backbone.Model.extend({
initialize:function(){
this.jobs = new Jobs(this.attributes.jobs));
}
});
This will set your 'jobs' property to a new jobs object with the data that was sent over for the jobs. But, alas, it won't automatically fire events on the Jobs collection, nor will it allow you to use helpers like this.get('jobs').each(fn); - you'll only be able to use it as Foo.jobs.each(fn).
In order for you to use the attribute as an actual collection, you'll have to do a lot more complicated things.
var Foo = Backbone.Model.extend({
initialize:function(){
this.createJobs(this.attributes.jobs);
},
toJSON: function () {
var json = Backbone.Model.prototype.toJSON.apply(this);
json.jobs = this.get('jobs').toJSON();
return json;
},
set: function (key, val) {
var attributes;
if(!_.isObject(key)) {
attributes = {}; attributes[key] = val;
} else {
attributes = key;
}
safeAttributes = _.omit(attributes, 'jobs');
Backbone.Model.prototype.set.call(this, safeAttributes);
if(attributes.jobs) { this.get('jobs').reset(attributes.jobs); }
},
clear: function () {
if(this.get('jobs') && this.get('jobs').destroy) {
this.get('jobs').off();
this.get('jobs').destroy();
}
Backbone.Model.prototype.clear.apply(this);
this.createJobs();
},
createJobs: function (jobsArray) {
var jobsCollection = new Jobs(jobsArray);
jobsCollection.on('change', function () {this.trigger('change'); }, this);
this.set('jobs', jobsCollection);
}
});
Note that this is completely untested, but hopefully it shows some of the way you'd do this.

Resources