Firestore: Data modelling for activities / feed / timeline

Viewed 86

I'm trying to find a suitable Firestore data model for some kind of activity feed for my application.

Example:

  • A user from company A creates new task and assigns it to a user of company B.

Requirements:

  • Users should see activities based on their company_id and user_role_id
  • Every activity affects at least two companies (sender and receiver)
  • Users should be able to archive activities. If a user archives an activity, it is archived for this particular user only. Other users can still access this activity until they archive it for themselves.

Current state:

// data model: activities collection
affected_companies: Array,
affected_user_roles: Map,
description: String

// Example
affected_companies:['c1', 'c2'],
affected_user_roles: { '2': true, '5': true },
description: 'Task A1'

Info: because we can't use multiple array-contains operators and I have a limited amount of user_role_ids, I simply use the field names for affected_user_roles.

Querying this collection is quite easy:

let company_id = 'c1';
let user_role_id = '2';

// Query activities based on given company_id and user_role_id
db.collection('activities').ref
  .where('affected_companies', 'array-contains', company_id) 
  .where('affected_user_roles.' + user_role_id, '==', true)

Problem: How to query only non-archived activities? As described above users should be able to archive an activity for themselves (= hide it from their feed).

Approach 1: Practically I would simply add another array to my activity documents with all user_ids who have archived the activity for themselves. I would then add .where('core.archived_by_users', 'array-contains', user_id) to my query. Although this does not work. Multiple array-contains are not allowed.

Approach 2: I also thought about doing it similar to affected_user_roles: use the user_ids as field names and set boolean values. Although I would need to query for documents where a value does not exist, which is impossible as far as I know:

// Using user_ids as field names
archived_by_users: { '111': true, '241': true }

// Does not work because field 'archived_by_users.557' does not exist.
let user_id = '557';
.where('archived_by_users.' + user_id, '==', false)

Approach 3: Using a subcollection archived_by_users in every activity document where I would store the user_id of each user who has archived the activity. Although it's not possible to query documents based on a value in their subcollection.

I did not come up with a good solution. What would work is:

  • Filter on client-side (not really an option since the activity feed will grow to thousands of documents. The goal is to fetch only the non-archived ones)
  • Add another collection users_activities which keeps track of the activities archived by users. Although this would require additional queries and probably won't save me any reads.

Can anybody think of a good approach here? Every answer is welcome. Thank you in advance!

0 Answers
Related