Simple Search Help

Hi Everyone (again)! :slight_smile:

Wondering if anyone can help me get a simple search to work. I am attempting to create a simple search solution for an apps collection in my database. It just doesn’t seem to filter at all, and will only display “all” results. If I attempt to type anything in the search box, it doesn’t “search” on keyup, and won’t do anything until I actually hit “return.” At that point it simply states “Loading…” until I delete all characters in the search box, then all the results show up again.

Client side template and code:

  <template name="search">
    <div class="page-header clearfix">
      <h4 class="pull-left">Apps</h4>
      <div class="pull-right">
        <input type="text" id="search" class="form-control" width="250px" placeholder="Find an app...">
      </div>
    </div>
    <ul class="list-group">
      {{#if searching}}
        <p>Loading...</p>
      {{else}}
        {{#each apps}}
          <li class="list-group-item clearfix">
            <span class="pull-left"><strong>{{name}}</strong></span>
            <span class="pull-right">{{subject}} | age: {{age}} | standard: {{standard}}</span>
          </li>
        {{else}}
          <p class="alert alert-warning">Nothing found for {{query}}. Sorry, bub.</p>
        {{/each}}
      {{/if}}
    </ul>
  </template>

Template.search.onCreated( () => {
    let template = Template.instance();

    template.searchQuery = new ReactiveVar();
    template.searching   = new ReactiveVar( false );

    template.autorun( () => {
      template.subscribe( 'searchApps', template.searchQuery.get(), () => {
        setTimeout( () => {
          template.searching.set( false );
        }, 300 );
      });
    });
  });

  Template.search.helpers({
    searching: ()=> {
      return Template.instance().searching.get();
    },
    query: ()=> {
      return Template.instance().searchQuery.get();
    },
    apps: ()=> {
      let apps = Apps.find();
      if ( apps ) {
        return apps;
      }
    }
  });

  Template.search.events({
    'keyup #search': function( event, template ) {

      let value = event.target.value.trim();

      if ( value !== '' && event.keyCode === 13 ) {
        template.searchQuery.set( value );
        template.searching.set( true );
      }

      if ( value === '' ) {
        template.searchQuery.set( value );
      }
    }
  });

And here is my server publication:

    Apps = new Mongo.Collection('apps');

    if ( Meteor.isServer ) {
      Apps._ensureIndex( { name: 1, age: 1, subject: 1, standard: 1 } );
    }

  Meteor.publish( 'searchApps', function( search ) {
    check( search, Match.OneOf( String, null, undefined ) );

    let query      = {},
        projection = { limit: 10, sort: { name: 1 } };

    if ( search ) {
      let regex = new RegExp( search, 'i' );

      query = {
        $or: [
          { name: regex },
          { subject: regex },
          { age: regex },
          { standard: regex }
        ]
      };

      projection.limit = 100;
    };

    return Apps.find( query, projection );
  });

Any help in the right direction would be wonderful, if anyone is able to take a look!

Thanks again.

Hey man did you find a solution?

Cheers

I’m not sure what @fernaceman’s eventual solution was, but the only reason why his code above isn’t working is because of this line:

if (value !== '' && event.keyCode === 13 ) {

That check is preventing the keyup from working properly since it’s only allowing the searchQuery ReactiveVar to be set if the event key code is the enter/return key (13). Changing the above to:

if (value !== '') {

will fix the issue.

@hwillson @D_S

I had actually moved on to another solution as I couldn’t figure it out. But, I am going to use the solution @hwillson provided to implement this is another way.

Thanks for the follow-ups everyone!

What about mobile? This solution may not work on tablets for instance. Shouldn’t you listen to change event and add in a timer so that you only start searching when the user stops typing (or slows down)?