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