Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
Hide categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Developer Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Working with Node.js
      • Node.js Behavior in Heroku
      • Troubleshooting Node.js Apps
    • Ruby
      • Rails Support
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Model Context Protocol
    • Vector Database
    • Heroku Inference
      • Inference Essentials
      • AI Models
      • Inference API
      • Quick Start Guides
    • Working with AI
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Add-ons
  • All Add-ons
  • Algolia Realtime Search
Algolia Realtime Search

This add-on is operated by Algolia

A powerful Search API delivering relevant results from the first keystroke

Algolia Realtime Search

Last updated July 07, 2021

Table of Contents

  • Installing the add-on
  • Upgrading the add-on
  • Using with Rails
  • Setup
  • Quick Start
  • Options
  • Configuration example
  • Indexing
  • Master/slave
  • Target multiple indexes
  • Tags
  • Search
  • Faceting
  • Geo-Search
  • Typeahead UI
  • Caveats
  • Note on testing
  • Dashboard
  • Support

Algolia Search is an add-on that provides hosted full-text, numerical and faceted search.

Algolia’s Search API makes it easy to deliver a great search experience in your apps & websites providing:

  • REST and JSON-based API
  • search among infinite attributes from a single searchbox
  • instant-search after each keystroke
  • relevance & popularity combination
  • mobile compatibility
  • 99.99% SLA
  • first-class data security

Algolia’s official API clients are available on github: Ruby, Rails, Python, Node.js, PHP, JavaScript, Objective-C, Java, Android, C#, Shell.

Installing the add-on

AlgoliaSearch can be installed using the following addons:create command, replacing PLAN with the name of the Algolia plan you’ve chosen:

$ heroku addons:create algoliasearch:PLAN
-----> Adding algoliasearch:PLAN to myapp... done

The PLAN variable can be either:

  • free
  • starter
  • growth
  • pro

for example:

$ heroku addons:create algoliasearch:starter
-----> Adding algoliasearch:starter to myapp... done

Once installed, the add-on provides you 3 configuration variables that you’ll need to setup your API/REST client:

$ heroku config | grep ALGOLIASEARCH
ALGOLIASEARCH_API_KEY:           67c7681237e9c6059bc651d916da891f
ALGOLIASEARCH_API_KEY_SEARCH:    58f9095a2ff3e1df04c1e547256104f5
ALGOLIASEARCH_APPLICATION_ID:    97KEISL1BB

Upgrading the add-on

AlgoliaSearch can be upgraded using the following addons:upgrade command, replacing PLAN with the name of the Algolia plan you’ve chosen:

$ heroku addons:upgrade algoliasearch:PLAN
-----> Adding algoliasearch:PLAN to myapp... done

Using with Rails

The algoliasearch-rails gem let you easily integrate the Algolia Search API to your favorite ORM. It’s based on the algoliasearch-client-ruby gem.

$ gem install algoliasearch-rails

If you are using Rails 3, add the gem to your Gemfile:

gem "algoliasearch-rails"

And run:

$ bundle install

Setup

Create a new file config/initializers/algoliasearch.rb to setup your APPLICATION_ID and API_KEY.

AlgoliaSearch.configuration = { application_id: 'YourApplicationID', api_key: 'YourAPIKey' }

We support both will_paginate and kaminari as pagination back-end. For example to use :will_paginate, specify the :pagination_backend as follow:

AlgoliaSearch.configuration = { application_id: 'YourApplicationID', api_key: 'YourAPIKey', pagination_backend: :will_paginate }

Quick Start

The following code will create a Contact index and add search capabilities to your Contact model:

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch do
    attribute :first_name, :last_name, :email
  end
end

You can either specify the attributes to send (here we restricted to :first_name, :last_name, :email) or not (in that case, all attributes are sent).

class Product < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch do
    # all attributes will be sent
  end
end

You can also use the add_attribute method, to send all model attributes + extra ones:

class Product < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch do
    # all attributes + extra_attr will be sent
    add_attribute :extra_attr
  end

  def extra_attr
    "extra_val"
  end
end

Front-end Search (realtime experience)

We recommend the usage of our JavaScript API Client to perform queries. The JS API client is part of the gem, just require algolia/algoliasearch.min somewhere in your JavaScript manifest, for example in application.js if you are using Rails 3.1+:

//= require algolia/algoliasearch.min

Back-end Search

A search returns ORM-compliant objects reloading them from your database.

p Contact.search("jon doe")

If you want to retrieve the raw JSON answer from the API, without re-loading the objects from the database, you can use:

p Contact.raw_search("jon doe")

Notes

All methods injected by the AlgoliaSearch include are prefixed by algolia_ and aliased to the associated short names if they aren’t already defined.

Contact.algolia_reindex! # <=> Contact.reindex!

Contact.algolia_search("jon doe") # <=> Contact.search("jon doe")

Options

Auto-indexing & asynchronism

Each time a record is saved; it will be - asynchronously - indexed. On the other hand, each time a record is destroyed, it will be - asynchronously - removed from the index.

You can disable auto-indexing and auto-removing setting the following options:

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch auto_index: false, auto_remove: false do
    attribute :first_name, :last_name, :email
  end
end

You can temporary disable auto-indexing using the without_auto_index scope. This is often used for performance reason.

Contact.delete_all
Contact.without_auto_index do
  1.upto(10000) { Contact.create! attributes } # inside the block, auto indexing task will noop
end
Contact.reindex! # will use batch operations

You can force indexing and removing to be synchronous by setting the following option:

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch synchronous: true do
    attribute :first_name, :last_name, :email
  end
end

Custom index name

You can force the index name using the following option:

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch index_name: "MyCustomName" do
    attribute :first_name, :last_name, :email
  end
end

Per-environment indexes

You can suffix the index name with the current Rails environment using the following option:

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch per_environment: true do # index name will be "Contact_#{Rails.env}"
    attribute :first_name, :last_name, :email
  end
end

Custom attribute definition

You can use a block to specify a complex attribute value

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch do
    attribute :email
    attribute :full_name do
      "#{first_name} #{last_name}"
    end
  end
end

Custom objectID

By default, the objectID is based on your record’s id. You can change this behavior specifying the :id option (be sure to use a uniq field).

class UniqUser < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch id: :uniq_name do
  end
end

Restrict indexing to a subset of your data

You can add constraints controlling if a record must be indexed by using options the :if or :unless options.

class Post < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch if: :published?, unless: :deleted? do
  end

  def published?
    # [...]
  end

  def deleted?
    # [...]
  end
end

Notes: As soon as you use those constraints, deleteObjects calls will be performed in order to keep the index synced with the DB (The state-less gem doesn’t know if the object don’t match your constraints anymore or never matched, so we force DELETE operations, even on never-indexed objects).

You can index a subset of your records using either:

# will generate batch API calls (recommended)
MyModel.where('updated_at > ?', 10.minutes.ago).reindex!

or

MyModel.index_objects MyModel.limit(5)

Configuration example

Here is a real-word configuration example (from HN Search):

class Item < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch per_environment: true do
    # the list of attributes sent to Algolia's API
    attribute :created_at, :title, :url, :author, :points, :story_text, :comment_text, :author, :num_comments, :story_id, :story_title, :

    # integer version of the created_at datetime field, to use numerical filtering
    attribute :created_at_i do
      created_at.to_i
    end

    # `title` is more important than `{story,comment}_text`, `{story,comment}_text` more than `url`, `url` more than `author`
    # btw, do not take into account position in most fields to avoid first word match boost
    attributesToIndex ['unordered(title)', 'unordered(story_text)', 'unordered(comment_text)', 'unordered(url)', 'author', 'created_at_i']

    # list of attributes to highlight
    attributesToHighlight ['title', 'story_text', 'comment_text', 'url', 'story_url', 'author', 'story_title']

    # tags used for filtering
    tags do
      [item_type, "author_#{author}", "story_#{story_id}"]
    end

    # use associated number of HN points to sort results (last sort criteria)
    customRanking ['desc(points)', 'desc(num_comments)']

    # controls the way results are sorted sorting on the following 4 criteria (one after another)
    # I removed the 'exact' match critera (improve 1-words query relevance, doesn't fit HNSearch needs)
    ranking ['typo', 'proximity', 'attribute', 'custom']

    # google+, $1.5M raises, C#: we love you
    separatorsToIndex '+#$'
  end

  def story_text
    item_type_cd != Item.comment ? text : nil
  end

  def story_title
    comment? && story ? story.title : nil
  end

  def story_url
    comment? && story ? story.url : nil
  end

  def comment_text
    comment? ? text : nil
  end

  def comment?
    item_type_cd == Item.comment
  end

  # [...]
end

Indexing

Manual indexing

You can trigger indexing using the index! instance method.

c = Contact.create!(params[:contact])
c.index!

Manual removal

And trigger index removing using the remove_from_index! instance method.

c.remove_from_index!
c.destroy

Reindexing

To safely reindex all your records (index to a temporary index + move the temporary index to the current one atomically), use the reindex class method:

Contact.reindex

To reindex all your records (in place, without deleting out-dated records), use the reindex! class method:

Contact.reindex!

Clearing an index

To clear an index, use the clear_index! class method:

Contact.clear_index!

Master/slave

Where possible, we changed noninclusive terms to align with our company value of Equality. We retained noninclusive terms to document a third-party system, but we encourage the developer community to embrace more inclusive language. We will update the term when it’s no longer required for technical accuracy.

You can define slave indexes using the add_slave method:

class Book < ActiveRecord::Base
  attr_protected

  include AlgoliaSearch

  algoliasearch per_environment: true do
    attributesToIndex [:name, :author, :editor]

    # define a slave index to search by `author` only
    add_slave 'Book_by_author', per_environment: true do
      attributesToIndex [:author]
    end

    # define a slave index to search by `editor` only
    add_slave 'Book_by_editor', per_environment: true do
      attributesToIndex [:editor]
    end
  end

end

Target multiple indexes

You can index a record in several indexes using the add_index method:

class Book < ActiveRecord::Base
  attr_protected

  include AlgoliaSearch

  PUBLIC_INDEX_NAME  = "Book_#{Rails.env}"
  SECURED_INDEX_NAME = "SecuredBook_#{Rails.env}"

  # store all books in index 'SECURED_INDEX_NAME'
  algoliasearch index_name: SECURED_INDEX_NAME do
    attributesToIndex [:name, :author]
    # convert security to tags
    tags do
      [released ? 'public' : 'private', premium ? 'premium' : 'standard']
    end

    # store all 'public' (released and not premium) books in index 'PUBLIC_INDEX_NAME'
    add_index PUBLIC_INDEX_NAME, if: :public? do
      attributesToIndex [:name, :author]
    end
  end

  private
  def public?
    released && !premium
  end

end

Tags

Use the tags method to add tags to your record:

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch do
    tags ['trusted']
  end
end

or using dynamical values:

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch do
    tags do
      [first_name.blank? || last_name.blank? ? 'partial' : 'full', has_valid_email? ? 'valid_email' : 'invalid_email']
    end
  end
end

At query time, specify { tagFilters: 'tagvalue' } or { tagFilters: ['tagvalue1', 'tagvalue2'] } as search parameters to restrict the result set to specific tags.

Search

Notes: We recommend the usage of our JavaScript API Client to perform queries directly from the end-user browser without going through your server.

A search returns ORM-compliant objects reloading them from your database. We recommend the usage of our JavaScript API Client to perform queries to decrease the overall latency and offload your servers.

hits =  Contact.search("jon doe")
p hits
p hits.raw_answer # to get the original JSON raw answer

If you want to retrieve the raw JSON answer from the API, without re-loading the objects from the database, you can use:

json_answer = Contact.raw_search("jon doe")
p json_answer
p json_answer['hits']
p json_answer['facets']

Search parameters can be specified either through the index’s settings statically in your model or dynamically at search time specifying search parameters as second argument of the search method:

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch do
    attribute :first_name, :last_name, :email

    # default search parameters stored in the index settings
    minWordSizeForApprox1 4
    minWordSizeForApprox2 8
    hitsPerPage 42
  end
end
# dynamical search parameters
p Contact.search("jon doe", { :hitsPerPage => 5, :page => 2 })

Faceting

Facets can be retrieved calling the extra facets method of the search answer.

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch do
    # [...]

    # specify the list of attributes available for faceting
    attributesForFaceting [:company, :zip_code]
  end
end
hits = Contact.search("jon doe", { :facets => '*' })
p hits                    # ORM-compliant array of objects
p hits.facets             # extra method added to retrieve facets
p hits.facets['company']  # facet values+count of facet 'company'
p hits.facets['zip_code'] # facet values+count of facet 'zip_code'
raw_json = Contact.raw_search("jon doe", { :facets => '*' })
p raw_json['facets']

Geo-Search

Use the geoloc method to localize your record:

class Contact < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch do
    geoloc :lat_attr, :lng_attr
  end
end

At query time, specify { aroundLatLng: "37.33, -121.89", aroundRadius: 50000 } as search parameters to restrict the result set to 50KM around San Jose.

Typeahead UI

Require algolia/algoliasearch.min (see algoliasearch-client-js) and algolia/typeahead.jquery.js somewhere in your JavaScript manifest, for example in application.js if you are using Rails 3.1+:

//= require algolia/algoliasearch.min
//= require algolia/typeahead.jquery

We recommend the usage of hogan, a JavaScript templating engine from Twitter.

//= require hogan

Turns any input[type="text"] element into a typeahead, for example:

<input name="email" placeholder="test@example.org" id="user_email" />

<script type="text/javascript">
  $(document).ready(function() {
    var client = new AlgoliaSearch('YourApplicationID', 'SearchOnlyApplicationKey');
    var template = Hogan.compile('{{{_highlightResult.email.value}}} ({{{_highlightResult.first_name.value}}} {{{_highlightResult.last_name.value}}})');
    $('input#user_email').typeahead(null, {
      source: client.initIndex('<%= Contact.index_name %>').ttAdapter(),
      displayKey: 'email',
      templates: {
        suggestion: function(hit) {
          return template.render(hit);
        }
      }
    });
  });
</script>

Caveats

This gem makes intensive use of Rails’ callbacks to trigger the indexing tasks. If you’re using methods bypassing after_validation, before_save or after_save callbacks, it will not index your changes. For example: update_attribute doesn’t perform validations checks, to perform validations when updating use update_attributes.

Note on testing

To run the specs, please set the ALGOLIA_APPLICATION_ID and ALGOLIA_API_KEY environment variables. Since the tests are creating and removing indexes, DO NOT use your production account.

You may want to disable all indexing (add, update & delete operations) API calls, you can set the disable_indexing option:

class User < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch :per_environment => true, :disable_indexing => Rails.env.test? do
  end
end

class User < ActiveRecord::Base
  include AlgoliaSearch

  algoliasearch :per_environment => true, :disable_indexing => Proc.new { Rails.env.test? || more_complex_condition } do
  end
end

Or you may want to mock Algolia’s API calls. We provide a WebMock sample configuration that you can use including algolia/webmock:

require 'algolia/webmock'

describe 'With a mocked client' do

  before(:each) do
    WebMock.enable!
  end

  it "shouldn't perform any API calls here" do
    User.create(name: 'My Indexed User')  # mocked, no API call performed
    User.search('').should == {}          # mocked, no API call performed
  end

  after(:each) do
    WebMock.disable!
  end

end

Dashboard

You can monitor your consumption at any time opening your Algolia dashboard:

$ heroku addons:open algoliasearch

We do not provide you any login/password; we use Heroku’s SSO to log you in.

Support

All Algolia Search support and runtime issues should be submitted via on of the Heroku Support channels.

Keep reading

  • All Add-ons

Feedback

Log in to submit feedback.

Zara 4 Application Portfolio Manager

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices