hyrax 3.0.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +22 -0
- data/.dassie/Gemfile +10 -5
- data/.dassie/config/initializers/hyrax.rb +5 -0
- data/.dockerignore +3 -0
- data/.env +0 -1
- data/.rubocop.yml +4 -0
- data/CONTAINERS.md +1 -1
- data/Dockerfile +12 -6
- data/Gemfile +21 -27
- data/app/actors/hyrax/actors/base_actor.rb +1 -1
- data/app/actors/hyrax/actors/create_with_remote_files_actor.rb +85 -63
- data/app/actors/hyrax/actors/create_with_remote_files_ordered_members_actor.rb +7 -42
- data/app/controllers/concerns/hyrax/collections_controller_behavior.rb +20 -8
- data/app/controllers/concerns/hyrax/embargoes_controller_behavior.rb +21 -9
- data/app/controllers/concerns/hyrax/leases_controller_behavior.rb +14 -5
- data/app/controllers/concerns/hyrax/works_controller_behavior.rb +22 -3
- data/app/controllers/hyrax/admin/workflows_controller.rb +8 -2
- data/app/controllers/hyrax/dashboard/collection_members_controller.rb +13 -9
- data/app/controllers/hyrax/dashboard/collections_controller.rb +12 -10
- data/app/controllers/hyrax/file_sets_controller.rb +49 -13
- data/app/controllers/hyrax/permissions_controller.rb +3 -4
- data/app/controllers/hyrax/workflow_actions_controller.rb +3 -1
- data/app/forms/hyrax/forms/collection_form.rb +7 -3
- data/app/forms/hyrax/forms/dashboard/nest_collection_form.rb +24 -2
- data/app/forms/hyrax/forms/file_set_form.rb +46 -0
- data/app/forms/hyrax/forms/permission.rb +23 -0
- data/app/forms/hyrax/forms/permission_template_form.rb +8 -2
- data/app/forms/hyrax/forms/resource_form.rb +10 -17
- data/app/forms/hyrax/forms/work_form.rb +5 -2
- data/app/helpers/hyrax/batch_edits_helper.rb +3 -1
- data/app/helpers/hyrax/collections_helper.rb +88 -2
- data/app/helpers/hyrax/dashboard_helper_behavior.rb +3 -7
- data/app/helpers/hyrax/file_set_helper.rb +25 -6
- data/app/helpers/hyrax/work_form_helper.rb +53 -0
- data/app/indexers/hyrax/administrative_set_indexer.rb +18 -0
- data/app/indexers/hyrax/valkyrie_indexer.rb +3 -3
- data/app/inputs/controlled_vocabulary_input.rb +2 -5
- data/app/jobs/attach_files_to_work_job.rb +19 -10
- data/app/jobs/attach_files_to_work_with_ordered_members_job.rb +6 -5
- data/app/jobs/inherit_permissions_job.rb +9 -5
- data/app/models/admin_set.rb +6 -25
- data/app/models/concerns/hyrax/ability.rb +3 -1
- data/app/models/concerns/hyrax/collection_behavior.rb +17 -44
- data/app/models/concerns/hyrax/file_set/characterization.rb +18 -12
- data/app/models/concerns/hyrax/solr_document_behavior.rb +9 -52
- data/app/models/concerns/hyrax/suppressible.rb +5 -0
- data/app/models/concerns/hyrax/user.rb +9 -3
- data/app/models/hyrax/file_set.rb +6 -0
- data/app/models/hyrax/pcdm_collection.rb +1 -0
- data/app/models/hyrax/permission_template.rb +98 -12
- data/app/models/hyrax/virus_scanner.rb +27 -18
- data/app/models/sipity/agent.rb +1 -0
- data/app/models/sipity/entity.rb +30 -8
- data/app/models/sipity/workflow.rb +1 -0
- data/app/models/sipity.rb +42 -0
- data/app/presenters/hyrax/admin_set_options_presenter.rb +2 -10
- data/app/presenters/hyrax/admin_set_presenter.rb +5 -1
- data/app/presenters/hyrax/admin_set_selection_presenter.rb +116 -0
- data/app/presenters/hyrax/collection_presenter.rb +31 -6
- data/app/presenters/hyrax/file_set_presenter.rb +6 -1
- data/app/presenters/hyrax/file_usage.rb +3 -2
- data/app/presenters/hyrax/stats_usage_presenter.rb +2 -1
- data/app/presenters/hyrax/trophy_presenter.rb +33 -4
- data/app/presenters/hyrax/user_profile_presenter.rb +11 -1
- data/app/presenters/hyrax/version_list_presenter.rb +19 -0
- data/app/presenters/hyrax/version_presenter.rb +3 -2
- data/app/presenters/hyrax/work_show_presenter.rb +25 -4
- data/app/presenters/hyrax/work_usage.rb +5 -3
- data/app/renderers/hyrax/renderers/attribute_renderer.rb +10 -2
- data/app/search_builders/hyrax/admin_set_search_builder.rb +1 -1
- data/app/search_builders/hyrax/my/collections_search_builder.rb +1 -1
- data/app/services/hyrax/admin_set_create_service.rb +3 -1
- data/app/services/hyrax/collections/collection_member_search_service.rb +72 -0
- data/app/services/hyrax/collections/collection_member_service.rb +112 -27
- data/app/services/hyrax/collections/migration_service.rb +4 -2
- data/app/services/hyrax/collections/nested_collection_persistence_service.rb +12 -13
- data/app/services/hyrax/collections/nested_collection_query_service.rb +2 -0
- data/app/services/hyrax/collections/permissions_create_service.rb +6 -4
- data/app/services/hyrax/contextual_path.rb +23 -0
- data/app/services/hyrax/custom_queries/find_file_metadata.rb +7 -5
- data/app/services/hyrax/custom_queries/navigators/parent_collections_navigator.rb +46 -0
- data/app/services/hyrax/edit_permissions_service.rb +27 -20
- data/app/services/hyrax/find_objects_via_solr_service.rb +11 -7
- data/app/services/hyrax/multiple_membership_checker.rb +51 -31
- data/app/services/hyrax/resource_status.rb +7 -0
- data/app/services/hyrax/search_service.rb +4 -2
- data/app/services/hyrax/solr_query_builder_service.rb +29 -6
- data/app/services/hyrax/solr_query_service.rb +224 -0
- data/app/services/hyrax/solr_service.rb +8 -1
- data/app/services/hyrax/statistics/depositors/summary.rb +2 -1
- data/app/services/hyrax/work_uploads_handler.rb +17 -2
- data/app/services/hyrax/workflow/actionable_objects.rb +70 -0
- data/app/services/hyrax/workflow/object_in_workflow_decorator.rb +31 -0
- data/app/services/hyrax/workflow/status_list_service.rb +43 -13
- data/app/views/hyrax/base/_form_relationships.html.erb +1 -2
- data/app/views/hyrax/base/_form_rendering.html.erb +1 -1
- data/app/views/hyrax/base/_form_representative.html.erb +1 -1
- data/app/views/hyrax/base/_form_thumbnail.html.erb +1 -1
- data/app/views/hyrax/base/_guts4form.html.erb +2 -2
- data/app/views/hyrax/base/_representative_media.html.erb +1 -1
- data/app/views/hyrax/base/_show_actions.html.erb +1 -1
- data/app/views/hyrax/dashboard/collections/_form.html.erb +3 -3
- data/app/views/hyrax/dashboard/collections/_list_collections.html.erb +1 -1
- data/app/views/hyrax/dashboard/collections/edit.html.erb +4 -2
- data/app/views/hyrax/dashboard/collections/new.html.erb +4 -2
- data/app/views/hyrax/dashboard/collections/show.html.erb +1 -1
- data/app/views/hyrax/file_sets/edit.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_audio.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_default.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_image.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_office_document.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_pdf.html.erb +1 -1
- data/app/views/hyrax/file_sets/media_display/_video.html.erb +1 -1
- data/app/views/hyrax/file_sets/show.html.erb +1 -1
- data/app/views/hyrax/my/_admin_set_action_menu.html.erb +0 -11
- data/app/views/hyrax/my/_collection_action_menu.html.erb +1 -2
- data/app/views/hyrax/my/collections/_list_collections.html.erb +1 -1
- data/app/views/hyrax/my/collections/_modal_add_subcollection.html.erb +3 -5
- data/bin/solrcloud-assign-configset.sh +8 -5
- data/bin/solrcloud-upload-configset.sh +4 -2
- data/chart/hyrax/Chart.yaml +3 -3
- data/chart/hyrax/README.md +47 -1
- data/chart/hyrax/templates/_helpers.tpl +1 -1
- data/chart/hyrax/templates/configmap-env.yaml +1 -3
- data/chart/hyrax/templates/deployment-worker.yaml +6 -3
- data/chart/hyrax/templates/deployment.yaml +8 -3
- data/chart/hyrax/values.yaml +12 -0
- data/config/brakeman.ignore +2 -2
- data/config/locales/hyrax.de.yml +1 -1
- data/config/locales/hyrax.en.yml +1 -1
- data/config/locales/hyrax.es.yml +1 -1
- data/config/locales/hyrax.fr.yml +1 -1
- data/config/locales/hyrax.it.yml +1 -1
- data/config/locales/hyrax.pt-BR.yml +1 -1
- data/config/locales/hyrax.zh.yml +1 -1
- data/docker-compose.yml +1 -0
- data/documentation/developing-your-hyrax-based-app.md +1 -1
- data/documentation/legacyREADME.md +1 -1
- data/lib/generators/hyrax/templates/config/initializers/hyrax.rb +5 -0
- data/lib/hyrax/active_fedora_dummy_model.rb +62 -0
- data/lib/hyrax/configuration.rb +8 -0
- data/lib/hyrax/engine.rb +1 -0
- data/lib/hyrax/errors.rb +2 -0
- data/lib/hyrax/specs/capybara.rb +3 -1
- data/lib/hyrax/specs/shared_specs/valkyrie_storage_versions.rb +9 -0
- data/lib/hyrax/transactions/container.rb +21 -0
- data/lib/hyrax/transactions/file_set_destroy.rb +21 -0
- data/lib/hyrax/transactions/steps/add_file_sets.rb +3 -2
- data/lib/hyrax/transactions/steps/add_to_parent.rb +36 -0
- data/lib/hyrax/transactions/steps/remove_file_set_from_work.rb +47 -0
- data/lib/hyrax/transactions/work_create.rb +2 -1
- data/lib/hyrax/valkyrie_can_can_adapter.rb +1 -0
- data/lib/hyrax/version.rb +1 -1
- data/lib/hyrax.rb +9 -0
- data/lib/tasks/collection_type_global_id.rake +1 -1
- data/lib/tasks/regenerate_derivatives.rake +12 -0
- data/lib/wings/orm_converter.rb +18 -2
- data/lib/wings/setup.rb +1 -0
- data/lib/wings/valkyrie/storage.rb +56 -1
- data/template.rb +1 -1
- metadata +17 -2
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module Hyrax
|
|
3
|
+
# @deprecated This class is being replaced by Hyrax::SolrQueryService #get_objects method.
|
|
4
|
+
#
|
|
3
5
|
# Methods in this class search solr to get the ids and then use the query service to find the objects.
|
|
4
6
|
class FindObjectsViaSolrService
|
|
5
7
|
class_attribute :solr_query_builder, :solr_service, :query_service
|
|
6
|
-
self.solr_query_builder = Hyrax::
|
|
8
|
+
self.solr_query_builder = Hyrax::SolrQueryService
|
|
7
9
|
self.solr_service = Hyrax::SolrService
|
|
8
10
|
self.query_service = Hyrax.query_service
|
|
9
11
|
|
|
@@ -14,13 +16,15 @@ module Hyrax
|
|
|
14
16
|
# @param join_with [String] the value we're joining the clauses with (default: ' OR ' for backward compatibility with ActiveFedora where)
|
|
15
17
|
# @param type [String] The type of query to run. Either 'raw' or 'field' (default: 'field')
|
|
16
18
|
# @param use_valkyrie [Boolean] if true, return Valkyrie resource(s); otherwise, return ActiveFedora object(s)
|
|
17
|
-
# @return [Array<ActiveFedora|Valkyrie::Resource>] objects matching the query
|
|
19
|
+
# @return [Array<ActiveFedora::Base|Valkyrie::Resource>] objects matching the query
|
|
18
20
|
def find_for_model_by_field_pairs(model:, field_pairs:, join_with: ' OR ', type: 'field', use_valkyrie: Hyrax.config.use_valkyrie?)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
Deprecation.warn("'##{__method__}' will be removed in Hyrax 4.0. " \
|
|
22
|
+
"Instead, use 'Hyrax::SolrQueryService.new.with_model(...).with_field_pairs(...).get_objects'.")
|
|
23
|
+
|
|
24
|
+
solr_query_builder.new
|
|
25
|
+
.with_model(model: model)
|
|
26
|
+
.with_field_pairs(field_pairs: field_pairs, join_with: join_with, type: type)
|
|
27
|
+
.get_objects(use_valkyrie: use_valkyrie).to_a
|
|
24
28
|
end
|
|
25
29
|
end
|
|
26
30
|
end
|
|
@@ -6,8 +6,7 @@ module Hyrax
|
|
|
6
6
|
class MultipleMembershipChecker
|
|
7
7
|
attr_reader :item
|
|
8
8
|
|
|
9
|
-
# @param [#member_of_collection_ids] item an object that belongs to
|
|
10
|
-
# collections
|
|
9
|
+
# @param [#member_of_collection_ids] item an object that belongs to collections
|
|
11
10
|
def initialize(item:)
|
|
12
11
|
@item = item
|
|
13
12
|
end
|
|
@@ -20,57 +19,78 @@ module Hyrax
|
|
|
20
19
|
# `allow_multiple_membership` as `false` require that its members do not
|
|
21
20
|
# also belong to other collections of the same type.
|
|
22
21
|
#
|
|
23
|
-
# There are two contexts in which memberships are checked: when doing a
|
|
24
|
-
# wholesale replacement and when making an incremental change, such as
|
|
25
|
-
# adding a single collection membership to an object. In the former case,
|
|
26
|
-
# `#check` only scans the passed-in collection identifiers. In the latter,
|
|
27
|
-
# `#check` must also scan the collections to which an object currently
|
|
28
|
-
# belongs for potential conflicts.
|
|
29
|
-
#
|
|
30
22
|
# @param collection_ids [Array<String>] a list of collection identifiers
|
|
31
|
-
# @param include_current_members [Boolean]
|
|
32
|
-
#
|
|
23
|
+
# @param include_current_members [Boolean] if true, include item's existing
|
|
24
|
+
# collections in check; else if false, check passed in collections only
|
|
25
|
+
# * use `false` when collection_ids includes proposed new collections and existing
|
|
26
|
+
# collections (@see Hyrax::Actors::CollectionsMembershipActor #valid_membership?)
|
|
27
|
+
# * use `true` when collection_ids includes proposed new collections only
|
|
28
|
+
# (@see Hyrax::Collections::CollectionMemberService #add_member)
|
|
33
29
|
#
|
|
34
30
|
# @return [nil, String] nil if no conflicts; an error message string if so
|
|
35
31
|
def check(collection_ids:, include_current_members: false)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
collections_to_check =
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
problematic_collections = collections_to_check.uniq(&:id)
|
|
45
|
-
.group_by(&:collection_type_gid)
|
|
46
|
-
.select { |_gid, list| list.count > 1 }
|
|
47
|
-
return if problematic_collections.blank?
|
|
32
|
+
return unless single_membership_collection_types_exist?
|
|
33
|
+
|
|
34
|
+
proposed_single_membership_collections = filter_to_single_membership_collections(collection_ids)
|
|
35
|
+
return if proposed_single_membership_collections.blank?
|
|
36
|
+
|
|
37
|
+
collections_to_check = collections_to_check(proposed_single_membership_collections,
|
|
38
|
+
include_current_members)
|
|
39
|
+
problematic_collections = check_collections(collections_to_check)
|
|
48
40
|
build_error_message(problematic_collections)
|
|
49
41
|
end
|
|
50
42
|
|
|
51
43
|
private
|
|
52
44
|
|
|
53
|
-
def
|
|
54
|
-
|
|
45
|
+
def single_membership_collection_types_exist?
|
|
46
|
+
single_membership_collection_types_gids.present?
|
|
47
|
+
end
|
|
55
48
|
|
|
49
|
+
def single_membership_collection_types_gids
|
|
50
|
+
@single_membership_collection_types_gids ||=
|
|
51
|
+
Hyrax::CollectionType.gids_that_do_not_allow_multiple_membership&.map(&:to_s)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def filter_to_single_membership_collections(collection_ids)
|
|
55
|
+
return [] if collection_ids.blank?
|
|
56
56
|
field_pairs = {
|
|
57
|
-
|
|
58
|
-
Hyrax.config.collection_type_index_field.to_sym => collection_type_gids_that_disallow_multiple_membership
|
|
57
|
+
Hyrax.config.collection_type_index_field.to_sym => single_membership_collection_types_gids
|
|
59
58
|
}
|
|
60
|
-
Hyrax::
|
|
59
|
+
Hyrax::SolrQueryService.new
|
|
60
|
+
.with_generic_type(generic_type: "Collection")
|
|
61
|
+
.with_ids(ids: Array[collection_ids])
|
|
62
|
+
.with_field_pairs(field_pairs: field_pairs, join_with: ' OR ')
|
|
63
|
+
.get_objects(use_valkyrie: true).to_a
|
|
61
64
|
end
|
|
62
65
|
|
|
63
|
-
def
|
|
64
|
-
|
|
66
|
+
def collections_to_check(proposed, include_current_members)
|
|
67
|
+
# ActorStack does a wholesale collection membership replacement, such that
|
|
68
|
+
# proposed collections include existing and new collections. Parameter
|
|
69
|
+
# `include_current_members` will be false when coming from the actor stack
|
|
70
|
+
# to prevent member items being passed in and then added here as well.
|
|
71
|
+
return proposed unless include_current_members
|
|
72
|
+
proposed | filter_to_single_membership_collections(item.member_of_collection_ids)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def check_collections(collections_to_check)
|
|
76
|
+
# uniq insures we include a collection only once when it is in the list multiple
|
|
77
|
+
# group_by groups collections of the same collection type together
|
|
78
|
+
# select keeps only collection type groups that have more than one collection
|
|
79
|
+
# of the single collection type
|
|
80
|
+
collections_to_check.uniq(&:id)
|
|
81
|
+
.group_by(&:collection_type_gid)
|
|
82
|
+
.select { |_gid, list| list.count > 1 }
|
|
65
83
|
end
|
|
66
84
|
|
|
67
85
|
def build_error_message(problematic_collections)
|
|
86
|
+
return if problematic_collections.blank?
|
|
68
87
|
error_message_clauses = problematic_collections.map do |gid, list|
|
|
69
88
|
I18n.t('hyrax.admin.collection_types.multiple_membership_checker.error_type_and_collections',
|
|
70
89
|
type: collection_type_title_from_gid(gid),
|
|
71
90
|
collections: collection_titles_from_list(list))
|
|
72
91
|
end
|
|
73
|
-
"#{I18n.t('hyrax.admin.collection_types.multiple_membership_checker.error_preamble')}
|
|
92
|
+
"#{I18n.t('hyrax.admin.collection_types.multiple_membership_checker.error_preamble')}" \
|
|
93
|
+
"#{error_message_clauses.join('; ')}"
|
|
74
94
|
end
|
|
75
95
|
|
|
76
96
|
def collection_type_title_from_gid(gid)
|
|
@@ -36,6 +36,13 @@ module Hyrax
|
|
|
36
36
|
self.resource = resource
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
##
|
|
40
|
+
# @param [#state] resource
|
|
41
|
+
# @return [Boolean]
|
|
42
|
+
def self.inactive?(resource:)
|
|
43
|
+
new(resource: resource).inactive?
|
|
44
|
+
end
|
|
45
|
+
|
|
39
46
|
##
|
|
40
47
|
# @return [Boolean]
|
|
41
48
|
# @raise [NoMethodError] if the resource doesn't have a state attribute
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# SearchService returns search results from the repository
|
|
4
3
|
module Hyrax
|
|
5
|
-
|
|
4
|
+
##
|
|
5
|
+
# Returns search results from the repository.
|
|
6
|
+
#
|
|
7
|
+
# @note Adapted from Blacklight 7
|
|
6
8
|
class SearchService
|
|
7
9
|
def initialize(config:, user_params: nil, search_builder_class: config.search_builder_class, **context)
|
|
8
10
|
@blacklight_config = config
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module Hyrax
|
|
3
|
+
##
|
|
4
|
+
# @deprecated
|
|
5
|
+
# This class is being replaced by Hyrax::SolrQueryService.
|
|
6
|
+
#
|
|
3
7
|
# Methods in this class are from/based on ActiveFedora::SolrQueryBuilder
|
|
8
|
+
#
|
|
9
|
+
# @see Hyrax::SolrQueryService
|
|
4
10
|
class SolrQueryBuilderService
|
|
5
11
|
class << self
|
|
6
12
|
# Construct a solr query for a list of ids
|
|
7
13
|
# This is used to get a solr response based on the list of ids in an object's RELS-EXT relationhsips
|
|
8
14
|
# If the id_array is empty, defaults to a query of "id:NEVER_USE_THIS_ID", which will return an empty solr response
|
|
9
15
|
# @param [Array] id_array the ids that you want included in the query
|
|
16
|
+
# @return [String] a solr query
|
|
17
|
+
# @example
|
|
18
|
+
# construct_query_for_ids(['a1', 'b2'])
|
|
19
|
+
# # => "{!terms f=id}a1,b2"
|
|
20
|
+
# @deprecated
|
|
10
21
|
def construct_query_for_ids(id_array)
|
|
22
|
+
Deprecation.warn("'##{__method__}' will be removed in Hyrax 4.0. " \
|
|
23
|
+
"Instead, use 'Hyrax::SolrQueryService.new.with_ids'.")
|
|
11
24
|
ids = id_array.reject(&:blank?)
|
|
12
25
|
return "id:NEVER_USE_THIS_ID" if ids.empty?
|
|
13
|
-
|
|
26
|
+
Hyrax::SolrQueryService.new.with_ids(ids: id_array).build
|
|
14
27
|
end
|
|
15
28
|
|
|
16
29
|
# Construct a solr query from a list of pairs (e.g. [field name, values])
|
|
@@ -21,14 +34,19 @@ module Hyrax
|
|
|
21
34
|
# @example
|
|
22
35
|
# construct_query([['library_id_ssim', '123'], ['owner_ssim', 'Fred']])
|
|
23
36
|
# # => "_query_:\"{!field f=library_id_ssim}123\" AND _query_:\"{!field f=owner_ssim}Fred\""
|
|
37
|
+
# @deprecated
|
|
24
38
|
def construct_query(field_pairs, join_with = default_join_with, type = 'field')
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
Deprecation.warn("'##{__method__}' will be removed in Hyrax 4.0. " \
|
|
40
|
+
"Instead, use 'Hyrax::SolrQueryService.new.with_field_pairs'.")
|
|
41
|
+
Hyrax::SolrQueryService.new.with_field_pairs(field_pairs: field_pairs,
|
|
42
|
+
join_with: join_with,
|
|
43
|
+
type: type).build
|
|
29
44
|
end
|
|
30
45
|
|
|
46
|
+
# @deprecated
|
|
31
47
|
def default_join_with
|
|
48
|
+
Deprecation.warn("'##{__method__}' will be removed in Hyrax 4.0. " \
|
|
49
|
+
"There will not be a replacement for this method. See Hyrax::SolrQueryService which is replacing this class.")
|
|
32
50
|
' AND '
|
|
33
51
|
end
|
|
34
52
|
|
|
@@ -41,9 +59,14 @@ module Hyrax
|
|
|
41
59
|
# @example
|
|
42
60
|
# construct_query(Collection, [['library_id_ssim', '123'], ['owner_ssim', 'Fred']])
|
|
43
61
|
# # => "_query_:\"{!field f=has_model_ssim}Collection\" AND _query_:\"{!field f=library_id_ssim}123\" AND _query_:\"{!field f=owner_ssim}Fred\""
|
|
62
|
+
# @deprecated
|
|
44
63
|
def construct_query_for_model(model, field_pairs, join_with = default_join_with, type = 'field')
|
|
64
|
+
Deprecation.warn("'##{__method__}' will be removed in Hyrax 4.0. " \
|
|
65
|
+
"Instead, use 'Hyrax::SolrQueryService.new.with_model'.")
|
|
45
66
|
field_pairs["has_model_ssim"] = model.to_s
|
|
46
|
-
|
|
67
|
+
Hyrax::SolrQueryService.new.with_field_pairs(field_pairs: field_pairs,
|
|
68
|
+
join_with: join_with,
|
|
69
|
+
type: type).build
|
|
47
70
|
end
|
|
48
71
|
|
|
49
72
|
private
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Hyrax
|
|
3
|
+
##
|
|
4
|
+
# Supports building and executing a solr query.
|
|
5
|
+
#
|
|
6
|
+
# @note Methods in this class are providing functionality previously supported by
|
|
7
|
+
# ActiveFedora::SolrQueryBuilder.
|
|
8
|
+
class SolrQueryService < ::SearchBuilder # rubocop:disable Metrics/ClassLength
|
|
9
|
+
class_attribute :query_service
|
|
10
|
+
self.query_service = Hyrax.query_service
|
|
11
|
+
|
|
12
|
+
attr_reader :query, :solr_service
|
|
13
|
+
|
|
14
|
+
def initialize(query: [], solr_service: Hyrax::SolrService)
|
|
15
|
+
@query = query
|
|
16
|
+
@solr_service = solr_service
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
# @api private
|
|
21
|
+
# @see Blacklight::Configuration#document_model
|
|
22
|
+
#
|
|
23
|
+
# @return [Class] the model class to use for solr documents
|
|
24
|
+
def self.document_model
|
|
25
|
+
CatalogController.blacklight_config.document_model
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# @return [Hash] the results returned from solr for the current query
|
|
30
|
+
def get
|
|
31
|
+
solr_service.get(build)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
##
|
|
35
|
+
# @return [Enumerable<SolrDocument>]
|
|
36
|
+
def solr_documents
|
|
37
|
+
get['response']['docs'].map { |doc| self.class.document_model.new(doc) }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
# @return [Array<String>] ids of documents matching the current query
|
|
42
|
+
def get_ids # rubocop:disable Naming/AccessorMethodName
|
|
43
|
+
results = get
|
|
44
|
+
results['response']['docs'].map { |doc| doc['id'] }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# @return [Array<Valkyrie::Resource|ActiveFedora::Base>] objects matching the current query
|
|
49
|
+
def get_objects(use_valkyrie: Hyrax.config.use_valkyrie?)
|
|
50
|
+
ids = get_ids
|
|
51
|
+
return ids.map { |id| ActiveFedora::Base.find(id) } unless use_valkyrie
|
|
52
|
+
query_service.find_many_by_ids(ids: ids)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# @return [Integer] the number of results that match the query in solr
|
|
57
|
+
def count
|
|
58
|
+
solr_service.count(build)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# @return [String] the combined query that can be submitted to solr
|
|
63
|
+
def build
|
|
64
|
+
return 'id:NEVER_USE_THIS_ID' if @query.blank? # forces this method to always return a valid solr query
|
|
65
|
+
@query.join(' AND ')
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# @return [Hyrax::SolrQueryService] the existing service with the query reset to empty
|
|
70
|
+
def reset
|
|
71
|
+
@query = []
|
|
72
|
+
self
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
##
|
|
76
|
+
# @param ids [Array] id_array the ids that you want included in the query
|
|
77
|
+
# @return [Hyrax::SolrQueryService] the existing service with id query appended
|
|
78
|
+
def with_ids(ids: [])
|
|
79
|
+
ids = ids.reject(&:blank?)
|
|
80
|
+
raise ArgumentError, "Expected there to be at least one non-blank id." if ids.blank?
|
|
81
|
+
id_query = construct_query_for_ids(ids)
|
|
82
|
+
@query += [id_query]
|
|
83
|
+
self
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
##
|
|
87
|
+
# @param model [#to_s] a class from the model (e.g. Hyrax::Work, Hyrax::FileSet, etc.)
|
|
88
|
+
# @return [SolrQueryService] the existing service with model query appended
|
|
89
|
+
def with_model(model:)
|
|
90
|
+
model_query = construct_query_for_model(model)
|
|
91
|
+
@query += [model_query]
|
|
92
|
+
self
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
##
|
|
96
|
+
# @param generic_type [String] (Default: Work)
|
|
97
|
+
# @return [SolrQueryService] the existing service with model query appended
|
|
98
|
+
def with_generic_type(generic_type: 'Work')
|
|
99
|
+
# TODO: Generic type was originally stored as `sim`. Since it is never multi-valued, it is moving to being stored
|
|
100
|
+
# as `si`. Until a migration is created to correct existing solr docs, this query searches in both fields.
|
|
101
|
+
field_pairs = { generic_type_si: generic_type, generic_type_sim: generic_type }
|
|
102
|
+
type_query = construct_query_for_pairs(field_pairs, ' OR ', 'field')
|
|
103
|
+
@query += [type_query]
|
|
104
|
+
self
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# @param field_pairs [Hash] a list of pairs of property name and values (e.g. { field1: values, field2: values })
|
|
109
|
+
# @param join_with [String] the connector used to join the field pairs (default: ' AND ')
|
|
110
|
+
# @param type [String] type of query to run. Either 'raw' or 'field' (default: 'field')
|
|
111
|
+
# @return [SolrQueryService] the existing service with field_pair query appended
|
|
112
|
+
def with_field_pairs(field_pairs: {}, join_with: default_join_with, type: 'field')
|
|
113
|
+
pairs_query = construct_query_for_pairs(field_pairs, join_with, type)
|
|
114
|
+
return self if pairs_query.blank?
|
|
115
|
+
@query += [pairs_query]
|
|
116
|
+
self
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
##
|
|
120
|
+
# @param ability [???] the user's abilities
|
|
121
|
+
# @param action [Symbol] the action the user is taking (e.g. :index, :edit, :show, etc.) (default: :index)
|
|
122
|
+
# @return [SolrQueryService] the existing service with access filters query appended
|
|
123
|
+
def accessible_by(ability:, action: :index)
|
|
124
|
+
access_filters_query = construct_query_for_ability(ability, action)
|
|
125
|
+
@query += [access_filters_query] if access_filters_query.present?
|
|
126
|
+
self
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
# Construct a solr query for a list of ids
|
|
132
|
+
# @param [Array] ids to be included in the query
|
|
133
|
+
# @return [String] a solr query
|
|
134
|
+
# @example
|
|
135
|
+
# construct_query_for_ids(['id1', 'id2'])
|
|
136
|
+
# # => "{!terms f=id}id1,id2"
|
|
137
|
+
def construct_query_for_ids(ids)
|
|
138
|
+
"{!terms f=#{Hyrax.config.id_field}}#{ids.join(',')}"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Construct a solr query from a list of pairs (e.g. { field1: values, field2: values })
|
|
142
|
+
# @param [Hash] field_pairs a list of pairs of property name and values
|
|
143
|
+
# @param [String] join_with the value we're joining the clauses with (default: ' AND ')
|
|
144
|
+
# @param [String] type of query to run. Either 'raw' or 'field' (default: 'field')
|
|
145
|
+
# @return [String] a solr query
|
|
146
|
+
# @example
|
|
147
|
+
# construct_query([['library_id_ssim', '123'], ['owner_ssim', 'Fred']])
|
|
148
|
+
# # => "_query_:\"{!field f=library_id_ssim}123\" AND _query_:\"{!field f=owner_ssim}Fred\""
|
|
149
|
+
def construct_query_for_pairs(field_pairs, join_with = default_join_with, type = 'field')
|
|
150
|
+
clauses = pairs_to_clauses(field_pairs, type)
|
|
151
|
+
return "" if clauses.count.zero?
|
|
152
|
+
return clauses.first if clauses.count == 1
|
|
153
|
+
"(#{clauses.join(join_with)})"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Construct a solr query from the model (e.g. Collection, Monograph)
|
|
157
|
+
# @param [Class] model class
|
|
158
|
+
# @return [String] a solr query
|
|
159
|
+
# @example
|
|
160
|
+
# construct_query_for_model(Monograph)
|
|
161
|
+
# # => "_query_:\"{!field f=has_model_ssim}Monograph\""
|
|
162
|
+
def construct_query_for_model(model)
|
|
163
|
+
field_pairs = { "has_model_ssim" => model.to_s }
|
|
164
|
+
construct_query_for_pairs(field_pairs)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Construct a solr query based on a User's abilities and the action they taking
|
|
168
|
+
# @param ability [???] the user's abilities
|
|
169
|
+
# @param action [Symbol] the action the user is taking (e.g. :index, :edit, :show, etc.) (default: :index)
|
|
170
|
+
# @return [String] a solr query
|
|
171
|
+
# @example
|
|
172
|
+
# construct_query_for_ability(user, :edit)
|
|
173
|
+
# # => "(({!terms f=edit_access_group_ssim}public,user_group_A}) OR " \
|
|
174
|
+
# "edit_access_person_ssim:#{user@example.com})"
|
|
175
|
+
def construct_query_for_ability(ability, action)
|
|
176
|
+
permission_types = case action
|
|
177
|
+
when :index then [:discover, :read, :edit]
|
|
178
|
+
when :show, :read then [:read, :edit]
|
|
179
|
+
when :update, :edit, :create, :new, :destroy then [:edit]
|
|
180
|
+
end
|
|
181
|
+
filters = gated_discovery_filters(permission_types, ability).join(' OR ')
|
|
182
|
+
return "" if filters.blank?
|
|
183
|
+
"(#{filters})"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def default_join_with
|
|
187
|
+
' AND '
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @param [Array<Array>] pairs a list of (key, value) pairs. The value itself may
|
|
191
|
+
# @param [String] type The type of query to run. Either 'raw' or 'field'
|
|
192
|
+
# @return [Hash] a list of solr clauses
|
|
193
|
+
def pairs_to_clauses(pairs, type)
|
|
194
|
+
pairs.flat_map do |field, value|
|
|
195
|
+
condition_to_clauses(field, value, type)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# @param [String] field
|
|
200
|
+
# @param [String, Array<String>] values
|
|
201
|
+
# @param [String] type The type of query to run. Either 'raw' or 'field'
|
|
202
|
+
# @return [Array<String>]
|
|
203
|
+
def condition_to_clauses(field, values, type)
|
|
204
|
+
values = Array(values)
|
|
205
|
+
values << nil if values.empty?
|
|
206
|
+
values.map do |value|
|
|
207
|
+
if value.present?
|
|
208
|
+
query_clause(type, field, value)
|
|
209
|
+
else
|
|
210
|
+
# Check that the field is not present. In SQL: "WHERE field IS NULL"
|
|
211
|
+
"-#{field}:[* TO *]"
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Create a raw query clause suitable for sending to solr as an fq element
|
|
217
|
+
# @param [String] type The type of query to run. Either 'raw' or 'field'
|
|
218
|
+
# @param [String] key
|
|
219
|
+
# @param [String] value
|
|
220
|
+
def query_clause(type, key, value)
|
|
221
|
+
"_query_:\"{!#{type} f=#{key}}#{value.gsub('"', '\"')}\""
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|