telvue-rsolr 2.2.2
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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +14 -0
- data/CHANGES.txt +47 -0
- data/Gemfile +5 -0
- data/LICENSE +13 -0
- data/README.rdoc +229 -0
- data/Rakefile +19 -0
- data/lib/rsolr.rb +52 -0
- data/lib/rsolr/char.rb +6 -0
- data/lib/rsolr/client.rb +342 -0
- data/lib/rsolr/document.rb +59 -0
- data/lib/rsolr/error.rb +136 -0
- data/lib/rsolr/field.rb +87 -0
- data/lib/rsolr/generator.rb +5 -0
- data/lib/rsolr/json.rb +60 -0
- data/lib/rsolr/response.rb +95 -0
- data/lib/rsolr/uri.rb +25 -0
- data/lib/rsolr/version.rb +7 -0
- data/lib/rsolr/xml.rb +150 -0
- data/rsolr.gemspec +46 -0
- data/spec/api/client_spec.rb +355 -0
- data/spec/api/document_spec.rb +48 -0
- data/spec/api/error_spec.rb +47 -0
- data/spec/api/json_spec.rb +198 -0
- data/spec/api/pagination_spec.rb +31 -0
- data/spec/api/rsolr_spec.rb +31 -0
- data/spec/api/uri_spec.rb +37 -0
- data/spec/api/xml_spec.rb +255 -0
- data/spec/fixtures/basic_configs/_rest_managed.json +1 -0
- data/spec/fixtures/basic_configs/currency.xml +67 -0
- data/spec/fixtures/basic_configs/lang/stopwords_en.txt +54 -0
- data/spec/fixtures/basic_configs/protwords.txt +21 -0
- data/spec/fixtures/basic_configs/schema.xml +530 -0
- data/spec/fixtures/basic_configs/solrconfig.xml +572 -0
- data/spec/fixtures/basic_configs/stopwords.txt +14 -0
- data/spec/fixtures/basic_configs/synonyms.txt +29 -0
- data/spec/integration/solr5_spec.rb +34 -0
- data/spec/spec_helper.rb +94 -0
- metadata +232 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RSolr::Document do
|
4
|
+
describe RSolr::Field do
|
5
|
+
describe ".instance" do
|
6
|
+
subject { RSolr::Field }
|
7
|
+
|
8
|
+
it "uses the class name of the field value" do
|
9
|
+
expect(subject.instance({}, Time.new)).to be_a_kind_of(RSolr::TimeField)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "uses the provided type option when it is a class" do
|
13
|
+
expect(subject.instance({:type => RSolr::TimeField}, nil)).to be_a_kind_of(RSolr::TimeField)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "uses the provided type option when it is a string" do
|
17
|
+
expect(subject.instance({:type => 'Time'}, nil)).to be_a_kind_of(RSolr::TimeField)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "falls back to the base Field class" do
|
21
|
+
expect(subject.instance({:type => 'UndefinedType'}, nil)).to be_a_kind_of(RSolr::Field)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "defaults to the base Field class" do
|
25
|
+
expect(subject.instance({}, nil)).to be_a_kind_of(RSolr::Field)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe RSolr::TimeField do
|
31
|
+
it "convert value to string" do
|
32
|
+
time_value = Time.utc(2013, 9, 11, 18, 10, 0)
|
33
|
+
expect(RSolr::Field.instance({}, time_value).value).to eq '2013-09-11T18:10:00Z'
|
34
|
+
end
|
35
|
+
|
36
|
+
it "convert time to UTC" do
|
37
|
+
time_value = Time.new(2013, 9, 11, 18, 10, 0, '+02:00')
|
38
|
+
expect(RSolr::Field.instance({}, time_value).value).to eq '2013-09-11T16:10:00Z'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe RSolr::DateField do
|
43
|
+
it "convert value to string" do
|
44
|
+
date_value = Date.new(2013, 9, 11)
|
45
|
+
expect(RSolr::Field.instance({}, date_value).value).to eq '2013-09-11T00:00:00Z'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RSolr::Error do
|
4
|
+
def generate_error_with_backtrace(request, response)
|
5
|
+
raise RSolr::Error::Http.new request, response
|
6
|
+
rescue RSolr::Error::Http => exception
|
7
|
+
exception
|
8
|
+
end
|
9
|
+
let (:response_lines) { (1..15).to_a.map { |i| "line #{i}" } }
|
10
|
+
let(:request) { double :[] => "mocked" }
|
11
|
+
let(:response_body) { response_lines.join("\n") }
|
12
|
+
let(:response) {{
|
13
|
+
:body => response_body,
|
14
|
+
:status => 400
|
15
|
+
}}
|
16
|
+
subject { generate_error_with_backtrace(request, response).to_s }
|
17
|
+
|
18
|
+
context "when the response body is wrapped in a <pre> element" do
|
19
|
+
let(:response_body) { "<pre>" + response_lines.join("\n") + "</pre>" }
|
20
|
+
|
21
|
+
it "only shows the first eleven lines of the response" do
|
22
|
+
expect(subject).to match(/line 1\n.+line 11\n\n/m)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when the response is one line long" do
|
26
|
+
let(:response_body) { "<pre>failed</pre>" }
|
27
|
+
it { should match(/Error: failed/) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when the response body is not wrapped in a <pre> element" do
|
32
|
+
|
33
|
+
it "only shows the first eleven lines of the response" do
|
34
|
+
expect(subject).to match(/line 1\n.+line 11\n\n/m)
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when the response is one line long" do
|
38
|
+
let(:response_body) { 'failed' }
|
39
|
+
it { should match(/Error: failed/) }
|
40
|
+
end
|
41
|
+
context "when the response body contains a msg key" do
|
42
|
+
let(:msg) { "'org.apache.solr.search.SyntaxError: Cannot parse \\':false\\': Encountered \" \":\" \": \"\" at line 1, column 0.'" }
|
43
|
+
let(:response_body) { (response_lines << "'error'=>{'msg'=> #{msg}").join("\n") }
|
44
|
+
it { should include msg }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RSolr::JSON do
|
4
|
+
|
5
|
+
let(:generator){ RSolr::JSON::Generator.new }
|
6
|
+
|
7
|
+
context :add do
|
8
|
+
# add a single hash ("doc")
|
9
|
+
it 'should create an add from a hash' do
|
10
|
+
data = {
|
11
|
+
:id=>"1",
|
12
|
+
:name=>'matt'
|
13
|
+
}
|
14
|
+
message = JSON.parse(generator.add(data), symbolize_names: true)
|
15
|
+
expect(message.length).to eq 1
|
16
|
+
expect(message.first).to eq data
|
17
|
+
end
|
18
|
+
|
19
|
+
# add an array of hashes
|
20
|
+
it 'should create many adds from an array of hashes' do
|
21
|
+
data = [
|
22
|
+
{
|
23
|
+
:id=>"1",
|
24
|
+
:name=>'matt'
|
25
|
+
},
|
26
|
+
{
|
27
|
+
:id=>"2",
|
28
|
+
:name=>'sam'
|
29
|
+
}
|
30
|
+
]
|
31
|
+
message = JSON.parse(generator.add(data), symbolize_names: true)
|
32
|
+
expect(message).to eq data
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should yield a Document object when #add is called with a block' do
|
36
|
+
documents = [{:id=>1, :name=>'sam', :cat=>['cat 1', 'cat 2']}]
|
37
|
+
result = generator.add(documents) do |doc|
|
38
|
+
doc.field_by_name(:name).attrs[:boost] = 10
|
39
|
+
end
|
40
|
+
|
41
|
+
message = JSON.parse(result, symbolize_names: true)
|
42
|
+
|
43
|
+
expect(message.length).to eq 1
|
44
|
+
expect(message.first).to include name: { boost: 10, value: 'sam' }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with add_attr' do
|
48
|
+
it 'should create an add command with the attributes from a hash' do
|
49
|
+
data = {
|
50
|
+
:id=>"1",
|
51
|
+
:name=>'matt'
|
52
|
+
}
|
53
|
+
message = JSON.parse(generator.add(data, boost: 1), symbolize_names: true)
|
54
|
+
expect(message).to include :add
|
55
|
+
expect(message[:add][:doc]).to eq data
|
56
|
+
expect(message[:add][:boost]).to eq 1
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should create multiple add command with the attributes from a hash' do
|
60
|
+
data = [
|
61
|
+
{
|
62
|
+
:id=>"1",
|
63
|
+
:name=>'matt'
|
64
|
+
},
|
65
|
+
{
|
66
|
+
:id=>"2",
|
67
|
+
:name=>'sam'
|
68
|
+
},
|
69
|
+
]
|
70
|
+
|
71
|
+
# custom JSON object class to handle Solr's non-standard JSON command format
|
72
|
+
tmp = Class.new do
|
73
|
+
def initialize
|
74
|
+
@source ||= {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def []=(k, v)
|
78
|
+
if k == :add
|
79
|
+
@source[k] ||= []
|
80
|
+
@source[k] << v.to_h
|
81
|
+
elsif v.class == self.class
|
82
|
+
@source[k] = v.to_h
|
83
|
+
else
|
84
|
+
@source[k] = v
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_h
|
89
|
+
@source
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
request = generator.add(data, boost: 1)
|
94
|
+
message = JSON.parse(request, object_class: tmp, symbolize_names: true).to_h
|
95
|
+
expect(message[:add].length).to eq 2
|
96
|
+
expect(message[:add].map { |x| x[:doc] }).to eq data
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'allows for atomic updates' do
|
101
|
+
data = {
|
102
|
+
foo: { set: 'Bar' }
|
103
|
+
}
|
104
|
+
|
105
|
+
message = JSON.parse(generator.add(data), symbolize_names: true)
|
106
|
+
expect(message.length).to eq 1
|
107
|
+
expect(message.first).to eq data
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'supports nested child documents' do
|
111
|
+
data = {
|
112
|
+
_childDocuments_: [
|
113
|
+
{
|
114
|
+
id: 1
|
115
|
+
},
|
116
|
+
{
|
117
|
+
id: 2
|
118
|
+
}
|
119
|
+
]
|
120
|
+
}
|
121
|
+
|
122
|
+
message = JSON.parse(generator.add(data), symbolize_names: true)
|
123
|
+
expect(message.length).to eq 1
|
124
|
+
expect(message.first).to eq data
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'supports nested child documents with only a single document' do
|
128
|
+
data = {
|
129
|
+
_childDocuments_: [
|
130
|
+
{
|
131
|
+
id: 1
|
132
|
+
}
|
133
|
+
]
|
134
|
+
}
|
135
|
+
|
136
|
+
message = JSON.parse(generator.add(data), symbolize_names: true)
|
137
|
+
expect(message.length).to eq 1
|
138
|
+
expect(message.first).to eq data
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should create multiple fields from array values' do
|
143
|
+
data = {
|
144
|
+
:id => "1",
|
145
|
+
:name => ['matt1', 'matt2']
|
146
|
+
}
|
147
|
+
message = JSON.parse(generator.add(data), symbolize_names: true)
|
148
|
+
expect(message.length).to eq 1
|
149
|
+
expect(message.first).to eq data
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should create multiple fields from array values with options' do
|
153
|
+
test_values = [nil, 'matt1', 'matt2']
|
154
|
+
message = JSON.parse(
|
155
|
+
generator.add(id: '1') { |doc| doc.add_field(:name, test_values, boost: 3) },
|
156
|
+
symbolize_names: true
|
157
|
+
)
|
158
|
+
expect(message).to eq [{ id: '1', name: { boost: 3, value: test_values } }]
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '#commit' do
|
162
|
+
it 'generates a commit command' do
|
163
|
+
expect(JSON.parse(generator.commit, symbolize_names: true)).to eq(commit: {})
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#optimize' do
|
168
|
+
it 'generates a optimize command' do
|
169
|
+
expect(JSON.parse(generator.optimize, symbolize_names: true)).to eq(optimize: {})
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe '#rollback' do
|
174
|
+
it 'generates a rollback command' do
|
175
|
+
expect(JSON.parse(generator.rollback, symbolize_names: true)).to eq(rollback: {})
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '#delete_by_id' do
|
180
|
+
it 'generates a delete_by_id command for single documents' do
|
181
|
+
expect(JSON.parse(generator.delete_by_id('x'), symbolize_names: true)).to eq(delete: 'x')
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'generates a delete_by_id command for an array of documents' do
|
185
|
+
expect(JSON.parse(generator.delete_by_id(%w(a b c)), symbolize_names: true)).to eq(delete: %w(a b c))
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe '#delete_by_query' do
|
190
|
+
it 'generates a delete_by_id command for single documents' do
|
191
|
+
expect(JSON.parse(generator.delete_by_query('id:x'), symbolize_names: true)).to eq(delete: { query: 'id:x'})
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'generates a delete_by_id command for an array of documents' do
|
195
|
+
expect(JSON.parse(generator.delete_by_id(%w(a b c)), symbolize_names: true)).to eq(delete: %w(a b c))
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RSolr::Client do
|
4
|
+
context "build_paginated_request" do
|
5
|
+
it "should create the proper solr params and query string" do
|
6
|
+
c = RSolr::Client.new(nil, {})#.extend(RSolr::Pagination::Client)
|
7
|
+
r = c.build_paginated_request 3, 25, "select", {:params => {:q => "test"}}
|
8
|
+
#r[:page].should == 3
|
9
|
+
#r[:per_page].should == 25
|
10
|
+
expect(r[:params]["start"]).to eq(50)
|
11
|
+
expect(r[:params]["rows"]).to eq(25)
|
12
|
+
expect(r[:uri].query).to match(/rows=25/)
|
13
|
+
expect(r[:uri].query).to match(/start=50/)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
context "paginate" do
|
17
|
+
it "should build a paginated request context and call execute" do
|
18
|
+
c = RSolr::Client.new(nil, {})#.extend(RSolr::Pagination::Client)
|
19
|
+
expect(c).to receive(:execute).with(hash_including({
|
20
|
+
#:page => 1,
|
21
|
+
#:per_page => 10,
|
22
|
+
:params => {
|
23
|
+
"rows" => 10,
|
24
|
+
"start" => 0,
|
25
|
+
:wt => :json
|
26
|
+
}
|
27
|
+
}))
|
28
|
+
c.paginate 1, 10, "select"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RSolr do
|
4
|
+
|
5
|
+
it "has a version that can be read via #version or VERSION" do
|
6
|
+
expect(RSolr.version).to eq(RSolr::VERSION)
|
7
|
+
end
|
8
|
+
|
9
|
+
context "connect" do
|
10
|
+
it "should return a RSolr::Client instance" do
|
11
|
+
expect(RSolr.connect).to be_a(RSolr::Client)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context '.solr_escape' do
|
16
|
+
it "adds backslash to Solr query syntax chars" do
|
17
|
+
# per http://lucene.apache.org/core/4_0_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Escaping_Special_Characters
|
18
|
+
special_chars = [ "+", "-", "&", "|", "!", "(", ")", "{", "}", "[", "]", "^", '"', "~", "*", "?", ":", "\\", "/" ]
|
19
|
+
escaped_str = RSolr.solr_escape("aa#{special_chars.join('aa')}aa")
|
20
|
+
special_chars.each { |c|
|
21
|
+
# note that the ruby code sending the query to Solr will un-escape the backslashes
|
22
|
+
# so the result sent to Solr is ultimately a single backslash in front of the particular character
|
23
|
+
expect(escaped_str).to match "\\#{c}"
|
24
|
+
}
|
25
|
+
end
|
26
|
+
it "leaves other chars alone" do
|
27
|
+
str = "nothing to see here; let's move along people."
|
28
|
+
expect(RSolr.solr_escape(str)).to eq str
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe RSolr::Uri do
|
4
|
+
|
5
|
+
let(:uri) { RSolr::Uri }
|
6
|
+
|
7
|
+
context '.params_to_solr' do
|
8
|
+
it "converts Hash to Solr query string w/o a starting ?" do
|
9
|
+
hash = {:q => "gold", :fq => ["mode:one", "level:2"]}
|
10
|
+
query = uri.params_to_solr hash
|
11
|
+
expect(query[0]).not_to eq(??)
|
12
|
+
[/q=gold/, /fq=mode%3Aone/, /fq=level%3A2/].each do |p|
|
13
|
+
expect(query).to match p
|
14
|
+
end
|
15
|
+
expect(query.split('&').size).to eq(3)
|
16
|
+
end
|
17
|
+
it 'should URL escape &' do
|
18
|
+
expect(uri.params_to_solr(:fq => "&")).to eq('fq=%26')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should convert spaces to +' do
|
22
|
+
expect(uri.params_to_solr(:fq => "me and you")).to eq('fq=me+and+you')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should URL escape complex queries, part 1' do
|
26
|
+
my_params = {'fq' => '{!raw f=field_name}crazy+\"field+value'}
|
27
|
+
expected = 'fq=%7B%21raw+f%3Dfield_name%7Dcrazy%2B%5C%22field%2Bvalue'
|
28
|
+
expect(uri.params_to_solr(my_params)).to eq(expected)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should URL escape complex queries, part 2' do
|
32
|
+
my_params = {'q' => '+popularity:[10 TO *] +section:0'}
|
33
|
+
expected = 'q=%2Bpopularity%3A%5B10+TO+*%5D+%2Bsection%3A0'
|
34
|
+
expect(uri.params_to_solr(my_params)).to eq(expected)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'builder'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
RSpec.describe RSolr::Xml do
|
6
|
+
let(:generator){ RSolr::Xml::Generator.new }
|
7
|
+
|
8
|
+
builder_engines = {
|
9
|
+
:builder => { :val => false, :class => Builder::XmlMarkup, :engine => Builder::XmlMarkup.new(:indent => 0, :margin => 0, :encoding => 'UTF-8') },
|
10
|
+
:nokogiri => { :val => true, :class => Nokogiri::XML::Builder, :engine => Nokogiri::XML::Builder.new }
|
11
|
+
}
|
12
|
+
|
13
|
+
[:builder,:nokogiri].each do |engine_name|
|
14
|
+
describe engine_name do
|
15
|
+
before :all do
|
16
|
+
@engine = builder_engines[engine_name]
|
17
|
+
@old_ng_setting = RSolr::Xml::Generator.use_nokogiri
|
18
|
+
RSolr::Xml::Generator.use_nokogiri = @engine[:val]
|
19
|
+
end
|
20
|
+
|
21
|
+
after :all do
|
22
|
+
RSolr::Xml::Generator.use_nokogiri = @old_ng_setting
|
23
|
+
end
|
24
|
+
|
25
|
+
before :each do
|
26
|
+
builder_engines.each_pair do |name,spec|
|
27
|
+
expect(spec[:class]).not_to receive(:new) unless name == engine_name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context :xml_engine do
|
32
|
+
it "should use #{engine_name}" do
|
33
|
+
expect(@engine[:class]).to receive(:new).and_return(@engine[:engine])
|
34
|
+
generator.send(:commit)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# call all of the simple methods...
|
39
|
+
# make sure the xml string is valid
|
40
|
+
# ensure the class is actually Solr::XML
|
41
|
+
[:optimize, :rollback, :commit].each do |meth|
|
42
|
+
it "#{meth} should generator xml" do
|
43
|
+
result = generator.send(meth)
|
44
|
+
expect(result).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><#{meth}/>")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context :add do
|
49
|
+
|
50
|
+
it 'should yield a Message::Document object when #add is called with a block' do
|
51
|
+
documents = [{:id=>1, :name=>'sam', :cat=>['cat 1', 'cat 2']}]
|
52
|
+
add_attrs = {:boost=>200.00}
|
53
|
+
result = generator.add(documents, add_attrs) do |doc|
|
54
|
+
doc.field_by_name(:name).attrs[:boost] = 10
|
55
|
+
end
|
56
|
+
expect(result).to match(%r(name="cat">cat 1</field>))
|
57
|
+
expect(result).to match(%r(name="cat">cat 2</field>))
|
58
|
+
expect(result).to match(%r(<add boost="200.0">))
|
59
|
+
expect(result).to match(%r(boost="10"))
|
60
|
+
expect(result).to match(%r(<field name="id">1</field>))
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should work for values that yield enumerators' do
|
64
|
+
documents = [{id: 1, cat: ['cat 1', 'cat 2'].to_enum}]
|
65
|
+
result = generator.add(documents)
|
66
|
+
|
67
|
+
expect(result).to match(%r(name="cat">cat 1</field>))
|
68
|
+
expect(result).to match(%r(name="cat">cat 2</field>))
|
69
|
+
end
|
70
|
+
|
71
|
+
# add a single hash ("doc")
|
72
|
+
it 'should create an add from a hash' do
|
73
|
+
data = {
|
74
|
+
:id=>1,
|
75
|
+
:name=>'matt'
|
76
|
+
}
|
77
|
+
result = generator.add(data)
|
78
|
+
expect(result).to match(/<field name="name">matt<\/field>/)
|
79
|
+
expect(result).to match(/<field name="id">1<\/field>/)
|
80
|
+
end
|
81
|
+
|
82
|
+
# add a single hash ("doc")
|
83
|
+
it 'should create an add from a hash formatted for atomic updates' do
|
84
|
+
data = {
|
85
|
+
:id=>1,
|
86
|
+
:name=> { set: 'matt' }
|
87
|
+
}
|
88
|
+
result = generator.add(data)
|
89
|
+
expect(result).to match(/<field name="name" update="set">matt<\/field>/)
|
90
|
+
expect(result).to match(/<field name="id">1<\/field>/)
|
91
|
+
end
|
92
|
+
it 'should remove a field from a hash formatted for atomic updates' do
|
93
|
+
data = {
|
94
|
+
:id => 1,
|
95
|
+
:name => nil
|
96
|
+
}
|
97
|
+
result = generator.add(data)
|
98
|
+
expect(result).to match(%r{<field name="name" null="true"})
|
99
|
+
expect(result).to match(/<field name="id">1<\/field>/)
|
100
|
+
end
|
101
|
+
|
102
|
+
# add an array of hashes
|
103
|
+
it 'should create many adds from an array of hashes' do
|
104
|
+
data = [
|
105
|
+
{
|
106
|
+
:id=>1,
|
107
|
+
:name=>'matt'
|
108
|
+
},
|
109
|
+
{
|
110
|
+
:id=>2,
|
111
|
+
:name=>'sam'
|
112
|
+
}
|
113
|
+
]
|
114
|
+
message = generator.add(data)
|
115
|
+
expect(message).to match %r{<field name="name">matt</field>}
|
116
|
+
expect(message).to match %r{<field name="name">sam</field>}
|
117
|
+
end
|
118
|
+
|
119
|
+
# multiValue field support test, thanks to Fouad Mardini!
|
120
|
+
it 'should create multiple fields from array values' do
|
121
|
+
data = {
|
122
|
+
:id => 1,
|
123
|
+
:name => ['matt1', 'matt2']
|
124
|
+
}
|
125
|
+
result = generator.add(data)
|
126
|
+
expect(result).to match(/<field name="name">matt1<\/field>/)
|
127
|
+
expect(result).to match(/<field name="name">matt2<\/field>/)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should allow for objects which can be casted to an array' do
|
131
|
+
name = double("name", to_ary: ['matt1', 'matt2'])
|
132
|
+
data = {
|
133
|
+
:id => 1,
|
134
|
+
:name => name
|
135
|
+
}
|
136
|
+
result = generator.add(data)
|
137
|
+
expect(result).to match(/<field name="name">matt1<\/field>/)
|
138
|
+
expect(result).to match(/<field name="name">matt2<\/field>/)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should create an add from a single Message::Document' do
|
142
|
+
document = RSolr::Document.new
|
143
|
+
document.add_field('id', 1)
|
144
|
+
document.add_field('name', 'matt', :boost => 2.0)
|
145
|
+
result = generator.add(document)
|
146
|
+
expect(result).to match(Regexp.escape('<?xml version="1.0" encoding="UTF-8"?>'))
|
147
|
+
expect(result).to match(/<field name="id">1<\/field>/)
|
148
|
+
expect(result).to match Regexp.escape('boost="2.0"')
|
149
|
+
expect(result).to match Regexp.escape('name="name"')
|
150
|
+
expect(result).to match Regexp.escape('matt</field>')
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should create adds from multiple Message::Documents' do
|
154
|
+
documents = (1..2).map do |i|
|
155
|
+
doc = RSolr::Document.new
|
156
|
+
doc.add_field('id', i)
|
157
|
+
doc.add_field('name', "matt#{i}")
|
158
|
+
doc
|
159
|
+
end
|
160
|
+
result = generator.add(documents)
|
161
|
+
expect(result).to match(/<field name="name">matt1<\/field>/)
|
162
|
+
expect(result).to match(/<field name="name">matt2<\/field>/)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'supports nested child documents' do
|
166
|
+
data = {
|
167
|
+
:_childDocuments_ => [
|
168
|
+
{
|
169
|
+
:id => 1
|
170
|
+
},
|
171
|
+
{
|
172
|
+
:id => 2
|
173
|
+
}
|
174
|
+
]
|
175
|
+
}
|
176
|
+
|
177
|
+
result = generator.add(data)
|
178
|
+
expect(result).to match(%r{<add><doc><doc>})
|
179
|
+
expect(result).to match(%r{<doc><field name="id">1</field></doc>})
|
180
|
+
expect(result).to match(%r{<doc><field name="id">2</field></doc>})
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context :delete_by_id do
|
185
|
+
|
186
|
+
it 'should create a doc id delete' do
|
187
|
+
expect(generator.delete_by_id(10)).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>10</id></delete>")
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should create many doc id deletes' do
|
191
|
+
expect(generator.delete_by_id([1, 2, 3])).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>1</id><id>2</id><id>3</id></delete>")
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
context :delete_by_query do
|
197
|
+
it 'should create a query delete' do
|
198
|
+
expect(generator.delete_by_query('status:"LOST"')).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query>status:\"LOST\"</query></delete>")
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should create many query deletes' do
|
202
|
+
expect(generator.delete_by_query(['status:"LOST"', 'quantity:0'])).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query>status:\"LOST\"</query><query>quantity:0</query></delete>")
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context :formatting do
|
210
|
+
it 'should format date objects into ISO 8601' do
|
211
|
+
data = {
|
212
|
+
dt: Date.new(1992, 03, 15)
|
213
|
+
}
|
214
|
+
result = generator.add(data)
|
215
|
+
expect(result).to match(/<field name="dt">1992-03-15T00:00:00Z<\/field>/)
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should format time objects into ISO 8601' do
|
219
|
+
data = {
|
220
|
+
dt: Time.new(1992, 03, 15, 16, 23, 55, 3600)
|
221
|
+
}
|
222
|
+
result = generator.add(data)
|
223
|
+
expect(result).to match(/<field name="dt">1992-03-15T15:23:55Z<\/field>/)
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'should format datetime objects into ISO 8601' do
|
227
|
+
data = {
|
228
|
+
dt: DateTime.new(1992, 03, 15, 16, 23, 55, '+1')
|
229
|
+
}
|
230
|
+
result = generator.add(data)
|
231
|
+
expect(result).to match(/<field name="dt">1992-03-15T15:23:55Z<\/field>/)
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'passes through other values' do
|
235
|
+
data = {
|
236
|
+
whatever: 'some string'
|
237
|
+
}
|
238
|
+
|
239
|
+
result = generator.add(data)
|
240
|
+
expect(result).to match(/<field name="whatever">some string<\/field>/)
|
241
|
+
end
|
242
|
+
|
243
|
+
# rails monkey-patches String to add a #to_time casting..
|
244
|
+
context 'with rails monkey patching' do
|
245
|
+
it 'passes through string values' do
|
246
|
+
data = {
|
247
|
+
whatever: double(to_s: 'some string', to_time: nil)
|
248
|
+
}
|
249
|
+
|
250
|
+
result = generator.add(data)
|
251
|
+
expect(result).to match(/<field name="whatever">some string<\/field>/)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|