Filtering/Searching Query Strategies

Traditionally Brainstem has ignored all filters if you defined a search block in your presenter. Brainstem relies on your search implementation to do any necessary filtering. A downside of this is that you may have to implement your filters twice: once inside your presenters and once inside your searching solution. This causes extra work, particularly for complex queries and associations that the Brainstem DSL is well equipped to handle.

The current version of Brainstem offers a beta feature for allowing both searching and filtering to take place. To enable it, add the following to your presenter:

query_strategy :filter_and_search

The query_strategy DSL method can take a symbol or a lambda. If you pass it a lambda you can programmatically determine what strategy to use. Example:

query_strategy lambda {
  if current_user.filter_and_search?
    :filter_and_search
  else
    :legacy
  end
}

Utilizing this strategy will enable Brainstem to take the intersection of your search results and your filter results, effectively giving you the best of both worlds: fast, efficient searching using something like ElasticSearch and in depth ActiveRecord filtering provided by Brainstem.

You must take the following important notes into account when using the filter_and_search query strategy:

This is not a perfect solution for all situations, which is why all presenters will default to the old behavior. You should only use the filter_and_search strategy if you've determined that:

A.) Your API will still be fast enough when there are 10,000 possible results.

B.) It's not critical for the user to be able to retrieve ALL possible results when searching.

C.) It's actually important for your API that it support Brainstem filters and searching at the same time.

D.) You either have less than 10,000 entities to search and filter from or do not need to be be able to return more than 10,000.

Other strategies

Implementing a strategy

If you have a different filtering or searching strategy you would like to employ, you can create a strategy class in lib/brainstem/query_strategies. Your class should inherit from BaseStrategy and implement an execute method. The execute method should accept a current scope and return an array of models and the count of all possible modes.

Example:

module Brainstem
  module QueryStrategies
    class MyAwesomeFilterStrat < BaseStrategy
      def execute(scope)
        scope = do_something_awesome(scope)
        count = scope.count
        scope = paginate(scope)
        [scope.to_a, count]
      end
    end
  end
end

You should then add the logic for using that strategy in the strategy method of PresenterCollection.

Example:

def strategy(options, scope)
  strat = if options[:primary_presenter].configuration.has_key? :query_strategy
            options[:primary_presenter].configuration[:query_strategy]
          else
            :legacy
          end

  return Brainstem::QueryStrategies::MyAwesomeFilterStrat.new(options) if strat == :my_awesome_filter_strat
  return Brainstem::QueryStrategies::FilterOrSearch.new(options)
end

This can then be enabled in a presenter with:

query_strategy :my_awesome_filter_strat`.