To connect to production Solr
ssh -L 8985:sul-solr-prod-a.stanford.edu:80 [email protected]
In a separate terminal window:
SETTINGS__SOLR__URL=http://localhost:8985/solr/argo_prod bin/setup
Note that bin/setup will create the database, run yarn, and perform other setup tasks.
The roles, email address, and name of the test user can be provided in environment variables. Defaults are set in bin/dev.
To run all configured linters, run bin/rake lint.
To run linters individually, run which ones you need:
- Ruby code:
bin/rubocop(add-aflag to autocorrect violations) - ERB templates:
bin/erb_lint --lint-all --format compact(add-aflag to autocorrect violations) - ERB templates:
bin/herb analyze app --no-log-file - JavaScript code:
yarn run lint(add--fixflag to autocorrect violations) - SCSS stylesheets:
yarn run stylelint(add--fixflag to autocorrect violations)
A dashboard for SolidQueue background jobs is available at http://localhost:3000/jobs
NOTE: The application is deployed continuously by our on-prem Jenkins service (sul-ci-prod) to the poc environment on every merge to main. See Jenkinsfile for how that is wired up.
To reset Solr before and after a test, mark the test as :solr. For example:
RSpec.describe 'My test', :solr do
Solr document test fixtures can be created with the Solr factories. For example:
let!(:solr_doc) { create(:solr_item) }
In addition to supporting discovery of items (DROs, collections, and admin policies), the discovery system:
- Supports search of field values. So, for example, in addition to returning a list of item results, a search from the home page will also return a list of matching projects. (This is a list of projects that match the query, not project facets.)
- Is optimized for slow searching / faceting (1) by asynchronously loading some search results and facets (2) by splitting up searching for item results and a small number of primary facets from the rest of the facets (secondary facets)/
The following will help illustrate the discovery system components involved for a search from the home page:
- The search form is rendered from
SearchForm. - The user enters a query in the search form and starts the search.
- The page is rendered with:
- An async turbo frame for items and primary facets.
- An async turbo frame for secondary facets.
- Async turbo frames for each of field value types (e.g., projects).
- Empty divs for each non-lazy (fast!) facet (e.g., object types).
- Async turbo frames for each lazy (slow!) facet (e.g., project tags).
- The items async turbo frame for items calls
Search::ItemsController.index. This invokes the items searcher (Searchers::Item) which queries Solr and returnsSearchResults::Items(a wrapper around the Solr response). The rendered response includes:
- The item search results
- Turbo stream replace elements (
<turbo-stream action="replace">) for the primary facets containing the facet content. When rendering the page, Turbo replaces the empty divs with the facet content.
- Concurrently, the secondary facets async turbo frame calls
Search::ItemsController.secondary_facets. This invokes the secondary facets searcher (Searchers::SecondaryFacet) which queries Solr and returnsSearchResults::Items. The rendered response includes turbo stream replace elements for the secondary facets containing the facet content. - Concurrently, each of the async field value turbo frames calls the appropriate search controller (e.g.,
Search::ProjectsController.index). This invokes the appropriate searcher (e.g.,Searchers::Project) which queries Solr and returnsSearchResults::FacetValues(a wrapper around the Solr response). The rendered response includes the field value search results (e.g., a list of projects). - Concurrently, each of the lazy facet async turbo frames calls the appropriate endpoint on the
Search::FacetsController(e.g.,project_tagsfor the projects facet). This invokes the facets searcher (Searchers::Facet) which queries Solr and returnsSearchResults::FacetCounts(a wrapper around the Solr response). The rendered response includes the facet content.
Notes:
- On the home page, items AND field values are searched. Once the user has selected facets, ONLY items are searched.
- Putting Turbo stream replace elements directly in HTML is not a typical pattern for turbo streams.
To view the Solr response for all Solr requests made to render a page, add debug=true to the URL.
The Solr requests will be executed with debugQuery=true, so the response will include debugging informations
including the amount of time to execute each part of the query / each facet.
The lazy async pattern should be used for slow facets. Each of these facets involves a separate query to Solr.
- Add an attribute for the facet to
SearchForm. - Add any new solr fields to
Search::Fields. - Add a
Search::LoadingFacetFrameComponentfor the facet toSearch::FacetsSectionComponent. This adds a placeholderturbo-framethat will be replaced with the facet content. - Add a new
*_facetsresource toroutes.rbproviding theindexroute. See for example,:tag_facets. - Add a new
Search::*FacetsControllerand add a request spec. See for example,Search::TagFacetsController. This should implement theindexmethod. - Add a configuration constant to
Search::Facets. This must include theform_field,field, andfacet_path_helperattributes. - Add the facet to
Search::ItemQueryBuilder::FACETS. - Optionally, add a label for the facet to
en.yml.
Note:
- Currently, the only lazy async facets that are supported are for hierarchical facets. However, additional types could be supported by provided alternatives to
Search::HierarchicalFacetFrameComponent(which is rendered inSearch::*FacetsController.index)
The non-lazy sync pattern should be used for fast facets. The facet values are retrieved as part of the main query to Solr (i.e., the query that returns the search results).
- Add an attribute for the facet to
SearchForm. - Add any new solr fields to
Search::Fields. - Add a configuration constant to
Search::Facets. This must include theform_fieldandfieldattributes. - Add a
Search::LoadingFacetDivComponentfor the facet toSearch::FacetsSectionComponent. This adds a placeholderdivthat will be replaced with the facet content. - Add the facet to the Solr request in
Searchers::Item::FACETSorSearchers::SecondaryFacet::FACETS. - Add a turbo stream replace element (
Search::FacetTurboStreamReplaceComponent) for the facet toviews/search/items/index.html.erborviews/search/items/secondary_facets.html.erb. This allows specifying the type of facet component to use to render the facet (e.g., aSearch::CheckboxFacetComponent). - Add the facet to
Search::ItemQueryBuilder::FACETS. - Optionally, add a label for the facet to
en.yml.
- Add a new
*_facetsresource toroutes.rb. See for example,:mimetype_facets. This only needs to provide anindexroute. - Add a new
Search::*FacetsControllerand add a request spec. See for example,Search::MimetypeFacetsController. This should implement theindexmethod by includingFacetPagingConcern. - Add
facet_path_helperto the configuration constant inSearch::Facets.
Note:
- Some of these steps may already have been performed, e.g., for a lazy, async facet.
- Add a new
*_facetsresource toroutes.rb. See for example,:project_facets. This should provide asearchroute. - Add a new
Search::*FacetsControllerand add a request spec. See for example,Search::ProjectFacetsController. This should implement thesearchmethod by includingFacetSearchingConcern. - Add
facet_search_path_helperto the configuration constant inSearch::Facets.
Note:
- Some of these steps may already have been performed, e.g., for a lazy, async facet.
- Set
exclude: truein the configuration constant inSearch::Facets.
Note:
- The default is to only return the facet values for items that match the query.
- This is a good candidate for a
Search::CheckboxFacetComponent, e.g., for object types.
- Add a new
*_facetsresource toroutes.rb. See for example,:tag_facets. This should provide thechildrenroute. - Add a new
Search::*FacetsControllerand add a request spec. See for example,Search::TagFacetsController. This should implement thechildrenmethod. - Add
facet_children_path_helperandhierarchical_fieldto the configuration constant inSearch::Facets. - Change the facet to be rendered with the hierarchical facet component. For a lazy async facet, render a
Search::HierarchicalFacetFrameComponentinSearch::*FacetsController.index. For a non-lazy sync facet, set the turbo stream replace element inviews/search/items/index.html.erbto render aSearch::HierarchicalFacetComponent.
Note:
- Hierarchical faceting requires 2 separate fields, each which has a specific format.
Dynamic facets have facet values that are the result of a specified query.
- Add
dynamic_facetto the configuration constant inSearch::Facets. - When adding the facet to the Solr request in
Searchers::Item.facet_jsonuse aSearch::DynamicFacetBuilder. - When adding a method to
SearchResults::Items, return aSearchResults::DynamicFacetCountsfor the facet. - When adding a turbo stream replace element (
Search::FacetTurboStreamReplaceComponent) for the facet toviews/search/items/index.html.erbuse aSearch::DynamicFacetComponent. - When adding the facet to
Search::ItemQueryBuilder.filter_queries, calldynamic_facet_filter_query().
Dynamic facets may optionally have a date range filter (where the user specifies a date from and/or date to). See, for example, the "Earliest accessioned" facet.
- Add
*_fromand*_toattributes toSearchForm. For example:
attribute :earliest_accessioned_date_from, :date, default: nil
attribute :earliest_accessioned_date_to, :date, default: nil
- Add
date_from_form_field,date_to_form_field, andfieldto the configuration constant inSearch::Facets. - When adding a turbo stream replace element (
Search::FacetTurboStreamReplaceComponent) for the facet toviews/search/items/index.html.erbprovide thedate_from_form_fieldanddate_to_form_fieldtoSearch::DynamicFacetComponent
Currently, excluding is only available for basic facets (i.e., not hierarchical, dynamic, checkbox, etc.).
- Add an attribute (
*_exclude) for the facet exclude toSearchForm. - Assign the attribute name to
exclude_form_fieldfor the configuration constant inSearch::Facets.
- Add any new solr fields to
Search::Fields. - Add the field to
flinSearchers::Item.solr_request. - Possibly add a method to
SearchResults::Item. See description of how missing methods are handled. - Display the field in
Search::ItemResultComponent.
- Add any new solr fields to
Search::Fields. - Add a sort constant for the sort config to
Search::SortOptions - Add the new search, in the expected order to the
sort_optionsinSearch::SortComponent
- Add a job that is a subclass of
BulkActionJoband a test. - Add a new resource to
routes.rbunder thebulk_actionsnamespace. - Add configuration to
services/bulk_actions.rb. - Add the bulk action to the list of bulk actions in
views/bulk_actions/new.html.erb. - Add a controller for the bulk action that is a subclass of
BulkActionApplicationController. - Add a
new.html.erbview. - Add a system test. (The job can be stubbed out.)