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

Best way to fetch child and parent objects with Parse Server in iOS

我正在 Parse Server 上为 iOS 尝试 "Sample Blog App" 并且无法弄清楚获取另一个 class 的所有子对象的聪明方法是什么(连同父对象)。

"Sample Blog App"(在您创建新帐户时自动创建)包含 classes CommentPost。评论 class 包含与 Post class 的关系,如下所示(来自仪表板),但没有相反方向的关系。

现在,我想获取 所有 post 以及与每个 post 相关的所有评论。下面的代码就是这样做的,但我假设一定有更聪明的方法......?如果你知道怎么做,请分享。提前致谢!

- (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);
        }
    }];
}

不应在查询中使用 include,而应使用 whereKey:equals: 并将 post 对象作为第二个参数传递。这将过滤并 return 仅包含将 post 作为其值 post

的评论对象

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

因此,您实际上应该对 "Post" 进行查询,并在完成后对 "Comment" 进行查询。这样你就不会错过任何评论为 0 的 post。执行此操作时,您不需要在 Comment 查询中包含 "post" 键。这有很多好处。

首先,每个包含也是对该对象的另一个查询。因此,每个新的 Comment 对象都会在后端创建另一个查询。你会自动摆脱它。

其次,对于具有多个评论的 "Post",您将多次查询相同的 post,并且相同的 post 将被返回多次,这会消耗不必要的带宽。

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

除此之外,我会像这样进行组合,我觉得这样更具可读性,但这只是个人喜好。

- (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);
    }
}];
}

我就是这样做的,使用 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;
    }];
}