Solr Search Builders - samvera/hyrax GitHub Wiki

Search Builders

Building searches is core to any Blacklight app, and Hyrax is no exception. The app/search_builders/hyrax directory contains our Search Builders, originally so-named because the class design followed a Builder pattern. Builder setter methods return the object itself when invoked, so that invocations can be chained, like:

builder = Blacklight::SearchBuilder.new(processor_chain, scope)
            .rows(20)
            .page(3)
            .with(q: 'Abraham Lincoln')

However, at this level, many if not most of the additional methods do not follow this pattern. So these builders create searches in the general sense of the name, not by strictly following the Builder pattern. Refer to the Blacklight::SearchBuilder class if you want to be certain of whether a method can be chained. That leads to the next topic...

Ancestry

Most of the SearchBuilders have ::SearchBuilder as a parent or ancestor. ::SearchBuilder does not exist in any repo: it is generated by Blacklight and modified by Hyrax. Others descend from Blacklight::SearchBuilder, or various other relatives.

::SearchBuilder

The generated parent class SearchBuilder descends from Blacklight::SearchBuilder. As modified by Hyrax's installer, it includes additional modules and overrides. So if your SearchBuilder has ::SearchBuilder as a parent class, you are getting:

This is not a comprehensive list, but it is sufficient to trace some of the complexity of interaction between various layers.

Development: AKA Doing Something Useful

Important note: the default_processor_chain defined by Blacklight::Solr::SearchBuilderBehavior provides a way to extend functionality, but also many possible points of override (namely method names). When you need to do something novel and additional, adding to the chain is completely reasonable. For example:

module MySearchBuilder
  extend ActiveSupport::Concern

  included do
    self.default_processor_chain += [:special_filter]
  end

  def special_filter(solr_parameters)
    solr_parameters[:fq] << "{!field f=some_field_ssim}#{...}"
  end
end

But to the extent that you are overriding (or undoing) something already done, Hyrax::FileSetSearchBuilder is a better example:

module Hyrax
  class FileSetSearchBuilder < ::SearchBuilder
    include Hyrax::SingleResult

    # This overrides the filter_models in FilterByType
    def filter_models(solr_parameters)
      solr_parameters[:fq] << ActiveFedora::SolrQueryBuilder.construct_query_for_rel(has_model: ::FileSet.to_class_uri)
    end
  end
end

There is no point having the other filter_models methods apply :fqs that we then try to undo or overwrite. In general, directly overwriting the whole default_processor_chain or solr parameters like :fq is less flexible than appending constraints sufficient for your use case. In particular, you might find that you have overwritten components that implement access controls, thereby making your SearchBuilder less useful and less secure. When in doubt, examine the actual solr queries produced.