active-fedora 7.0.0.pre2 → 7.0.0.pre3

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.
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