Search analytics made easy
Search analytics made easy
Works with any search platform, including Elasticsearch, OpenSearch, Sphinx, and Solr
💘 An amazing companion to Searchkick
Add this line to your application’s Gemfile:
gem "searchjoy"
And run the generator. This creates a migration to store searches.
rails generate searchjoy:install
rails db:migrate
Next, add the dashboard to your config/routes.rb
.
mount Searchjoy::Engine, at: "searchjoy"
Be sure to protect the endpoint in production - see the Authentication section for ways to do this.
Track searches by creating a record in the database.
Searchjoy::Search.create(
search_type: "Item", # typically the model name
query: "apple",
results_count: 12,
user_id: 1
)
With Searchkick, you can use the track
option to do this automatically.
Item.search("apple", track: {user_id: 1})
If you want to track more attributes, add them to the searchjoy_searches
table.
add_column :searchjoy_searches, :source, :string
Then, pass the values to the track
option.
Item.search("apple", track: {user_id: 1, source: "web"})
First, choose a conversion metric. At Instacart, an item added to the cart from the search results page counts as a conversion.
Next, when a user searches, keep track of the search id. With Searchkick, you can get the id with:
results = Item.search("apple", track: true)
results.search.id
When a user converts, find the record and call convert
.
search = Searchjoy::Search.find(params[:id])
search.convert
Better yet, record the model that converted.
search.convert(item)
Don’t forget to protect the dashboard in production.
In your config/routes.rb
:
authenticate :user, ->(user) { user.admin? } do
mount Searchjoy::Engine, at: "searchjoy"
end
Set the following variables in your environment or an initializer.
ENV["SEARCHJOY_USERNAME"] = "andrew"
ENV["SEARCHJOY_PASSWORD"] = "secret"
Data should only be retained for as long as it’s needed. Delete older data with:
Searchjoy::Search.where("created_at < ?", 1.year.ago).find_in_batches do |searches|
search_ids = searches.map(&:id)
Searchjoy::Conversion.where(search_id: search_ids).delete_all
Searchjoy::Search.where(id: search_ids).delete_all
end
You can use Rollup to aggregate important data before you do.
Searchjoy::Search.rollup("Searches")
Delete data for a specific user with:
user_id = 123
search_ids = Searchjoy::Search.where(user_id: user_id).pluck(:id)
Searchjoy::Conversion.where(search_id: search_ids).delete_all
Searchjoy::Search.where(id: search_ids).delete_all
To customize, create an initializer config/initializers/searchjoy.rb
.
Change the time zone
Searchjoy.time_zone = "Pacific Time (US & Canada)" # defaults to Time.zone
Change the number of top searches shown
Searchjoy.top_searches = 500 # defaults to 100
Link to the search results
Searchjoy.query_url = ->(search) { Rails.application.routes.url_helpers.items_path(q: search.query) }
Add additional info to the query in the live stream
Searchjoy.query_name = ->(search) { "#{search.query} #{search.city}" }
Show the conversion name in the live stream
Searchjoy.conversion_name = ->(model) { model.name }
Searchjoy now supports multiple conversions per search 🎉
Before updating the gem, create a migration with:
create_table :searchjoy_conversions do |t|
t.references :search
t.references :convertable, polymorphic: true, index: {name: "index_searchjoy_conversions_on_convertable"}
t.datetime :created_at
end
Deploy and run the migration, then update the gem.
You can optionally backfill the conversions table
Searchjoy.backfill_conversions
And optionally remove convertable
from searches
remove_reference :searchjoy_searches, :convertable, polymorphic: true
You can stay with single conversions (and skip all the previous steps) by creating an initializer with:
Searchjoy.multiple_conversions = false
View the changelog
Everyone is encouraged to help improve this project. Here are a few ways you can help:
To get started with development and testing:
git clone https://github.com/ankane/searchjoy.git
cd searchjoy
bundle install
bundle exec rake test