active-fedora 7.0.0.pre2 → 7.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -7
  3. data/active-fedora.gemspec +2 -2
  4. data/lib/active_fedora/associations/builder/belongs_to.rb +1 -0
  5. data/lib/active_fedora/associations/has_many_association.rb +3 -2
  6. data/lib/active_fedora/core.rb +9 -0
  7. data/lib/active_fedora/datastream.rb +13 -0
  8. data/lib/active_fedora/datastream_attribute.rb +10 -9
  9. data/lib/active_fedora/datastream_hash.rb +7 -0
  10. data/lib/active_fedora/datastreams.rb +1 -1
  11. data/lib/active_fedora/indexing.rb +1 -2
  12. data/lib/active_fedora/om_datastream.rb +9 -1
  13. data/lib/active_fedora/persistence.rb +2 -5
  14. data/lib/active_fedora/querying.rb +5 -1
  15. data/lib/active_fedora/rdf/indexing.rb +18 -10
  16. data/lib/active_fedora/relation/finder_methods.rb +5 -5
  17. data/lib/active_fedora/rels_ext_datastream.rb +4 -24
  18. data/lib/active_fedora/semantic_node.rb +24 -9
  19. data/lib/active_fedora/solr_instance_loader.rb +1 -7
  20. data/lib/active_fedora/solr_service.rb +106 -98
  21. data/lib/active_fedora/version.rb +1 -1
  22. data/lib/generators/active_fedora/config/solr/templates/solr_conf/conf/schema.xml +5 -0
  23. data/lib/generators/active_fedora/config/solr/templates/solr_conf/conf/solrconfig.xml +51 -0
  24. data/spec/integration/has_many_associations_spec.rb +7 -0
  25. data/spec/integration/scoped_query_spec.rb +1 -1
  26. data/spec/integration/solr_service_spec.rb +6 -6
  27. data/spec/unit/base_extra_spec.rb +4 -4
  28. data/spec/unit/base_spec.rb +2 -2
  29. data/spec/unit/core_spec.rb +77 -0
  30. data/spec/unit/ntriples_datastream_spec.rb +4 -4
  31. data/spec/unit/om_datastream_spec.rb +40 -4
  32. data/spec/unit/query_spec.rb +19 -19
  33. data/spec/unit/rels_ext_datastream_spec.rb +3 -2
  34. data/spec/unit/solr_config_options_spec.rb +1 -4
  35. data/spec/unit/solr_service_spec.rb +7 -1
  36. metadata +10 -8
@@ -66,13 +66,7 @@ module ActiveFedora
66
66
  end
67
67
 
68
68
  def active_fedora_class
69
- @active_fedora_class ||= begin
70
- if solr_doc[ActiveFedora::SolrService.solr_name('has_model', :symbol)]
71
- ActiveFedora::SolrService.class_from_solr_document(solr_doc)
72
- else
73
- ActiveFedora::Base
74
- end
75
- end
69
+ @active_fedora_class ||= ActiveFedora::SolrService.class_from_solr_document(solr_doc)
76
70
  end
77
71
 
78
72
  def profile_json
@@ -2,19 +2,12 @@ require 'rsolr'
2
2
 
3
3
  module ActiveFedora
4
4
  class SolrService
5
+ extend Deprecation
5
6
 
6
7
  include Loggable
7
8
 
8
9
  attr_reader :conn
9
10
 
10
- def self.register(host=nil, args={})
11
- Thread.current[:solr_service]=self.new(host, args)
12
- end
13
-
14
- def self.reset!
15
- Thread.current[:solr_service] = nil
16
- end
17
-
18
11
  def initialize(host, args)
19
12
  host = 'http://localhost:8080/solr' unless host
20
13
  args = {:read_timeout => 120, :open_timeout => 120}.merge(args.dup)
@@ -22,120 +15,135 @@ module ActiveFedora
22
15
  @conn = RSolr.connect args
23
16
  end
24
17
 
25
- def self.instance
26
- # Register Solr
27
-
28
- unless Thread.current[:solr_service]
29
- register(ActiveFedora.solr_config[:url])
18
+ class << self
19
+ def register(host=nil, args={})
20
+ Thread.current[:solr_service] = new(host, args)
30
21
  end
31
22
 
32
- raise SolrNotInitialized unless Thread.current[:solr_service]
33
- Thread.current[:solr_service]
34
- end
23
+ def reset!
24
+ Thread.current[:solr_service] = nil
25
+ end
35
26
 
36
- def self.lazy_reify_solr_results(solr_results, opts = {})
37
- Enumerator.new do |yielder|
38
- solr_results.each do |hit|
39
- yielder.yield(reify_solr_result(hit, opts))
27
+ def instance
28
+ # Register Solr
29
+
30
+ unless Thread.current[:solr_service]
31
+ register(ActiveFedora.solr_config[:url])
40
32
  end
33
+
34
+ raise SolrNotInitialized unless Thread.current[:solr_service]
35
+ Thread.current[:solr_service]
41
36
  end
42
- end
43
-
44
- def self.reify_solr_results(solr_results, opts = {})
45
- solr_results.collect {|hit| reify_solr_result(hit, opts)}
46
- end
47
37
 
48
- def self.reify_solr_result(hit, opts = {})
49
- klass = class_from_solr_document(hit)
50
- if opts[:load_from_solr]
51
- klass.load_instance_from_solr(hit[SOLR_DOCUMENT_ID], hit)
52
- else
53
- klass.find(hit[SOLR_DOCUMENT_ID], cast: true)
38
+ def lazy_reify_solr_results(solr_results, opts = {})
39
+ Enumerator.new do |yielder|
40
+ solr_results.each do |hit|
41
+ yielder.yield(reify_solr_result(hit, opts))
42
+ end
43
+ end
44
+ end
45
+
46
+ def reify_solr_results(solr_results, opts = {})
47
+ solr_results.collect {|hit| reify_solr_result(hit, opts)}
54
48
  end
55
- end
56
-
57
- def self.class_from_solr_document(hit, opts = {})
58
- #Set the default starting point to the class specified, if available.
59
- best_model_match = Model.from_class_uri(opts[:class]) unless opts[:class].nil?
60
49
 
61
- hit[solr_name("has_model", :symbol)].each do |value|
50
+ def reify_solr_result(hit, opts = {})
51
+ klass = class_from_solr_document(hit)
52
+ if opts[:load_from_solr]
53
+ klass.load_instance_from_solr(hit[SOLR_DOCUMENT_ID], hit)
54
+ else
55
+ klass.find(hit[SOLR_DOCUMENT_ID], cast: true)
56
+ end
57
+ end
58
+
59
+ def class_from_solr_document(hit, opts = {})
60
+ #Set the default starting point to the class specified, if available.
61
+ best_model_match = Model.from_class_uri(opts[:class]) unless opts[:class].nil?
62
+ hit[HAS_MODEL_SOLR_FIELD].each do |value|
62
63
 
63
- model_value = Model.from_class_uri(value)
64
+ model_value = Model.from_class_uri(value)
64
65
 
65
- if model_value
66
+ if model_value
66
67
 
67
- # Set as the first model in case opts[:class] was nil
68
- best_model_match ||= model_value
68
+ # Set as the first model in case opts[:class] was nil
69
+ best_model_match ||= model_value
69
70
 
70
- # If there is an inheritance structure, use the most specific case.
71
- if best_model_match > model_value
72
- best_model_match = model_value
71
+ # If there is an inheritance structure, use the most specific case.
72
+ if best_model_match > model_value
73
+ best_model_match = model_value
74
+ end
73
75
  end
74
76
  end
75
- end
76
-
77
- logger.warn "Could not find a model for #{hit["id"]}, defaulting to ActiveFedora::Base" unless best_model_match
78
- best_model_match || ActiveFedora::Base
79
- end
80
-
81
- # Construct a solr query for a list of pids
82
- # This is used to get a solr response based on the list of pids in an object's RELS-EXT relationhsips
83
- # If the pid_array is empty, defaults to a query of "id:NEVER_USE_THIS_ID", which will return an empty solr response
84
- # @param [Array] pid_array the pids that you want included in the query
85
- def self.construct_query_for_pids(pid_array)
86
77
 
87
- q = pid_array.reject { |x| x.empty? }.map do |pid|
88
- "_query_:\"{!raw f=#{SOLR_DOCUMENT_ID}}#{pid.gsub('"', '\"')}\""
78
+ logger.warn "Could not find a model for #{hit["id"]}, defaulting to ActiveFedora::Base" unless best_model_match
79
+ best_model_match || ActiveFedora::Base
80
+ end
81
+
82
+ # Construct a solr query for a list of pids
83
+ # This is used to get a solr response based on the list of pids in an object's RELS-EXT relationhsips
84
+ # If the pid_array is empty, defaults to a query of "id:NEVER_USE_THIS_ID", which will return an empty solr response
85
+ # @param [Array] pid_array the pids that you want included in the query
86
+ def construct_query_for_pids(pid_array)
87
+ q = pid_array.reject { |x| x.empty? }.map { |pid| raw_query(SOLR_DOCUMENT_ID, pid) }
88
+
89
+ q.empty? ? "id:NEVER_USE_THIS_ID" : q.join(" OR ".freeze)
89
90
  end
90
91
 
91
- return "id:NEVER_USE_THIS_ID" if q.empty?
92
+ # Create a raw query clause suitable for sending to solr as an fq element
93
+ # @param [String] key
94
+ # @param [String] value
95
+ def raw_query(key, value)
96
+ "_query_:\"{!raw f=#{key}}#{value.gsub('"', '\"')}\""
97
+ end
92
98
 
93
- return q.join(" OR ")
94
- end
99
+ def solr_name(*args)
100
+ Solrizer.default_field_mapper.solr_name(*args)
101
+ end
102
+
103
+ def escape_uri_for_query(uri)
104
+ Deprecation.warn SolrService, "escape_uri_for_query is deprecated and will be removed in active-fedora 8.0.0. Use RSolr.escape instead."
105
+ RSolr.escape(uri)
106
+ end
107
+
108
+ # Create a query with a clause for each key, value
109
+ # @param [Hash] args key is the predicate, value is the target_uri
110
+ def construct_query_for_rel(args)
111
+ clauses = args.map { |predicate, target_uri| raw_query(solr_name(predicate, :symbol), target_uri) }
112
+ clauses.join(" AND ".freeze)
113
+ end
95
114
 
96
- def self.solr_name(*args)
97
- Solrizer.default_field_mapper.solr_name(*args)
98
- end
99
-
100
- def self.escape_uri_for_query(uri)
101
- return uri.gsub(/(:)/, '\\:').gsub(/(\/)/, '\\/')
102
- end
103
-
104
- # Create a query with a clause for each key, value
105
- # @param [Hash] args key is the predicate, value is the target_uri
106
- def self.construct_query_for_rel(args)
107
- clauses = args.map do |predicate, target_uri|
108
- "_query_:\"{!raw f=#{solr_name(predicate, :symbol)}}#{target_uri.gsub('"', '\"')}\""
115
+ def query(query, args={})
116
+ raw = args.delete(:raw)
117
+ args = args.merge(:q=>query, :qt=>'standard')
118
+ result = SolrService.instance.conn.get('select', :params=>args)
119
+ return result if raw
120
+ result['response']['docs']
109
121
  end
110
- clauses.join(" AND ")
111
- end
112
122
 
113
- def self.query(query, args={})
114
- raw = args.delete(:raw)
115
- args = args.merge(:q=>query, :qt=>'standard')
116
- result = SolrService.instance.conn.get('select', :params=>args)
117
- return result if raw
118
- result['response']['docs']
119
- end
123
+ # Get the count of records that match the query
124
+ # @param [String] query a solr query
125
+ # @param [Hash] args arguments to pass through to `args' param of SolrService.query (note that :rows will be overwritten to 0)
126
+ # @return [Integer] number of records matching
127
+ def count(query, args={})
128
+ args = args.merge(:raw=>true, :rows=>0)
129
+ SolrService.query(query, args)['response']['numFound'].to_i
130
+ end
120
131
 
121
- # Get the count of records that match the query
122
- # @param [String] query a solr query
123
- # @param [Hash] args arguments to pass through to `args' param of SolrService.query (note that :rows will be overwritten to 0)
124
- # @return [Integer] number of records matching
125
- def self.count(query, args={})
126
- args = args.merge(:raw=>true, :rows=>0)
127
- SolrService.query(query, args)['response']['numFound'].to_i
128
- end
132
+ # @param [Hash] doc the document to index
133
+ # @param [Hash] params
134
+ # :commit => commits immediately
135
+ # :softCommit => commit to memory, but don't flush to disk
136
+ def add(doc, params = {})
137
+ SolrService.instance.conn.add(doc, params: params)
138
+ end
129
139
 
130
- def self.add(doc)
131
- SolrService.instance.conn.add(doc)
140
+ def commit
141
+ SolrService.instance.conn.commit
142
+ end
132
143
  end
133
144
 
134
- def self.commit
135
- SolrService.instance.conn.commit
136
- end
145
+ HAS_MODEL_SOLR_FIELD = solr_name("has_model", :symbol).freeze
137
146
 
138
-
139
- end #SolrService
140
- class SolrNotInitialized < StandardError;end
147
+ end #SolrService
148
+ class SolrNotInitialized < StandardError;end
141
149
  end #ActiveFedora
@@ -1,3 +1,3 @@
1
1
  module ActiveFedora
2
- VERSION = "7.0.0.pre2"
2
+ VERSION = "7.0.0.pre3"
3
3
  end
@@ -7,6 +7,11 @@
7
7
  <uniqueKey>id</uniqueKey>
8
8
 
9
9
  <fields>
10
+ <!-- If you remove this field, you must _also_ disable the update log in solrconfig.xml
11
+ or Solr won't start. _version_ and update log are required for SolrCloud
12
+ -->
13
+ <field name="_version_" type="long" indexed="true" stored="true"/>
14
+
10
15
  <field name="id" type="string" stored="true" indexed="true" multiValued="false" required="true"/>
11
16
  <field name="timestamp" type="date" indexed="true" stored="true" default="NOW" multiValued="false"/>
12
17
 
@@ -16,6 +16,57 @@
16
16
 
17
17
  <dataDir>${solr.data.dir:}</dataDir>
18
18
 
19
+ <!-- The default high-performance update handler -->
20
+ <updateHandler class="solr.DirectUpdateHandler2">
21
+
22
+ <!-- Enables a transaction log, used for real-time get, durability, and
23
+ and solr cloud replica recovery. The log can grow as big as
24
+ uncommitted changes to the index, so use of a hard autoCommit
25
+ is recommended (see below).
26
+ "dir" - the target directory for transaction logs, defaults to the
27
+ solr data directory. -->
28
+ <updateLog>
29
+ <str name="dir">${solr.ulog.dir:}</str>
30
+ </updateLog>
31
+
32
+ <!-- AutoCommit
33
+
34
+ Perform a hard commit automatically under certain conditions.
35
+ Instead of enabling autoCommit, consider using "commitWithin"
36
+ when adding documents.
37
+
38
+ http://wiki.apache.org/solr/UpdateXmlMessages
39
+
40
+ maxDocs - Maximum number of documents to add since the last
41
+ commit before automatically triggering a new commit.
42
+
43
+ maxTime - Maximum amount of time in ms that is allowed to pass
44
+ since a document was added before automatically
45
+ triggering a new commit.
46
+ openSearcher - if false, the commit causes recent index changes
47
+ to be flushed to stable storage, but does not cause a new
48
+ searcher to be opened to make those changes visible.
49
+
50
+ If the updateLog is enabled, then it's highly recommended to
51
+ have some sort of hard autoCommit to limit the log size.
52
+ -->
53
+ <autoCommit>
54
+ <maxTime>${solr.autoCommit.maxTime:15000}</maxTime>
55
+ <openSearcher>false</openSearcher>
56
+ </autoCommit>
57
+
58
+ <!-- softAutoCommit is like autoCommit except it causes a
59
+ 'soft' commit which only ensures that changes are visible
60
+ but does not ensure that data is synced to disk. This is
61
+ faster and more near-realtime friendly than a hard commit.
62
+ -->
63
+
64
+ <autoSoftCommit>
65
+ <maxTime>${solr.autoSoftCommit.maxTime:-1}</maxTime>
66
+ </autoSoftCommit>
67
+
68
+ </updateHandler>
69
+
19
70
  <requestHandler name="search" class="solr.SearchHandler" default="true">
20
71
  <!-- default values for query parameters can be specified, these
21
72
  will be overridden by parameters in the request
@@ -188,6 +188,13 @@ describe "Deleting a dependent relationship" do
188
188
  item.delete
189
189
  end
190
190
 
191
+ it "should not save deleted objects" do
192
+ item.components << component
193
+ item.save!
194
+ c2 = Component.find(component.id)
195
+ c2.delete
196
+ item.delete
197
+ end
191
198
  end
192
199
 
193
200
  describe "Autosave" do
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "scoped queries" do
4
-
4
+
5
5
  before(:each) do
6
6
  module ModelIntegrationSpec
7
7
  class Basic < ActiveFedora::Base
@@ -36,7 +36,7 @@ describe ActiveFedora::SolrService do
36
36
  Object.send(:remove_const, :FooObject)
37
37
  end
38
38
  it "should return an array of objects that are of the class stored in active_fedora_model_s" do
39
- query = "id\:#{ActiveFedora::SolrService.escape_uri_for_query(@test_object.pid)} OR id\:#{ActiveFedora::SolrService.escape_uri_for_query(@foo_object.pid)}"
39
+ query = "id\:#{RSolr.escape(@test_object.pid)} OR id\:#{RSolr.escape(@foo_object.pid)}"
40
40
  solr_result = ActiveFedora::SolrService.query(query)
41
41
  result = ActiveFedora::SolrService.reify_solr_results(solr_result)
42
42
  result.length.should == 2
@@ -46,7 +46,7 @@ describe ActiveFedora::SolrService do
46
46
  end
47
47
 
48
48
  it 'should load objects from solr data if a :load_from_solr option is passed in' do
49
- query = "id\:#{ActiveFedora::SolrService.escape_uri_for_query(@test_object.pid)} OR id\:#{ActiveFedora::SolrService.escape_uri_for_query(@foo_object.pid)}"
49
+ query = "id\:#{RSolr.escape(@test_object.pid)} OR id\:#{RSolr.escape(@foo_object.pid)}"
50
50
  solr_result = ActiveFedora::SolrService.query(query)
51
51
  result = ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
52
52
  result.length.should == 2
@@ -67,7 +67,7 @@ describe ActiveFedora::SolrService do
67
67
  end
68
68
 
69
69
  it 'should #reify a lightweight object as a new instance' do
70
- query = "id\:#{ActiveFedora::SolrService.escape_uri_for_query(@foo_object.pid)}"
70
+ query = "id\:#{RSolr.escape(@foo_object.pid)}"
71
71
  solr_result = ActiveFedora::SolrService.query(query)
72
72
  result = ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
73
73
  solr_foo = result.first
@@ -79,7 +79,7 @@ describe ActiveFedora::SolrService do
79
79
  end
80
80
 
81
81
  it 'should #reify! a lightweight object within the same instance' do
82
- query = "id\:#{ActiveFedora::SolrService.escape_uri_for_query(@foo_object.pid)}"
82
+ query = "id\:#{RSolr.escape(@foo_object.pid)}"
83
83
  solr_result = ActiveFedora::SolrService.query(query)
84
84
  result = ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
85
85
  solr_foo = result.first
@@ -90,7 +90,7 @@ describe ActiveFedora::SolrService do
90
90
  end
91
91
 
92
92
  it 'should raise an exception when attempting to reify a first-class object' do
93
- query = "id\:#{ActiveFedora::SolrService.escape_uri_for_query(@foo_object.pid)}"
93
+ query = "id\:#{RSolr.escape(@foo_object.pid)}"
94
94
  solr_result = ActiveFedora::SolrService.query(query)
95
95
  result = ActiveFedora::SolrService.reify_solr_results(solr_result,{:load_from_solr=>true})
96
96
  solr_foo = result.first
@@ -101,7 +101,7 @@ describe ActiveFedora::SolrService do
101
101
  end
102
102
 
103
103
  it 'should call load_instance_from_solr if :load_from_solr option passed in' do
104
- query = "id\:#{ActiveFedora::SolrService.escape_uri_for_query(@test_object.pid)} OR id\:#{ActiveFedora::SolrService.escape_uri_for_query(@foo_object.pid)}"
104
+ query = "id\:#{RSolr.escape(@test_object.pid)} OR id\:#{RSolr.escape(@foo_object.pid)}"
105
105
  solr_result = ActiveFedora::SolrService.query(query)
106
106
  ActiveFedora::Base.should_receive(:load_instance_from_solr).once
107
107
  FooObject.should_receive(:load_instance_from_solr).once
@@ -9,8 +9,9 @@ describe ActiveFedora::Base do
9
9
  describe ".update_index" do
10
10
  before do
11
11
  mock_conn = double("SolrConnection")
12
- mock_conn.should_receive(:add)
13
- mock_conn.should_receive(:commit)
12
+ mock_conn.should_receive(:add) do |_, opts|
13
+ opts.should == {:params=>{:softCommit=>true}}
14
+ end
14
15
  mock_ss = double("SolrService")
15
16
  mock_ss.stub(:conn).and_return(mock_conn)
16
17
  ActiveFedora::SolrService.stub(:instance).and_return(mock_ss)
@@ -60,8 +61,7 @@ describe ActiveFedora::Base do
60
61
  it "should delete object from repository and index" do
61
62
  @test_object.inner_object.stub(:delete)
62
63
  mock_conn = double("SolrConnection")
63
- mock_conn.should_receive(:delete_by_id).with(nil)
64
- mock_conn.should_receive(:commit)
64
+ mock_conn.should_receive(:delete_by_id).with(nil, {:params=>{"softCommit"=>true}})
65
65
  mock_ss = double("SolrService")
66
66
  mock_ss.stub(:conn).and_return(mock_conn)
67
67
  ActiveFedora::SolrService.stub(:instance).and_return(mock_ss)
@@ -203,13 +203,13 @@ describe ActiveFedora::Base do
203
203
  ### Methods for ActiveModel::Conversions
204
204
  it "should have to_param once it's saved" do
205
205
  @test_object.to_param.should be_nil
206
- @test_object.inner_object.stub(:new? => false, :pid => 'foo:123')
206
+ @test_object.inner_object.stub(new_record?: false, pid: 'foo:123')
207
207
  @test_object.to_param.should == 'foo:123'
208
208
  end
209
209
 
210
210
  it "should have to_key once it's saved" do
211
211
  @test_object.to_key.should be_nil
212
- @test_object.inner_object.stub(new?: false, pid: 'foo:123')
212
+ @test_object.inner_object.stub(new_record?: false, pid: 'foo:123')
213
213
  @test_object.to_key.should == ['foo:123']
214
214
  end
215
215