2

I've built an application that uses Parse. My application allows users to register, login and then post to a parse cloud database.
I have two Parse classes, one called User and one called Posts. User is made up of ObjectId, username and password, and Posts is made up of ObjectId, text and user. Of which user is a pointer to ObjectId within the User class.

I've created a method in my app called getData() which contains a ParseQuery, this queries the Posts class, selects the text field and includes the user field. The query then retrieves the data into a List and then loops through each row of the List, collecting the String from the text field and then adds it into a ListView on the UI using postList.add(textList.get(i).getString("text")); each time the program goes through the loop.
Within the loop is another query, which queries the User class, selects the objectId field, I then add a constraint to the query to tell it to only retrieve data where the objectId field is equal to the user field within the Posts class(I think).

ParseQuery<ParseObject> queryUser = ParseQuery.getQuery("User");
                        queryUser.selectKeys(Arrays.asList("objectId"));
                        queryUser.whereEqualTo("objectId", textList.get(i).getString("user"));

Next I want to take the collected username data that the query retrieved, put it into a String and display it on screen in a toast. So basically the getData() method should collect all of the strings from the text field and the username of the user that posted it.

The problem is that I'm unsure if i'm trying to go about this in the right way. My app throws an error when this piece of code is executed so I'm obviously doing something wrong.

java.lang.IllegalStateException: ParseObject has no data for this key.  Call fetchIfNeeded() to get the data.
            at com.parse.ParseObject.checkGetAccess(ParseObject.java:3235)
            at com.parse.ParseObject.getString(ParseObject.java:2817)
            at com.text.parse.MainActivity$3.done(MainActivity.java:186)

Code at line 186 : queryUser.whereEqualTo("objectId", textList.get(i).getString("user"));

My questions are:
1. Am I trying to do this in the right way?
2. Why am I receiving this error?

Code for getData() method:

public void getData() {

        final ArrayList<String> postList = new ArrayList<String>();
        final ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(this, R.layout.listview_row, postList);


        final ParseQuery<ParseObject> queryPosts = ParseQuery.getQuery("Posts");
        queryPosts.selectKeys(Arrays.asList("text"));
        queryPosts.include("user");
        queryPosts.addDescendingOrder("createdAt");
        queryPosts.setLimit(20);

        queryPosts.findInBackground(new FindCallback<ParseObject>() {

            @Override
            public void done(List<ParseObject> textList, ParseException e) {

                if (e == null) {
                    //query successful

                    for (int i = 0; i < textList.size(); i++) {

                        postList.add(textList.get(i).getString("text"));

                        ParseQuery<ParseObject> queryUser = ParseQuery.getQuery("User");
                        queryUser.selectKeys(Arrays.asList("objectId"));
                        queryUser.whereEqualTo("objectId", textList.get(i).getString("user"));
                        queryUser.setLimit(20);

                        queryUser.findInBackground(new FindCallback<ParseObject>() {
                            @Override
                            public void done(List<ParseObject> userList, ParseException e) {

                                if (e == null) {

                                    String s = userList.get(0).getString("username").toString();

                                    Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG).show();
                                }

                                else {
                                    //query error

                                    Toast.makeText(MainActivity.this, "query error: " + e, Toast.LENGTH_LONG).show();
                                }
                            }
                        });
                    }

                    lvText.setAdapter(listAdapter);

                } else {
                    //query error

                    Toast.makeText(MainActivity.this, "query error: " + e, Toast.LENGTH_LONG).show();
                }

            }
        });
    }



Sorry for the long question. Any help would be greatly appreciated, thanks.


UPDATE:
For anyone stuck with a similar problem, here's how I got it to work:

public void getData() {

    final ArrayList<String> postList = new ArrayList<String>();
    final ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(this, R.layout.listview_row, postList);


    final ParseQuery<ParseObject> queryPosts = ParseQuery.getQuery("Posts");
    queryPosts.include("user");
    queryPosts.addDescendingOrder("createdAt");
    queryPosts.setLimit(20);

    queryPosts.findInBackground(new FindCallback<ParseObject>() {

        @Override
        public void done(List<ParseObject> textList, ParseException e) {

            if (e == null) {
                //query successful

                for (int i = 0; i < textList.size(); i++) {

                    postList.add(textList.get(i).getString("text"));


                    ParseObject po1 = textList.get(i);
                    ParseObject po2 = po1.getParseObject("user");
                    String username = po2.getString("username");

                    Toast.makeText(MainActivity.this, username, Toast.LENGTH_SHORT).show();
                }

                lvText.setAdapter(listAdapter);

            } else {
                //query error

                Toast.makeText(MainActivity.this, "query error: " + e, Toast.LENGTH_LONG).show();
            }

        }
    });
}


You simply include the column in the class you are querying that holds a pointer to another class, that then gives you access to all of the columns of data within the second class.

Paul Alexander
  • 2,686
  • 4
  • 33
  • 69

1 Answers1

0

This method as shown is doing nothing useful:

ParseQuery<ParseObject> queryUser = ParseQuery.getQuery("User");
queryUser.selectKeys(Arrays.asList("objectId"));
queryUser.whereEqualTo("objectId", textList.get(i).getString("user"));

The selectKeys statement is telling it to only return the contents of the objectId column, which you are passing in to the whereEqualTo statement as a parameter... seems silly to run a query to get a value you already have!?. I would not user selectKeys until you think you need to optimise your queries. The only use this query would have is to let you know if the objectId is valid, since the query will return null if it isn't a valid objectId for a User.

I'm hoping that you want to get more information about the user, so if you remove selectKeys then the other columns will be returned.

The fact that fetchIfNeeded is throwing an exception on due to this line:

queryUser.whereEqualTo("objectId", textList.get(i).getString("user"));

That suggests that textList.get(i).getString("user") is not returning an objectId for a user. If that is instead returning a username as suggested by some of your other comments (not sure here), then you need to change that line of code to read:

queryUser.whereEqualTo("username", textList.get(i).getString("user"));

If there are some other questions you have, you'll need to be a bit more precise in your questions as it isn't really clear what you're asking at the moment.

Timothy Walters
  • 16,866
  • 2
  • 41
  • 49
  • Thanks for the reply. I just want to get it to do what I would like it to do, return all posts and the username of the user that belongs to each post. I didn't want to put my question as "how to I make this work" as I know people on here would go crazy about that. – Paul Alexander Mar 24 '15 at 08:46
  • 1
    If you make the `user` column a pointer to a `User` in the post, then you can just use `postQuery.include("user")` to make all properties of the user available. – Timothy Walters Mar 24 '15 at 09:08
  • I'll give it a try later on when I get home from work :) – Paul Alexander Mar 24 '15 at 14:53
  • Out of interest, if I include the "user" pointer when I do my first query of the Posts class, does that give me access to all of the data within my User class? – Paul Alexander Mar 24 '15 at 15:07
  • 1
    Yes, as long as you call `include("user")` on your Post query then all user data will be populated. – Timothy Walters Mar 24 '15 at 15:20
  • So I could query the Posts class, include the user pointer within that class and then retrieve any field of data from my User class because I've included the user pointer field? That would then mean that I wouldn't need the second query within the loop wouldnt it? – Paul Alexander Mar 24 '15 at 16:29
  • just to make sure we're clear here, when you say include("user"), you mean the user pointer field within my Posts class, correct? not the User class – Paul Alexander Mar 24 '15 at 16:43
  • 1
    Correct, you add that to the query and in includes the full contents of the user for that post – Timothy Walters Mar 25 '15 at 01:06