active-fedora 1.1.13 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,9 @@
1
1
  require 'solr'
2
- require "active_fedora/solr_mapper"
3
- require "yaml"
4
- module ActiveFedora
5
- class SolrService
2
+ require "solrizer/field_name_mapper"
3
+
4
+ module ActiveFedora
5
+ class SolrService
6
6
 
7
- @@mappings = {}
8
7
  attr_reader :conn
9
8
 
10
9
  def self.register(host=nil, args={})
@@ -22,19 +21,23 @@ module ActiveFedora
22
21
  Thread.current[:solr_service]
23
22
  end
24
23
 
25
- def self.reify_solr_results(solr_result)
24
+ def self.reify_solr_results(solr_result,opts={})
26
25
  unless solr_result.is_a?(Solr::Response::Standard)
27
26
  raise ArgumentError.new("Only solr responses (Solr::Response::Standard) are allowed. You provided a #{solr_result.class}")
28
27
  end
29
28
  results = []
30
29
  solr_result.hits.each do |hit|
31
- model_value = hit[ActiveFedora::SolrMapper.solr_name("active_fedora_model", :symbol)].first
30
+ model_value = hit[Solrizer::FieldNameMapper.solr_name("active_fedora_model", :symbol)].first
32
31
  if model_value.include?("::")
33
32
  classname = eval(model_value)
34
33
  else
35
34
  classname = Kernel.const_get(model_value)
36
35
  end
37
- results << Fedora::Repository.instance.find_model(hit[SOLR_DOCUMENT_ID], classname)
36
+ if opts[:load_from_solr]
37
+ results << classname.load_instance_from_solr(hit[SOLR_DOCUMENT_ID])
38
+ else
39
+ results << Fedora::Repository.instance.find_model(hit[SOLR_DOCUMENT_ID], classname)
40
+ end
38
41
  end
39
42
  return results
40
43
  end
@@ -54,40 +57,22 @@ module ActiveFedora
54
57
  end
55
58
 
56
59
  def self.mappings
57
- @@mappings
60
+ Solrizer::FieldNameMapper.mappings
58
61
  end
59
62
  def self.mappings=(mappings)
60
- @@mappings = mappings
61
- end
62
-
63
+ Solrizer::FieldNameMapper.mappings = mappings
64
+ end
65
+
63
66
  def self.logger
64
67
  @logger ||= defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : Logger.new(STDOUT)
65
68
  end
66
69
 
67
- # Loads solr mappings from yml file.
68
- # @config_path This is the path to the directory where your mappings file is stored. @default "RAILS_ROOT/config/solr_mappings.yml"
69
- # @mappings_file This is the filename for your solr mappings YAML file. @default solr_mappings.yml
70
+ # (re)load solr field name mappings
70
71
  def self.load_mappings( config_path=nil )
71
-
72
- if config_path.nil?
73
- if defined?(RAILS_ROOT)
74
- config_path = File.join(RAILS_ROOT, "config", "solr_mappings.yml")
75
- end
76
- # Default to using the config file within the gem
77
- if !File.exist?(config_path.to_s)
78
- config_path = File.join(File.dirname(__FILE__), "..", "..", "config", "solr_mappings.yml")
79
- end
80
- end
81
-
82
- logger.info("FEDORA: loading SolrService mappings from #{File.expand_path(config_path)}")
83
-
84
- @@mappings = YAML::load(File.open(config_path))
85
-
86
- mappings["id"] = "id" unless mappings["id"]
72
+ Solrizer::FieldNameMapper.load_mappings(config_path)
87
73
  end
88
74
 
89
- self.load_mappings
90
-
91
- end
92
- class SolrNotInitialized < StandardError;end
93
- end
75
+
76
+ end #SolrService
77
+ class SolrNotInitialized < StandardError;end
78
+ end #ActiveFedora
@@ -186,9 +186,13 @@ module Fedora
186
186
  # Creates new Net::HTTP instance for communication with
187
187
  # remote service and resources.
188
188
  def http
189
- http = Net::HTTP.new(@site.host, @site.port)
190
- http.use_ssl = @site.is_a?(URI::HTTPS)
191
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
189
+ http = Net::HTTP.new(@site.host, @site.port)
190
+ if(@site.is_a?(URI::HTTPS) && !SSL_CLIENT_CERT_FILE.nil? && !SSL_CLIENT_KEY_FILE.nil? && !SSL_CLIENT_KEY_PASS.nil?)
191
+ http.use_ssl = true
192
+ http.cert = OpenSSL::X509::Certificate.new( File.read(SSL_CLIENT_CERT_FILE) )
193
+ http.key = OpenSSL::PKey::RSA.new( File.read(SSL_CLIENT_KEY_FILE), SSL_CLIENT_KEY_PASS )
194
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
195
+ end
192
196
  http
193
197
  end
194
198
 
@@ -1,100 +1,63 @@
1
1
  require "hydra"
2
2
  module Hydra
3
3
  class Hydra::SampleModsDatastream < ActiveFedora::NokogiriDatastream
4
-
5
- # have to call this in order to set namespace & schema
6
- root_property :mods, "mods", "http://www.loc.gov/mods/v3", :attributes=>["id", "version"], :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd"
7
-
8
- property :title_info, :path=>"titleInfo",
9
- :convenience_methods => {
10
- :main_title => {:path=>"title"},
11
- :language => {:path=>{:attribute=>"lang"}},
12
- }
13
- property :abstract, :path=>"abstract"
14
- property :topic_tag, :path=>'subject',:default_content_path => "topic"
15
-
16
- property :name_, :path=>"name",
17
- :attributes=>[:xlink, :lang, "xml:lang", :script, :transliteration, {:type=>["personal", "enumerated", "corporate"]} ],
18
- :subelements=>["namePart", "displayForm", "affiliation", :role, "description"],
19
- :default_content_path => "namePart",
20
- :convenience_methods => {
21
- :date => {:path=>"namePart", :attributes=>{:type=>"date"}},
22
- :family_name => {:path=>"namePart", :attributes=>{:type=>"family"}},
23
- :first_name => {:path=>"namePart", :attributes=>{:type=>"given"}},
24
- :terms_of_address => {:path=>"namePart", :attributes=>{:type=>"termsOfAddress"}}
25
- }
26
-
27
- property :person, :variant_of=>:name_, :attributes=>{:type=>"personal"}
28
- property :organizaton, :variant_of=>:name_, :attributes=>{:type=>"institutional"}
29
- property :conference, :variant_of=>:name_, :attributes=>{:type=>"conference"}
30
-
31
- property :role, :path=>"role",
32
- :parents=>[:name_],
33
- :convenience_methods => {
34
- :text => {:path=>"roleTerm", :attributes=>{:type=>"text"}},
35
- :code => {:path=>"roleTerm", :attributes=>{:type=>"code"}},
36
- }
37
-
38
- property :journal, :path=>'relatedItem', :attributes=>{:type=>"host"},
39
- :subelements=>[:title_info, :origin_info, :issue],
40
- :convenience_methods => {
41
- :issn => {:path=>"identifier", :attributes=>{:type=>"issn"}},
42
- }
43
-
44
- property :origin_info, :path=>'originInfo',
45
- :subelements=>["publisher","dateIssued"]
46
-
47
- property :issue, :path=>'part',
48
- :subelements=>[:start_page, :end_page],
49
- :convenience_methods => {
50
- :volume => {:path=>"detail", :attributes=>{:type=>"volume"}},
51
- :level => {:path=>"detail", :attributes=>{:type=>"level"}},
52
- :publication_date => {:path=>"date"}
53
- }
54
- property :start_page, :path=>"extent", :attributes=>{:unit=>"pages"}, :default_content_path => "start"
55
- property :end_page, :path=>"extent", :attributes=>{:unit=>"pages"}, :default_content_path => "end"
56
-
57
- generate_accessors_from_properties
58
- # accessor :title_info, :relative_xpath=>'oxns:titleInfo', :children=>[
59
- # {:main_title=>{:relative_xpath=>'oxns:title'}},
60
- # {:language =>{:relative_xpath=>{:attribute=>"lang"} }}
61
- # ]
62
- # accessor :abstract
63
- # accessor :topic_tag, :relative_xpath=>'oxns:subject/oxns:topic'
64
- # accessor :person, :relative_xpath=>'oxns:name[@type="personal"]', :children=>[
65
- # {:last_name=>{:relative_xpath=>'oxns:namePart[@type="family"]'}},
66
- # {:first_name=>{:relative_xpath=>'oxns:namePart[@type="given"]'}},
67
- # {:institution=>{:relative_xpath=>'oxns:affiliation'}},
68
- # {:role=>{:children=>[
69
- # {:text=>{:relative_xpath=>'oxns:roleTerm[@type="text"]'}},
70
- # {:code=>{:relative_xpath=>'oxns:roleTerm[@type="code"]'}}
71
- # ]}}
72
- # ]
73
- # accessor :organization, :relative_xpath=>'oxns:name[@type="institutional"]', :children=>[
74
- # {:role=>{:children=>[
75
- # {:text=>{:relative_xpath=>'oxns:roleTerm[@type="text"]'}},
76
- # {:code=>{:relative_xpath=>'oxns:roleTerm[@type="code"]'}}
77
- # ]}}
78
- # ]
79
- # accessor :conference, :relative_xpath=>'oxns:name[@type="conference"]', :children=>[
80
- # {:role=>{:children=>[
81
- # {:text=>{:relative_xpath=>'oxns:roleTerm[@type="text"]'}},
82
- # {:code=>{:relative_xpath=>'oxns:roleTerm[@type="code"]'}}
83
- # ]}}
84
- # ]
85
- # accessor :journal, :relative_xpath=>'oxns:relatedItem[@type="host"]', :children=>[
86
- # {:title=>{:relative_xpath=>'oxns:titleInfo/oxns:title'}},
87
- # {:publisher=>{:relative_xpath=>'oxns:originInfo/oxns:publisher'}},
88
- # {:issn=>{:relative_xpath=>'oxns:identifier[@type="issn"]'}},
89
- # {:date_issued=>{:relative_xpath=>'oxns:originInfo/oxns:dateIssued'}},
90
- # {:issue => {:relative_xpath=>"oxns:part", :children=>[
91
- # {:volume=>{:relative_xpath=>'oxns:detail[@type="volume"]'}},
92
- # {:level=>{:relative_xpath=>'oxns:detail[@type="level"]'}},
93
- # {:start_page=>{:relative_xpath=>'oxns:extent[@unit="pages"]/oxns:start'}},
94
- # {:end_page=>{:relative_xpath=>'oxns:extent[@unit="pages"]/oxns:end'}},
95
- # {:publication_date=>{:relative_xpath=>'oxns:date'}}
96
- # ]}}
97
- # ]
4
+
5
+ set_terminology do |t|
6
+ t.root(:path=>"mods", :xmlns=>"http://www.loc.gov/mods/v3", :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd")
7
+
8
+ t.title_info(:path=>"titleInfo") {
9
+ t.main_title(:path=>"title", :label=>"title")
10
+ t.language(:path=>{:attribute=>"lang"})
11
+ }
12
+ t.abstract
13
+ t.topic_tag(:path=>"subject", :default_content_path=>"topic")
14
+ # This is a mods:name. The underscore is purely to avoid namespace conflicts.
15
+ t.name_ {
16
+ # this is a namepart
17
+ t.namePart(:index_as=>[:searchable, :displayable, :facetable, :sortable], :required=>:true, :type=>:string, :label=>"generic name")
18
+ # affiliations are great
19
+ t.affiliation
20
+ t.displayForm
21
+ t.role(:ref=>[:role])
22
+ t.description
23
+ t.date(:path=>"namePart", :attributes=>{:type=>"date"})
24
+ t.last_name(:path=>"namePart", :attributes=>{:type=>"family"})
25
+ t.first_name(:path=>"namePart", :attributes=>{:type=>"given"}, :label=>"first name")
26
+ t.terms_of_address(:path=>"namePart", :attributes=>{:type=>"termsOfAddress"})
27
+ }
28
+ # lookup :person, :first_name
29
+ t.person(:ref=>:name, :attributes=>{:type=>"personal"})
30
+ t.organizaton(:ref=>:name, :attributes=>{:type=>"institutional"})
31
+ t.conference(:ref=>:name, :attributes=>{:type=>"conference"})
32
+
33
+ t.role {
34
+ t.text(:path=>"roleTerm",:attributes=>{:type=>"text"})
35
+ t.code(:path=>"roleTerm",:attributes=>{:type=>"code"})
36
+ }
37
+ t.journal(:path=>'relatedItem', :attributes=>{:type=>"host"}) {
38
+ t.title_info
39
+ t.origin_info(:path=>"originInfo") {
40
+ t.publisher
41
+ t.date_issued(:path=>"dateIssued")
42
+ }
43
+ t.issn(:path=>"identifier", :attributes=>{:type=>"issn"})
44
+ t.issue(:path=>"part") {
45
+ t.volume(:path=>"detail", :attributes=>{:type=>"volume"}, :default_content_path=>"number")
46
+ t.level(:path=>"detail", :attributes=>{:type=>"number"}, :default_content_path=>"number")
47
+ t.extent
48
+ t.pages(:path=>"extent", :attributes=>{:type=>"pages"}) {
49
+ t.start
50
+ t.end
51
+ }
52
+ t.publication_date(:path=>"date")
53
+ }
54
+ }
55
+
56
+ end
57
+
58
+ # Changes from OM::Properties implementation
59
+ # renamed family_name => last_name
60
+ # start_page & end_page now accessible as [:journal, :issue, :pages, :start] (etc.)
98
61
 
99
62
  end
100
63
  end
@@ -0,0 +1,215 @@
1
+ require File.join( File.dirname(__FILE__), "..", "spec_helper" )
2
+
3
+ class MockAFBaseQuerySolr < ActiveFedora::Base
4
+ has_metadata :name => "properties", :type => ActiveFedora::MetadataDatastream do |m|
5
+ m.field "holding_id", :string
6
+ end
7
+
8
+ has_metadata :name => "descMetadata", :type => ActiveFedora::QualifiedDublinCoreDatastream do |m|
9
+ m.field "created", :date, :xml_node => "created"
10
+ m.field "language", :string, :xml_node => "language"
11
+ m.field "creator", :string, :xml_node => "creator"
12
+ # Created remaining fields
13
+ m.field "geography", :string, :xml_node => "geography"
14
+ m.field "title", :string, :xml_node => "title"
15
+ end
16
+ end
17
+
18
+ describe ActiveFedora::Base do
19
+
20
+ before(:each) do
21
+ @test_object = ActiveFedora::Base.new
22
+ @test_object.new_object = true
23
+ end
24
+
25
+ after(:each) do
26
+ begin
27
+ @test_object.delete
28
+ rescue
29
+ end
30
+ begin
31
+ @test_object2.delete
32
+ rescue
33
+ end
34
+ begin
35
+ @test_object3.delete
36
+ rescue
37
+ end
38
+ begin
39
+ @test_object4.delete
40
+ rescue
41
+ end
42
+ begin
43
+ @test_object5.delete
44
+ rescue
45
+ end
46
+ end
47
+
48
+ describe '#find_by_fields_by_solr' do
49
+ it 'should return fedora objects of the model of self that match the given solr query, queries the active_fedora solr instance' do
50
+ #get objects into fedora and solr
51
+ @test_object2 = MockAFBaseQuerySolr.new
52
+ @test_object2.new_object = true
53
+ attributes = {"holding_id"=>{0=>"Holding 1"},
54
+ "language"=>{0=>"Italian"},
55
+ "creator"=>{0=>"Linguist, A."},
56
+ "geography"=>{0=>"Italy"},
57
+ "title"=>{0=>"Italian and Spanish: A Comparison of Common Phrases"}}
58
+ @test_object2.update_indexed_attributes(attributes)
59
+ @test_object2.save
60
+
61
+ @test_object3 = MockAFBaseQuerySolr.new
62
+ @test_object3.new_object = true
63
+ attributes = {"holding_id"=>{0=>"Holding 2"},
64
+ "language"=>{0=>"Spanish;Latin"},
65
+ "creator"=>{0=>"Linguist, A."},
66
+ "geography"=>{0=>"Spain"},
67
+ "title"=>{0=>"A study of the evolution of Spanish from Latin"}}
68
+ @test_object3.update_indexed_attributes(attributes)
69
+ @test_object3.save
70
+
71
+ @test_object4 = MockAFBaseQuerySolr.new
72
+ @test_object4.new_object = true
73
+ attributes = {"holding_id"=>{0=>"Holding 3"},
74
+ "language"=>{0=>"Spanish;Latin"},
75
+ "creator"=>{0=>"Linguist, A."},
76
+ "geography"=>{0=>"Spain"},
77
+ "title"=>{0=>"An obscure look into early nomadic tribes of Spain"}}
78
+ @test_object4.update_indexed_attributes(attributes)
79
+ @test_object4.save
80
+
81
+ #query based on just model
82
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({})
83
+ found_pids = []
84
+ results.hits.each do |hit|
85
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
86
+ end
87
+
88
+ found_pids.should == [@test_object2.pid,@test_object3.pid,@test_object4.pid]
89
+
90
+ #query on certain fields
91
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"language"=>"Latin"})
92
+ found_pids = []
93
+ results.hits.each do |hit|
94
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
95
+ end
96
+
97
+ found_pids.should == [@test_object3.pid,@test_object4.pid]
98
+
99
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"language"=>"Italian"})
100
+ found_pids = []
101
+ results.hits.each do |hit|
102
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
103
+ end
104
+
105
+ found_pids.should== [@test_object2.pid]
106
+
107
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"language"=>"Spanish"})
108
+ found_pids = []
109
+ results.hits.each do |hit|
110
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
111
+ end
112
+
113
+ found_pids.should == [@test_object3.pid,@test_object4.pid]
114
+
115
+ #assume spaces removed at index time so query by 'Linguist,A.' instead of 'Linguist, A.'
116
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"creator"=>"Linguist,A."})
117
+ found_pids = []
118
+ results.hits.each do |hit|
119
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
120
+ end
121
+
122
+ found_pids.should == [@test_object2.pid,@test_object3.pid,@test_object4.pid]
123
+
124
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"geography"=>"Italy"})
125
+ found_pids = []
126
+ results.hits.each do |hit|
127
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
128
+ end
129
+
130
+ found_pids.should == [@test_object2.pid]
131
+
132
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"creator"=>"Linguist,A.","title"=>"latin"})
133
+ found_pids = []
134
+ results.hits.each do |hit|
135
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
136
+ end
137
+
138
+ found_pids.should == [@test_object3.pid]
139
+
140
+ #query with value with embedded ':' (pid)
141
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"id"=>@test_object3.pid})
142
+ found_pids = []
143
+ results.hits.each do |hit|
144
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
145
+ end
146
+
147
+ found_pids.should == [@test_object3.pid]
148
+
149
+ #query with different options
150
+ #sort defaults to system_create_date
151
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"creator"=>"Linguist,A.","language"=>"Spanish"})
152
+ found_pids = []
153
+ results.hits.each do |hit|
154
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
155
+ end
156
+
157
+ found_pids.should == [@test_object3.pid,@test_object4.pid]
158
+
159
+ #change sort direction
160
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"creator"=>"Linguist,A."},{:sort=>[{"system_create"=>"desc"}]})
161
+ found_pids = []
162
+ results.hits.each do |hit|
163
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
164
+ end
165
+
166
+ found_pids.should == [@test_object4.pid,@test_object3.pid,@test_object2.pid]
167
+
168
+ #pass in sort without direction defined and make ascending by default
169
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"creator"=>"Linguist,A."},{:sort=>["system_create"]})
170
+ found_pids = []
171
+ results.hits.each do |hit|
172
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
173
+ end
174
+
175
+ found_pids.should == [@test_object2.pid,@test_object3.pid,@test_object4.pid]
176
+
177
+ #sort on multiple fields
178
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"creator"=>"Linguist,A."},{:sort=>["geography",{"system_create"=>"desc"}]})
179
+ found_pids = []
180
+ results.hits.each do |hit|
181
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
182
+ end
183
+
184
+ found_pids.should == [@test_object2.pid,@test_object4.pid,@test_object3.pid]
185
+
186
+ #check appropriate logic for system_modified_date field name transformation
187
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"creator"=>"Linguist,A."},{:sort=>["geography",{"system_mod"=>"desc"}]})
188
+ found_pids = []
189
+ results.hits.each do |hit|
190
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
191
+ end
192
+
193
+ found_pids.should == [@test_object2.pid,@test_object4.pid,@test_object3.pid]
194
+
195
+ #check pass in rows values
196
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"creator"=>"Linguist,A."},{:rows=>2})
197
+ found_pids = []
198
+ results.hits.each do |hit|
199
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
200
+ end
201
+
202
+ found_pids.should == [@test_object2.pid,@test_object3.pid]
203
+
204
+ #check query with field mapping to solr field and with solr field that is not a field in object
205
+ #should be able to query by either active fedora model field name or solr key name
206
+ results = MockAFBaseQuerySolr.find_by_fields_by_solr({"geography_t"=>"Italy"})
207
+ found_pids = []
208
+ results.hits.each do |hit|
209
+ found_pids.push(hit[SOLR_DOCUMENT_ID])
210
+ end
211
+
212
+ found_pids.should == [@test_object2.pid]
213
+ end
214
+ end
215
+ end