Subscription from Search page, when I don't know id's of query results on Template.onCreated

I’m working on arabic-russian dictionary. It’s my first app with Meteor. I’ve learned docs and watched level up tutorial, and some other sources, but still have a question about subscription.
My dictionary collection at now have 37297 documents, with nested fields, arrays of objects about 10MB.

Main page of app is search page, when I type word, then in helper I split it and add some regex patterns of Arabic morphology. And It’s work. But on first opening of app it load so long… about 20sec. What should I do? Can I subscribe in helper on especial docs after there was found in MongoDb?
I tried use “template level subscription” in find page. But after that Single pages from results don’t opens. Now I use just subscribe on all articles .

That is how it works now:

Template.Search.onCreated(function(){
/*    var self = this; 
    self.autorun(function(){
        self.subscribe("articles");
    });*/
    Meteor.subscribe('articles'); 
});

Template.Search.events({
    'submit'(event, instance){
        event.preventDefault();
        const searchFor = event.target.searchFor.value; 
        FlowRouter.go('search', { searchFor });
    }, 
}); 

Template.Search.helpers({
  result() {
    const instance = Template.instance();
    const searchFor = FlowRouter.getParam('searchFor');
    const regexp_template = arabicWordToRegExPatern(searchFor);  
    const articles = Articles.find(
                            {
                                "words.word" : regexp_template
                            }, 
                            {
                                limit: 50
                            });
    const count = articles.count(); 
    return { articles, count }
  },

  searchFor() {
      return FlowRouter.getParam('searchFor');
  }
});

What are works fine?
ArticleSingle is base template. Further, on it ArticlePage.html:

<template name="ArticlePage">
    <div class="container article-page">
        {{#if Template.subscriptionsReady}}
          {{> ArticleSingle article}}
        {{else}}
            <p>Loading...</p>
        {{/if}}
    </div>
</template>

ArticlePage.js :

Template.ArticlePage.onCreated(function(){
    var self = this;
    var id = FlowRouter.getParam('id');
    self.autorun(function(){
        self.subscribe("articleSingle", id);
    });
    //Meteor.subscribe('articles'); 
});

Template.ArticlePage.helpers({
    article() {
        const id = FlowRouter.getParam('id');
        return Articles.findOne({'_id': id});
    }, 
});

Also I use aldeed:Autoform and it seems working good in duet with ArticlePage And ArticleSingle.

But this delay about 20sec on first run Search-page is so long. I suppose it’s because of subscription on too many data from MongoDB, and I did’t find yet how to fix it :frowning:

You need to subscribe to your collection with a search term to filter/limit, that is the only way to speed this up

1 Like

Thanks for answer!

Should I create publish like this? :

 Meteor.publish('articlesFound', function articlesPublication(searchFor) {
    return Articles.find({ "words.word" : searchFor });
  });

And when should I place it? Usual place for subscribe is Template.Search.onCreated but there app don’t have searchFor value.

Since you would want the subscription to re-run when you change the search parameters, I would do something like this. Also, you might want to think about pagination and not firing the subscription if the searchFor value is empty, etc.

Template.Search.onCreated(function(){
    var self = this; 
    var searchFor = new ReactiveVar('');
    self.autorun(function(){
        self.subscribe("articles", self.searchFor.get() );
    });
});
Template.Search.events({
  'change input.searchFor'(event, instance) {
    instance.searchFor.set( event.currentTarget.value );
  }
});
1 Like

I don’t want use reactivity. I like passing search param through FlowRouter , and is useful when you can go though all searches in browser history…
One more question, (sorry if it is stupid ) : when I get all needed objects (Articles) in Template.onCreated subscribe part , can I pass this result to Template.helpers? Or I should do new query Articles.find({…}) ?
And thanks a lot. Your advises turn my mind in good direction! :slight_smile:

Yes, that is how it’s usually done: a template helper provides the results from the subscription, usually by just returning the find cursor you will iterate over in your template with {{#each}} ... {{/each}}

1 Like

following your advice I’ve created publication based on search input:

if (Meteor.isServer) {
  
  Meteor.publish('articlesSearchResult', function(searchFor) {
    return Articles.find( { "words.word" : searchFor }, { limit: 50 } );
  });
}

And on client helper:

Template.Search.helpers({
  result() {
    const articles = Articles.find({});
    const count = articles.count(); 
    return { articles, count }
  },

But now I faced new challenge: RegExp which worked well on client, doesn’t work on server…
if searchFor = ‘جَمَعَ’ in this example there are all letters like in database (with diacritics) - my publication/subscription working well.
But when I put on searchFor = /^ج[ّ]?[ًٌٍَُِْ]?م[ّ]?[ًٌٍَُِْ]?ع[ّ]?[ًٌٍَُِْ]?$/ - RegExp with alternative diacritics after each letter, it doesn’t work anymore… and It was working well on client… (above there are example of working code with RegExp)… But I know , I should learn now about differences of usage RegExp on client and server…

You need to tell the server that you want to use a regular expression in the query

Meteor.publish('articlesFound', function articlesPublication(searchFor) {
    var query = {'words.word': new RegExp(searchFor)};
    return Articles.find( query );
});
1 Like

it works with some correction: on the client we should pass variable with .source self.subscribe('articlesSearchResult', searchFor.source);
Thanks a lot, @jamgold I’m so grateful to you! With your help I reached what I wanted! Now search through the 10MB of db-texts spend milliseconds! And first loading is too short, less than 1 second. Instead of 10-20sec before!
If someone are interested about my solution, here is it:

//on CLIENT
Template.Search.onCreated(function(){
    var self = this; 
    self.autorun(function(){
        const searchFor = FlowRouter.getParam('searchFor');
        if(searchFor){
            const regexp_searchFor= arabicWordToRegExPatern(searchFor);
            self.subscribe('articlesSearchResult', regexp_searchFor.source);            
        }
    });
});

Template.Search.events({
    'submit'(event, instance){
        event.preventDefault();
        let searchFor = event.target.searchFor.value;  
        FlowRouter.go('search', { searchFor });

    }, 
}); 

Template.Search.helpers({
  result() {
    const articles = Articles.find({});
    const count = articles.count(); 
    return { articles, count }
  },
  searchFor() {
      return FlowRouter.getParam('searchFor');
  }
});
//on SERVER
  Meteor.publish('articlesSearchResult', function(searchFor) {
    return Articles.find( { "words.word" : new RegExp(searchFor) }, { limit: 50 } );
  });
1 Like