5

I have a server side mongo collection called Profiles.

I need to publish and subscribe to the entire collection of Profiles if user: adminId.

That way the administrator can edit, updated, etc... each Profile collection item.

But I want users to be able to see their Profile record.

So I tried this...

CLIENT SIDE

MyProfile = new Meteor.Collection("myprofile");
Meteor.subscribe('profiles');
Meteor.subscribe('myprofile');

COMMON - CLIENT AND SERVER SIDE

Profiles = new Meteor.Collection("profiles");

SERVER SIDE - The publishing and subscribing of profiles works fine.

// this returns all profiles for this User
// if they belong to an ACL Group that has acl_group_fetch rights
Meteor.publish("profiles", function() { 
    var user_groups = Groups.find({users: this.userId()});
    var user_groups_selector = [];
    user_groups.forEach(function (group) {
       user_groups_selector.push(group._id);
    });
    return Profiles.find( {
       acl_group_fetch: {
          $in: user_groups_selector
        } 
    });
});

Here is where the problem seems to begin. The Profiles.find is returning collection items because I can output them to the console server side. But for some reason the publish and subscribe is not working. The client receives nothing.

//  return just the users profile as myprofile
Meteor.publish("myprofile", function() {
  return  Profiles.find({user: this.userId()});
});

Any ideas what I am doing wrong. I want to be able to publish collections of records that User A can insert, fetch, update, delete but User B (C, D and E) can only see their record.

Steeve Cannon
  • 3,682
  • 3
  • 36
  • 49
  • Steeve - a minor point: be careful about the first subscription ('profiles'); it will not automatically update when the user's groups change unless you wrap it in a autosubscribe context. I don't think this is the issue you are seeing though. – Tom Coleman Aug 01 '12 at 02:38
  • The user isn't seeing their own profile? Or just not the list of profiles from the 'profile' subscription? Are you definitely waiting until the data is loaded? – Tom Coleman Aug 01 '12 at 02:40
  • MyProfile.find or .findOne has nothing in the document collection on the client. I have even pulled all the group code out of Profiles so it publishes to all clients now just to see that it does. This is the first time I have attempted two publish/subscribes on the same server side mongo collection. – Steeve Cannon Aug 01 '12 at 02:49
  • Probably easier to continue this discussion on IRC if you are able to jump on? – Tom Coleman Aug 01 '12 at 02:52
  • I will touch base with you later, can't do IRC right now. Thank you for the help though here on Google groups! – Steeve Cannon Aug 01 '12 at 02:57
  • No problem mate, try out the answer I have below and I'll catch you on IRC later on. – Tom Coleman Aug 01 '12 at 03:00

3 Answers3

1

I think your issue is more on the MongoDB side than with meteor. Given your case I'd do two collections (Group and Profile).

Each document in the Group collection would feature an array containing DBRefs to documents in the Profile collection (actually users so I would think about renaming the Profile collection to User as imo that's more intuitive).

Same for the Profile collection and its documents; each document in the profile collection (representing a user) would have an array field containing DBrefs to groups the user belongs to (documents inside the Group collection).

evdama
  • 2,166
  • 1
  • 16
  • 17
  • I was following the Child Links pattern for [Trees] (http://www.mongodb.org/display/DOCS/Trees+in+MongoDB) in the Groups collection. Each Group has a `users` field composed of [DBRefs] to the Users document. I think you are suggesting keeping the Child Links pattern for Groups and then implementing a `groups` field which is an Array of Ancestors inside the Users collection? – Steeve Cannon Aug 01 '12 at 11:07
  • This works but I have to maintain two sets of records. First, the array of 'users._id' within the 'Group.users' field. Second, the array of 'groups._id' within the 'users.groups' field. Which means I would have to be very careful when deleting or removing a user from a group. – Steeve Cannon Aug 01 '12 at 21:58
  • BTW, I am using this solution for now but it doesn't seem like the "Meteor" way of doing things. The "Meteor" way would be "named" subscription client collections no matter the originating server side collection as long as the server side publishing collection had same name as client. – Steeve Cannon Aug 01 '12 at 22:03
1

I'm not entirely sure of how you're checking for the error or not, but I think you may be running into a gotcha I ran into. When you publish data with the Profiles collection, even though the pub/sub calls use the 'myprofile' name, the data always is always available in the collection that you're returning a cursor for... in this case, the data you publish in the 'myprofile' publication will show up in the 'profiles' collection on the client. The publish call doesn't create a 'myprofile' collection on the client. So if you're trying to find() on the 'myprofile' collection you won't see any data. (Meteor/MongoDB won't complain that the collection doesn't exist because they always will lazily create it when you reference it.)

badslug
  • 11
  • 1
  • Iain you are absolutely correct. I felt that logically I could call the client collection whatever I wanted regardless of the server collection. Which when you consider publishing from X server collection to an N-number of client collections with varying 'names' would be quite powerful and flexible. It would allow say an end user to see their specific "users" collection but you could publish "allusers" if the end user is Admin. The possibilities are endless if it worked that way. – Steeve Cannon Aug 01 '12 at 21:46
  • Secondary note, correct, no error but empty collection in MyProfiles from "myprofiles" named and the data was showing up in the Profiles collection. You can test this with any collection. Setup the first pub/sub as Collection and "collection" and then the second as MyCollection and "mycollection". Have "collections" query restrictive so it doesn't return a collection. Have "mycollection" query wide open and have it calling Collection.find({}) for it's result set. Items will show on the client in Collection and not MyCollection. It is easy to test. – Steeve Cannon Aug 01 '12 at 22:01
  • The issue here is that you are conflating collection names with subscription names. If you look at the docs for publishing (http://docs.meteor.com/#meteor_publish) you'll see you can send data to any collection within a publish method, it's just that if you return a Cursor (ie. `Collection.find`) in a publish method, Meteor auto wires up calls to `this.set` etc to sync the records found of that collection. – Tom Coleman Aug 01 '12 at 23:56
  • @Steeve: you can, but you'd just need to manually wire up observe + set/unset calls yourself. If you return a `Cursor` in `Meteor.publish` it does it for you, and things "just work" (except obviously the restriction on collection names). See here if you are interested: https://github.com/meteor/meteor/blob/master/packages/livedata/livedata_server.js#L537. – Tom Coleman Aug 02 '12 at 01:01
  • @TomColeman interesting. So, theoretically one could create a package that allowed a single collection to be published through different "named" Meteor.collections on the client. – Steeve Cannon Aug 02 '12 at 02:03
  • @SteeveCannon exactly. In fact if you call `_publishCursor` yourself you can provide an alternate `name` for it to publish itself on. (I don't know if this is a good idea). Perhaps I should post that as an answer to this question.. it does what you wanted to do I suppose.. – Tom Coleman Aug 02 '12 at 03:07
  • @TomColeman and SteeveCannon: I've been doing exactly that for a few months now. In fact I pass the subscription name to _publishCursor instead of the collection name. The trouble with this is that after all is said and done, you won't get latency compensation. I don't think this feature is just a nice-to-have. I really believe Meteor should allow for publishing subsets of a single collection while allowing the whole latency comp technique to work. – matb33 Oct 03 '12 at 02:15
0

I think the problem here is that you only need the one collection: Profiles.

So if you just remove the offending line

MyProfile = new Meteor.Collection("myprofile");

Everything should work fine (you'll have both datasets within the Profiles collection).

Tom Coleman
  • 3,037
  • 18
  • 16
  • yes that does work to a degree, but it becomes a bit limiting. How does the Admin or anyone with the rights to crud all Profiles know which one is specifically theirs? Which is why I only wanted to publish all Profiles to someone who can do stuff to the collection and then MyProfile to whoever is logged in. It is more complex with Groups. There is a difference between all Groups and the Groups a user belongs. I still have to know what Groups a user is in even if they can crud all or some Groups as they may be in a Group they cannot crud but which limits them elsewhere in app – Steeve Cannon Aug 01 '12 at 21:55
  • Steeve I would have thought you'd want to have code that did something along the lines of `myprofile = Profiles.findOne({_id: this.userId()})` to find the user's profile anyway-- that way you could reuse that code on both the client and server and not be relying on different behaviour on the client. – Tom Coleman Aug 01 '12 at 23:58