Powering Actions with Elasticsearch Percolate

by Ben Ubois

Actions are built on a great feature of elasticsearch called percolate.

Percolate allows you to register queries ahead of time, then whenever you send a new document to be indexed, elasticsearch can tell you if this document matches any saved queries.

Here’s how it works.

Feedbin is a Rails app and uses the tire gem for working with elasticsearch.

Whenever an action is added or updated, it is also sent to elasticsearch as a percolator query.

Entry.index.register_percolator_query(3) do
  string "kittens"
end

The 3 is the id of this action. This is used later on to find what user this action belongs to and which actions should be performed.

A model called Entry is used for storing RSS articles. Whenever a new entry is added it also gets sent to elasticsearch for indexing.

class Entry < ActiveRecord::Base
  include Tire::Model::Search
  after_commit :search_index_store, on: :create

  private

  def search_index_store
    result = self.index.store(self, {percolate: true})
    ActionsPerform.perform_async(self.id, result['matches'])
  end

end

An array of matched actions ids is returned in result['matches']. So if this entry mentions “kittens” an array like ["3"] would be returned.

Feedbin uses Sidekiq to process background jobs. The ActionsPerform.perform_async part is creating a new background job that performs the actions. A simplified version of this looks like:

class ActionsPerform
  include Sidekiq::Worker

  def perform(entry_id, action_ids)
    actions = Action.where(id: action_ids)
    actions.each do |action|
      if action.actions.include?("send_push_notification")
        PushNotificationSend.perform_async(entry_id, user_ids)
      end
    end
  end

end

Get in touch if you have any questions about this or sign up to see it in action.