CouchDB does not offer a good way to do this, but if your backend has a facility for maintaining lots of shared databases (in addition to single-user ones) I think you could pull it off.
The initial impulse is to use continuous filtered replication to/from a central "master" hub, but this leaves you with two problems:
- Replication can't delete documents unless they are deleted! I.e. filtered replication doesn't determine which documents exist in the target, but rather whether a document's changes will propagate to the target. The effect of this is that you can share, but you can't unshare.
- This sort of replication doesn't scale. For N user databases you need to keep in sync, every change to the central database forces all N replications to process that change independently. Now consider that the amount of changes M that happen on the central database will be almost directly proportional to N (i.e. the more users you have the more frequently all those users will have to process changes to the central database!) You could somewhat mitigate this by adding more layers (fanning out from one central hub to semi-shared hub to individual databases), but that also adds practical complications.
How is your sharing organized? What you might be able to do is set up a shared database for each "share" combination.
When user A wants to share document D with user B, "move" the document into a new database AB. Practically this means: copy the contents of D to a new document D' in database AB, and then delete the original D from database A.
How does this sync?
Keep in mind PouchDB clients can replicate to/from more than one source database, so user A will replicate from A and AB, while user B replicates from B and AB. The trick is to use filtered replication in the opposite direction, back to the server databases:
The now-shared document D' should never go to database A or database B, and likewise an unshared document X should never go to database AB. Something like this:
┌───────────────────────┐
│ server:5984/user_a │
└───┬───────────────▲───┘
└─┐ ┌─┘ ┌──────────────────────────────┐
│ ●──────│ if (doc.shared_with == null) │
┌─▼───────────┴─┐ └──────────────────────────────┘
│ local │
└──▲──────────┬─┘
┌─┘ └─┐ ┌──────────────────────────────┐
│ ●──────│ if (doc.shared_with == 'ab') │
┌───┴──────────────▼────┐ └──────────────────────────────┘
│ server:5984/shares_ab │
└───────────────────────┘
So, assuming the share database(s) are already set up, when the local client wants to share some data it actually adds _deleted:true
to the original (unshared) document and creates a new (shared) document. The deleted original propagates to the user_a database on the server and the created copy propagates to the shares_ab.
Unsharing then works pretty much the same: the client adds _deleted:true
to the shared document and recreates the data again in a new unshared copy. From B's perspective, the document had showed up once it was in shares_ab and now disappears because it is deleted from shares_ab.
(Instead of user "pairs" you could extend this to ad-hoc sets of users, or simplify it to specific groups that users are already in, or whatnot. The key idea is to create an actually shared database for each unique sharing context needed.)