在iOS中使用Parse Server提取子对象和父对象的最佳方法

时间:2019-02-04 14:24:54

标签: ios objective-c parse-platform nspredicate back4app

我正在尝试在Parse Server上针对iOS的“示例博客应用程序”,无法弄清楚什么是获取另一个类的所有子对象(连同父对象)的聪明方法。

“示例博客应用程序”(在创建新帐户时自动创建)包含类CommentPost。注释类包含与Post类的关系,如下所示(从仪表板显示),但是没有相反的关系。

enter image description here

现在,我想获取所有帖子以及与每个帖子相关的所有评论。下面的代码可以做到这一点,但是我假设必须有一种更聪明的方法...?如果您知道如何,请分享。预先感谢!

- (void)fetchPosts {

    NSString *commentsKey = @"comments";
    NSString *postKey = @"post";

    PFQuery *query = [PFQuery queryWithClassName:@"Comment"];
    [query includeKey:postKey];
    [query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {

        if (error == nil) {

            NSMutableArray *array = [[NSMutableArray alloc]init];

            for (PFObject *comment in objects) {

                PFObject *post = [comment objectForKey:postKey];
                NSDictionary *existingPostDict = [[array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%K = %@", @"post.objectId", post.objectId]] firstObject];

                if (existingPostDict) {
                    // update comments
                    NSArray *comments = [[existingPostDict objectForKey:commentsKey] arrayByAddingObject:comment];

                    // create new dictionary and replace the old one
                    NSDictionary *newPostDict = [[NSDictionary alloc]initWithObjectsAndKeys:[existingPostDict objectForKey:postKey], postKey, comments, commentsKey, nil];
                    [array replaceObjectAtIndex:[array indexOfObject:existingPostDict] withObject:newPostDict];
                }
                else {
                    // first post, create a new dict
                    NSDictionary *newPostDict = [[NSDictionary alloc]initWithObjectsAndKeys:post, postKey, @[comment], commentsKey, nil];
                    [array addObject:newPostDict];
                }
            }
            self.posts = array; // assuming: @property (nonatomic, strong) NSArray *posts; 
        }
        else {
            NSLog(@"Error fetching posts: %@", error.localizedDescription);
        }
    }];
}

3 个答案:

答案 0 :(得分:0)

您应该使用include而不是在查询中使用whereKey:equals:并将post对象作为第二个参数传递。这将过滤并仅返回包含该帖子作为post

值的评论对象。

答案 1 :(得分:0)

我在查询中看到的一个问题是,这可能不会获取数据库中的每个帖子。如果帖子的评论为0,则Comment对象都不会引用它,因此您不会收到它。

因此,您实际上应该对“ Post”进行查询,并在完成时对“ Comment”进行查询。这样,您将不会错过任何带有0条评论的帖子。当您这样做时,您将不需要在Comment查询中包括“ post”键。这有很多好处。

首先,每个include也是对该对象的另一个查询。因此,每个新的Comment对象将在后端创建另一个查询。您将自动摆脱它。

第二,对于带有多个注释的“帖子”,您将多次查询同一帖子,并且同一帖子将被多次返回,这会浪费不必要的带宽。

分别获取帖子和评论后,只需将它们组合即可。

除此之外,我会像这样进行合并,我发现它更具可读性,但这只是个人喜好。

- (void)fetchPosts {

NSString *commentsKey = @"comments";
NSString *postKey = @"post";

PFQuery *query = [PFQuery queryWithClassName:@"Comment"];
[query includeKey:postKey];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {

    if (error == nil) {

        NSMutableArray *array = [[NSMutableArray alloc]init];
        NSMutableDictionary *d = [NSMutableDictionary dictionary];
        for (PFObject *comment in objects) {

            PFObject *post = [comment objectForKey:postKey];
            if (d[post.objectId]) {
                [d[post.objectId][commentsKey] addObject:comment];
            }
            else{
                d[post.objectId] = [NSMutableDictionary dictionary];
                d[post.objectId][postKey]=post;
                d[post.objectId][commentsKey] = [NSMutableArray arrayWithObject:comment];
            }

        }
        for (NSString *key in [d allKeys]) {
            [array addObject:d[key]];
        }

        self.posts = array; // assuming: @property (nonatomic, strong) NSArray *posts;
    }
    else {
        NSLog(@"Error fetching posts: %@", error.localizedDescription);
    }
}];
}

答案 2 :(得分:0)

这是我的方法,结合使用findObjectsInBackgroundcontinueWithSuccessBlock:方法(为了更好地处理错误,可以选择continueWithBlock:

- (void)fetchPosts {
    /**
     create "post" and "comment" queries and use a BFTask-method from 
     Bolts.framework to chain downloading tasks together (bundled with Parse SDK)
     */
    NSMutableArray *posts = [NSMutableArray new];
    PFQuery *postQuery = [PFQuery queryWithClassName:@"Post"];
    [[[postQuery findObjectsInBackground] continueWithSuccessBlock:^id(BFTask * task) {

        [posts addObjectsFromArray:task.result];
        PFQuery *commentsQuery = [PFQuery queryWithClassName:@"Comment"];
        return [commentsQuery findObjectsInBackground];
    }] continueWithSuccessBlock:^id(BFTask * task) {
        /**
         loop through posts and filter out comments with the same objectId in post,
         then create a dictionary with post and related comments. done! :)
         */
        NSMutableArray *postsAndComments = [NSMutableArray new];
        for (PFObject *post in posts) {
            NSArray *comments = [task.result filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"%K == %@", @"post.objectId", post.objectId]];
            [postsAndComments addObject:@{@"post":post, @"comments":comments}];
        }
        /**
         note: BFTask-blocks not running in main thread!
        */
        dispatch_async(dispatch_get_main_queue(), ^{
            self.posts = postsAndComments;  // assuming: @property (nonatomic, strong) NSArray *posts;
        });
        return nil;
    }];
}