Overview

Blacklight generates app/models/search_builder.rb into your application. It defaults to including Blacklight::Solr::SearchBuilderBehavior which are functions that allow Blacklight to talk to Solr (vs another search engine). Samvera generates include Hydra::AccessControlsEnforcement for filtering searches by access (groups & users). Finally Hyrax generates include Hyrax::SearchFilters which overrides the Samvera filters and allows users with the admin role to see all documents.

Hyrax then sets the blacklight_config.config.search_builder_class = Hyrax::CatalogSearchBuilder in the catalog controller. This enables the public search to not show works that are suppressed (in workflow) and to find a work if an attached file matches the search query.

Search Builders

Building searches is a core component of any Blacklight app. The app/search_builders/ 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 CurationConcerns. Others descend from Blacklight::SearchBuilder, or various other relatives.

::SearchBuilder

The generated parent class SearchBuilder descends from Blacklight::SearchBuilder. As modified by the Hyrax 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 models in FilterByType
    def models
      [::FileSet]
    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.

More Information

Blacklight Quickstart

Solr Quickstart

Legacy

Blacklight Search Results, hydra-head version 6.0.0

Gated Discovery Filter Search Results, permissions-based, hydra-head versions 6.4.0 and 7.2.2

Filtering Search Results with Hydra Access Controls, 2012