active-fedora 9.4.3 → 9.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/History.txt +13 -0
- data/lib/active_fedora.rb +1 -0
- data/lib/active_fedora/association_hash.rb +117 -0
- data/lib/active_fedora/associations.rb +28 -25
- data/lib/active_fedora/associations/basic_contains_association.rb +14 -2
- data/lib/active_fedora/associations/builder/contains.rb +6 -1
- data/lib/active_fedora/associations/id_composite.rb +4 -1
- data/lib/active_fedora/core.rb +12 -0
- data/lib/active_fedora/errors.rb +4 -0
- data/lib/active_fedora/file.rb +5 -2
- data/lib/active_fedora/files_hash.rb +1 -68
- data/lib/active_fedora/fixity_service.rb +13 -4
- data/lib/active_fedora/persistence.rb +18 -9
- data/lib/active_fedora/reflection.rb +5 -1
- data/lib/active_fedora/relation/calculations.rb +2 -2
- data/lib/active_fedora/relation/finder_methods.rb +25 -36
- data/lib/active_fedora/relation/query_methods.rb +5 -3
- data/lib/active_fedora/solr_query_builder.rb +62 -6
- data/lib/active_fedora/solr_service.rb +2 -2
- data/lib/active_fedora/version.rb +1 -1
- data/spec/integration/contains_association_spec.rb +34 -0
- data/spec/integration/scoped_query_spec.rb +8 -4
- data/spec/unit/attached_files_spec.rb +8 -0
- data/spec/unit/file_spec.rb +14 -0
- data/spec/unit/finder_methods_spec.rb +50 -0
- data/spec/unit/fixity_service_spec.rb +33 -9
- data/spec/unit/query_spec.rb +97 -56
- data/spec/unit/solr_config_options_spec.rb +7 -3
- metadata +5 -2
@@ -145,15 +145,15 @@ module ActiveFedora
|
|
145
145
|
assign_rdf_subject
|
146
146
|
serialize_attached_files
|
147
147
|
@ldp_source = @ldp_source.create
|
148
|
-
|
149
|
-
|
148
|
+
assign_uri_to_contained_resources
|
149
|
+
save_contained_resources
|
150
150
|
refresh
|
151
151
|
end
|
152
152
|
|
153
153
|
def update_record(options = {})
|
154
154
|
serialize_attached_files
|
155
155
|
execute_sparql_update
|
156
|
-
|
156
|
+
save_contained_resources
|
157
157
|
refresh
|
158
158
|
end
|
159
159
|
|
@@ -195,16 +195,25 @@ module ActiveFedora
|
|
195
195
|
ActiveFedora.fedora.connection.put(path, "")
|
196
196
|
end
|
197
197
|
|
198
|
-
def
|
199
|
-
|
200
|
-
|
198
|
+
def assign_uri_to_contained_resources
|
199
|
+
contained_resources.each do |name, source|
|
200
|
+
source.uri= "#{uri}/#{name}"
|
201
201
|
end
|
202
202
|
end
|
203
203
|
|
204
|
-
def
|
205
|
-
|
206
|
-
|
204
|
+
def save_contained_resources
|
205
|
+
contained_resources.changed.each do |_, resource|
|
206
|
+
resource.save
|
207
207
|
end
|
208
208
|
end
|
209
|
+
|
210
|
+
def contained_resources
|
211
|
+
@contained_resources ||= attached_files.merge(contained_rdf_sources)
|
212
|
+
end
|
213
|
+
|
214
|
+
def contained_rdf_sources
|
215
|
+
@contained_rdf_sources ||=
|
216
|
+
AssociationHash.new(self, self.class.contained_rdf_source_reflections)
|
217
|
+
end
|
209
218
|
end
|
210
219
|
end
|
@@ -41,7 +41,11 @@ module ActiveFedora
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def child_resource_reflections
|
44
|
-
reflections.select { |_, reflection| reflection.kind_of?(AssociationReflection) && reflection.macro == :contains }
|
44
|
+
reflections.select { |_, reflection| reflection.kind_of?(AssociationReflection) && reflection.macro == :contains && reflection.klass <= ActiveFedora::File}
|
45
|
+
end
|
46
|
+
|
47
|
+
def contained_rdf_source_reflections
|
48
|
+
reflections.select { |_, reflection| reflection.kind_of?(AssociationReflection) && reflection.macro == :contains && !(reflection.klass <= ActiveFedora::File)}
|
45
49
|
end
|
46
50
|
|
47
51
|
# Returns the AssociationReflection object for the +association+ (use the symbol).
|
@@ -8,12 +8,12 @@ module ActiveFedora
|
|
8
8
|
opts = {}
|
9
9
|
opts[:rows] = limit_value if limit_value
|
10
10
|
opts[:sort] = order_values if order_values
|
11
|
-
|
11
|
+
|
12
12
|
calculate :count, where_values, opts
|
13
13
|
end
|
14
14
|
|
15
15
|
def calculate(calculation, conditions, opts={})
|
16
|
-
SolrService.query(create_query(conditions), :
|
16
|
+
SolrService.query(create_query(conditions), raw: true, rows: 0).fetch('response'.freeze)['numFound'.freeze]
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -111,7 +111,7 @@ module ActiveFedora
|
|
111
111
|
unless opts.include?(:sort)
|
112
112
|
opts[:sort]=@klass.default_sort_params
|
113
113
|
end
|
114
|
-
SolrService.query(create_query(conditions), opts)
|
114
|
+
SolrService.query(create_query(conditions), opts)
|
115
115
|
end
|
116
116
|
|
117
117
|
# Yields the found ActiveFedora::Base object to the passed block
|
@@ -199,7 +199,7 @@ module ActiveFedora
|
|
199
199
|
# The true class may be a subclass of @klass, so always use from_class_uri
|
200
200
|
resource_class = Model.from_class_uri(has_model_value(resource)) || ActiveFedora::Base
|
201
201
|
unless equivalent_class?(resource_class)
|
202
|
-
raise ActiveFedora::ActiveFedoraError.new("Model mismatch. Expected #{@klass}. Got: #{resource_class}")
|
202
|
+
raise ActiveFedora::ActiveFedoraError.new("Model mismatch. Expected #{@klass}. Got: #{resource_class}")
|
203
203
|
end
|
204
204
|
resource_class
|
205
205
|
end
|
@@ -254,45 +254,42 @@ module ActiveFedora
|
|
254
254
|
private
|
255
255
|
|
256
256
|
# Returns a solr query for the supplied conditions
|
257
|
-
# @param[Hash] conditions solr conditions to match
|
257
|
+
# @param[Hash,String,Array] conditions solr conditions to match
|
258
258
|
def create_query(conditions)
|
259
|
-
|
260
|
-
when Hash
|
261
|
-
build_query([create_query_from_hash(conditions)])
|
262
|
-
when String
|
263
|
-
build_query(["(#{conditions})"])
|
264
|
-
else
|
265
|
-
build_query(conditions)
|
266
|
-
end
|
259
|
+
build_query(build_where(conditions))
|
267
260
|
end
|
268
261
|
|
262
|
+
# @param [Array<String>] conditions
|
269
263
|
def build_query(conditions)
|
270
|
-
clauses = search_model_clause ?
|
271
|
-
clauses += conditions.reject{|c| c.blank?}
|
264
|
+
clauses = search_model_clause ? [search_model_clause] : []
|
265
|
+
clauses += conditions.reject { |c| c.blank? }
|
272
266
|
return "*:*" if clauses.empty?
|
273
267
|
clauses.compact.join(" AND ")
|
274
268
|
end
|
275
269
|
|
270
|
+
# @param [Hash<Symbol,String>] conditions
|
271
|
+
# @returns [Array<String>]
|
276
272
|
def create_query_from_hash(conditions)
|
277
|
-
conditions.map {|key,value| condition_to_clauses(key, value)}.compact
|
273
|
+
conditions.map { |key, value| condition_to_clauses(key, value) }.compact
|
278
274
|
end
|
279
275
|
|
276
|
+
# @param [Symbol] key
|
277
|
+
# @param [String] value
|
280
278
|
def condition_to_clauses(key, value)
|
281
|
-
|
282
|
-
|
283
|
-
if @klass.delegated_attributes.key?(key)
|
284
|
-
# TODO Check to see if `key' is a possible solr field for this class, if it isn't try :searchable instead
|
285
|
-
key = ActiveFedora::SolrQueryBuilder.solr_name(key, :stored_searchable, type: :string)
|
286
|
-
end
|
279
|
+
SolrQueryBuilder.construct_query([[field_name_for(key), value]])
|
280
|
+
end
|
287
281
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
282
|
+
# If the key is a property name, turn it into a solr field
|
283
|
+
# @param [Symbol] key
|
284
|
+
# @return [String]
|
285
|
+
def field_name_for(key)
|
286
|
+
if @klass.delegated_attributes.key?(key)
|
287
|
+
# TODO Check to see if `key' is a possible solr field for this class, if it isn't try :searchable instead
|
288
|
+
ActiveFedora::SolrQueryBuilder.solr_name(key, :stored_searchable, type: :string)
|
289
|
+
elsif key == :id
|
290
|
+
SOLR_DOCUMENT_ID
|
291
|
+
else
|
292
|
+
key.to_s
|
296
293
|
end
|
297
294
|
end
|
298
295
|
|
@@ -306,13 +303,5 @@ module ActiveFedora
|
|
306
303
|
clauses.size == 1 ? clauses.first : "(#{clauses.join(" OR ")})"
|
307
304
|
end
|
308
305
|
end
|
309
|
-
|
310
|
-
|
311
|
-
private
|
312
|
-
# Adds esaping for spaces which are not handled by RSolr.solr_escape
|
313
|
-
# See rsolr/rsolr#101
|
314
|
-
def solr_escape terms
|
315
|
-
RSolr.solr_escape(terms).gsub(/\s+/,"\\ ")
|
316
|
-
end
|
317
306
|
end
|
318
307
|
end
|
@@ -17,7 +17,7 @@ module ActiveFedora
|
|
17
17
|
def where_values=(values)
|
18
18
|
raise ImmutableRelation if @loaded
|
19
19
|
@values[:where] = values
|
20
|
-
end
|
20
|
+
end
|
21
21
|
|
22
22
|
def order_values
|
23
23
|
@values[:order] || []
|
@@ -62,11 +62,13 @@ module ActiveFedora
|
|
62
62
|
self
|
63
63
|
end
|
64
64
|
|
65
|
+
# @param [Hash,String] values
|
66
|
+
# @return [Array<String>] list of solr hashes
|
65
67
|
def build_where(values)
|
66
68
|
return [] if values.blank?
|
67
69
|
case values
|
68
70
|
when Hash
|
69
|
-
|
71
|
+
create_query_from_hash(values)
|
70
72
|
when String
|
71
73
|
["(#{values})"]
|
72
74
|
else
|
@@ -76,7 +78,7 @@ module ActiveFedora
|
|
76
78
|
|
77
79
|
# Order the returned records by the field and direction provided
|
78
80
|
#
|
79
|
-
# @option [Array<String>] args a list of fields and directions to sort by
|
81
|
+
# @option [Array<String>] args a list of fields and directions to sort by
|
80
82
|
#
|
81
83
|
# @example
|
82
84
|
# Person.where(occupation_s: 'Plumber').order('name_t desc', 'color_t asc')
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module ActiveFedora
|
2
2
|
module SolrQueryBuilder
|
3
|
+
PARSED_SUFFIX = '_tesim'.freeze
|
4
|
+
|
3
5
|
# Construct a solr query for a list of ids
|
4
6
|
# This is used to get a solr response based on the list of ids in an object's RELS-EXT relationhsips
|
5
7
|
# If the id_array is empty, defaults to a query of "id:NEVER_USE_THIS_ID", which will return an empty solr response
|
@@ -29,18 +31,72 @@ module ActiveFedora
|
|
29
31
|
# # => _query_:"{!raw f=has_model_ssim}info:fedora/afmodel:ComplexCollection" OR _query_:"{!raw f=has_model_ssim}info:fedora/afmodel:ActiveFedora_Base"
|
30
32
|
#
|
31
33
|
# construct_query_for_rel [[Book.reflect_on_association(:library), "foo/bar/baz"]]
|
32
|
-
def self.construct_query_for_rel(field_pairs, join_with = 'AND')
|
34
|
+
def self.construct_query_for_rel(field_pairs, join_with = ' AND ')
|
33
35
|
field_pairs = field_pairs.to_a if field_pairs.kind_of? Hash
|
36
|
+
construct_query(property_values_to_solr(field_pairs), join_with)
|
37
|
+
end
|
34
38
|
|
35
|
-
|
36
|
-
|
39
|
+
# Construct a solr query from a list of pairs (e.g. [field name, values])
|
40
|
+
# @param [Array<Array>] pairs a list of pairs of property name and values
|
41
|
+
# @param [String] join_with ('AND') the value we're joining the clauses with
|
42
|
+
# @returns [String] a solr query
|
43
|
+
# @example
|
44
|
+
# construct_query([['library_id_ssim', '123'], ['owner_ssim', 'Fred']])
|
45
|
+
# # => "_query_:\"{!raw f=library_id_ssim}123\" AND _query_:\"{!raw f=owner_ssim}Fred\""
|
46
|
+
def self.construct_query(field_pairs, join_with = ' AND ')
|
47
|
+
pairs_to_clauses(field_pairs).join(join_with)
|
37
48
|
end
|
38
49
|
|
39
50
|
private
|
40
|
-
#
|
51
|
+
# @param [Array<Array>] pairs a list of (key, value) pairs. The value itself may
|
52
|
+
# @return [Array] a list of solr clauses
|
41
53
|
def self.pairs_to_clauses(pairs)
|
42
|
-
pairs.
|
43
|
-
|
54
|
+
pairs.flat_map do |field, value|
|
55
|
+
condition_to_clauses(field, value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param [String] field
|
60
|
+
# @param [String, Array<String>] values
|
61
|
+
# @return [Array<String>]
|
62
|
+
def self.condition_to_clauses(field, values)
|
63
|
+
values = Array(values)
|
64
|
+
values << nil if values.empty?
|
65
|
+
values.map do |value|
|
66
|
+
if value.present?
|
67
|
+
if parsed?(field)
|
68
|
+
# If you do a raw query on a parsed field you won't get the matches you expect.
|
69
|
+
"#{field}:#{solr_escape(value)}"
|
70
|
+
else
|
71
|
+
raw_query(field, value)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
# Check that the field is not present. In SQL: "WHERE field IS NULL"
|
75
|
+
"-#{field}:[* TO *]"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.parsed?(field)
|
81
|
+
field.end_with?(PARSED_SUFFIX)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Adds esaping for spaces which are not handled by RSolr.solr_escape
|
85
|
+
# See rsolr/rsolr#101
|
86
|
+
def self.solr_escape terms
|
87
|
+
RSolr.solr_escape(terms).gsub(/\s+/,"\\ ")
|
88
|
+
end
|
89
|
+
|
90
|
+
# Given a list of pairs (e.g. [field name, values]), convert the field names
|
91
|
+
# to solr names
|
92
|
+
# @param [Array<Array>] pairs a list of pairs of property name and values
|
93
|
+
# @returns [Hash] map of solr fields to values
|
94
|
+
# @example
|
95
|
+
# property_values_to_solr([['library_id', '123'], ['owner', 'Fred']])
|
96
|
+
# # => [['library_id_ssim', '123'], ['owner_ssim', 'Fred']]
|
97
|
+
def self.property_values_to_solr(pairs)
|
98
|
+
pairs.each_with_object([]) do |(property, value), list|
|
99
|
+
list << [solr_field(property), value]
|
44
100
|
end
|
45
101
|
end
|
46
102
|
|
@@ -103,8 +103,8 @@ module ActiveFedora
|
|
103
103
|
|
104
104
|
def query(query, args={})
|
105
105
|
raw = args.delete(:raw)
|
106
|
-
args = args.merge(:
|
107
|
-
result = SolrService.instance.conn.get('select', :
|
106
|
+
args = args.merge(q: query, qt: 'standard')
|
107
|
+
result = SolrService.instance.conn.get('select', params: args)
|
108
108
|
return result if raw
|
109
109
|
result['response']['docs']
|
110
110
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe ActiveFedora::Base do
|
4
|
+
before do
|
5
|
+
class Source < ActiveFedora::Base
|
6
|
+
contains :sub_resource, class_name: "Source"
|
7
|
+
property :title, predicate: ::RDF::DC.title, multiple: false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
after do
|
11
|
+
Object.send(:remove_const, :Source)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "contains relationships" do
|
15
|
+
it "should be able to have RDF sources" do
|
16
|
+
s = Source.new
|
17
|
+
s.sub_resource.title = "Test"
|
18
|
+
expect(s.sub_resource).not_to be_persisted
|
19
|
+
expect{ s.save }.not_to raise_error
|
20
|
+
s.reload
|
21
|
+
expect(s.sub_resource.title).to eq "Test"
|
22
|
+
expect(s.sub_resource.uri).to eq s.uri.to_s + "/sub_resource"
|
23
|
+
end
|
24
|
+
it "should be able to add RDF sources" do
|
25
|
+
s = Source.create
|
26
|
+
s.sub_resource.title = "Test"
|
27
|
+
expect(s.sub_resource).not_to be_persisted
|
28
|
+
expect{ s.save }.not_to raise_error
|
29
|
+
s.reload
|
30
|
+
expect(s.sub_resource.title).to eq "Test"
|
31
|
+
expect(s.sub_resource.uri).to eq s.uri.to_s + "/sub_resource"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -30,7 +30,6 @@ describe "scoped queries" do
|
|
30
30
|
Object.send(:remove_const, :ModelIntegrationSpec)
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
33
|
describe "When there is one object in the store" do
|
35
34
|
let!(:test_instance) { ModelIntegrationSpec::Basic.create!()}
|
36
35
|
|
@@ -67,7 +66,7 @@ describe "scoped queries" do
|
|
67
66
|
test_instance3.delete
|
68
67
|
end
|
69
68
|
|
70
|
-
it "
|
69
|
+
it "queries" do
|
71
70
|
field = ActiveFedora::SolrQueryBuilder.solr_name('foo', type: :string)
|
72
71
|
expect(ModelIntegrationSpec::Basic.where(field => 'Beta')).to eq [test_instance1]
|
73
72
|
expect(ModelIntegrationSpec::Basic.where('foo' => 'Beta')).to eq [test_instance1]
|
@@ -90,8 +89,13 @@ describe "scoped queries" do
|
|
90
89
|
expect(ModelIntegrationSpec::Basic.where("foo:bar OR bar:baz").where_values).to eq ["(foo:bar OR bar:baz)"]
|
91
90
|
end
|
92
91
|
|
93
|
-
it "
|
94
|
-
|
92
|
+
it "chains where queries" do
|
93
|
+
first_condition = { ActiveFedora::SolrQueryBuilder.solr_name('bar', type: :string) => 'Peanuts' }
|
94
|
+
second_condition = "foo_tesim:bar"
|
95
|
+
where_values = ModelIntegrationSpec::Basic.where(first_condition)
|
96
|
+
.where(second_condition).where_values
|
97
|
+
expect(where_values).to eq ["bar_tesim:Peanuts",
|
98
|
+
"(foo_tesim:bar)"]
|
95
99
|
end
|
96
100
|
|
97
101
|
it "should chain count" do
|
@@ -4,19 +4,24 @@ describe ActiveFedora::AttachedFiles do
|
|
4
4
|
subject { ActiveFedora::Base.new }
|
5
5
|
describe "contains" do
|
6
6
|
before do
|
7
|
+
class Z < ActiveFedora::File
|
8
|
+
end
|
7
9
|
class FooHistory < ActiveFedora::Base
|
8
10
|
contains 'dsid', class_name: 'ActiveFedora::SimpleDatastream'
|
9
11
|
contains 'complex_ds', autocreate: true, class_name: 'Z'
|
10
12
|
contains 'thumbnail'
|
13
|
+
contains 'child_resource', class_name: 'ActiveFedora::Base'
|
11
14
|
end
|
12
15
|
end
|
13
16
|
after do
|
17
|
+
Object.send(:remove_const, :Z)
|
14
18
|
Object.send(:remove_const, :FooHistory)
|
15
19
|
end
|
16
20
|
|
17
21
|
it "should have a child_resource_reflection" do
|
18
22
|
expect(FooHistory.child_resource_reflections).to have_key(:dsid)
|
19
23
|
expect(FooHistory.child_resource_reflections).to have_key(:thumbnail)
|
24
|
+
expect(FooHistory.child_resource_reflections).not_to have_key(:child_resource)
|
20
25
|
end
|
21
26
|
|
22
27
|
it "should let you override defaults" do
|
@@ -34,6 +39,8 @@ describe ActiveFedora::AttachedFiles do
|
|
34
39
|
before do
|
35
40
|
@original_behavior = Deprecation.default_deprecation_behavior
|
36
41
|
Deprecation.default_deprecation_behavior = :silence
|
42
|
+
class Z < ActiveFedora::File
|
43
|
+
end
|
37
44
|
class FooHistory < ActiveFedora::Base
|
38
45
|
has_metadata :name => 'dsid', type: ActiveFedora::SimpleDatastream
|
39
46
|
has_metadata 'complex_ds', autocreate: true, type: 'Z'
|
@@ -42,6 +49,7 @@ describe ActiveFedora::AttachedFiles do
|
|
42
49
|
after do
|
43
50
|
Deprecation.default_deprecation_behavior = @original_behavior
|
44
51
|
Object.send(:remove_const, :FooHistory)
|
52
|
+
Object.send(:remove_const, :Z)
|
45
53
|
end
|
46
54
|
|
47
55
|
it "should have a child_resource_reflection" do
|
data/spec/unit/file_spec.rb
CHANGED
@@ -39,6 +39,20 @@ describe ActiveFedora::File do
|
|
39
39
|
it "sets the uri using the parent as the base" do
|
40
40
|
expect(subject).to eq "#{ActiveFedora.fedora.host}#{ActiveFedora.fedora.base_path}/1234/FOO1"
|
41
41
|
end
|
42
|
+
|
43
|
+
context "and it's initialized with the URI" do
|
44
|
+
let(:file) { described_class.new(parent.uri+"/FOO1") }
|
45
|
+
it "works" do
|
46
|
+
expect(subject.to_s).to eq "#{ActiveFedora.fedora.host}#{ActiveFedora.fedora.base_path}/1234/FOO1"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "and it's initialized with an ID" do
|
51
|
+
let(:file) { described_class.new(parent.id+"/FOO1") }
|
52
|
+
it "works" do
|
53
|
+
expect(subject.to_s).to eq "#{ActiveFedora.fedora.host}#{ActiveFedora.fedora.base_path}/1234/FOO1"
|
54
|
+
end
|
55
|
+
end
|
42
56
|
end
|
43
57
|
|
44
58
|
context "when the file doesn't have a uri" do
|