Skip to content

How to query and include parent object in child query #357

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
GursheeshSingh opened this issue Apr 17, 2020 · 17 comments
Closed

How to query and include parent object in child query #357

GursheeshSingh opened this issue Apr 17, 2020 · 17 comments

Comments

@GursheeshSingh
Copy link
Contributor

GursheeshSingh commented Apr 17, 2020

I have two classes -

PollLike and Poll

The PollLike is like child of the poll object

class Poll extends ParseObject implements ParseCloneable {
  Poll.clone(String className, PollType pollType) : this(className, pollType);

  @override
  clone(Map map) => Poll.clone(className, pollType)..fromJson(map);

  Poll(this.className, this.pollType) : super(className);

  String className;
  PollType pollType;

  String get question => get<String>("question");

  String get coverImage => get<String>("coverImage");

  String get optionsType => get<String>("optionsType");

  int get totalNumberOfVotes => get<int>("totalNumberOfVotes");

  int get numberOfLikes => get<int>("numberOfLikes");

  List<dynamic> get options => get<List<dynamic>>("options");

  List<dynamic> get tags => get<List<dynamic>>("tags");

  Map<String, dynamic> get votes => get<Map<String, dynamic>>("votes");

  ///Name of user
  String get userName => get<String>("userName");

  String get expiryDateTime => get<String>("expiryDateTime");

  set question(String question) => set<String>("question", question);

  set coverImage(String coverImage) => set<String>("coverImage", coverImage);

  set optionsType(String optionsType) =>
      set<String>("optionsType", optionsType);

  set totalNumberOfVotes(int totalNumberOfVotes) =>
      set<int>("totalNumberOfVotes", totalNumberOfVotes);

  set numberOfLikes(int numberOfLikes) =>
      set<int>("numberOfLikes", numberOfLikes);

  ///Name of user
  set userName(String userName) => set<String>("userName", userName);

  set expiryDateTime(String expiryDateTime) =>
      set<String>("expiryDateTime", expiryDateTime);

  set options(List<dynamic> options) => set<List<dynamic>>("options", options);

  set tags(List<dynamic> tags) => set<List<dynamic>>("tags", tags);

  set votes(Map<String, dynamic> votes) =>
      set<Map<String, dynamic>>("votes", votes);
}

class PollLike extends ParseObject implements ParseCloneable {
  PollLike.clone(String className, PollType pollType)
      : this(className, pollType);

  @override
  clone(Map map) => PollLike.clone(className, pollType)..fromJson(map);

  PollLike(this.className, this.pollType) : super(className);

  String className;
  PollType pollType;

  String get userId => get<String>("userId");

  String get pollId => get<String>("pollId");

  Poll get parent => get<Poll>("parent");

  set userId(String userId) => set<String>("userId", userId);

  set pollId(String pollId) => set<String>("pollId", pollId);

  set userName(String userName) => set<String>("userName", userName);

  set userProfilePicture(String userProfilePicture) =>
      set<String>("userProfilePicture", userProfilePicture);

  set parent(Poll parent) => set<Poll>("parent", parent);
}

Each class represents two classes in the Actual database.

Poll - OfficialPolls and UserPolls
PollLike - OfficialPollLikes and UserPollLikes

Now I save a OfficialPollLike with an official poll -

Screenshot 2020-04-17 at 4 56 50 PM

OfficialPollLike stores a pointer to the OfficialPoll object

Now, I want to retrieve OfficialPollLikes of a particular user with the Poll details already included in the response-

  QueryBuilder queryBuilder =
      QueryBuilder(PollLike("OfficialPollLikes", PollType.OFFICIAL))
        ..orderByDescending("_created_at")
        ..setLimit(PollLikesProvider.LIMIT)
        ..includeObject(["parent"]);

Here is the response-

flutter: {"className":"OfficialPollLikes","objectId":"T8wVTurJef","createdAt":"2020-04-17T11:17:12.026Z","updatedAt":"2020-04-17T11:17:12.026Z","userId":"3g3lk4M6Xl","pollId":"5e96e1d47b37522678375591","parent":{"__type":"Pointer","className":"OfficialPolls","objectId":"5e96e1d47b37522678375591"}}
flutter: {"className":"OfficialPollLikes","objectId":"AO5g5CAJFx","createdAt":"2020-04-17T11:17:08.647Z","updatedAt":"2020-04-17T11:17:08.647Z","userId":"3g3lk4M6Xl","pollId":"5e96e1e57b37522678375592","parent":{"__type":"Pointer","className":"OfficialPolls","objectId":"5e96e1e57b37522678375592"}}

It does not contain any fields of the Poll class except the pointer.
I am also new to Pointers and Relation on Parse SDK.

@fischerscode
Copy link
Contributor

In your screenshot the pointer is called "_p_parent" in your query it's called "parent".
Please try this:

QueryBuilder queryBuilder =
      QueryBuilder(PollLike("OfficialPollLikes", PollType.OFFICIAL))
        ..orderByDescending("_created_at")
        ..setLimit(PollLikesProvider.LIMIT)
        ..includeObject(["_p_parent"]);

@GursheeshSingh
Copy link
Contributor Author

Output:

flutter: {"className":"OfficialPollLikes","objectId":"T8wVTurJef","createdAt":"2020-04-17T11:17:12.026Z","updatedAt":"2020-04-17T11:17:12.026Z","userId":"3g3lk4M6Xl","pollId":"5e96e1d47b37522678375591","parent":{"__type":"Pointer","className":"OfficialPolls","objectId":"5e96e1d47b37522678375591"}}
flutter: {"className":"OfficialPollLikes","objectId":"AO5g5CAJFx","createdAt":"2020-04-17T11:17:08.647Z","updatedAt":"2020-04-17T11:17:08.647Z","userId":"3g3lk4M6Xl","pollId":"5e96e1e57b37522678375592","parent":{"__type":"Pointer","className":"OfficialPolls","objectId":"5e96e1e57b37522678375592"}}

No effect.

@fischerscode
Copy link
Contributor

Have you tried to query manually through the rest API?

@GursheeshSingh
Copy link
Contributor Author

GursheeshSingh commented Apr 18, 2020

Yes I tried that and it looks like it works using REST API.

Thank you.

Here is the URL-
http://localhost:1337/parse/classes/OfficialPollLikes?include=parent

Here is the response-

{
    "results": [
        {
            "objectId": "AO5g5CAJFx",
            "userId": "3g3lk4M6Xl",
            "pollId": "5e96e1e57b37522678375592",
            "parent": {
                "objectId": "5e96e1e57b37522678375592",
                "question": "Who is the best player? 3",
                "coverImage": "https://i.pinimg.com/originals/45/54/a1/4554a1c1f17cc22d7c40960d507fa717.png",
                "optionsType": "OptionType.text",
                "options": [
                    {
                        "optionNumber": 1,
                        "text": "Messi"
                    },
                    {
                        "optionNumber": 2,
                        "text": "Ronaldo"
                    }
                ],
                "tags": [
                    "messi",
                    "ronaldo",
                    "fcbarcelona",
                    "juventus",
                    "realmadrid"
                ],
                "totalNumberOfVotes": 51,
                "votes": {
                    "1": 36,
                    "2": 15
                },
                "createdAt": "2020-04-12T12:54:59.000Z",
                "updatedAt": "2020-04-17T11:17:08.843Z",
                "numberOfLikes": 7,
                "expiryDateTime": "2020-04-20T12:54:59.000Z",
                "__type": "Object",
                "className": "OfficialPolls"
            },
            "createdAt": "2020-04-17T11:17:08.647Z",
            "updatedAt": "2020-04-17T11:17:08.647Z"
        },
        {
            "objectId": "T8wVTurJef",
            "userId": "3g3lk4M6Xl",
            "pollId": "5e96e1d47b37522678375591",
            "parent": {
                "objectId": "5e96e1d47b37522678375591",
                "question": "Who is the best player? 2",
                "coverImage": "https://i.pinimg.com/originals/45/54/a1/4554a1c1f17cc22d7c40960d507fa717.png",
                "optionsType": "OptionType.image",
                "options": [
                    {
                        "optionNumber": 1,
                        "image": "https://thumbor.forbes.com/thumbor/fit-in/416x416/filters%3Aformat%28jpg%29/https%3A%2F%2Fspecials-images.forbesimg.com%2Fimageserve%2F5cfea7bb4c687b0008593c0a%2F0x0.jpg%3Fbackground%3D000000%26cropX1%3D1554%26cropX2%3D2474%26cropY1%3D240%26cropY2%3D1159",
                        "text": "Messi"
                    },
                    {
                        "optionNumber": 2,
                        "image": "https://i.pinimg.com/originals/d6/fe/c1/d6fec1cc149ac7fac63cef3abec08a6e.jpg",
                        "text": "Ronaldo"
                    }
                ],
                "tags": [
                    "messi",
                    "ronaldo",
                    "fcbarcelona",
                    "juventus",
                    "realmadrid"
                ],
                "totalNumberOfVotes": 53,
                "votes": {
                    "1": 37,
                    "2": 16
                },
                "createdAt": "2020-04-10T12:54:59.000Z",
                "updatedAt": "2020-04-17T11:17:12.216Z",
                "numberOfLikes": 5,
                "expiryDateTime": "2020-04-20T12:54:59.000Z",
                "__type": "Object",
                "className": "OfficialPolls"
            },
            "createdAt": "2020-04-17T11:17:12.026Z",
            "updatedAt": "2020-04-17T11:17:12.026Z"
        }
    ]
}

This is what I using hoping would be the response using the SDK.

Now this a bug in the Parse SDK right?

@fischerscode
Copy link
Contributor

As the rest API works, it has to be a problem with the SDK, I think.

Could you please add

print('http://localhost:1337/parse/classes/OfficialPollLikes?${queryBuilder.buildQuery()}');

after your

  QueryBuilder queryBuilder =
      QueryBuilder(PollLike("OfficialPollLikes", PollType.OFFICIAL))
        ..orderByDescending("_created_at")
        ..setLimit(PollLikesProvider.LIMIT)
        ..includeObject(["parent"]);

and try to query the printed URL through the REST API?
This will show us, if there is a problem with the generated URL.

@GursheeshSingh
Copy link
Contributor Author

Here is the output:

http://localhost:1337/parse/classes/OfficialPollLikes?where={}&order=-_created_at&limit=10&include=parent

However when I use the generated Url in Postman:

The response includes the parent details

@fischerscode
Copy link
Contributor

fischerscode commented Apr 18, 2020

Please try this:

  QueryBuilder queryBuilder =
      QueryBuilder(ParseObject("OfficialPollLikes"))
        ..orderByDescending("_created_at")
        ..setLimit(PollLikesProvider.LIMIT)
        ..includeObject(["parent"]);

It might be related to your custom class.

EDIT:
I've also noticed this: Poll get parent => get<Poll>("parent"); As far as I know, you wont get Objects of type Poll back, jet. See #350 (comment).

@GursheeshSingh
Copy link
Contributor Author

Tried the above query-

Still the same response with no details

Also got an exception-

type 'ParseObject' is not a subtype of type 'PollLike' in type cast

Also the method registerSubClass isn't defined for ParseCoreData.

What should I do for using subclasses?

@fischerscode
Copy link
Contributor

fischerscode commented Apr 18, 2020

Are you just calling queryBuilder.query() to query?

registerSubClass is currently not available in this repo. I am working on such a feature, but as it is quite a huge change, we decided to add it after I have used/tested it for a while. (#350 (comment))
If you want, feel free to use it now already, but there might be bugs.

Edit:
Are you using the current released version of the SDK (1.0.26) or 1.0.27 or something older?

Edit2:

Also got an exception-

type 'ParseObject' is not a subtype of type 'PollLike' in type cast

As the query currently does not know about PollLike, this makes sens.

@GursheeshSingh
Copy link
Contributor Author

Yup just calling queryBuilder.query();

I am using version - 1.0.26

@fischerscode
Copy link
Contributor

I might have found the problem...
The debug message never contains the included data although the data got transmitted.
If you start the debugger and have a lock into the ParseResponse, you should see the included data.

@GursheeshSingh
Copy link
Contributor Author

Screenshot 2020-04-18 at 8 03 46 PM

You are right.

I guess it is because of the Poll type.

Is there any way to handle that?

@fischerscode
Copy link
Contributor

Please try this query again:

  QueryBuilder queryBuilder =
      QueryBuilder(PollLike("OfficialPollLikes", PollType.OFFICIAL))
        ..orderByDescending("_created_at")
        ..setLimit(PollLikesProvider.LIMIT)
        ..includeObject(["parent"]);

and have a look into the object trough the debugger.

Inside the debugger you should see the included object.

@GursheeshSingh
Copy link
Contributor Author

GursheeshSingh commented Apr 18, 2020

I changed the Poll type in PollLike to ParseObject

ParseObject get parent => get<ParseObject>("parent");
set parent(ParseObject parent) => set<ParseObject>("parent", parent);

Some checks

print(officialPollLike.parent.toString());

print(officialPollLike.parent.get("question"));
            
print(officialPollLike.parent.runtimeType);

Output:

{"className":"OfficialPolls","objectId":"5e96e1e57b37522678375592","createdAt":"2020-04-12T12:54:59.000Z","updatedAt":"2020-04-17T11:17:08.843Z","question":"Who is the best player? 3","coverImage":"https://i.pinimg.com/originals/45/54/a1/4554a1c1f17cc22d7c40960d507fa717.png","optionsType":"OptionType.text","options":[{"optionNumber":1,"text":"Messi"},{"optionNumber":2,"text":"Ronaldo"}],"tags":["messi","ronaldo","fcbarcelona","juventus","realmadrid"],"totalNumberOfVotes":51,"votes":{"1":36,"2":15},"numberOfLikes":7,"expiryDateTime":"2020-04-20T12:54:59.000Z"}
flutter: Who is the best player? 3
flutter: ParseObject

It has the ParseObject with details and I can access it using
.get(key)

I created a class for OfficialPolls

class OfficialPoll extends ParseObject implements ParseCloneable {
  OfficialPoll.clone() : this();

  @override
  clone(Map map) => OfficialPoll.clone()..fromJson(map);

  OfficialPoll() : super("OfficialPolls");

  String get question => get<String>("question");

  String get coverImage => get<String>("coverImage");

  String get optionsType => get<String>("optionsType");

  int get totalNumberOfVotes => get<int>("totalNumberOfVotes");

  int get numberOfLikes => get<int>("numberOfLikes");

  List<dynamic> get options => get<List<dynamic>>("options");

  List<dynamic> get tags => get<List<dynamic>>("tags");

  Map<String, dynamic> get votes => get<Map<String, dynamic>>("votes");

  ///Name of user
  String get userName => get<String>("userName");

  String get expiryDateTime => get<String>("expiryDateTime");

  set question(String question) => set<String>("question", question);

  set coverImage(String coverImage) => set<String>("coverImage", coverImage);

  set optionsType(String optionsType) =>
      set<String>("optionsType", optionsType);

  set totalNumberOfVotes(int totalNumberOfVotes) =>
      set<int>("totalNumberOfVotes", totalNumberOfVotes);

  set numberOfLikes(int numberOfLikes) =>
      set<int>("numberOfLikes", numberOfLikes);

  ///Name of user
  set userName(String userName) => set<String>("userName", userName);

  set expiryDateTime(String expiryDateTime) =>
      set<String>("expiryDateTime", expiryDateTime);

  set options(List<dynamic> options) => set<List<dynamic>>("options", options);

  set tags(List<dynamic> tags) => set<List<dynamic>>("tags", tags);

  set votes(Map<String, dynamic> votes) =>
      set<Map<String, dynamic>>("votes", votes);
}

Then I tried to type cast

 OfficialPoll officialPoll = officialPollLike.parent as OfficialPoll;

Which fails as it is not a subtype of ParseObject

Is there any way to convert it to OfficialPoll type even using some of the existing SDK code?

@GursheeshSingh
Copy link
Contributor Author

GursheeshSingh commented Apr 18, 2020

Found the way to convert the ParseObject in PollLike to Official Type

print(officialPollLike.parent.runtimeType);
var officialPoll = jsonDecode(officialPollLike.parent.toString());
print(officialPoll.runtimeType);
OfficialPoll poll = OfficialPoll();
poll = poll.clone(officialPoll);
print(poll.question);

Output of the above code-

ParseObject
flutter: _InternalLinkedHashMap<String, dynamic>
flutter: Who is the best player? 2

Poll poll = Poll("OfficialPolls", PollType.OFFICIAL);

instead of

OfficialPoll poll = OfficialPoll();

also works

So I used the clone function which needs a map
As the parent object is of ParseObject type, I cannot use it to clone
Therefore, I use the toString() and jsonDecode function to convert it to a map
and then was able to convert it to OfficialPoll Type successfully

@fischerscode
Copy link
Contributor

So you are doing something like this now?
Poll get parent => Poll.clone()..fromJson(get<Poll>("parent")?.toJson());

Does now everything work?

@GursheeshSingh
Copy link
Contributor Author

GursheeshSingh commented Apr 19, 2020

Nope
Right now I added another variable to the PollLike class

Poll parentPoll;
for (int i = 0; i < response.results.length; i++) {
    PollLike pollLike = response.results[i] as PollLike;
    userPollLikes.add(pollLike);

    var userPoll = jsonDecode(pollLike.parent.toString());
    Poll poll = Poll("UserPolls", PollType.USER);
    poll = poll.clone(userPoll);
    pollLike.parentPoll = poll;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants