Questions about publishing and subscribing

First let me tell you about the app I’m trying to code, its a database of animes tv series/movies and mangas, and the user can filter the genres and themes he wants to search for.

Now for my problem, I have a huge number of results to show to the user and the application is slowing down while im rendering it all and I realize my approach with publishing and subscribing might be the root of the problem.

Heres my publish:

Meteor.publish("filteredWorks", function (filters) {
    if (filters !== null) {
        if (filters.genres !== undefined && filters.genres !== "") {
            filters.genres = filters.genres.split(",");
        }

        if (filters.themes !== undefined && filters.themes !== "") {
            filters.themes = filters.themes.split(",");
        }

        var queryObject = Meteor.call("createWorkQueryObject", filters);

        if (_.isEmpty(queryObject)) {
            return [];
        }

        return Works.find(queryObject, {sort: {name: 1}});
    }
});

And heres my subscribe:

Tracker.autorun(function () {
    var filters = Session.get("filters");
    Meteor.subscribe("filteredWorks", filters);
});

What do you guys think of this approach? What bothers me right now is that depending on the filter, thousands of titles need to be rendered on screen and the application takes way too long and slows down the browser.

You can see the whole code here, it is a WIP though.

Just a question, are you going to make it of MAL scope or is it just for testing / learning / personal use?

I’m asking because in upcoming time Anilist switches to open source, they just need to finish refactoring the code. Although they’re on Laravel + Angular stack.

Its just for learning, maybe I’ll publish the app later if i’m satisfied with code quality but thats not the scope of this project right now

Blaze is terribly slow when updating a lot of small DOM changes, which is probably what happens when you change your filter. What you need to do is:

  1. Stop Blaze rendering
  2. Subscribe with the new filter
  3. Wait for the subscription to be ready
  4. Start Blaze rendering again
    This is usually done by using Template.subscriptionsReady in your html.

Hey @Steve,
how do you stop and start Blaze rendering?

The easiest solution would be to limit the query, simply make a default limit like 10 and let the user increment it if he wants to.

I’m already dealing with 25 items chunks in my filtered query, but I’m observing that the time between subscription ready and the end of Tracker.afterFlush increases with the # of records I fetch.
Even with just 325 records I got almost 1 sec between the 2 and my sub template is damn simple. I’ve something like:

<template name="messages">
	{{#if Template.subscriptionsReady}}
		{{#each allMessages}}
			{{> message}}
		{{/each}}
	{{/if}}
</template>

and

<template name="message">
	<div class="message">
		<div id ="{{getMessageId}}" contenteditable="false" data-message="true">{{{messageBody}}}</div>
	</div>
</template>

In Messages Template onCreated I have some thing like this:

Template.messages.onCreated(function(){
    	var that = this;
    	this.autorun(function(){
    	 that.subscribe('messages', Session.get('current-channel')._id, Session.get('messages-limit'));
    	});

    	this.autorun(function() {
    		if (that.subscriptionsReady()) {
     			Tracker.afterFlush(function(){
    				// do some minor stuff that takes 1-5% of the exec time
    			});
    		}
    	});
    });

Now I supposed that stopping Blaze rendering before subscribe and starting it again after subscriptionsReady could provide some benefits, but I don’t know how to do it.

You can take a few benefits by using next way:

Collection._collection.pauseObservers();
<subscribe>
<wait until subscribtion is ready>
<Tracker.afterFlush>
Collection._collection.resumeObservers();

Thanks for the tip.
Just tried but I’ve got exactly the same performance as before.

Blaze rendering can be suspended with an #if, just like you do in your template code. This way Blaze doesn’t progressively update the DOM while receiving the data. Indeed, it is often best to render the DOM once and for all after the initial subscribed data set is fully loaded (this issue might be fixed in the near future, see here).

Your case is different: since you already wait for the subscription to be ready before rendering anything, the problem comes from somewhere else. You can start by checking those helpers to see if they are not too cpu-intensive: allMessages, getMessageId, messageBody.