speedy-af 0.4.0 → 0.5.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0fd6d0ac71c3da9c7807fe63bf98774a75abf4796130be797e19a9eba4ac6292
4
- data.tar.gz: 549a1835b24011c7ebebef142654afb7c8ab0afdd0b70e39a713e7e1c9055dca
3
+ metadata.gz: 172c12ad71984a304a61ef5ac110109eec26907ad11d73f2fd58d0ced0b88f6a
4
+ data.tar.gz: 0be67e88288ee13d853619cb91a2608450824d908230713ec3f8a9d535bde91f
5
5
  SHA512:
6
- metadata.gz: 50e4f754a2f59937b4d08a3ccd9d460e64ecbba50d1e5eed821a28fcb77aab99f77a62307c9185269d27ac1f0baab58e72f8e56cc9debbab71bd99c2c54700e5
7
- data.tar.gz: f0f1e0fd4e04f475085ada08769cf9617a95f6ddcd2b0955cea44adc91284844862d0c3c6729c9bc17c32ba456067e448d83c81be9899520003b5dea3b142ed1
6
+ metadata.gz: 7ff404cde599d7b7111cbceca64ca13f400740b1b4e794d93906b0c9e2987403f289f99294d3e7002946efd18d2cd41bfbdd7810f385a10e860bb6af33de0ec7
7
+ data.tar.gz: a38247cc3afe52ce3db0b9dce0b4f12b456ffe03654e8fab460f4489f60744ec7b8b602454b3853ecbf520699684caf96364e2736b8a6deec5c4593a9aca8460
data/.circleci/config.yml CHANGED
@@ -37,6 +37,14 @@ jobs:
37
37
  workflows:
38
38
  ci:
39
39
  jobs:
40
+ - bundle_and_test:
41
+ name: "ruby3-4_rails8-0"
42
+ ruby_version: "3.4.5"
43
+ rails_version: "8.0.1"
44
+ - bundle_and_test:
45
+ name: "ruby3-3_rails8-0"
46
+ ruby_version: "3.3.4"
47
+ rails_version: "8.0.1"
40
48
  - bundle_and_test:
41
49
  name: "ruby3-3_rails7-2"
42
50
  ruby_version: "3.3.4"
data/README.md CHANGED
@@ -91,6 +91,12 @@ If you have questions or need help, please email [the Hydra community tech list]
91
91
  be more mindful of both local resources and Solr request limits.
92
92
  * `Base` may not play nicely with language-tagged RDF literals, as ActiveFedora does not
93
93
  currently index/encode the language tag into Solr.
94
+ * belongs_to relationships do not respect `include_reflections` filtering and will fetch all
95
+ belongs_to reflections when the `include_reflections` is not false.
96
+
97
+ # Development
98
+
99
+ To run tests locally, spin up Fedora and Solr using `docker-compose up` then run tests using `bundle exec rspec`.
94
100
 
95
101
  # Acknowledgments
96
102
 
@@ -0,0 +1,26 @@
1
+ volumes:
2
+ fedora:
3
+ solr:
4
+
5
+ services:
6
+ fedora:
7
+ image: ghcr.io/samvera/fcrepo4:4.7.5
8
+ volumes:
9
+ - fedora:/data
10
+ ports:
11
+ - '8986:8080'
12
+
13
+ solr:
14
+ image: solr:9
15
+ volumes:
16
+ - ./solr/conf:/opt/solr/core_conf
17
+ - solr:/var/solr
18
+ command:
19
+ - solr-precreate
20
+ - hydra-test
21
+ - /opt/solr/core_conf
22
+ environment:
23
+ - SOLR_MODULES=analysis-extras,extraction
24
+ ports:
25
+ - '8985:8983'
26
+
@@ -90,11 +90,11 @@ module SpeedyAF
90
90
  reflections
91
91
  end
92
92
 
93
- def query_for_belongs_to(proxy_hash, _opts)
93
+ def query_for_belongs_to(proxy_hash, opts)
94
94
  proxy_hash.collect do |_id, proxy|
95
- proxy.belongs_to_reflections.collect do |_name, reflection|
96
- id = proxy.attrs[predicate_for_reflection(reflection).to_sym]
97
- id.blank? ? nil : "id:#{id}"
95
+ proxy.belongs_to_reflections(only: opts[:load_reflections]).collect do |_name, reflection|
96
+ ids = proxy.attrs[predicate_for_reflection(reflection).to_sym]
97
+ ids.blank? ? nil : Array(ids).collect { |id| "id:#{id}" }.join(" OR ")
98
98
  end.compact
99
99
  end.flatten.join(" OR ")
100
100
  end
@@ -103,7 +103,7 @@ module SpeedyAF
103
103
  hash = {}
104
104
  proxy_hash.each do |proxy_id, proxy|
105
105
  proxy.belongs_to_reflections.each do |name, reflection|
106
- doc = docs.find { |d| d.id == proxy.attrs[predicate_for_reflection(reflection).to_sym] }
106
+ doc = docs.find { |d| proxy.attrs[predicate_for_reflection(reflection).to_sym].include?(d.id) && d["has_model_ssim"].include?(reflection.class_name) }
107
107
  next unless doc
108
108
  hash[proxy_id] ||= {}
109
109
  hash[proxy_id][name] = doc
@@ -113,9 +113,13 @@ module SpeedyAF
113
113
  hash
114
114
  end
115
115
 
116
- def query_for_has_many(proxy_hash, _opts)
116
+ def query_for_has_many(proxy_hash, opts)
117
117
  proxy_hash.collect do |id, proxy|
118
- proxy.has_many_reflections.collect { |_name, reflection| "#{predicate_for_reflection(reflection)}_ssim:#{id}" }
118
+ proxy.has_many_reflections(only: opts[:load_reflections]).collect do |_name, reflection|
119
+ query = "#{predicate_for_reflection(reflection)}_ssim:#{id}"
120
+ query = "(#{query} AND has_model_ssim:#{reflection.class_name})"
121
+ query
122
+ end
119
123
  end.flatten.join(" OR ")
120
124
  end
121
125
 
@@ -125,7 +129,7 @@ module SpeedyAF
125
129
  docs.each do |doc|
126
130
  has_many_reflections.each do |name, reflection|
127
131
  Array(doc["#{predicate_for_reflection(reflection)}_ssim"]).each do |proxy_id|
128
- next unless proxy_hash.key?(proxy_id)
132
+ next unless proxy_hash.key?(proxy_id) && doc["has_model_ssim"].include?(reflection.class_name)
129
133
  hash[proxy_id] ||= {}
130
134
  hash[proxy_id][name] ||= []
131
135
  hash[proxy_id][name] << doc
@@ -137,9 +141,9 @@ module SpeedyAF
137
141
  hash
138
142
  end
139
143
 
140
- def query_for_subresources(proxy_hash, _opts)
144
+ def query_for_subresources(proxy_hash, opts)
141
145
  proxy_hash.collect do |id, proxy|
142
- proxy.subresource_reflections.collect { |name, _reflection| "id:#{id}/#{name}" }
146
+ proxy.subresource_reflections(only: opts[:load_reflections]).collect { |name, _reflection| "id:#{id}/#{name}" }
143
147
  end.flatten.join(" OR ")
144
148
  end
145
149
 
@@ -199,7 +203,6 @@ module SpeedyAF
199
203
 
200
204
  def method_missing(sym, *args)
201
205
  return real_object.send(sym, *args) if real?
202
-
203
206
  if @attrs.key?(sym)
204
207
  # Lazy convert the solr document into a speedy_af proxy object
205
208
  @attrs[sym] = SpeedyAF::Base.for(@attrs[sym]) if @attrs[sym].is_a?(ActiveFedora::SolrHit)
@@ -223,18 +226,33 @@ module SpeedyAF
223
226
  super
224
227
  end
225
228
 
226
- def subresource_reflections
229
+ def subresource_reflections(only: nil)
227
230
  SpeedyAF::Base.model_reflections[:subresource][model] ||= model.reflections.select { |_name, reflection| reflection.is_a? ActiveFedora::Reflection::HasSubresourceReflection }
231
+ if only.is_a?(Array) && only.present?
232
+ SpeedyAF::Base.model_reflections[:subresource][model].select { |name, _reflection| only.include? name }
233
+ else
234
+ SpeedyAF::Base.model_reflections[:subresource][model]
235
+ end
228
236
  end
229
237
 
230
238
  # rubocop:disable Naming/PredicateName
231
- def has_many_reflections
239
+ def has_many_reflections(only: nil)
232
240
  SpeedyAF::Base.model_reflections[:has_many][model] ||= model.reflections.select { |_name, reflection| reflection.has_many? && reflection.respond_to?(:predicate_for_solr) }
241
+ if only.is_a?(Array) && only.present?
242
+ SpeedyAF::Base.model_reflections[:has_many][model].select { |name, _reflection| only.include? name }
243
+ else
244
+ SpeedyAF::Base.model_reflections[:has_many][model]
245
+ end
233
246
  end
234
247
  # rubocop:enable Naming/PredicateName
235
248
 
236
- def belongs_to_reflections
249
+ def belongs_to_reflections(only: nil)
237
250
  SpeedyAF::Base.model_reflections[:belongs_to][model] ||= model.reflections.select { |_name, reflection| reflection.belongs_to? && reflection.respond_to?(:predicate_for_solr) }
251
+ if only.is_a?(Array) && only.present?
252
+ SpeedyAF::Base.model_reflections[:belongs_to][model].select { |name, _reflection| only.include? name }
253
+ else
254
+ SpeedyAF::Base.model_reflections[:belongs_to][model]
255
+ end
238
256
  end
239
257
 
240
258
  protected
@@ -260,7 +278,7 @@ module SpeedyAF
260
278
  attr_name, type, _stored, _indexed, _multi = k.scan(/^(.+)_(.+)(s)(i?)(m?)$/).first
261
279
  return [k, v] if attr_name.nil?
262
280
  value = Array(v).map { |m| transforms.fetch(type, transforms[nil]).call(m) }
263
- value = value.first unless @model.respond_to?(:properties) && multiple?(@model.properties[attr_name])
281
+ value = value.first unless reflection_solr_fields(@model).include?(attr_name) || (@model.respond_to?(:properties) && multiple?(@model.properties[attr_name]))
264
282
  [attr_name, value]
265
283
  end
266
284
 
@@ -268,10 +286,15 @@ module SpeedyAF
268
286
  prop.present? && prop.respond_to?(:multiple?) && prop.multiple?
269
287
  end
270
288
 
289
+ def reflection_solr_fields(model)
290
+ return [] unless model.respond_to?(:reflections)
291
+ model.reflections.values.select { |v| v.belongs_to? && v.respond_to?(:predicate_for_solr) }.collect(&:predicate_for_solr)
292
+ end
293
+
271
294
  def load_from_reflection(reflection, ids_only = false)
272
295
  return load_through_reflection(reflection, ids_only) if reflection.options.key?(:through)
273
- return load_belongs_to_reflection(reflection.predicate_for_solr, ids_only) if reflection.belongs_to? && reflection.respond_to?(:predicate_for_solr)
274
- return load_has_many_reflection(reflection.predicate_for_solr, ids_only) if reflection.has_many? && reflection.respond_to?(:predicate_for_solr)
296
+ return load_belongs_to_reflection(reflection, ids_only) if reflection.belongs_to? && reflection.respond_to?(:predicate_for_solr)
297
+ return load_has_many_reflection(reflection, ids_only) if reflection.has_many? && reflection.respond_to?(:predicate_for_solr)
275
298
  return load_subresource_content(reflection) if reflection.is_a?(ActiveFedora::Reflection::HasSubresourceReflection)
276
299
  # :nocov:
277
300
  raise NotAvailable, "`#{reflection.name}' cannot be quick-loaded. Falling back to model."
@@ -302,14 +325,18 @@ module SpeedyAF
302
325
  ids
303
326
  end
304
327
 
305
- def load_belongs_to_reflection(predicate, ids_only = false)
306
- id = @attrs[predicate.to_sym]
307
- return id if ids_only
308
- Base.find(id)
328
+ def load_belongs_to_reflection(reflection, ids_only = false)
329
+ id = @attrs[reflection.predicate_for_solr.to_sym]
330
+ return Array(id).first if ids_only && Array(id).size == 1
331
+ return if id.blank?
332
+ query = Array(id).collect { |i| "id:#{i}" }.join(" OR ")
333
+ query = "(#{query}) AND has_model_ssim:#{reflection.class_name}"
334
+ obj = Base.where(query, rows: 1).first
335
+ ids_only ? obj&.id : obj
309
336
  end
310
337
 
311
- def load_has_many_reflection(predicate, ids_only = false)
312
- query = %(#{predicate}_ssim:#{id})
338
+ def load_has_many_reflection(reflection, ids_only = false)
339
+ query = "#{reflection.predicate_for_solr}_ssim:#{id} AND has_model_ssim:#{reflection.class_name}"
313
340
  return Base.where(query) unless ids_only
314
341
  docs = ActiveFedora::SolrService.query query, rows: SOLR_ALL
315
342
  docs.collect { |doc| doc['id'] }
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module SpeedyAF
3
- VERSION = '0.4.0'
3
+ VERSION = '0.5.1'
4
4
  end
@@ -0,0 +1,3 @@
1
+ {
2
+ "initArgs":{},
3
+ "managedList":[]}
@@ -0,0 +1,31 @@
1
+ <!--
2
+ Licensed to the Apache Software Foundation (ASF) under one or more
3
+ contributor license agreements. See the NOTICE file distributed with
4
+ this work for additional information regarding copyright ownership.
5
+ The ASF licenses this file to You under the Apache License, Version 2.0
6
+ (the "License"); you may not use this file except in compliance with
7
+ the License. You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ -->
17
+
18
+ <!-- The content of this page will be statically included into the top
19
+ of the admin page. Uncomment this as an example to see there the content
20
+ will show up.
21
+
22
+ <hr>
23
+ <i>This line will appear before the first table</i>
24
+ <tr>
25
+ <td colspan="2">
26
+ This row will be appended to the end of the first table
27
+ </td>
28
+ </tr>
29
+ <hr>
30
+
31
+ -->
@@ -0,0 +1,36 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <!--
3
+ Licensed to the Apache Software Foundation (ASF) under one or more
4
+ contributor license agreements. See the NOTICE file distributed with
5
+ this work for additional information regarding copyright ownership.
6
+ The ASF licenses this file to You under the Apache License, Version 2.0
7
+ (the "License"); you may not use this file except in compliance with
8
+ the License. You may obtain a copy of the License at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ Unless required by applicable law or agreed to in writing, software
13
+ distributed under the License is distributed on an "AS IS" BASIS,
14
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ See the License for the specific language governing permissions and
16
+ limitations under the License.
17
+ -->
18
+
19
+ <!-- If this file is found in the config directory, it will only be
20
+ loaded once at startup. If it is found in Solr's data
21
+ directory, it will be re-loaded every commit.
22
+ -->
23
+
24
+ <elevate>
25
+ <query text="foo bar">
26
+ <doc id="1" />
27
+ <doc id="2" />
28
+ <doc id="3" />
29
+ </query>
30
+
31
+ <query text="ipod">
32
+ <doc id="MA147LL/A" /> <!-- put the actual ipod at the top -->
33
+ <doc id="IW-02" exclude="true" /> <!-- exclude this cable -->
34
+ </query>
35
+
36
+ </elevate>
@@ -0,0 +1,246 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ # Syntax:
14
+ # "source" => "target"
15
+ # "source".length() > 0 (source cannot be empty.)
16
+ # "target".length() >= 0 (target can be empty.)
17
+
18
+ # example:
19
+ # "??" => "A"
20
+ # "\u00C0" => "A"
21
+ # "\u00C0" => "\u0041"
22
+ # "??" => "ss"
23
+ # "\t" => " "
24
+ # "\n" => ""
25
+
26
+ # ?? => A
27
+ "\u00C0" => "A"
28
+
29
+ # ?? => A
30
+ "\u00C1" => "A"
31
+
32
+ # ?? => A
33
+ "\u00C2" => "A"
34
+
35
+ # ?? => A
36
+ "\u00C3" => "A"
37
+
38
+ # ?? => A
39
+ "\u00C4" => "A"
40
+
41
+ # ?? => A
42
+ "\u00C5" => "A"
43
+
44
+ # ?? => AE
45
+ "\u00C6" => "AE"
46
+
47
+ # ?? => C
48
+ "\u00C7" => "C"
49
+
50
+ # ?? => E
51
+ "\u00C8" => "E"
52
+
53
+ # ?? => E
54
+ "\u00C9" => "E"
55
+
56
+ # ?? => E
57
+ "\u00CA" => "E"
58
+
59
+ # ?? => E
60
+ "\u00CB" => "E"
61
+
62
+ # ?? => I
63
+ "\u00CC" => "I"
64
+
65
+ # ?? => I
66
+ "\u00CD" => "I"
67
+
68
+ # ?? => I
69
+ "\u00CE" => "I"
70
+
71
+ # ?? => I
72
+ "\u00CF" => "I"
73
+
74
+ # ?? => IJ
75
+ "\u0132" => "IJ"
76
+
77
+ # ?? => D
78
+ "\u00D0" => "D"
79
+
80
+ # ?? => N
81
+ "\u00D1" => "N"
82
+
83
+ # ?? => O
84
+ "\u00D2" => "O"
85
+
86
+ # ?? => O
87
+ "\u00D3" => "O"
88
+
89
+ # ?? => O
90
+ "\u00D4" => "O"
91
+
92
+ # ?? => O
93
+ "\u00D5" => "O"
94
+
95
+ # ?? => O
96
+ "\u00D6" => "O"
97
+
98
+ # ?? => O
99
+ "\u00D8" => "O"
100
+
101
+ # ?? => OE
102
+ "\u0152" => "OE"
103
+
104
+ # ??
105
+ "\u00DE" => "TH"
106
+
107
+ # ?? => U
108
+ "\u00D9" => "U"
109
+
110
+ # ?? => U
111
+ "\u00DA" => "U"
112
+
113
+ # ?? => U
114
+ "\u00DB" => "U"
115
+
116
+ # ?? => U
117
+ "\u00DC" => "U"
118
+
119
+ # ?? => Y
120
+ "\u00DD" => "Y"
121
+
122
+ # ?? => Y
123
+ "\u0178" => "Y"
124
+
125
+ # ?? => a
126
+ "\u00E0" => "a"
127
+
128
+ # ?? => a
129
+ "\u00E1" => "a"
130
+
131
+ # ?? => a
132
+ "\u00E2" => "a"
133
+
134
+ # ?? => a
135
+ "\u00E3" => "a"
136
+
137
+ # ?? => a
138
+ "\u00E4" => "a"
139
+
140
+ # ?? => a
141
+ "\u00E5" => "a"
142
+
143
+ # ?? => ae
144
+ "\u00E6" => "ae"
145
+
146
+ # ?? => c
147
+ "\u00E7" => "c"
148
+
149
+ # ?? => e
150
+ "\u00E8" => "e"
151
+
152
+ # ?? => e
153
+ "\u00E9" => "e"
154
+
155
+ # ?? => e
156
+ "\u00EA" => "e"
157
+
158
+ # ?? => e
159
+ "\u00EB" => "e"
160
+
161
+ # ?? => i
162
+ "\u00EC" => "i"
163
+
164
+ # ?? => i
165
+ "\u00ED" => "i"
166
+
167
+ # ?? => i
168
+ "\u00EE" => "i"
169
+
170
+ # ?? => i
171
+ "\u00EF" => "i"
172
+
173
+ # ?? => ij
174
+ "\u0133" => "ij"
175
+
176
+ # ?? => d
177
+ "\u00F0" => "d"
178
+
179
+ # ?? => n
180
+ "\u00F1" => "n"
181
+
182
+ # ?? => o
183
+ "\u00F2" => "o"
184
+
185
+ # ?? => o
186
+ "\u00F3" => "o"
187
+
188
+ # ?? => o
189
+ "\u00F4" => "o"
190
+
191
+ # ?? => o
192
+ "\u00F5" => "o"
193
+
194
+ # ?? => o
195
+ "\u00F6" => "o"
196
+
197
+ # ?? => o
198
+ "\u00F8" => "o"
199
+
200
+ # ?? => oe
201
+ "\u0153" => "oe"
202
+
203
+ # ?? => ss
204
+ "\u00DF" => "ss"
205
+
206
+ # ?? => th
207
+ "\u00FE" => "th"
208
+
209
+ # ?? => u
210
+ "\u00F9" => "u"
211
+
212
+ # ?? => u
213
+ "\u00FA" => "u"
214
+
215
+ # ?? => u
216
+ "\u00FB" => "u"
217
+
218
+ # ?? => u
219
+ "\u00FC" => "u"
220
+
221
+ # ?? => y
222
+ "\u00FD" => "y"
223
+
224
+ # ?? => y
225
+ "\u00FF" => "y"
226
+
227
+ # ??? => ff
228
+ "\uFB00" => "ff"
229
+
230
+ # ??? => fi
231
+ "\uFB01" => "fi"
232
+
233
+ # ??? => fl
234
+ "\uFB02" => "fl"
235
+
236
+ # ??? => ffi
237
+ "\uFB03" => "ffi"
238
+
239
+ # ??? => ffl
240
+ "\uFB04" => "ffl"
241
+
242
+ # ??? => ft
243
+ "\uFB05" => "ft"
244
+
245
+ # ??? => st
246
+ "\uFB06" => "st"
@@ -0,0 +1,21 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ #-----------------------------------------------------------------------
14
+ # Use a protected word file to protect against the stemmer reducing two
15
+ # unrelated words to the same base word.
16
+
17
+ # Some non-words that normally won't be encountered,
18
+ # just to test that they won't be stemmed.
19
+ dontstems
20
+ zwhacky
21
+