Help with Flexible Sync design

Hi,

I have what I consider a somewhat complex schema design and requirement. I’m hoping that Flexible Sync can support it, but I’m a bit doubtful from what I’ve read thus far. I’m happy to post and explain it here, but I’m unsure if this is the best place for an extended discussion like that; are there other, better resources available where I could discuss the applicability of Flexible Sync for my design? Thanks!

cc: @Ian_Ward

1 Like

Sure - the forums is a great place to discuss desgin

1 Like

Thanks, @Ian_Ward!

So here’s the basic design (leaving out some other unimportant fields).


It’s a pretty straightforward hierarchy: a given user can have multiple Notebook records; each Notebook can have multiple Pages; each Page can have multiple PageItems. A PageItem is linked to an Item which holds the actual content of the item; the reason for this distinction is that an Item can have multiple PageItems that point to it (i.e. you can clone an Item so that it shows up on multiple Pages).

One important criteria is that I need to be able to download/sync an entire user’s data - all of their notebooks, pages within those notebooks, etc. - when the user logs into the app. This is so a user’s whole data store can be accessed offline, be available for global searching, etc.

If this were strictly a single-user system, as I understand it I think the Realm implementation would be really easy and could even use Partition-based Sync: just have a UserID field in every table, set the Partition key to be UserID, and it’s done. I think.

However, I want a Notebook to be able to be shared among either multiple individual users (i.e. a given Notebook can be made accessible to a list of UserIDs), or if it allows for an easier Realm implementation I would be fine if the sharing were among a Group object (i.e. a given Notebook is accessible to one Group, and that Group would be composed of a list of UserIDs).

Hopefully the above explanation is clear, but of course, ask away if it’s not. The question I’m trying to answer is, can my design and sharing model be implemented via Realm (in either sync model) and if so, how?

TIA for any assistance here!! :pray:

1 Like

Yes this is definitely possible with Flexible Sync. You’ll need to create a sync subscription each for Notebook, Pages, PageItems, and Items which likely will be done based on some ‘foreign-key’-esque field in each class. In the future we plan to add a feature that will enable you to automatically pull in any relationships you specify, so you would be able to just query on Notebooks and then it could pull in all child objects - but its not there yet.

You can also share Notebooks between users by using permissions and a query on notebooks which gathers all notebooks for which the user has access to.

In terms, of assigning permissions, what you’ll likely want to do is implement custom user data, so you could store the notebook id for every notebook a user has access to - https://docs.mongodb.com/realm/users/enable-custom-user-data/

From there, once user’s login, you can use an Authentication trigger to insert the user’s permissions into their custom user data once they register -

Lastly, you can set up your sync permissions to match

I hope this helps

-Ian

2 Likes

Thanks much, @Ian_Ward - I’ll digest this in detail but happy to hear it’s possible!

1 Like

Hi @Ian_Ward,

I’ve studied your post and the docs but am fairly sure I don’t grasp the solution yet. Here’s what I’ve come up with so far, but pretty sure it’s all or partially wrong…

In the Notebook object, have an “allowed users” array property which stores the User IDs of every user that has access to that notebook.

Write a function in the Login authentication trigger so that whenever a user logs in, the function queries the above “allowed users” array in the Notebook object so as to gather the list of Notebook IDs that this user can access, and it writes that list to an “allowed notebooks” array element of their custom user data document.

In Sync Roles for the Notebooks collection, set up a role so that it looks at the above “allowed notebooks” in their custom user data and only gives them access to notebooks that they should be allowed to access - I’m not clear on whether this part is correct or how it would be configured - is the filtering done in the “applies to” expression or the “read” expression…

If the above is at all correct, then it covers Notebooks, but I’m not clear on how I would access the correct subset of records for the other objects (you mentioned them all having a foreign-key-esque field, but I’m not sure what that would be or how it would work).

Any guidance is appreciated!

I’m not clear on whether this part is correct or how it would be configured - is the filtering done in the “applies to” expression or the “read” expression…

You’ll want to use the custom user data to configure your subscriptions. So you parse the custom_data.notebooks and create a subscription for each _id of Notebook that is in your custom user data. From there you set up permissions for all users for Notebooks, you’ll want to set a field on Notebooks so it contains all users’ ids that have access to that Notebook. If the field was users it might look something like

  "Notebook": {
    "roles": [
      {
        "name": "allUsers",
        "applyWhen": { },  // Apply to all users
        "read": { "users": "%%user.id" },
        "write": { "users": "%%user.id" },
      },
    ],
  },

If the above is at all correct, then it covers Notebooks, but I’m not clear on how I would access the correct subset of records for the other objects (you mentioned them all having a foreign-key-esque field, but I’m not sure what that would be or how it would work).

For now, you’ll need a field on each sub-object that is something like NotebookId where you store the _id of the Notebook for which it is referenced - you then create a subscription from the client that says - "Give me all Pages, PageItems, and Items where NotebookId matches the ids stored in my custom user data. Later we will introduce an API that will do this for you automatically but this is how you can accomplish this now. You can use a similar permissions model for the other collections as you did for the Notebook collection above.

1 Like

One thing I should mention, that is presumed in this explanation, is that we currently do not support queries on arrays, which we working on right now to deliver. This will delivered soon.

In fact, if you only have a single role in your permissions (ie. allUsers), you could simplify your architecture and not use custom user data all. And instead just make subscription to give me all Notebooks where my userId is included as an element in the array of Notebook.allowedUsers etc.

1 Like

Ah, OK - I was just going through your reply and actually wondering about that!

Cool, I think that will work - I think I will in fact only need one role. Just to confirm, though: I’ll still need to wait for “queries on arrays” to be able to implement this simpler solution, correct?

You will need to wait for queries - although this is on the order of weeks versus quarters for delivery.

In the mean time, there are ways to work around this limitation - as my colleague @Andrew_Morgan demonstrates in his example with the RChat application -

Essentially you can use backend triggers to update permissions and queries as needed.

1 Like

Got it, thanks! I can wait weeks, that’s actually not a problem. :slight_smile:

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.