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.
- checksums.yaml +4 -4
- data/.travis.yml +0 -7
- data/active-fedora.gemspec +2 -2
- data/lib/active_fedora/associations/builder/belongs_to.rb +1 -0
- data/lib/active_fedora/associations/has_many_association.rb +3 -2
- data/lib/active_fedora/core.rb +9 -0
- data/lib/active_fedora/datastream.rb +13 -0
- data/lib/active_fedora/datastream_attribute.rb +10 -9
- data/lib/active_fedora/datastream_hash.rb +7 -0
- data/lib/active_fedora/datastreams.rb +1 -1
- data/lib/active_fedora/indexing.rb +1 -2
- data/lib/active_fedora/om_datastream.rb +9 -1
- data/lib/active_fedora/persistence.rb +2 -5
- data/lib/active_fedora/querying.rb +5 -1
- data/lib/active_fedora/rdf/indexing.rb +18 -10
- data/lib/active_fedora/relation/finder_methods.rb +5 -5
- data/lib/active_fedora/rels_ext_datastream.rb +4 -24
- data/lib/active_fedora/semantic_node.rb +24 -9
- data/lib/active_fedora/solr_instance_loader.rb +1 -7
- data/lib/active_fedora/solr_service.rb +106 -98
- data/lib/active_fedora/version.rb +1 -1
- data/lib/generators/active_fedora/config/solr/templates/solr_conf/conf/schema.xml +5 -0
- data/lib/generators/active_fedora/config/solr/templates/solr_conf/conf/solrconfig.xml +51 -0
- data/spec/integration/has_many_associations_spec.rb +7 -0
- data/spec/integration/scoped_query_spec.rb +1 -1
- data/spec/integration/solr_service_spec.rb +6 -6
- data/spec/unit/base_extra_spec.rb +4 -4
- data/spec/unit/base_spec.rb +2 -2
- data/spec/unit/core_spec.rb +77 -0
- data/spec/unit/ntriples_datastream_spec.rb +4 -4
- data/spec/unit/om_datastream_spec.rb +40 -4
- data/spec/unit/query_spec.rb +19 -19
- data/spec/unit/rels_ext_datastream_spec.rb +3 -2
- data/spec/unit/solr_config_options_spec.rb +1 -4
- data/spec/unit/solr_service_spec.rb +7 -1
- metadata +10 -8
@@ -66,13 +66,7 @@ module ActiveFedora
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def active_fedora_class
|
69
|
-
@active_fedora_class ||=
|
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
|
-
|
26
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
23
|
+
def reset!
|
24
|
+
Thread.current[:solr_service] = nil
|
25
|
+
end
|
35
26
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
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
|
-
|
64
|
+
model_value = Model.from_class_uri(value)
|
64
65
|
|
65
|
-
|
66
|
+
if model_value
|
66
67
|
|
67
|
-
|
68
|
-
|
68
|
+
# Set as the first model in case opts[:class] was nil
|
69
|
+
best_model_match ||= model_value
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
88
|
-
|
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
|
-
|
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
|
-
|
94
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
114
|
-
|
115
|
-
args
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
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
|
-
|
131
|
-
|
140
|
+
def commit
|
141
|
+
SolrService.instance.conn.commit
|
142
|
+
end
|
132
143
|
end
|
133
144
|
|
134
|
-
|
135
|
-
SolrService.instance.conn.commit
|
136
|
-
end
|
145
|
+
HAS_MODEL_SOLR_FIELD = solr_name("has_model", :symbol).freeze
|
137
146
|
|
138
|
-
|
139
|
-
end
|
140
|
-
class SolrNotInitialized < StandardError;end
|
147
|
+
end #SolrService
|
148
|
+
class SolrNotInitialized < StandardError;end
|
141
149
|
end #ActiveFedora
|
@@ -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
|
@@ -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\:#{
|
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\:#{
|
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\:#{
|
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\:#{
|
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\:#{
|
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\:#{
|
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
|
-
|
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)
|
data/spec/unit/base_spec.rb
CHANGED
@@ -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(
|
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(
|
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
|
|