Search code examples
mongodbmeteormeteor-blaze

How to search collection in meteor with more parameters


I need help with searching the meteor collection with more parameters. I am using search query and filters to see certain objects from a collection. The problem is that I want client to load whole collection and then reactively change what the user sees, but only changing the subscribe, not calling server again. Thill now search query + one filter is working okay, but only if I call server every time something changes. Now in my code below you can see that I am doing it with if else elements, but that is not a good way. Any suggestion will help. Thank you.

        Template.jobs.onCreated( function showOnCreate() {
        Meteor.subscribe('Jobs');
        this.searchQuery = new ReactiveVar('');

        this.remoteQuery = new ReactiveVar(false);
        this.typeQuery = new ReactiveVar(false);
    });

    Template.jobs.helpers({
        job: () => {
            query = Template.instance().searchQuery.get();

            remoteQuery = Template.instance().remoteQuery.get();
            typeQuery = Template.instance().typeQuery.get();

            let regex = new RegExp( query, 'i' );
    // **************************
           // the problem starts here
    // **************************
            if (Router.current().params.slug) {
                const companyJobs = Company.findOne({slug: Router.current().params.slug}).jobs;
                if ( companyJobs !== undefined) {

                    return Meteor.subscribe('Jobs', {'_id': { '$in': companyJobs }});
                }
                return false
            } else if (Router.current().params.slug === undefined && remoteQuery === true ) {
                return Job.find({  $or: [ { Name: regex }, { Description: regex }, ]  , Remote: true, positionType: [],});
            } else if (typeQuery = '') {
            return Job.find({  $or: [ { Name: regex }, { Description: regex }, ] , positionType: typeQuery, });
        },
// -------*****************************

        employer: () => {
            if (Router.current().params.slug === undefined) {
                Meteor.subscribe('Companies');
                return 'Poslodavac: ' + Company.findOne({_id: Template.currentData().CompanyId}).Name;
            }
            return false
        },

        jobUrl: () => {
            Meteor.subscribe('Companies');
            companySlug = Company.findOne({_id: Template.currentData().CompanyId}).slug;
            return ('/company/' + companySlug + '/job/' );
        }
    });

Template.jobs.events({ 
    'click .positionType': (event, templateInstance) => {
        if (Template.instance().remoteQuery.get().lenght > 1){
            Template.instance().typeQuery.set(Template.instance().remoteQuery.get().push(event.target.value));
            console.log(Template.instance().remoteQuery.get())
        } else {
            console.log(Template.instance().remoteQuery.get())
            console.log('ggggggg')
            Template.instance().typeQuery.set(event.target.value);
        }
    },
    'click #remoteFriendly': (event, templateInstance) => {
        Template.instance().remoteQuery.set(!Template.instance().remoteQuery.get());
    },

}); 

Html tempalte with filters:

    <template name="jobs" >

    <div>
        <p>Filteri:</p>

        <span>
            <input type="checkbox" id="remoteFriendly" name="remote"> <span for="remoteFriendly"> Remote friendly? </span>
        </span>

        <span>
            <p>Tip pozicije:</p>
            <input type="checkbox" class="positionType" id="1" value="Programiranje" > <span for="1"> Programiranje </span>
            <input type="checkbox" class="positionType" id="2" value="Dizajn" > <span for="2"> Dizajn </span>
            <input type="checkbox" class="positionType" id="3" value="Marketing" > <span for="3"> Marketing </span>
            <input type="checkbox" class="positionType" id="4" value="Ostalo" > <span for="4"> Ostalo </span>
        </span>
    </div>

    {{#each job}}
        <div style="border: 0.1rem solid black; margin: 1cm; padding: 5px; max-width: 420px;" > <!-- OVO JE PRIVREMENI STIL, OBRISATI-->
            <p> Posao: {{Name}} <br> Opis: {{Description}}</p>
            <p> {{employer}} </p>
            <p>Remote friendly?: {{Remote}}</p>
            <p>Tip pozicije: {{positionType}}</p>
            <p> Saznajte vise <a href="{{jobUrl}}{{_id}}"> OVDE</a></p>
        </div>    
    {{/each}}
    <p id="nesto"></p>
</template>

Solution

  • Welcome to SO!

    You seem to be confused between Pub/Sub and Collection.find.

    You should first realize that the 2 are different mechanisms, which provide different functionalities.

    • Pub/Sub indeed sends data from your Server into your Client's Minimongo database. But this data is not displayed yet.
    • Collection.find is used on your Server against your actual MongoDB, and on your Client against your local Minimongo DB.

    Therefore on your client, once you have correctly subscribed to your server publication (typically at app level or template level / in onCreated hook), you can directly call Jobs.find in your helpers (or anywhere else) to get your documents, without having to change the subscription (unless the latter needs new parameters).

    There should be nothing wrong with your commented code:

    return Job.find({'_id': { '$in': companyJobs }});
    

    In general, avoid any expensive computation in helpers (like Meteor.subscribe), as helpers may be executed many times without you noticing it. Your Meteor.subscribe('Companies') should also go to template level (i.e. in onCreated hook).

    Therefore, instead of doing your if / else conditions in your helper, simply do it once at your template level. To account for your need to use a value from another document in another collection, why not just passing directly the company's slug as an argument to your Jobs subscription, and performing the computation Server-side? Or even just subscribing to everything, as your current initial subscription seems to do.

    Then your helper will just use Jobs.find, which queries against your Client's local minimongo DB, leaving your Server unbothered.