Activity Feeds V3 is in closed alpha — do not use it in production (just yet).

Feeds

Creating a Feed

// Feed with no extra fields, of feed group "user"
val feed = client.feed(group = "user", id = "john")
feed.getOrCreate()

// More options
val query = FeedQuery(
    group = "user",
    id = "jack",
    data = FeedInputData(
        description = "My personal feed",
        name = "jack",
        visibility = FeedVisibility.Public
    )
)
val feed2 = client.feed(query = query)
feed2.getOrCreate()

Reading a Feed

Here is a basic example of how to read a feed:

val feed = client.feed(group = "user", id = "john")
feed.getOrCreate()
val feedData = feed.state.feed
val activities = feed.state.activities
val members = feed.state.members

The response will contain the following data.

You have more options when reading a feed, let’s go over a few:

val query = FeedQuery(
    group = "user",
    id = "john",
    activityFilter = Filters.`in`("filter_tags", listOf("green")), // filter activities with filter tag green
    activityLimit = 10,
    externalRanking = mapOf("user_score" to 0.8), // additional data used for ranking
    followerLimit = 10,
    followingLimit = 10,
    memberLimit = 10,
    view = "myview", // overwrite the default ranking or aggregation logic for this feed. good for split testing
    watch = true // receive web-socket events with real-time updates
)
val feed = client.feed(query = query)
feed.getOrCreate()
val activities = feed.state.activities
val feedData = feed.state.feed

Feed Pagination

Here is how you can read the next page on the feed:

val feed = client.feed(
    query = FeedQuery(
        group = "user",
        id = "john",
        activityLimit = 10
    )
)
// Page 1
feed.getOrCreate()
val activities = feed.state.activities // First 10 activities

// Page 2
val page2Activities: Result<List<ActivityData>> = feed.queryMoreActivities(limit = 10)

val page1And2Activities = feed.state.activities

Filtering Examples

Another common use case is filtering a feed. This is trickier than it seems. Keep in mind that for performance reasons feeds often have to be computed on write time. To allow for filtering we expose the following API.

Filter tags are indexed, so filtering by this field is performant.

// Add a few activities
val fid = FeedId(group = "user", id = "john")
client.upsertActivities(
    listOf(
        ActivityRequest(feeds = listOf(fid.rawValue), filterTags = listOf("green", "blue"), text = "first", type = "post"),
        ActivityRequest(feeds = listOf(fid.rawValue), filterTags = listOf("yellow", "blue"), text = "second", type = "post"),
        ActivityRequest(feeds = listOf(fid.rawValue), filterTags = listOf("orange"), text = "third", type = "post")
    )
)
// Now read the feed, this will fetch activity 1 and 2
val query = FeedQuery(fid = fid, activityFilter = Filters.`in`("filter_tags", listOf("blue")))
val feed = client.feed(query = query)
feed.getOrCreate()
val activities = feed.state.activities // contains first and second

The filter syntax also supports $or and $and, so here’s an example that’s a little more complicated:

// Get all the activities where tags contain "green" and type is "post" or tag contains "orange" and type is "activity"
val query = FeedQuery(
    group = "user",
    id = "john",
    activityFilter = Filters.or(
        Filters.and(
            Filters.`in`("filter_tags", listOf("green")),
            Filters.equal("type", "post")
        ),
        Filters.and(
            Filters.`in`("filter_tags", listOf("orange")),
            Filters.equal("type", "activity")
        )
    )
)
feed.getOrCreate()
val activities = feed.state.activities

The following fields and opearators are supported when filtering:

nametypedescriptionsupported operationsexample
idstring or list of stringsThe ID of the activity$in, $eq{ id: { $in: [ 'abc', 'xyz' ] } }
activity_typestring or list of stringsThe type of the activity$in, $eq{ activity_type: { $in: [ 'abc', 'xyz' ] } }
user_idstring or list of stringsThe ID of the user who created the activity$in, $eq{ user_id: { $in: [ 'abc', 'xyz' ] } }
textstringThe text content of the activity$eq, $q, $autocomplete{ text: { $q: 'popularity' } }
search_dataobjectThe extra metadata for search indexing$contains, $path_exists{ search_data: { $contains: { 'category': 'sports', 'status': 'active' } } }
interest_tagslist of stringsTags for user interests$eq, $contains{ interest_tags: { $in: [ 'sports', 'music' ] } }
filter_tagslist of stringsTags for filtering$eq, $contains{ filter_tags: { $in: [ 'categoryA', 'categoryB' ] } }
created_atstring, must be formatted as an RFC3339 timestampThe time the activity was created$eq, $gt, $lt, $gte, $lte{ created_at: { $gte: '2023-12-04T09:30:20.45Z' } }
popularitynumberThe popularity score of the activity$eq, $ne, $gt, $lt, $gte, $lte{ popularity: { $gte: 70 } }
nearobjectIndicates the GEO point to search nearby activities$eq{ near: { $eq: { lat: 40.0, lng: -74.0, distance: 200 } } }
within_boundsobjectIndicates the GEO bounds to search for activities within$eq{ within_bounds: { $eq: { ne_lat: 40.0, ne_lng: -115.0, sw_lat: 32.0, sw_lng: -125.0 } } }
visible_to_userbooleanIndicate whether the activity is visible to the user$eq{ visible_to_user: { $eq: true } }

Feed Members

You can add and remove members to a feed. This is useful for building communities where a set of users can add content to the feed.

It’s not possible to set/update member role on client-side. Use server-side SDKs for this. When adding members client-side all new members will have feed_member role:

// The following methods are available to edit the members of a feed
feed.updateFeedMembers(
    request = UpdateFeedMembersRequest(
        members = listOf(
            FeedMemberRequest(
                custom = mapOf("joined" to "2024-01-01"),
                role = "moderator",
                userId = "john"
            )
        ),
        operation = UpdateFeedMembersRequest.Operation.Upsert
    )
)
// Remove members
feed.updateFeedMembers(
    request = UpdateFeedMembersRequest(
        members = listOf(
            FeedMemberRequest(userId = "john"),
            FeedMemberRequest(userId = "jane")
        ),
        operation = UpdateFeedMembersRequest.Operation.Remove
    )
)
// Set members (overwrites the list)
feed.updateFeedMembers(
    request = UpdateFeedMembersRequest(
        members = listOf(
            FeedMemberRequest(role = "moderator", userId = "john")
        ),
        operation = UpdateFeedMembersRequest.Operation.Set
    )
)

Feed members vs followers

Followers and members might seem like similar concepts, but they serve two different purposes with some key differences.

Followers can only be feeds (for example the timeline feed of Alice follows the user feed of Bob). Followers’ aim is to access the content of a feed they’re interested in and interact with it.

Members can only be users (for example Alice adds Bob as a member to her feed about “Travel Hacks”). The aim of feed members is usually to help out with admin tasks (helpful if you want to build apps similar to Facebook pages) or to decide what activities a user has access to using membership levels (for example Bob becomes a premium member in Alice’s community).

Member invites

You can invite members with the invite flag, where invited users can accept or reject the membership.

// Request to become a member
feed.updateFeedMembers(
    request = UpdateFeedMembersRequest(
        members = listOf(
            FeedMemberRequest(
                custom = mapOf("reason" to "community builder"),
                invite = true,
                role = "moderator",
                userId = "john"
            )
        ),
        operation = UpdateFeedMembersRequest.Operation.Upsert
    )
)
// Accept and reject member requests
feed.acceptFeedMember()
feed.rejectFeedMember()

Query Feeds

Querying feeds allows you to do things like showing the list of communities you’ve joined.

Here’s an example of how to query feeds:

Querying My Feeds

val query = FeedsQuery(
    filter = Filters.equal("created_by_id", "john"),
    sort = listOf(FeedsSort(FeedsSortField.CreatedAt, SortDirection.REVERSE)),
    limit = 10,
    watch = true
)
val feedList = client.feedList(query = query)
// Page 1
val page1: Result<List<FeedData>> = feedList.get()

// Page 2
val page2: Result<List<FeedData>> = feedList.queryMoreFeeds(limit = 10)

val page1And2 = feedList.state.feeds

Querying Feeds Where I Am a Member

val query = FeedsQuery(
    filter = Filters.contains("members", "john")
)
val feedList = client.feedList(query = query)
val feeds: Result<List<FeedData>> = feedList.get()

Querying feeds by name or visibility

val sportsQuery = FeedsQuery(
    filter = Filters.and(
        Filters.equal("visibility", "public"),
        Filters.query("name", "Sports")
    )
)
val sportsFeedList = client.feedList(query = sportsQuery)
val sportsFeeds: Result<List<FeedData>> = sportsFeedList.get()

val techQuery = FeedsQuery(
    filter = Filters.and(
        Filters.equal("visibility", "public"),
        Filters.autocomplete("description", "tech")
    )
)
val techFeedList = client.feedList(query = techQuery)
val techFeeds: Result<List<FeedData>> = techFeedList.get()

Querying feeds by creator name

val query = FeedsQuery(
    filter = Filters.and(
        Filters.equal("visibility", "public"),
        Filters.query("created_by.name", "Thompson")
    )
)
val feedList = client.feedList(query = query)
val feeds: Result<List<FeedData>> = feedList.get()

Feeds Queryable Built-in Fields

nametypedescriptionsupported operationsexample
idstring or list of stringsThe ID of the feed$in, $eq{ id: { $in: [ 'abc', 'xyz' ] } }
group_idstring or list of stringsThe ID of the group this feed belongs to$in, $eq{ group_id: { $in: [ 'abc', 'xyz' ] } }
feedstring or list of stringsThe fully qualified feed ID (group_id:id)$in, $eq{ fid: { $in: [ 'abc', 'xyz' ] } }
visibilitystring or list of stringsThe visibility setting of the feed$in, $eq{ visibility: { $eq: 'public' } }
created_by_idstring or list of stringsThe ID of the user who created the feed$in, $eq{ created_by_id: { $in: [ 'abc', 'xyz' ] } }
created_by.namestringThe name of the user who created the feed$eq, $q, $autocomplete{ 'created_by.name': { $autocomplete: 'Frank' } }
namestringThe name of the feed$eq, $q, $autocomplete{ name: { $q: 'Sports' } }
descriptionstringThe description of the feed$eq, $q, $autocomplete{ description: { $autocomplete: 'tech' } }
member_countnumberThe number of members in this feed$eq, $ne, $gt, $lt, $gte, $lte{ member_count: { $gt: 100 } }
memberslist of stringsThe list of members in this feed$in{ members: { $in: [ 'bob', 'alice' ] } }
following_countnumberThe number of feeds this feed follows$eq, $ne, $gt, $lt, $gte, $lte{ following_count: { $gt: 100 } }
following_feedslist of stringsThe list of feeds this feed follows$in{ following_feeds: { $in: [ 'feed1', 'feed2' ] } }
follower_countnumberThe number of followers of this feed$eq, $ne, $gt, $lt, $gte, $lte{ follower_count: { $gt: 100 } }
created_atstring, RFC3339 timestampThe time the feed was created$eq, $gt, $lt, $gte, $lte{ created_at: { $gte: '2023-12-04T09:30:20.45Z' } }
updated_atstring, RFC3339 timestampThe time the feed was updated$eq, $gt, $lt, $gte, $lte{ updated_at: { $gte: '2023-12-04T09:30:20.45Z' } }

Feeds can be sorted by created_at, updated_at, member_count, follower_count, and following_count.

Be sure to reach out to support if you need additional query feed capabilities.

© Getstream.io, Inc. All Rights Reserved.