Implementing public and followers only news feed - node.js

I want to implement a news feed where users can post both private ( shared ) and public activities. The difference between the two is public activities are shown in all the user's news feed and private activities are shown to all of my followers news feed.
I have been reading some resources about implementing news feed and I have come across this open source project.
Quoting from the documentation
Next up we want to start publishing this activity on several feeds. First of all we want to insert it into your personal feed, and then into your followers' feeds
Now this works well for private activities, I post the activity to author's own feed as well as all of my followers feed.
But the issue is when the activity is public. In this case I have to post it to user's own feed as well as all other users in the system so that this activity is shown in their feed.
Suppose there are 1 million users in the system than this will require posting to 1 million feeds (possibly 1 million DB records). I believe this is not correct.
I thought of separating out public activities in other collection and these activities are visible to all. This issue with this solution is suppose I want to retrieve a user's feed than how do I combine data from user's followers feed with public feed.
Consider this example.
User A has 10 followers and they posted 10 activities in total. So User A's feed has 10 activities from his followers. Now there is User B which user A does not follow. User B also posted 2 public activities. Now the user A's feed should have 12 activities (10 from followers + 2 from user B public activities) so I do I combine data from these two collection and implement sorting, filtering etc on the combined result set.
Additional Info:
platform: node.js
DB: rethinkdb

A possible solution is to setup a notifications table that contains the private and public feeds, with an index on for.
To insert a private notification:
r.table('notifications')
.insert({'for': ['user', 'sonia'], message: "you've got mail"})
To insert a public notification:
r.table('notifications')
.insert({'for': ['public'], message: 'hello, world'})
Meanwhile, use getAll to select which notifications to subscribe too:
r.table('notifications')
.getAll(['public'], ['user', 'sonia'], {index: 'for'})
.changes()
If your notifications are stored across multiple tables, you can use union to combine them into a single changefeed, for example:
r.table('notifications').getAll('sonia', {index: 'for'})
.union(r.table('public_notifications'))
.changes()

Related

GetStream IO - Create "Discover" like feeds

I've been working with GetStream this week and it worked as expected for the first part. However, now that I would like to move forward, I would like to create a "discover like" (like the one on Instagram by example?) feature
The thing is, I am not really sure how to achieve it. My end goal would be something like:
You have posts (from users I don't follow) in the Discover feed of the userA
UserA follow a user from the discover (owner of a post, let say UserX)
UserX activities (previous and future ones) gets forwarded to userA flat feed and disappear from discover feed for the UserA
This would means (from my understanding) following everyone when a user create an account, and then unfollow people from discover flat feed (on follow) and follow them from timeline flat feed? But it seems to be unscalable to me, isn't it?...
For now, I have 2 feeds:
- users-timeline -> flat feed containing user post (post of UserA by example)
- followings-timeline -> flat feed which follows activities on selected "users-timelines"
How could I have a third feed which would act as a discover?

Avoiding duplicates in different feeds with getstream.io

Let's say I want to build a system where each user has access to a notification feed and an aggregated feed, with the following groups:
user as flat feed
hashtag as flat feed
notification as notification feed
timeline as aggregated feed
We also have the following relations:
user:b follows hashtag:a
user:b follows user:a
Now consider the following situation:
If user A posts with hashtag A, I would like user B to get an activity in its notification feed (thanks to relation 1). But I also would also like all followers of user A to see in their timeline that user A did something. Then, user B will get the activity in its notification feed, and in its timeline (because of relation 2): there is a duplicate.
Is there a way to avoid this situation ?
A naive way would be to manually filter the feed and prune the aggregated activities we do not want to see. But this of course seems non optimal.
Thank you very much!
You can use discard rules to avoid activities matching one of rule from getting added to a follower's feed.
This is usually something you use to avoid own activities from showing up in your notification feed.
Here's the link to the docs on Discard Rules:
https://getstream.io/docs/#discard-rules

A way to mark notification as seen without reading the notification feed?

My situation is the following:
I'm using the stream-js library. I add entries to the notification feeds of users for certain events - comments, follows, etc. After I write to their feed I also send a push notification to that user's device.
If a user clicks on a push notification I want to be able to mark the corresponding activity as seen. There's currently no way to do that since the add or addToMany calls do not return the ids of the added activities for me to send in the notification payload.
Ideally I'd want a way to mark a notification feed item as seen either by an activity group id or by some other unique id (or the foreignId). Is there a way to do that? If not, what is the alternative?
Two parts to this answer:
Getting the ID of an activity that you just added
The addActivity call in the various Stream client libraries (I'm using stream-js in this case) will return back the created activity, which should include the activity ID. Response looks something like this:
{
actor: 'ken',
duration: '9.65ms',
foreign_id: '',
id: '8b5d69a9-8b73-11e8-98ab-12cb9e7b86a4',
object: 'some-object',
origin: null,
target: '',
time: '2018-07-19T16:48:21.045496',
verb: 'add-activity'
}
Marking notification feed items as seen or read
The way to mark a notification feed item as seen or read is a little funky - first, you get the feed, like you would normally do, but you'll also pass in the mark_seen or mark_read options. (true will mark all items as seen or read, and an array of activity group IDs will mark only those items.)
From that call, the notification feed will be returned without the items marked as seen or read - but the next call to retrieve the notification feed will have the items marked accordingly.
More docs on that here: https://getstream.io/docs/flat_feeds/#notification_feeds
activity ID --> activity group ID
You might have noticed that you get the activity ID when adding the activity, but you need to pass in the activity group ID when marking items seen or read.
All notification feeds are actually aggregated feeds as well - by default, the aggregation format that they use is just the activity ID, which means that there will be only one activity per activity group, and the activity group ID will be the same as the activity ID. So, you can just use the activity ID returned by the addActivity call to get the notification feed and mark that activity group as seen or read.
If you're not using the default aggregation format (e.g., the activity group ID is not the same as the activity ID), then you'll likely have to retrieve the notification feed and grab the necessary activity group ID from there.

Related objects in activity feed

I'm building an activity feed application, where a user can like/comments on each activity feed. I went through GetStream.io documentation and looks like I'll have to send the activity with object ids.
{
id:"ef696c12-69ab-11e4-8080-80003644b625",
actor:"User:1",
object:"Comment:12",
started_at:"2014-11-11T15:06:16+01:00",
target:"Feed:100",
time:"2014-11-11T14:06:30.494",
verb:"add"
}
User:1 and Feed:12 are the objects in my application database? Does it mean that, while retrieving activities, I'll have to hit my database to retrieve the complete feeds?
Say the Feed:12 had few likes and comments earlier from other users. How do I get the complete set of likes/comments on user timeline feed?
What if I want to customize the view, say I want to show all users (image, name, the profile like etc) along with comment with timestamp similar to FB? Do I need to send these attributes as additional parameters for each feed?
Thanks,
Yes, when you fetch a feed from Stream and we give you back these references like user:1 or comment:12, we expect that you'd "enrich" those details from your database.
Typically what our users do is track the name of the model (eg, user) and the user_id (eg, 1). When you get the feed and put it into a hash map, you'll iterate over the activities, pull out all of the actor attributes, and do a single lookup like select * from user where id in (1,3,5,6,9,12) so that you're only hitting your database one time for all user objects or all comment objects or whatever. Then, replace those activities in your hash map so now you'd have actor: <object for User 9> and any other attributes you'd need for your UI presentment.
Then do the same for other references you pass in the activity, and so on.
Things we DON'T recommend are putting in string references for things that could change on your side. For example, if you had actor: "user:ian" instead of my user_id, if I ever change my username later then things probably wouldn't work properly on your side.

Duplicated items if I follow 2 feeds with intersecting items in getstream-io

User1 follows feed1 and feed2. Activity1 is added to both feeds (image user follows 2 playlists and the same song was added to both playlists at the same time because these are system playlists by genres).
What happens then? Will user1 see two records in his timeline? song1 is added to feed1 and song1 is added to feed2?
uniqueness is determined based on the foreign_id and time field. If you specify both these fields, Stream will understand uniqueness and you'll only see the activity once.
Note this only works if the activity is identical. If you need to group similar activities, aggregated feeds will work well.

Resources