Isnor Creative
Isnor Creative Blog
Ruby, Ruby on Rails, Ember, Elm, Phoenix, Elixir, React, Vue

Nov 18, 2016

An Ember javascript full text search setup

I’ve been building mobile applications for iOS, Android and PWA with Ember, that rely on no external API for data. They don’t even rely on Ember Data, just POJOs. This is how I set up full text search for one such app.

Elasticlunr and ember-browserify to the rescue!

I discovered an NPM package called elasticlunr that promised lightweight, full-text search.

Sounds good right? It delivered on its promise!

I’m thinking maybe I could create an Ember addon to more easily plug this into an Ember app, but it was frankly very easy anyway, just a yarn add away. Note that I also added ember-browserify to make it easy to import elasticlunr.

Initializer

Next I added an initializer for each search type, plants for example:

export function initialize(application) {
  application.inject('route', 'lunr', 'service:plant-search');
}
export default {
  name: 'plantSearch',
  initialize
};

Service

And then I created a corresponding service.

The service imports my plant data POJO, as well as elasticlunr. It then sets up a search index, adding fields and the plant ID field as reference returned in search results.

The search function takes a search term (weighted fields could be an additional param here, see elasticlunr docs for info) and then performs an elasticlunr search. When it gets results, it then matches those ID references up to plant IDs in my POJO.

import Ember from 'ember';
import Plants from 'wildcrafting/plant/data';
import elasticlunr from 'npm:elasticlunr';

export default Ember.Service.extend({

  index: null,

  setup: function() {
    let index = elasticlunr(function () {
      this.addField('name');
      this.addField('latinName');
      // etc fields you want to make searchable
      this.setRef('id');
    });

    Plants.forEach(item => index.addDoc(item));
    this.set('index', index);
  }.on('init'),

  search(term) {
    let results = this.get('index').search(term);
    let plants = [];
    results.forEach(result => {
      let plant = Plants.find(item => item.id === parseInt(result.ref));
      plants.push(plant);
    });
    return plants;
  }
});

Controller

In my controller I inject the search service, and then do the search. It boils down to two lines of code:

// inject the service
plantSearch: Ember.inject.service(),

// do the search
items = this.get('plantSearch').search(term);

Wrapping up…

This was very fast to implement, and worked. It’s not a huge data set –– but I was nevertheless impressed with how fast searches work and the search results are great!

It’s probably an unusual and relatively simple use case, but I can imagine this basic idea being modified to suit a variety of other situations.


I am available for Ember consulting work – get in touch to learn more.

Gordon B. Isnor

Gordon B. Isnor writes about Ruby on Rails, Ember.js, Elm, Elixir, Phoenix, React, Vue and the web.
If you enjoyed this article, you may be interested in the occasional newsletter.

I am now available for project work. I have availability to build greenfield sites and applications, to maintain and update/upgrade existing applications, team augmentation. I offer website/web application assessment packages that can help with SEO/security/performance/accessibility and best practices. Let’s talk

comments powered by Disqus