On the Theory of Collection Subscrption

Hi all, love being back in Meteor. Solves so many problems.

Except one. Coming from PHP and MySQL my mind works a bit different regarding database access.

So my understanding is like this:

  • I have a collection called Posts where users save new content to
  • If I want to ‘query’ for it, do I have to subscribe the user to that collection?
  • Do I dynamically set what information they’re subscribed to, depending on a route?
    • /r/all for example would subscribe them to all post data, so like, find {}
    • /r/science would then only subscribe them to find{“category”:“science”}

Is this an accurate understanding, or am I still way off?

Reading this guide. Brain hurts.

Publish functions can return a Collection.Cursor, in which case Meteor will publish that cursor’s documents to each subscribed client. You can also return an array of Collection.Cursors, in which case Meteor will publish all of the cursors.

I don’t even grasp this.

Essentially, I want to be able to do:

SELECT * FROM posts WHERE user_id = {{Meteor.userId}} LIMIT 5 ORDER BY time_created

Why is this so hard to do in Meteor? I have to… have the server only publish that data… once? Or… when does that happen. So confused.

Your publish should be pretty simple.

// server
Meteor.publish("posts", function(filter) {
  check(filter, Match.Optional(String)); // validate input. 
  if (this.userId) { // check to see if user is logged in. You can remove it if you don't need it.
    if (filter) {
      return posts.find({ category: filter}, {sort: {time_created: 1}, limit: 5});
    }
    return posts.find({}, {sort: {time_created: 1}, limit: 5});
  }
  return this.ready(); // Needed if you're not returning anything.
});

//client
Meteor.subscribe("posts", "science");

If you’re using Iron router, you can put the subscription in waitOn hook for that route, which is reactive and gets destroyed when you go to a different route.

Or you can subscribe to it in onCreated.

If you’re concerned about performance, I would suggest reading this article. https://www.discovermeteor.com/blog/improving-the-performance-of-your-meteor-app/

Thank you.

I JUST became aware you can pass data in to the subscription model. So you’re exactly right on the subscription from client. Makes sense with minimongo! Very cool.

Now, last question which as I can see in the docs, I can build some sort of array to pass multiple data sets back.

What I really need to do is:

Meteor.subscribe(“posts”, “science”);
Meteor.subscribe(“posts”, “art”);

So how would your publish function look now?

Oh, and can I use multiple parameters or am I limited to just one?

Thank you SO MUCH. I’ve been publishing all collection data to every one this whole time lol.

Ah yes, I’d like to pass in a second parameter for specifying how many results to return.

You can parameterize your publish as much as you like.

Meteor.publish("posts", function(param1, param2, param3, ...) {
  // you can return cursors from multiple collections here, like
  return [
     posts.find({}),
     users.find({}),
     ...
  ];

  // however you can't return multiple cursors from the same collection in the same publish. You have to build your query to handle that instead.

  return posts.find({category: {$in: ["science", "art", ...]}});
});

Hope that answers your question.

EXCELLENT! This is exactly what I needed. Very cool.

The very last problem I’m having is that my find result doesn’t seem to be working.

So when I do a “Meteor.subscribe(‘posts’, “desk_posts”);” nothing is returned. When I set all as the parameter, it works. What am I missing?

// Publish
if (Meteor.isServer) {
	
	Meteor.publish('posts', function(action) {
		
		if(action == "all"){
			return Posts.find({},{ sort: { timestamp: -1 }, limit:10 });
		}
		
		if(action == "desk_posts"){
			return Posts.find({"type":"desk_posts"},{ sort: { timestamp: -1 }, limit:10 });
		}
		
	});
	
}

I think I need to have a Simple Schema to do this. Sigh. All the small things that get in the way of development. Why doesn’t Meteor just come with routing, schemas, and things that friggin let you work out of the box.

1 Like

The publish looks fine. Check to see if there are records in the db matching your query. You can run meteor mongo in your app directory in terminal.

Also, try console logging the action to make sure you’re passing the right thing.

Yeah it’s for sure got data there. If I query for all it works. Well thanks for the assistance, learned a lot today.

Sorry mate, if you have a moment I’d appreciate it. Here’s a record from my DB:

"owner_id": "pNzsfQDShojij8Bcj",
  "owner_username": "andy",
  "owner_avatar": "/profileImages/AJonZaAMNw6vcm3By.jpg",
  "createdAt": "2017-02-28T23:50:29.251Z",
  "timestamp": 1488325829251,
  "timeAgo": "2017-02-28T23:50:29.251Z",
  "title": "",
  "content": "a",
  "type": "desk_post",
  

But

return Posts.find({"type":"desk_posts"}, { sort: { timestamp: -1 }, limit:10 });

Gives me NOTHING back. Any ideas?

Based on your record, looks like you should be querying for type: 'desk_post' instead of type: 'desk_posts'.

I got excited when I saw your comment, what a rookie mistake.

However, when changed to desk_post it still does the same thing. It’s either all or nothing. So frustrating!

1 Like

You were correct. I also had a typo in the if statement.

So, it was always coming back empty… like the code is being told to do. Worked too long today :confused:

Basic trouble shooting would have solved this, but hey, new platform.

THANKS for every ones help, I hope this post is informational to the next PHP guy coming to the future.

1 Like

Thought I’d share the FINISHED code for the next pour soul.

Router.route('/desk',{
	data:function(){
		
		if( Meteor.user() && Meteor.user().profile.guest ){
			Router.go('/');
		} else {
			Router.go('/desk');
		}
	},
	waitOn: function(){
		Meteor.subscribe('posts', "desk_post"); 
		return;
	},
	template:'screen',
	yieldTemplates: {
		'desk': {to: 'content'},
	}
	
});

// Publish
if (Meteor.isServer) {
	
	Meteor.publish('posts', function(action) {
		
		check(action, String);
		
		if(action == "all"){
			return Posts.find({},{ sort: { timestamp: -1 }, limit:10 });
		}
		
		if(action == "desk_post"){
			return Posts.find({type:"desk_post"},{ sort: { timestamp: -1 }, limit:10 });			
		}
		
		if(action == "notes"){
			return Posts.find({"_id":"v4k45FL7kgDJvEzqW"},{ sort: { timestamp: -1 }, limit:10 });
		}
		
	});
	
}

1 Like