active-fedora 2.3.1 → 2.3.3
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.
- data/CONSOLE_GETTING_STARTED.textile +14 -14
- data/Gemfile.lock +6 -6
- data/NOKOGIRI_DATASTREAMS.textile +19 -17
- data/config/solr.yml +15 -0
- data/lib/active_fedora.rb +14 -7
- data/lib/active_fedora/base.rb +5 -7
- data/lib/active_fedora/datastream.rb +12 -0
- data/lib/active_fedora/semantic_node.rb +15 -254
- data/lib/active_fedora/solr_service.rb +4 -0
- data/lib/active_fedora/version.rb +1 -1
- data/lib/fedora/datastream.rb +14 -3
- data/lib/tasks/active_fedora_dev.rake +2 -2
- data/spec/fixtures/rails_root/config/fedora.yml +4 -4
- data/spec/integration/bug_spec.rb +1 -1
- data/spec/integration/model_spec.rb +1 -1
- data/spec/integration/nokogiri_datastream_spec.rb +1 -2
- data/spec/integration/semantic_node_spec.rb +2 -259
- data/spec/unit/active_fedora_spec.rb +7 -2
- data/spec/unit/base_spec.rb +16 -5
- data/spec/unit/datastream_spec.rb +41 -1
- data/spec/unit/property_spec.rb +1 -1
- data/spec/unit/relationship_spec.rb +1 -1
- data/spec/unit/rf_datastream_spec.rb +14 -0
- data/spec/unit/semantic_node_spec.rb +7 -14
- metadata +5 -5
- data/lib/active_fedora/samples/sample_af_obj_relationship_query_param.rb +0 -11
@@ -112,7 +112,7 @@ To see a more complete implementation of importing and deleting Fedora objects,
|
|
112
112
|
*When you're done playing around with importing and deleting, make sure that you leave a copy of hydrangea:fixture_mods_article1 in fedora so we can play with it.*
|
113
113
|
|
114
114
|
|
115
|
-
|
115
|
+
h2. Define a Model for Your (Active)Fedora Objects
|
116
116
|
|
117
117
|
Look at the SpecialThing model defined in lib/active_fedora/samples/special_thing.rb to see how you declare an ActiveFedora model.
|
118
118
|
|
@@ -133,7 +133,7 @@ This pid was retrieved from Fedora's getNextPid method.
|
|
133
133
|
|
134
134
|
Your object will not show up in the actual Fedora repository until you save it using newthing.save, but let's hold off on saving it for now.
|
135
135
|
|
136
|
-
|
136
|
+
h2. Fedora RELATIONSHIPS in ActiveFedora
|
137
137
|
|
138
138
|
ActiveFedora provides convenience methods for creating and editing Fedora RELS-EXT relationships. It also auto-generates methods for searching these relationships with Solr. (see https://github.com/projecthydra/solrizer and https://github.com/projecthydra/solrizer-fedora)
|
139
139
|
|
@@ -168,7 +168,7 @@ newthing.inspirations
|
|
168
168
|
|
169
169
|
This method is actually making a search request to Solr -- it is looking in Solr to see if the "newthing" object has any "inspirations" relationships.
|
170
170
|
|
171
|
-
Now we'll create another Fedora object (using the default ActiveFedora object model) and we'll use ActiveFedora's add_relationship method to relate our new object to the SpecialThing
|
171
|
+
Now we'll create another Fedora object (using the default ActiveFedora object model) and we'll use ActiveFedora's add_relationship method to relate our new object to the SpecialThing object. We'll also save our new object in our Fedora repository.
|
172
172
|
|
173
173
|
<pre>
|
174
174
|
newobj = ActiveFedora::Base.new
|
@@ -179,10 +179,10 @@ newobj.relationships
|
|
179
179
|
newobj.save
|
180
180
|
=> ...
|
181
181
|
newobj.pid
|
182
|
-
=> "changeme:164" # this is the pid you want to put in the following URLs as a replacement for
|
182
|
+
=> "changeme:164" # this is the pid you want to put in the following URLs as a replacement for (PID)
|
183
183
|
</pre>
|
184
184
|
|
185
|
-
You can see objects in Fedora by going to http://localhost:8983/fedora/objects/
|
185
|
+
You can see objects in Fedora by going to http://localhost:8983/fedora/objects/(PID) and you can see the relationships for an object by looking at the RELS-EXT datastream content: http://localhost: 8983/fedora/objects/(PID)/datastreams/RELS-EXT/content
|
186
186
|
|
187
187
|
Now let's see if the "newthing" object has an "inspiration" relationship with our "newobj"
|
188
188
|
|
@@ -200,9 +200,9 @@ Note that you didn't have to save the "newthing" object in order for this relati
|
|
200
200
|
Only the ActiveFedora object making the assertion needs to be saved in order for the search to work. In our example above, new_obj asserts :has_derivation (rather than the derivative asserting :is_derivation_of), so only new_obj had to be saved.
|
201
201
|
|
202
202
|
|
203
|
-
|
203
|
+
h2. Fedora DATASTREAMS & METADATA in ActiveFedora
|
204
204
|
|
205
|
-
|
205
|
+
h3. Blobs (a.k.a. File Datastreams, a.k.a Managed Content Datastreams)
|
206
206
|
|
207
207
|
Here we create a simple Datastream (using the default Datastream model).
|
208
208
|
|
@@ -220,7 +220,7 @@ newthing.save
|
|
220
220
|
Now use your browser to find the file datastreams in Fedora ...
|
221
221
|
|
222
222
|
|
223
|
-
|
223
|
+
h3. On auto-generating datastream ids
|
224
224
|
|
225
225
|
If you don't specify a dsid, ActiveFedora will generate one for you. In the example below, "DS1" is the dsID assigned to the new datastream
|
226
226
|
|
@@ -244,7 +244,7 @@ newthing.datastreams.keys
|
|
244
244
|
newthing.save
|
245
245
|
</pre>
|
246
246
|
|
247
|
-
h2.
|
247
|
+
h2. Retrieving Existing Fedora Repository Objects
|
248
248
|
|
249
249
|
When you want your code to interact with existing digital objects in a Fedora repository, use the ActiveFedora load_instance method.
|
250
250
|
|
@@ -316,11 +316,9 @@ This query will return a Solr::Result containing all of the objects that have co
|
|
316
316
|
|
317
317
|
h2. More About ActiveFedora Models
|
318
318
|
|
319
|
-
ActiveFedora Models don't actually do much. They mainly keep a list of datastream ids and associate them with classes that help you use the content from those datastreams.
|
319
|
+
ActiveFedora Models for Fedora objects don't actually do much. They mainly keep a list of datastream ids and associate them with classes that help you use the content from those datastreams.
|
320
320
|
|
321
|
-
|
322
|
-
|
323
|
-
For now, load an instance of the SpecialThing model and take a look at its datastreams.
|
321
|
+
Let's load an instance of the SpecialThing model and take a look at its datastreams.
|
324
322
|
|
325
323
|
<pre>
|
326
324
|
st = SpecialThing.load_instance("hydrangea:fixture_mods_article1")
|
@@ -366,5 +364,7 @@ properties:
|
|
366
364
|
|
367
365
|
Notice that properties and DC have been loaded as ActiveFedora::Datastream, RELS-EXT has been loaded as ActiveFedora::RelsExtDatastream, and the other three have been loaded as the classes specified in the Model.
|
368
366
|
|
369
|
-
|
367
|
+
h2. Where to Find More Information
|
368
|
+
|
369
|
+
You can examine the files in lib/active_fedora/samples to learn more about how to define ActiveFedora models and OM-based datastreams. We also suggest you read about "OM-based NokogiriDatastreams":https://github.com/mediashelf/active_fedora/blob/master/NOKOGIRI_DATASTREAMS.textile to learn about manipulating XML contained in datastreams.
|
370
370
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
active-fedora (2.3.
|
4
|
+
active-fedora (2.3.2)
|
5
5
|
activeresource
|
6
6
|
equivalent-xml
|
7
7
|
facets
|
@@ -18,13 +18,13 @@ GEM
|
|
18
18
|
remote: http://rubygems.org/
|
19
19
|
specs:
|
20
20
|
RedCloth (4.2.7)
|
21
|
-
activeresource (2.3.
|
22
|
-
activesupport (= 2.3.
|
23
|
-
activesupport (2.3.
|
21
|
+
activeresource (2.3.12)
|
22
|
+
activesupport (= 2.3.12)
|
23
|
+
activesupport (2.3.12)
|
24
24
|
builder (3.0.0)
|
25
25
|
columnize (0.3.2)
|
26
26
|
daemons (1.1.3)
|
27
|
-
equivalent-xml (0.2.
|
27
|
+
equivalent-xml (0.2.7)
|
28
28
|
nokogiri (>= 1.4.3)
|
29
29
|
facets (2.9.1)
|
30
30
|
git (1.2.5)
|
@@ -37,7 +37,7 @@ GEM
|
|
37
37
|
mediashelf-loggable (0.4.2)
|
38
38
|
mime-types (1.16)
|
39
39
|
mocha (0.9.12)
|
40
|
-
multipart-post (1.1.
|
40
|
+
multipart-post (1.1.2)
|
41
41
|
nokogiri (1.4.4)
|
42
42
|
om (1.2.4)
|
43
43
|
nokogiri (>= 1.4.2)
|
@@ -1,5 +1,21 @@
|
|
1
1
|
h1. Using OM-based NokogiriDatastreams
|
2
2
|
|
3
|
+
This document is about working with (Active)Fedora datastreams with XML content. Nokogiri is a ruby gem for working with xml, and OM (Opinionated Metadata) allows you to define a “terminology” to ease translation between XML and ruby objects.
|
4
|
+
|
5
|
+
h3. Learning More about OM (Opinionated Metadata)
|
6
|
+
|
7
|
+
For deeper exposure to what you can do with OM, see the "OM documentation":http://hudson.projecthydra.org/job/om/Documentation/ for "Getting Started":http://hudson.projecthydra.org/job/om/Documentation/file.GETTING_STARTED.html, "Querying Documents":http://hudson.projecthydra.org/job/om/Documentation/file.QUERYING_DOCUMENTS.html, and "Updating Documents":http://hudson.projecthydra.org/job/om/Documentation/file.UPDATING_DOCUMENTS.html. There is also information in the "solrizer":http://github.com/projecthydra/solrizer documentation about Solrizing documents.
|
8
|
+
|
9
|
+
You can run most of the examples from those tutorials against the descMetadata datastream you've created here.
|
10
|
+
|
11
|
+
<pre>
|
12
|
+
doc = st.datastreams["descMetadata"] # the datastream is the OM Document
|
13
|
+
...
|
14
|
+
doc.class
|
15
|
+
=> Hydra::ModsArticleDatastream # Hydra::ModsArticleDatastream is the Document Class
|
16
|
+
terminology = doc.class.terminology # The terminology is attached to the Document Class
|
17
|
+
</pre>
|
18
|
+
|
3
19
|
h2. Setup
|
4
20
|
|
5
21
|
This tutorial assumes that you've run script/console from the root of ActiveFedora and have imported the hydrangea:fixture_mods_article1 object. If you haven't done that, see "CONSOLE_GETTING_STARTED":https://github.com/mediashelf/active_fedora/blob/master/CONSOLE_GETTING_STARTED.textile for instructions.
|
@@ -13,9 +29,9 @@ Look in these datastream definitions to see the OM Terminologies they define. T
|
|
13
29
|
* {Marpa::DcDatastream} ( "see the code":https://github.com/mediashelf/active_fedora/blob/master/lib/active_fedora/samples/marpa-dc_datastream.rb )
|
14
30
|
|
15
31
|
|
16
|
-
h2.
|
32
|
+
h2. Using Existing OM Terminology
|
17
33
|
|
18
|
-
First, load the Fedora object as an instance of the SpecialThing Model
|
34
|
+
First, load the Fedora object as an instance of the SpecialThing ActiveFedora Model
|
19
35
|
|
20
36
|
<pre>
|
21
37
|
st = SpecialThing.load_instance("hydrangea:fixture_mods_article1")
|
@@ -64,21 +80,7 @@ mods_ds.find_by_terms(:person).length
|
|
64
80
|
mods_ds.find_by_terms(:person).each {|n| puts n.to_xml}
|
65
81
|
</pre>
|
66
82
|
|
67
|
-
|
68
|
-
h2. Learning More about OM
|
69
|
-
|
70
|
-
Hydra::ModsArticleDatastream has all of the behaviors of an OM::Document. For deeper exposure to what you can do with OM, see the "OM documentation":http://hudson.projecthydra.org/job/om/Documentation/ for "Getting Started":http://hudson.projecthydra.org/job/om/Documentation/file.GETTING_STARTED.html, "Querying Documents":http://hudson.projecthydra.org/job/om/Documentation/file.QUERYING_DOCUMENTS.html, and "Updating Documents":http://hudson.projecthydra.org/job/om/Documentation/file.UPDATING_DOCUMENTS.html. There is also information in the "solrizer":http://github.com/projecthydra/solrizer documentation about Solrizing documents.
|
71
|
-
|
72
|
-
You can run most of the examples from those tutorials against the descMetadata datastream you've created here.
|
73
|
-
|
74
|
-
<pre>
|
75
|
-
doc = st.datastreams["descMetadata"] # the datastream is the OM Document
|
76
|
-
...
|
77
|
-
doc.class
|
78
|
-
=> Hydra::ModsArticleDatastream # Hydra::ModsArticleDatastream is the Document Class
|
79
|
-
terminology = doc.class.terminology # The terminology is attached to the Document Class
|
80
|
-
</pre>
|
81
|
-
|
83
|
+
Hydra::ModsArticleDatastream has all of the behaviors of an OM::Document.
|
82
84
|
|
83
85
|
h1. Setting the XML in a NokogiriDatastream from a file
|
84
86
|
|
data/config/solr.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
development:
|
2
|
+
default:
|
3
|
+
url: http://localhost:8983/solr/development
|
4
|
+
full_text:
|
5
|
+
url: http://localhost:8983/solr/development
|
6
|
+
test:
|
7
|
+
default:
|
8
|
+
url: http://localhost:8983/solr/test
|
9
|
+
full_text:
|
10
|
+
url: http://localhost:8983/solr/test
|
11
|
+
production:
|
12
|
+
default:
|
13
|
+
url: http://localhost:8080/solr/production
|
14
|
+
full_text:
|
15
|
+
url: http://localhost:8080/solr/production
|
data/lib/active_fedora.rb
CHANGED
@@ -32,7 +32,7 @@ module ActiveFedora #:nodoc:
|
|
32
32
|
include Loggable
|
33
33
|
|
34
34
|
class << self
|
35
|
-
attr_accessor :solr_config, :fedora_config
|
35
|
+
attr_accessor :solr_config, :fedora_config, :config_env
|
36
36
|
end
|
37
37
|
|
38
38
|
# The configuration hash that gets used by RSolr.connect
|
@@ -40,13 +40,20 @@ module ActiveFedora #:nodoc:
|
|
40
40
|
@fedora_config ||= {}
|
41
41
|
|
42
42
|
# Initializes ActiveFedora's connection to Fedora and Solr based on the info in fedora.yml
|
43
|
-
# If
|
43
|
+
# If Rails.env is set, it will use that environment. Defaults to "development".
|
44
44
|
# @param [String] config_path (optional) the path to fedora.yml
|
45
45
|
# If config_path is not provided and Rails.root is set, it will look in RAILS_ENV/config/fedora.yml. Otherwise, it will look in your config/fedora.yml. Failing that, it will use localhost urls.
|
46
46
|
def self.init( config_path=nil )
|
47
47
|
|
48
|
-
|
49
|
-
|
48
|
+
if defined?(Rails.env)
|
49
|
+
@config_env = Rails.env
|
50
|
+
elsif defined?(ENV['environment'])
|
51
|
+
@config_env = ENV['environment']
|
52
|
+
else
|
53
|
+
@config_env = 'development'
|
54
|
+
logger.warn("No environment setting found: Using the default development environment.")
|
55
|
+
end
|
56
|
+
|
50
57
|
if config_path.nil?
|
51
58
|
if defined?(Rails.root)
|
52
59
|
config_path = "#{Rails.root}/config/fedora.yml"
|
@@ -62,9 +69,9 @@ module ActiveFedora #:nodoc:
|
|
62
69
|
|
63
70
|
logger.info("FEDORA: loading ActiveFedora config from #{File.expand_path(config_path)}")
|
64
71
|
fedora_config = YAML::load(File.open(config_path))
|
65
|
-
raise "The #{config_env} environment settings were not found in the fedora.yml config. If you already have a fedora.yml file defined, make sure it defines settings for the #{config_env} environment" unless fedora_config[config_env]
|
72
|
+
raise "The #{@config_env.to_s} environment settings were not found in the fedora.yml config. If you already have a fedora.yml file defined, make sure it defines settings for the #{@config_env} environment" unless fedora_config[@config_env]
|
66
73
|
|
67
|
-
ActiveFedora.solr_config[:url] = fedora_config[config_env]['solr']['url']
|
74
|
+
ActiveFedora.solr_config[:url] = fedora_config[@config_env]['solr']['url']
|
68
75
|
|
69
76
|
# Register Solr
|
70
77
|
logger.info("FEDORA: initializing ActiveFedora::SolrService with solr_config: #{ActiveFedora.solr_config.inspect}")
|
@@ -72,7 +79,7 @@ module ActiveFedora #:nodoc:
|
|
72
79
|
ActiveFedora::SolrService.register(ActiveFedora.solr_config[:url])
|
73
80
|
logger.info("FEDORA: initialized Solr with ActiveFedora.solr_config: #{ActiveFedora::SolrService.instance.inspect}")
|
74
81
|
|
75
|
-
ActiveFedora.fedora_config[:url] = fedora_config[config_env]['fedora']['url']
|
82
|
+
ActiveFedora.fedora_config[:url] = fedora_config[@config_env]['fedora']['url']
|
76
83
|
logger.info("FEDORA: initializing Fedora with fedora_config: #{ActiveFedora.fedora_config.inspect}")
|
77
84
|
|
78
85
|
Fedora::Repository.register(ActiveFedora.fedora_config[:url])
|
data/lib/active_fedora/base.rb
CHANGED
@@ -3,6 +3,7 @@ require 'active_fedora/model'
|
|
3
3
|
require 'active_fedora/semantic_node'
|
4
4
|
require "solrizer"
|
5
5
|
require 'nokogiri'
|
6
|
+
require "loggable"
|
6
7
|
|
7
8
|
SOLR_DOCUMENT_ID = "id" unless (defined?(SOLR_DOCUMENT_ID) && !SOLR_DOCUMENT_ID.nil?)
|
8
9
|
ENABLE_SOLR_UPDATES = true unless defined?(ENABLE_SOLR_UPDATES)
|
@@ -35,7 +36,8 @@ module ActiveFedora
|
|
35
36
|
include Model
|
36
37
|
include SemanticNode
|
37
38
|
include Solrizer::FieldNameMapper
|
38
|
-
|
39
|
+
include Loggable
|
40
|
+
|
39
41
|
attr_accessor :named_datastreams_desc
|
40
42
|
|
41
43
|
|
@@ -84,7 +86,7 @@ module ActiveFedora
|
|
84
86
|
#by any future instantiations.
|
85
87
|
def self.has_metadata(args, &block)
|
86
88
|
@ds_specs ||= Hash.new
|
87
|
-
@ds_specs[args[:name]]= [args[:type], block]
|
89
|
+
@ds_specs[args[:name]]= [args[:type], args.fetch(:label,""), block]
|
88
90
|
end
|
89
91
|
|
90
92
|
#Saves a Base object, and any dirty datastreams, then updates
|
@@ -1045,10 +1047,6 @@ module ActiveFedora
|
|
1045
1047
|
end
|
1046
1048
|
end
|
1047
1049
|
|
1048
|
-
def logger
|
1049
|
-
@logger ||= defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : Logger.new(STDOUT)
|
1050
|
-
end
|
1051
|
-
|
1052
1050
|
private
|
1053
1051
|
def configure_defined_datastreams
|
1054
1052
|
if self.class.ds_specs
|
@@ -1056,7 +1054,7 @@ module ActiveFedora
|
|
1056
1054
|
if self.datastreams.has_key?(name)
|
1057
1055
|
attributes = self.datastreams[name].attributes
|
1058
1056
|
else
|
1059
|
-
attributes = {:
|
1057
|
+
attributes = {:dsLabel=>ar[1]}
|
1060
1058
|
end
|
1061
1059
|
ds = ar.first.new(:dsid=>name)
|
1062
1060
|
# If you called has_metadata with a block, pass the block into the Datastream class
|
@@ -47,6 +47,18 @@ module ActiveFedora
|
|
47
47
|
self.attributes[:dsid] = dsid
|
48
48
|
end
|
49
49
|
|
50
|
+
def size
|
51
|
+
if !self.attributes.fetch(:dsSize,nil)
|
52
|
+
if self.new_object?
|
53
|
+
self.attributes[:dsSize]=nil
|
54
|
+
else
|
55
|
+
attrs = XmlSimple.xml_in(Fedora::Repository.instance.fetch_custom(self.pid,"datastreams/#{self.dsid}"))
|
56
|
+
self.attributes[:dsSize]=attrs["dsSize"].first
|
57
|
+
end
|
58
|
+
end
|
59
|
+
self.attributes[:dsSize]
|
60
|
+
end
|
61
|
+
|
50
62
|
#compatibility method for rails' url generators. This method will
|
51
63
|
#urlescape escape dots, which are apparently
|
52
64
|
#invalid characters in a dsid.
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveFedora
|
2
2
|
module SemanticNode
|
3
3
|
include MediaShelfClassLevelInheritableAttributes
|
4
|
-
ms_inheritable_attributes :class_relationships, :internal_uri
|
4
|
+
ms_inheritable_attributes :class_relationships, :internal_uri, :class_named_relationships_desc
|
5
5
|
|
6
6
|
attr_accessor :internal_uri, :named_relationship_desc, :relationships_are_dirty, :load_from_solr
|
7
7
|
|
@@ -442,54 +442,7 @@ module ActiveFedora
|
|
442
442
|
end
|
443
443
|
xml.to_s
|
444
444
|
end
|
445
|
-
|
446
|
-
# Returns a solr query for retrieving objects specified in a relationship.
|
447
|
-
# It enables the use of query_params defined within a relationship to attach a query filter
|
448
|
-
# on top of just the predicate being used.
|
449
|
-
# Instead of this method you can also use the helper method
|
450
|
-
# [relationship_name]_query, i.e. method "parts_query" for relationship "parts".
|
451
|
-
# @param [String] The name of the relationship defined in the model
|
452
|
-
# @return [String]
|
453
|
-
# @example
|
454
|
-
# Class SampleAFObjRelationshipQueryParam < ActiveFedora::Base
|
455
|
-
# #points to all parents linked via is_member_of
|
456
|
-
# has_relationship "parents", :is_member_of
|
457
|
-
# #returns only parents that have a level value set to "series"
|
458
|
-
# has_relationship "series_parents", :is_member_of, :query_params=>{:q=>{"level_t"=>"series"}}
|
459
|
-
# end
|
460
|
-
# s = SampleAFObjRelationshipQueryParam.new
|
461
|
-
# obj = ActiveFedora::Base.new
|
462
|
-
# s.parents_append(obj)
|
463
|
-
# s.series_parents_query
|
464
|
-
# #=> "(id:changeme\\:13020 AND level_t:series)"
|
465
|
-
# SampleAFObjRelationshipQueryParam.named_relationship_query("series_parents")
|
466
|
-
# #=> "(id:changeme\\:13020 AND level_t:series)"
|
467
|
-
def named_relationship_query(relationship_name)
|
468
|
-
query = ""
|
469
|
-
if self.class.is_bidirectional_relationship?(relationship_name)
|
470
|
-
id_array = []
|
471
|
-
predicate = outbound_named_relationship_predicates["#{relationship_name}_outbound"]
|
472
|
-
if !outbound_relationships[predicate].nil?
|
473
|
-
outbound_relationships[predicate].each do |rel|
|
474
|
-
id_array << rel.gsub("info:fedora/", "")
|
475
|
-
end
|
476
|
-
end
|
477
|
-
query = self.class.bidirectional_named_relationship_query(pid,relationship_name,id_array)
|
478
|
-
elsif outbound_relationship_names.include?(relationship_name)
|
479
|
-
id_array = []
|
480
|
-
predicate = outbound_named_relationship_predicates[relationship_name]
|
481
|
-
if !outbound_relationships[predicate].nil?
|
482
|
-
outbound_relationships[predicate].each do |rel|
|
483
|
-
id_array << rel.gsub("info:fedora/", "")
|
484
|
-
end
|
485
|
-
end
|
486
|
-
query = self.class.outbound_named_relationship_query(relationship_name,id_array)
|
487
|
-
elsif inbound_relationship_names.include?(relationship_name)
|
488
|
-
query = self.class.inbound_named_relationship_query(pid,relationship_name)
|
489
|
-
end
|
490
|
-
query
|
491
|
-
end
|
492
|
-
|
445
|
+
|
493
446
|
module ClassMethods
|
494
447
|
|
495
448
|
# Allows for a relationship to be treated like any other attribute of a model class. You define
|
@@ -499,10 +452,7 @@ module ActiveFedora
|
|
499
452
|
# class AudioRecord < ActiveFedora::Base
|
500
453
|
#
|
501
454
|
# has_relationship "oral_history", :has_part, :inbound=>true, :type=>OralHistory
|
502
|
-
# # returns all similar audio
|
503
455
|
# has_relationship "similar_audio", :has_part, :type=>AudioRecord
|
504
|
-
# #returns only similar audio with format wav
|
505
|
-
# has_relationship "similar_audio_wav", :has_part, :query_params=>{:q=>"format_t"=>"wav"}
|
506
456
|
#
|
507
457
|
# The first two parameters are required:
|
508
458
|
# name: relationship name
|
@@ -511,7 +461,6 @@ module ActiveFedora
|
|
511
461
|
# possible parameters
|
512
462
|
# :inbound => if true loads an external relationship via Solr (defaults to false)
|
513
463
|
# :type => The type of model to use when instantiated an object from the pid in this relationship (defaults to ActiveFedora::Base)
|
514
|
-
# :query_params => Additional filters to be attached via a solr query (currently only :q implemented)
|
515
464
|
#
|
516
465
|
# If inbound is true it expects the relationship to be defined by another object's RELS-EXT
|
517
466
|
# and to load that relationship from Solr. Otherwise, if inbound is true the relationship is stored in
|
@@ -523,24 +472,22 @@ module ActiveFedora
|
|
523
472
|
# For the oral_history relationship in the example above the following helper methods are created:
|
524
473
|
# oral_history: returns array of OralHistory objects that have this AudioRecord with predicate :has_part
|
525
474
|
# oral_history_ids: Return array of pids for OralHistory objects that have this AudioRecord with predicate :has_part
|
526
|
-
# oral_history_query: Return solr query that can be used to retrieve related objects as solr documents
|
527
475
|
#
|
528
476
|
# For the outbound relationship "similar_audio" there are two additional methods to append and remove objects from that relationship
|
529
477
|
# since it is managed internally:
|
530
478
|
# similar_audio: Return array of AudioRecord objects that have been added to similar_audio relationship
|
531
479
|
# similar_audio_ids: Return array of AudioRecord object pids that have been added to similar_audio relationship
|
532
|
-
# similar_audio_query: Return solr query that can be used to retrieve related objects as solr documents
|
533
480
|
# similar_audio_append: Add an AudioRecord object to the similar_audio relationship
|
534
481
|
# similar_audio_remove: Remove an AudioRecord from the similar_audio relationship
|
535
482
|
def has_relationship(name, predicate, opts = {})
|
536
483
|
opts = {:singular => nil, :inbound => false}.merge(opts)
|
537
484
|
if opts[:inbound] == true
|
538
|
-
|
485
|
+
raise "Duplicate use of predicate for named inbound relationship not allowed" if named_predicate_exists_with_different_name?(:inbound,name,predicate)
|
539
486
|
register_named_relationship(:inbound, name, predicate, opts)
|
540
487
|
register_predicate(:inbound, predicate)
|
541
488
|
create_inbound_relationship_finders(name, predicate, opts)
|
542
489
|
else
|
543
|
-
|
490
|
+
raise "Duplicate use of predicate for named outbound relationship not allowed" if named_predicate_exists_with_different_name?(:self,name,predicate)
|
544
491
|
register_named_relationship(:self, name, predicate, opts)
|
545
492
|
register_predicate(:self, predicate)
|
546
493
|
create_named_relationship_methods(name)
|
@@ -615,160 +562,6 @@ module ActiveFedora
|
|
615
562
|
opts.merge!({:predicate=>predicate})
|
616
563
|
named_relationships_desc[subject][name] = opts
|
617
564
|
end
|
618
|
-
|
619
|
-
# Returns a solr query for retrieving objects specified in an outbound relationship.
|
620
|
-
# This method is mostly used by internal method calls.
|
621
|
-
# It enables the use of query_params defined within a relationship to attach a query filter
|
622
|
-
# on top of just the predicate being used. Because it is static it
|
623
|
-
# needs the pids defined within RELS-EXT for this relationship to be passed in.
|
624
|
-
# If you are calling this method directly to get the query you should use the
|
625
|
-
# ActiveFedora::SemanticNode.named_relationship_query instead or use the helper method
|
626
|
-
# [relationship_name]_query, i.e. method "parts_query" for relationship "parts". This
|
627
|
-
# method would only be called directly if you had something like an array of outbound pids
|
628
|
-
# already in something like a solr document for object that has these relationships.
|
629
|
-
# @param [String] The name of the relationship defined in the model
|
630
|
-
# @param [Array] An array of pids to include in the query
|
631
|
-
# @return [String]
|
632
|
-
# @example
|
633
|
-
# Class SampleAFObjRelationshipQueryParam < ActiveFedora::Base
|
634
|
-
# #points to all parents linked via is_member_of
|
635
|
-
# has_relationship "parents", :is_member_of
|
636
|
-
# #returns only parents that have a level value set to "series"
|
637
|
-
# has_relationship "series_parents", :is_member_of, :query_params=>{:q=>{"level_t"=>"series"}}
|
638
|
-
# end
|
639
|
-
# s = SampleAFObjRelationshipQueryParam.new
|
640
|
-
# obj = ActiveFedora::Base.new
|
641
|
-
# s.series_parents_append(obj)
|
642
|
-
# s.series_parents_query
|
643
|
-
# #=> "(id:changeme\\:13020 AND level_t:series)"
|
644
|
-
# SampleAFObjRelationshipQueryParam.outbound_named_relationship_query("series_parents",["id:changeme:13020"])
|
645
|
-
# #=> "(id:changeme\\:13020 AND level_t:series)"
|
646
|
-
def outbound_named_relationship_query(relationship_name,outbound_pids)
|
647
|
-
query = ActiveFedora::SolrService.construct_query_for_pids(outbound_pids)
|
648
|
-
subject = :self
|
649
|
-
if named_relationships_desc.has_key?(subject) && named_relationships_desc[subject].has_key?(relationship_name) && named_relationships_desc[subject][relationship_name].has_key?(:query_params)
|
650
|
-
query_params = format_query_params(named_relationships_desc[subject][relationship_name][:query_params])
|
651
|
-
if query_params[:q]
|
652
|
-
unless query.empty?
|
653
|
-
#substitute in the filter query for each pid so that it is applied to each in the query
|
654
|
-
query.sub!(/OR /,"AND #{query_params[:q]}) OR (")
|
655
|
-
#add opening parenthesis for first case
|
656
|
-
query = "(" + query
|
657
|
-
#add AND filter case for last element as well since no 'OR' following it
|
658
|
-
query << " AND #{query_params[:q]})"
|
659
|
-
else
|
660
|
-
query = query_params[:q]
|
661
|
-
end
|
662
|
-
end
|
663
|
-
end
|
664
|
-
query
|
665
|
-
end
|
666
|
-
|
667
|
-
# Returns a solr query for retrieving objects specified in an inbound relationship.
|
668
|
-
# This method is mostly used by internal method calls.
|
669
|
-
# It enables the use of query_params defined within a relationship to attach a query filter
|
670
|
-
# on top of just the predicate being used. Because it is static it
|
671
|
-
# needs the pid of the object that has the inbound relationships passed in.
|
672
|
-
# If you are calling this method directly to get the query you should use the
|
673
|
-
# ActiveFedora::SemanticNode.named_relationship_query instead or use the helper method
|
674
|
-
# [relationship_name]_query, i.e. method "parts_query" for relationship "parts". This
|
675
|
-
# method would only be called directly if you were working only with Solr and already
|
676
|
-
# had the pid for the object in something like a solr document.
|
677
|
-
# @param [String] The pid for the object that has these inbound relationships
|
678
|
-
# @param [String] The name of the relationship defined in the model
|
679
|
-
# @return [String]
|
680
|
-
# @example
|
681
|
-
# Class SampleAFObjRelationshipQueryParam < ActiveFedora::Base
|
682
|
-
# #returns all parts
|
683
|
-
# has_relationship "parts", :is_part_of, :inbound=>true
|
684
|
-
# #returns only parts that have level to "series"
|
685
|
-
# has_relationship "series_parts", :is_part_of, :inbound=>true, :query_params=>{:q=>{"level_t"=>"series"}}
|
686
|
-
# end
|
687
|
-
# s = SampleAFObjRelationshipQueryParam.new
|
688
|
-
# s.pid
|
689
|
-
# #=> id:changeme:13020
|
690
|
-
# s.series_parts_query
|
691
|
-
# #=> "is_part_of_s:info\\:fedora/changeme\\:13021 AND level_t:series"
|
692
|
-
# SampleAFObjRelationshipQueryParam.inbound_named_relationship_query(s.pid,"series_parts")
|
693
|
-
# #=> "is_part_of_s:info\\:fedora/changeme\\:13021 AND level_t:series"
|
694
|
-
def inbound_named_relationship_query(pid,relationship_name)
|
695
|
-
query = ""
|
696
|
-
subject = :inbound
|
697
|
-
if named_relationships_desc.has_key?(subject) && named_relationships_desc[subject].has_key?(relationship_name)
|
698
|
-
predicate = named_relationships_desc[subject][relationship_name][:predicate]
|
699
|
-
internal_uri = "info:fedora/#{pid}"
|
700
|
-
escaped_uri = internal_uri.gsub(/(:)/, '\\:')
|
701
|
-
query = "#{predicate}_s:#{escaped_uri}"
|
702
|
-
if named_relationships_desc.has_key?(subject) && named_relationships_desc[subject].has_key?(relationship_name) && named_relationships_desc[subject][relationship_name].has_key?(:query_params)
|
703
|
-
query_params = format_query_params(named_relationships_desc[subject][relationship_name][:query_params])
|
704
|
-
if query_params[:q]
|
705
|
-
query << " AND " unless query.empty?
|
706
|
-
query << query_params[:q]
|
707
|
-
end
|
708
|
-
end
|
709
|
-
end
|
710
|
-
query
|
711
|
-
end
|
712
|
-
|
713
|
-
# Returns a solr query for retrieving objects specified in a bidirectional relationship.
|
714
|
-
# This method is mostly used by internal method calls.
|
715
|
-
# It enables the use of query_params defined within a relationship to attach a query filter
|
716
|
-
# on top of just the predicate being used. Because it is static it
|
717
|
-
# needs the pids defined within RELS-EXT for the outbound relationship as well as the pid of the
|
718
|
-
# object for the inbound portion of the relationship.
|
719
|
-
# If you are calling this method directly to get the query you should use the
|
720
|
-
# ActiveFedora::SemanticNode.named_relationship_query instead or use the helper method
|
721
|
-
# [relationship_name]_query, i.e. method "bi_parts_query" for relationship "bi_parts". This
|
722
|
-
# method would only be called directly if you had something like an array of outbound pids
|
723
|
-
# already in something like a solr document for object that has these relationships.
|
724
|
-
# @param [String] The pid for the object that has these inbound relationships
|
725
|
-
# @param [String] The name of the relationship defined in the model
|
726
|
-
# @param [Array] An array of pids to include in the query
|
727
|
-
# @return [String]
|
728
|
-
# @example
|
729
|
-
# Class SampleAFObjRelationshipQueryParam < ActiveFedora::Base
|
730
|
-
# has_bidirectional_relationship "bi_series_parts", :has_part, :is_part_of, :query_params=>{:q=>{"level_t"=>"series"}}
|
731
|
-
# end
|
732
|
-
# s = SampleAFObjRelationshipQueryParam.new
|
733
|
-
# obj = ActiveFedora::Base.new
|
734
|
-
# s.bi_series_parts_append(obj)
|
735
|
-
# s.pid
|
736
|
-
# #=> "changeme:13025"
|
737
|
-
# obj.pid
|
738
|
-
# #=> id:changeme:13026
|
739
|
-
# s.bi_series_parts_query
|
740
|
-
# #=> "(id:changeme\\:13026 AND level_t:series) OR (is_part_of_s:info\\:fedora/changeme\\:13025 AND level_t:series)"
|
741
|
-
# SampleAFObjRelationshipQueryParam.bidirectional_named_relationship_query(s.pid,"series_parents",["id:changeme:13026"])
|
742
|
-
# #=> "(id:changeme\\:13026 AND level_t:series) OR (is_part_of_s:info\\:fedora/changeme\\:13025 AND level_t:series)"
|
743
|
-
def bidirectional_named_relationship_query(pid,relationship_name,outbound_pids)
|
744
|
-
outbound_named_relationship_query("#{relationship_name}_outbound",outbound_pids) + " OR (" + inbound_named_relationship_query(pid,"#{relationship_name}_inbound") + ")"
|
745
|
-
end
|
746
|
-
|
747
|
-
# This will transform and encode any query_params defined in a relationship method to properly escape special characters
|
748
|
-
# and format strings such as query string properly for a solr query
|
749
|
-
# @param [Hash] The has of expected query params (including at least :q)
|
750
|
-
# @return [String]
|
751
|
-
def format_query_params(query_params)
|
752
|
-
if query_params && query_params[:q]
|
753
|
-
add_query = ""
|
754
|
-
if query_params[:q].is_a? Hash
|
755
|
-
query_params[:q].keys.each_with_index do |key,index|
|
756
|
-
add_query << " AND " if index > 0
|
757
|
-
add_query << "#{key}:#{query_params[:q][key].gsub(/:/, '\\\\:')}"
|
758
|
-
end
|
759
|
-
elsif !query_params[:q].empty?
|
760
|
-
add_query = "#{query_params[:q]}"
|
761
|
-
end
|
762
|
-
query_params[:q] = add_query unless add_query.empty?
|
763
|
-
query_params
|
764
|
-
end
|
765
|
-
end
|
766
|
-
|
767
|
-
#Tests if the relationship name passed is in bidirectional
|
768
|
-
# @return [Boolean]
|
769
|
-
def is_bidirectional_relationship?(relationship_name)
|
770
|
-
named_relationships_desc[:self]["#{relationship_name}_outbound"] && named_relationships_desc[:inbound]["#{relationship_name}_inbound"]
|
771
|
-
end
|
772
565
|
|
773
566
|
# ** EXPERIMENTAL **
|
774
567
|
#
|
@@ -808,8 +601,8 @@ module ActiveFedora
|
|
808
601
|
class_eval <<-END
|
809
602
|
def #{name}(opts={})
|
810
603
|
opts = {:rows=>25}.merge(opts)
|
811
|
-
|
812
|
-
solr_result = SolrService.instance.conn.query(
|
604
|
+
escaped_uri = self.internal_uri.gsub(/(:)/, '\\:')
|
605
|
+
solr_result = SolrService.instance.conn.query("#{predicate}_s:\#{escaped_uri}", :rows=>opts[:rows])
|
813
606
|
if opts[:response_format] == :solr
|
814
607
|
return solr_result
|
815
608
|
else
|
@@ -832,18 +625,8 @@ module ActiveFedora
|
|
832
625
|
def #{name}_from_solr
|
833
626
|
#{name}(:response_format => :load_from_solr)
|
834
627
|
end
|
835
|
-
def #{name}_solr_docs
|
836
|
-
#{name}(:response_format => :solr)
|
837
|
-
end
|
838
|
-
def #{name}_query
|
839
|
-
named_relationship_query("#{name}")
|
840
|
-
end
|
841
628
|
END
|
842
629
|
end
|
843
|
-
|
844
|
-
def relationship_has_query_params?(subject, relationship_name)
|
845
|
-
named_relationships_desc.has_key?(subject) && named_relationships_desc[subject].has_key?(relationship_name) && named_relationships_desc[subject][relationship_name].has_key?(:query_params)
|
846
|
-
end
|
847
630
|
|
848
631
|
def create_outbound_relationship_finders(name, predicate, opts = {})
|
849
632
|
class_eval <<-END
|
@@ -854,20 +637,13 @@ module ActiveFedora
|
|
854
637
|
id_array << rel.gsub("info:fedora/", "")
|
855
638
|
end
|
856
639
|
end
|
857
|
-
|
858
|
-
if opts[:response_format] == :id_array && !self.class.relationship_has_query_params?(:self,"#{name}")
|
640
|
+
if opts[:response_format] == :id_array
|
859
641
|
return id_array
|
860
642
|
else
|
861
|
-
query =
|
643
|
+
query = ActiveFedora::SolrService.construct_query_for_pids(id_array)
|
862
644
|
solr_result = SolrService.instance.conn.query(query)
|
863
645
|
if opts[:response_format] == :solr
|
864
646
|
return solr_result
|
865
|
-
elsif opts[:response_format] == :id_array
|
866
|
-
id_array = []
|
867
|
-
solr_result.hits.each do |hit|
|
868
|
-
id_array << hit[SOLR_DOCUMENT_ID]
|
869
|
-
end
|
870
|
-
return id_array
|
871
647
|
elsif opts[:response_format] == :load_from_solr || self.load_from_solr
|
872
648
|
return ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
|
873
649
|
else
|
@@ -881,15 +657,9 @@ module ActiveFedora
|
|
881
657
|
def #{name}_from_solr
|
882
658
|
#{name}(:response_format => :load_from_solr)
|
883
659
|
end
|
884
|
-
def #{name}_solr_docs
|
885
|
-
#{name}(:response_format => :solr)
|
886
|
-
end
|
887
|
-
def #{name}_query
|
888
|
-
named_relationship_query("#{name}")
|
889
|
-
end
|
890
660
|
END
|
891
661
|
end
|
892
|
-
|
662
|
+
|
893
663
|
# Generates relationship finders for predicates that point in both directions
|
894
664
|
# and registers predicate relationships for each direction.
|
895
665
|
#
|
@@ -910,15 +680,12 @@ module ActiveFedora
|
|
910
680
|
def #{name}(opts={})
|
911
681
|
opts = {:rows=>25}.merge(opts)
|
912
682
|
if opts[:response_format] == :solr || opts[:response_format] == :load_from_solr
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
end
|
920
|
-
#outbound_id_array = #{outbound_method_name}(:response_format=>:id_array)
|
921
|
-
query = self.class.bidirectional_named_relationship_query(self.pid,"#{name}",outbound_id_array)
|
683
|
+
escaped_uri = self.internal_uri.gsub(/(:)/, '\\:')
|
684
|
+
query = "#{inbound_predicate}_s:\#{escaped_uri}"
|
685
|
+
|
686
|
+
outbound_id_array = #{outbound_method_name}(:response_format=>:id_array)
|
687
|
+
query = query + " OR " + ActiveFedora::SolrService.construct_query_for_pids(outbound_id_array)
|
688
|
+
|
922
689
|
solr_result = SolrService.instance.conn.query(query, :rows=>opts[:rows])
|
923
690
|
|
924
691
|
if opts[:response_format] == :solr
|
@@ -939,12 +706,6 @@ module ActiveFedora
|
|
939
706
|
def #{name}_from_solr
|
940
707
|
#{name}(:response_format => :load_from_solr)
|
941
708
|
end
|
942
|
-
def #{name}_solr_docs
|
943
|
-
#{name}(:response_format => :solr)
|
944
|
-
end
|
945
|
-
def #{name}_query
|
946
|
-
named_relationship_query("#{name}")
|
947
|
-
end
|
948
709
|
END
|
949
710
|
end
|
950
711
|
|