I have a Player class. Players can have x number of Trophies. I have the Player objectId and need to get a list of all of their Trophies.
In the Parse.com data browser, the Player object has a column labeled:
trophies Relation<Trophy>
(view Relations)
This seems like it should be so simple but I'm having issues with it.
I have the ParseObject 'player' in memory:
var query = new Parse.Query("Trophy");
query.equalTo("trophies", player);
query.find({
/throws an error- find field has invalid type array.
I've also tried relational Queries:
var relation = new Parse.Relation(player, "trophies");
relation.query().find({
//also throws an error- something about a Substring being required.
This has to be a completely common task, but I can't figure out the proper way to do this.
Anyone know how to do this in Javscript CloudCode?
Many thanks!
EDIT--
I can do relational queries on the user fine:
var user = Parse.User.current();
var relation = user.relation("trophies");
relation.query().find({
I don't understand why this very same bit of code breaks if I'm using a non-user object.
I finally sorted this out, though there is a caveat that makes this work differently than the documentation would indicate.
//Assuming we have 'player', an object of Class 'Player'.
var r = player.relation("trophies");
r.query().find({
success: function(trophies){
response.success(trophies); //list of trophies pointed to by that player's "trophies" column.
},
error: function(error){
response.error(error);
}
})
The caveat: You must have a 'full' player object in memory for this to work. You can't save a player object, grab the object from the success callback and have this work. Some reason, the object that is returned in the success handler is appears to be an incomplete Parse.Object, and is missing some of the methods required to do this.
Another stumbling block about the Parse.com Javascript SDK- A query that finds nothing is still considered successful. So every time you query for something, you must check the length of the response for greater than 0, because the successful query could have returned nothing.
This is what worked for me:
var Comment = Parse.Object.extend("Comment");
var commentsQuery = new Parse.Query(Comment);
commentsQuery.equalTo("parent", video);//for the given 'video' instance, find its children (comments)
commentsQuery.descending("createdAt");
return commentsQuery.find();
Note: of course, video is an instance of the Video class. And returning find() means I'll have to handle the 'promise' in whatever function calls this one.
And here is another function coming from the other angle:
getRecentCommentsOfAllVideos: function(limit) {
var Comment = Parse.Object.extend("Comment");
var commentsQuery = new Parse.Query(Comment);
commentsQuery.descending("createdAt");
commentsQuery.limit(limit);
commentsQuery.include("parent");//this enables the details of the comment's associated video to be available in the result
return commentsQuery.find();
}
(Be sure to read https://parse.com/docs/js_guide and search for the line that says "Include the post data with each comment".)
I also looked at these materials; maybe they'll help you (though they weren't much help for me):
https://parse.com/questions/how-to-retrieve-parent-objects-with-their-children-in-one-call-javascript-api
http://blog.parse.com/2011/12/06/queries-for-relational-data/
Related
Problem
In a social media app I am making with react native and firebase, I am trying to grab the number of comments a post has using the snapshot function of a variable I have saved on my servers, then I am going to add one to this variable when a user adds a new comment. My code to do so is right here:
firebase.database().ref('posts').child(this.state.passKey).update({
comments: firebase.database().ref('posts/'+this.state.passKey).child('comments').snapshot.val() + 1
})
When I actually run this code, I get an error saying:
Reference.child failed: First argument was an invalid path = "undefined".
Paths must be non-empty strings and can't contain ".","#","$","[", or "["
At first I thought this might be that the "this.state.passKey" wasn't actually passing the key, but putting in a key I copied from the server didn't fix the problem.
My Server
-
To get the comments of particular post you should do like this
let postId='someId'
postRef=`/posts/${postId}`
firebase.database().ref(postRef).once("value", dataSnapshot => {
comment=dataSnapshot.val().comments
});
It looks like you're expecting this bit of code to query the database:
firebase.database().ref('posts/'+this.state.passKey).child('comments').snapshot.val() + 1
Unfortunately, it doesn't work that way. There's no snapshot property on a database Reference object returned by child() or ref().
Instead, you'll need to query the database at that reference, then when you're called back with its value, you can apply it elsewhere.
var ref = firebase.database().ref('posts/'+this.state.passKey+'/comments')
ref.once('value', function(snapshot) {
// use the snapshot here
})
I have probably misunderstood the documentation somehow but cannot figure this out.
What I want to do, is to be able to create a new ClientContact and save it to an array of pointers called contacts in Clients table.
This is the relevant code:
var Client = Parse.Object.extend("Client");
var selectedClient = new Client();
// sets the objectId based on URL params
selectedClient.id = $routeSegment.$routeParams.id;
var ClientContact = Parse.Object.extend("ClientContact");
var contact = new ClientContact();
contact.set('name', 'test');
contact.set('desc', 'some description');
contact.set('phoneNumber', '123');
selectedClient.add('contacts', contact);
selectedClient.save().then(function() {
console.log('saved');
}, function(error) {
console.error(error);
});
As expected, the contact is automatically saved and added to contacts when the selectedClient is saved.
But if I run the same code again (in the testing this means refreshing the page), a new ClientContact is saved but it replaces the contacts array entirely.
That is, only the most recent ClientContact is associated to the Client, any new additions replaces the array, leaving only one pointer.
I hope there is an obvious an easy fix that I have simply failed to spot.
Ok seems I have located the problem.
I was assuming that array operations on an object was independant on the local representation.
What I did above was to construct an object based on a known objectId:
var Client = Parse.Object.extend("Client");
var selectedClient = new Client();
// sets the objectId based on URL params
selectedClient.id = $routeSegment.$routeParams.id;
Turns out that array operations like add and remove are performed based on the local information. So doing the above construction leaves my array, in the selectedClient object, empty at each page refresh. This was the cause of my array being replaced with a new single valued array.
In short, one should fetch an object before performing array operations it seems.
The reason for the construction above was to avoid having to re-query the Client object when navigating around an AngularJS based webpage. The solution was fortunately simple: loading the Client object once in a parent scope.
This moves outside the scope of the question. Just wanted to mention that as well.
First off, sorry for being a complete javascript noob, I am more of a PHP guy and am just testing out the meteor framework.
I am trying to loop through a collection of objects and trying to add a property from another collections as so :
Template.host.hosts = function() {
var hosts = Hosts.find();
hosts.forEach(function(host) {
host.lastPing = Pings.findOne({id: host.id}, {sort: {timestamp : -1}});
// This works fine
// console.log(host.lastPing.id);
});
for (host in hosts) {
// This results in "TypeError: Cannot read property 'id' of undefined"
console.log(host.lastPing.id);
}
return hosts;
};
I don't understand why the second console.log is not working.
I have tried searching but I don't know if the problem is specific to the way meteor handles collections or the way I should be adding properties to a javascript object or someting completely unrelated (scope etc...)
I have simplified my problem to try to understand what is happening, my real problem is obviously looping in a template as per :
{{#each hosts}}
{{this.lastPing.id}}
{{/each}}
Thanks
Three things:
MongoDB and Meteor ids are stored in _id rather than id.
In the context of your forEach method, host iterates through the query set returned by Hosts.find(), but it doesn't actually give you access to the documents themselves. Essentially, it's a copy of the information in the MongoDB rather than the document in the database.
The correct (and only) way to update the actual document is by using the Collection.update method:
Hosts.update({_id: host._id}, {$set: {lastPing: Pings.findOne({id: host.id}, {sort: {timestamp : -1}}) }});
(note that you can only update by _id on the client which is why that's what I've used here, whereas you can supply any query on the server.)
The hosts object is a cursor rather than an array. This means that when you use for host in hosts, you're actually iterating through the properties of the cursor object (which are inherited from the prototype) rather than an array of hosts, and none of them has an id property. One way to make this work is to fetch the query set and put it into hosts like this:
var hosts = Hosts.find().fetch();
Alternatively, you can stick with the cursor and use forEach again, although you'll either have to rewind it with hosts.rewind(), or repeat the line above to reset it to the start of the query set.
Hope that's helpful.
I have a block of data holding user information, so the tree of data looks something like:
app
-Jweljralsdjfo49
username: "Fred"
age: 25
shoesize: 16
I'm trying to do:
var ref = new Firebase(my_app_path + stored_user_id);
ref.remove();
I've also tried;
var ref = new Firebase(my_app_path);
var child = ref.child(stored_user_id);
child.remove();
Neither one works, in that the data is always still there.
The documentation for remove() states: "Remove the data at this Firebase location. Any data at child locations will also be deleted.", so I expect it to all to be gone.
I've tried adding a callback, and in each case the callback gets called with null (indicating success, quoting the docs: "The callback will be passed an Error object on failure, else null.").
What am I doing wrong? Is it because I'm using the user ID? I'm not using any security rules at the moment, but once I do, for security restrictions, I don't want non-users to be able to delete user content.
THANKS!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EDIT: Found the problem.
I'm using EmberFire, which works fine; but the EmberFire.Object which keeps track of the object has to be destroy()'d, and then I had to do:
Ember.run.later(function() {
var ref = new Firebase(my_app_path + stored_user_id);
ref.remove();
}, 1);
In order for the object to be officially gone, and for Ember/EmberFire to release its grip on the data.
you can probe adding $ to remove.
Example:
var ref = new Firebase(my_app_path + stored_user_id);
ref.$remove();
work for me.
I have a backend on Parse.com. I am trying to get a value from a User object in javascript cloud code, but it is not working. Let's say I am trying to get the email.
Here is the cloud code that retrieves a User Object...
var printUser = function(){
var query = new Parse.Query(Parse.User);
query.get( "2FSYI1hoJ8");
query.find({
success: function(result) {
console.log(result);
}
});
};
and here is the result of that search...
I2014-03-07T22:43:21.894Z]
[{"email":"a#a.com","phoneVerified":true,"randomNumber":99862,"toPhone":"+13035551212","username":"a","objectId":"2FSYI1hoJ8","createdAt":"2014-03-02T21:07:02.192Z","updatedAt":"2014-03-07T22:43:13.103Z","__type":"Object","className":"_User"}]
So I know the search works. I tried using 'result.get("email")', 'result.email', 'result("email")' and any variation I could find in their documentation to get the email value, but nothing works.
The error codes vary based on what is being called, so I will spare you that.
What am I missing?
You have an array of objects (with only one element), not a standalone object. The first element in your array is your result object:
result[0].email
will get you the email property of that object.