2

I have an object called Post:

constructor(
    public repliers: string[''],
    public key: string = null
) {}

Here is the service method which pushs the post:

submitPost(post: Post) {
    this.af.database.list('posts').push(post);
}

Here is the method which subscribes to the posts table:

subscribeAllPosts(): Observable<Post[]> {
    return this.af.database.list('posts')
    .map(Post.fromJsonList);
}

In the post class here are the static methods for converting it from json:

static fromJsonList(array:any): Post[] {
    return array.map(Post.fromJson);
}

static fromJson({repliers, $key }:any): Post {
    return new Post(repliers, $key );
}

When I submit a post it works and the post on firebase looks like:

posts
    -> unique key
           ->  repliers
                     -> 0: ''

When I loop through my posts after submitting one I see the array as a property with the one default blank key.

My issue that I want to solve is how to update a post in the posts table to be able to push extra keys to the repliers array and also delete from the array.

A concern is how to update this list without replacing the whole thing as other users might also be adding/removing from it. Thanks

1 Answer 1

1

The proper way to solve this is to slightly restructure your database through a process called denormalization.

Instead of nesting "repliers" inside the posts node, you should store the repliers in it's own root level node, like so:

/posts/$postKey/{data1, data2, ...}
/post-repliers/$postKey/$replierKey/{data1, data2, ...}

Create a new service to manage the post repliers. Then inject that service into your Posts service. When you load a post, you can use this new service to also load the post repliers. Here's how this might look:

public getPost(postKey: string): Observable<Post>{
    let postSubscription = <Observable<Post>> this.angularFire.database.object(`/posts/${postKey}`)
      .map(this.mapPost);
    let postRepliersSubscription = <Observable<PostReplier[]>> this.postReplierService.getRepliersForPost(postKey);

    let postObservable = Observable.zip(postSubscription, postReplierSubscription).map((values) => {
      if(values[0])
        values[0].repliers = values[1];
      return values[0];
    });

    return postObservable;
  }

In this solution, we query firebase for the post, and the repiers, then return a new observale that will wait for both queries to complete before combinging the two, and returning the result (Post with .repliers set).

This is great when you need both data sets at the same time, but there is of course a performance hit of waiting for the second query to finish. The alternative would be to simply save the repliers FirebaseListObservable on the Post before returning.

Why Denormalize?

The main reason is security. Firebase security rules don't let you grant write access to one property and deny it on another. When you grant write access you do it for the entire node and all of its children.

To get around this, we'd add an ownerKey to the Post object, and only let users whose UID matches the ownerKey modify the Post object. Then, by saving the replierKey in the path of the post-repliers object, we allow anybody to reply to a post, but a user can only manage their own post-replier object.

For example, say my UID was 3498j3u9fjd0329, I could create a post-replier at:

/post-repliers/$postKey/3498j3u9fjd0329

but trying to delete another user's reply at:

/post-repliers/$postKey/dzz09cu09j234f0

would return a permission denied error.

What next?

I recommend you read about firebase denormalization here, as it will help you understand how to better structure your database.

Also go through the firebase security rules documentation here. Having a poorly structured database can make security very difficult, if not impossible.

Good luck!

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.