Meteor-Community-Packages / ground-db

GroundDB is a thin layer providing Meteor offline database and methods
https://atmospherejs.com/ground/db
MIT License
572 stars 77 forks source link

How to handle subscriptions of subsets of a collection #181

Closed ilan-schemoul closed 7 years ago

ilan-schemoul commented 7 years ago

Hello, First thanks to @raix for his package, absolutely necessary for making offline Meteor apps. I have a collection called worksheets. When one asks for an URL the server sends him only worksheets that the URL has asked for (if url = foo.bar/10, the server sends worksheets with category 10). Concretely : this.autorun(() => Meteor.subscribe(worksheets.${FlowRouter.getRouteName()})); The problem is that each time there's a new subscription EVERY SINGLE WORKSHEETS that have not the category of the url (for instance the category 10) ARE DELETED from the cache of my app (indexeddb), even tough I do not use the .keep() method ! So : how to keep multiple subsets of a collection in one grounded collection OR how to always add data when there's a new sub without deleting old ones in my grounded collection ?

ilan-schemoul commented 7 years ago

I was doing more test to see if I could change the behavior but I still have the same problem as described above.

ilan-schemoul commented 7 years ago

I think I will change the whole collection by creating fields for different kind of categories.

ilan-schemoul commented 7 years ago

Finally it wasn't a good idea. So how make grounddb keeping all different sub for a single collection ??

ilan-schemoul commented 7 years ago

@raix I so need your help.

raix commented 7 years ago

should be possible using v2 of ground db - it lets you observe and keep a subset of a meteor collection

ilan-schemoul commented 7 years ago

I use latest V2 RC5.

Tracker.autorun(() => Meteor.subscribe('worksheets', { type: FlowRouter.getRouteName() }));

The subscription will return something like this for category 9 (just an example) : return Worksheets.find(query, { { category: '9' }: { title: 1, category: 1, content: 1, updatedAt: 1 } });


export const Worksheets = new Mongo.Collection('worksheets');
export let localWorksheets;

if (Meteor.isClient) {
  localWorksheets = new RGround('worksheets');
  localWorksheets.observeSource(Worksheets.find());
}
raix commented 7 years ago

There are two patterns, I think the last pattern of the two will suite your case - if I understand you correctly:

export const Worksheets = new Mongo.Collection('worksheets');
export let localWorksheets;

if (Meteor.isClient) {
  localWorksheets = new RGround('worksheets');
  localWorksheets.observeSource(Worksheets.find());

  Tracker.autorun(() => {
    const sub = Meteor.subscribe('worksheets', { type: FlowRouter.getRouteName() });
    // When subscription data is ready, keep the most important
    Tracker.autorun(() => {
      if (sub.ready()) {
        c.stop();
        localWorksheets.keep(Worksheets.find(someSubSetToKeep));
      }
    });
  });
}

or

export const Worksheets = new Mongo.Collection('worksheets');
export let localWorksheets;

if (Meteor.isClient) {
  localWorksheets = new RGround('worksheets');

  Tracker.autorun(() => {
    Meteor.subscribe('worksheets', { type: FlowRouter.getRouteName() });
    // The previous source observer will be stopped and the new one will be used:
    localWorksheets.observeSource(Worksheets.find(someSubSetToKeep));
  });
}
ilan-schemoul commented 7 years ago

I'll try later on, thanks @Raix.

ilan-schemoul commented 7 years ago

Actually I had to remove ground:db which was causing all kind of bug since weeks/months (waste of hundreds of hours because I wasn't smart enough to understand the problem), I first taught it was Meteor who had problem but finally it's probably kernel:dispatcher so I wait a fix for that so I can continue coding and try your ideas. Thanks in any way for your great support @raix 👍

ilan-schemoul commented 7 years ago

Damn it's weird but it's fully working. I had to recreate an observeSource() when I subscribe to a new sub but please note that I had to specify what to find (as you did) .find({ category: specificCategory })not just to recreate an observeSource. I didn't know that ground:db is getting the context of the .find() to differentiate subsets, I mean it's logical I have to say 'I give you only those docs with categories so docs with other categories are probably still on the server, don't delete'em' But I didn't know how to do it and I didn't know that ground:db can know the parameters I used in .find().

ilan-schemoul commented 7 years ago

Hello, I was using the second pattern otherwise now I want the grounded collection to remove docs with a certain category (let's call this category X) which are not on the latest sub. So I came up with sthg like that :

    localWorksheets.observeSource(Worksheets.originalFind());
  this.autorun(() => {
    let sub;
    if (FlowRouter.getRouteName()) sub = Meteor.subscribe('worksheets', FlowRouter.getRouteName());

    Tracker.autorun(() => {
      if (sub.ready()) {
// originalFind is my custom bind to Worksheets.find() as I use .find() to be localWorksheets.find() on the client
        localWorksheets.keep(Worksheets.originalFind({
          category: categories.findIdCategory(FlowRouter.getRouteName()),
        }));
      }
    });
  });

What do I want ?

Each time I change the URL I want localWorksheets to keep in the localStorage worksheets with the category X (depends of the URL) in the sub but NOT deleting at all worksheets belonging to categories different than X.

What is the problem ?

Second pattern doesn't remove worksheets that are not on server in certain cases, first one removes all worksheets that doesn't belong to category X.

raix commented 7 years ago

There might be different ways of doing this:

you can pass in multiple cursors for keep,

  localWorksheets.keep(originalFind(a), originalFind(b));

you could also track an array of categories to track and use the $in: categoriesToKeep maybe have categoriesToKeep be reactive