solrsam 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,195 @@
1
+ module Solrsam
2
+ module Search
3
+ extend ActiveSupport::Concern
4
+ module ClassMethods
5
+
6
+ attr_accessor :solr_server
7
+
8
+ HL_START_TAG = "<mark>"
9
+ HL_END_TAG = "</mark>"
10
+
11
+ def class_name
12
+ to_s.underscore
13
+ end
14
+
15
+ def solr(method = :read)
16
+ @solr_server ||= :default
17
+ Solrsam::Config.instance.rsolr_object(@solr_server, method)
18
+ end
19
+
20
+ def perform_solr_command(method = :read)
21
+ yield(solr(method))
22
+ solr(method).commit
23
+ end
24
+
25
+ def search(search_params={})
26
+ start = search_params[:start] || 0
27
+ rows = search_params[:rows] || 20
28
+
29
+ solr_params = parse_params_for_solr(search_params)
30
+
31
+ begin
32
+ solr_response = solr.select :data => solr_params, :method => :post
33
+ parse_solr_response(solr_response)
34
+ rescue RSolr::Error::Http => e
35
+ {:docs => [],
36
+ :metadata =>
37
+ {:error => {:http_status_code => e.response[:status],
38
+ :http_message_status => RSolr::Error::Http::STATUS_CODES[e.response[:status].to_i],
39
+ :full_message => e.message}}}
40
+ end
41
+ end
42
+
43
+ def parse_params_for_solr(search_params={})
44
+ solr_params = { :echoParams => 'explicit',
45
+ :q => "*:*",
46
+ :facet => "on",
47
+ :'facet.mincount' => 1}.merge(search_params)
48
+
49
+ solr_params[:hl] = true unless search_params[:'hl.fl'].blank?
50
+ solr_params[:fq] = parse_fq(search_params[:fq]) + ( search_params[:untyped] ? [] : ["type:#{class_name}"])
51
+ solr_params
52
+ end
53
+
54
+ def parse_solr_response(solr_response)
55
+ docs = solr_response['response']['docs']
56
+ parsed_facet_counts = parse_facet_counts(solr_response['facet_counts'])
57
+ highlighting = solr_response['highlighting']
58
+ suggestions = solr_response['spellcheck']['suggestions'] rescue {}
59
+
60
+ metadata = {
61
+ :total_count => solr_response['response']['numFound'],
62
+ :start => solr_response['response']['start'],
63
+ :rows => solr_response['responseHeader']['params']['rows'],
64
+ :time => solr_response['responseHeader']['QTime'],
65
+ :status => solr_response['responseHeader']['status'],
66
+ :sort => solr_response['responseHeader']['params']['sort'],
67
+ :debug => solr_response['debug']
68
+ }
69
+ response = {:docs => docs, :metadata => metadata,
70
+ :facet_counts => parsed_facet_counts, :highlighting => highlighting}
71
+ response[:stats] = solr_response['stats'] if solr_response['stats']
72
+ response[:suggestions] = suggestions
73
+
74
+ embed_highlighting(response)
75
+ end
76
+
77
+ def parse_fq(fq)
78
+ return [] if fq.nil?
79
+ if fq.is_a?(Hash)
80
+ fq.map{|k,v| parse_element_in_fq({k => v})}.flatten
81
+ elsif fq.is_a?(Array)
82
+ fq.map{|ele| parse_element_in_fq(ele)}.flatten
83
+ else
84
+ raise "fq must be a hash or array"
85
+ end
86
+ end
87
+
88
+ def parse_element_in_fq(element)
89
+ if element.is_a?(String)
90
+ element
91
+ elsif element.is_a?(Hash)
92
+ element.map do |k,values|
93
+ if values.is_a?(String)
94
+ key_value_query(k,values)
95
+ else
96
+ values.map{|value| key_value_query(k,value) }
97
+ end
98
+ end
99
+ else
100
+ raise "each fq parameter must be a string or hash"
101
+ end
102
+ end
103
+
104
+ def key_value_query(key, value)
105
+ if value.starts_with?("[")
106
+ "#{key}:#{value}"
107
+ elsif value.starts_with?("(")
108
+ # we don't want to escape double quotes
109
+ "#{key}:" + value
110
+ else
111
+ "#{key}:\"#{value}\""
112
+ end
113
+ end
114
+
115
+ def parse_facet_counts(facet_counts)
116
+ return {} unless facet_counts
117
+
118
+ if facet_counts['facet_fields']
119
+ facet_counts['facet_fields'] = facet_counts['facet_fields'].reduce({}) do |acc, facet_collection|
120
+ acc[facet_collection[0]] = map_facet_array_to_facet_hash(facet_collection[1])
121
+ acc
122
+ end
123
+ end
124
+
125
+ if facet_counts['facet_queries']
126
+ facet_counts['facet_queries'] = facet_counts['facet_queries'].group_by{|k,v| k.split(":").first}.reduce({}) do |acc, facet_collection|
127
+ facet_name = facet_collection[0]
128
+ values = facet_collection[1]
129
+
130
+ acc[facet_name] = values.reduce({}) do |inner_acc, tuple|
131
+ range = tuple[0].split(":")[1]
132
+ inner_acc[range] = tuple[1]
133
+ inner_acc
134
+ end
135
+ acc
136
+ end
137
+ end
138
+
139
+ facet_counts
140
+ end
141
+
142
+ # solr facet_fields comes in tuple array format []
143
+ def map_facet_array_to_facet_hash(facet_collection)
144
+ if facet_collection.is_a?(Array)
145
+ facet_collection.each_slice(2).reduce({}){|acc, tuple| acc[tuple[0]] = tuple[1]; acc}
146
+ else
147
+ facet_collection
148
+ end
149
+ end
150
+
151
+ def embed_highlighting(search_response)
152
+ return search_response unless search_response[:highlighting]
153
+
154
+ excluded_highlighting_fields = ['id', 'db_id', 'type']
155
+
156
+ highlighted_docs = search_response[:docs].map do |doc|
157
+ hl_metadata = search_response[:highlighting][doc['id']]
158
+
159
+ hl_metadata.drop_while{|k,v| excluded_highlighting_fields.include?(k) }.each do |k,v|
160
+ new_value = if doc[k].is_a?(Array)
161
+ matched = v.map{|t| t.gsub(HL_START_TAG,"").gsub(HL_END_TAG,"") }
162
+ v.concat(doc[k].drop_while{|text| matched.include?(text) })
163
+ else
164
+ v
165
+ end
166
+ doc[k] = new_value
167
+ end if hl_metadata
168
+
169
+ doc
170
+ end
171
+
172
+ search_response[:docs] = highlighted_docs
173
+
174
+ search_response[:docs].extend RSolr::Pagination::PaginatedDocSet
175
+
176
+ search_response
177
+ end
178
+
179
+ end
180
+ end
181
+ end
182
+
183
+ # namespace test documents
184
+ if defined?(Rails) && Rails.env == "test"
185
+ module Solrsam
186
+ module Search
187
+ extend ActiveSupport::Concern
188
+ module ClassMethods
189
+ def class_name
190
+ "#{to_s.underscore}_test"
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,3 @@
1
+ module Solrsam
2
+ VERSION = "0.4.2"
3
+ end
data/lib/solrsam.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rsolr'
2
+ require 'active_support/json'
3
+ require 'active_support/concern'
4
+ require 'active_support/core_ext'
5
+
6
+ require 'singleton'
7
+ require 'solrsam/config'
8
+ require 'solrsam/search'
9
+ require 'solrsam/indexer'
10
+
11
+ module Solrsam
12
+ end
@@ -0,0 +1,71 @@
1
+ require 'uri'
2
+ require 'yaml'
3
+ require 'open-uri'
4
+
5
+ namespace :solr do
6
+ env = "development"
7
+ base_dir = File.join(File.dirname(__FILE__), "..", "..")
8
+
9
+ if defined? Rails
10
+ env = Rails.env
11
+ base_dir = Rails.root
12
+ end
13
+
14
+ solr_config = YAML::load( File.open( File.join(base_dir, "config", "solr.yml") ) )
15
+ solr_home = File.join(base_dir, "config", "solr")
16
+ solr_data_dir = solr_config[env]['solr_data_dir']
17
+ solr_server_url = solr_config[env]['solr_server_url']
18
+ java_opts = solr_config[env]['java_opts'] || ""
19
+
20
+ jetty_port = URI.parse(solr_server_url).port
21
+ jetty_path = solr_config[env]['jetty_path']
22
+
23
+ solr_server_dir = "cd #{jetty_path};"
24
+ start_solr_cmd = "java #{java_opts} -jar start.jar"
25
+ logging_xml = "etc/jetty-logging.xml"
26
+ jetty_port_opt = "jetty.port=#{jetty_port}"
27
+ solr_params = "#{jetty_port_opt} -Dsolr.solr.home=#{solr_home} -Dsolr.data.dir=#{solr_data_dir}"
28
+
29
+ desc "Start solr"
30
+ task(:start) do
31
+ # -Dsolr.clustering.enabled=true
32
+ cmd = kill_matching_process_cmd(jetty_port_opt)
33
+ stop_exit_status = run_system_command(cmd)
34
+
35
+ sleep(1)
36
+
37
+ cmd = "#{solr_server_dir} #{start_solr_cmd} #{logging_xml} #{solr_params} &"
38
+ run_system_command(cmd)
39
+ end
40
+
41
+ desc "Stop solr"
42
+ task(:stop) do
43
+ cmd = kill_matching_process_cmd(jetty_port_opt)
44
+ run_system_command(cmd)
45
+ end
46
+
47
+ desc "update spelling dictionary"
48
+ task :build_dictionary do
49
+ open("#{solr_server_url}/select?q=facebok&qt=spell&spellcheck.build=true")
50
+ end
51
+
52
+ def run_system_command(cmd)
53
+ puts cmd
54
+ status = system(cmd)
55
+ $?.exitstatus
56
+ end
57
+
58
+ def kill_matching_process_cmd(process_name)
59
+ cmd = "echo `ps -ef | grep -v grep | grep \"#{process_name.gsub("-", "\\-")}\" | awk '{print $2}'` | xargs kill"
60
+ end
61
+
62
+ # #example of a task to index all items
63
+ # desc "index food"
64
+ # task(:import_foods, :needs => :environment) do
65
+ # FoodDescription.find_in_batches do |batch|
66
+ # FoodIndexer.index(batch)
67
+ # puts "Done with batch of size: #{batch.size}"
68
+ # end
69
+ # end
70
+ end
71
+
data/solrsam.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "solrsam/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "solrsam"
7
+ s.version = Solrsam::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Tommy Chheng", "Sam Dalton"]
10
+ s.email = ["tommy.chheng@gmail.com", "sam@quid.com"]
11
+ s.homepage = "http://github.com/samdalton/solrsan"
12
+ s.summary = %q{Lightweight wrapper for using Apache Solr within a Ruby application including Rails and Sinatra.}
13
+ s.description = %q{solrsan is a lightweight wrapper for using Apache Solr within a Ruby application including Rails and Sinatra.}
14
+
15
+ s.rubyforge_project = "solrsam"
16
+
17
+ s.add_dependency 'activesupport'
18
+ s.add_dependency 'activemodel'
19
+ s.add_dependency 'rsolr', '1.0.0'
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_model/attribute_methods'
2
+
3
+ class Document
4
+ include ActiveModel::AttributeMethods
5
+ include Solrsam::Search
6
+ attr_accessor :attributes
7
+
8
+ def initialize(attributes={})
9
+ @attributes = attributes
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module SearchTestHelper
2
+ def document_mock
3
+ doc = Document.new({:id => 500,
4
+ :title => "Solr test document",
5
+ :author => "Tommy Chheng",
6
+ :content => "ruby scala search",
7
+ :review_count => 4,
8
+ :scores => [1,2,3,4],
9
+ :created_at => Date.parse("Dec 10 2010")})
10
+ doc
11
+ end
12
+ end
13
+
@@ -0,0 +1,27 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ MODELS = File.join(File.dirname(__FILE__), "models")
5
+ $LOAD_PATH.unshift(MODELS)
6
+
7
+ #simulate rails env
8
+ class Rails
9
+ def self.env
10
+ "test"
11
+ end
12
+ end
13
+
14
+ require "rubygems"
15
+ require "test/unit"
16
+ require 'solrsam'
17
+
18
+ #test models
19
+ require 'document'
20
+
21
+ solr_config = YAML::load( File.open( File.join(File.dirname(__FILE__), "..", "config", "solr.yml") ) )
22
+ solr_server_url = solr_config["test"]['solr_server_url']
23
+
24
+ Solrsam::Config.instance.add_server(:example, 'example.com', :read)
25
+ Solrsam::Config.instance.add_server(:example, 'example.com', :write)
26
+ Solrsam::Config.instance.add_server(:default, solr_server_url, :read)
27
+ Solrsam::Config.instance.add_server(:default, solr_server_url, :write)
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+ require 'search_test_helper'
3
+
4
+ class IndexerTest < Test::Unit::TestCase
5
+ include SearchTestHelper
6
+
7
+ def setup
8
+ @document = document_mock
9
+ end
10
+
11
+ def teardown
12
+ end
13
+
14
+ def test_indexed_fields
15
+ created_doc = @document.indexed_fields
16
+ assert_equal @document.attributes[:id], created_doc[:db_id]
17
+ assert_equal @document.attributes[:title], created_doc[:title]
18
+ assert_equal @document.attributes[:author], created_doc[:author]
19
+ assert_equal @document.attributes[:content], created_doc[:content]
20
+ assert_equal @document.attributes[:review_count], created_doc[:review_count]
21
+ assert_equal @document.attributes[:scores], created_doc[:scores]
22
+ assert_equal @document.attributes[:created_at].to_time.utc.xmlschema, created_doc[:created_at]
23
+ end
24
+
25
+ end
@@ -0,0 +1,251 @@
1
+ require 'test_helper'
2
+ require 'search_test_helper'
3
+
4
+ class SearchTest < Test::Unit::TestCase
5
+ include SearchTestHelper
6
+
7
+ def setup
8
+ Document.destroy_all_index_documents!
9
+ @document = document_mock
10
+ end
11
+
12
+ def teardown
13
+ Document.destroy_all_index_documents!
14
+ end
15
+
16
+ def test_simple_query
17
+ Document.index(@document)
18
+ q = @document.attributes[:title]
19
+
20
+ response = Document.search(:q => q)
21
+ metadata = response[:metadata]
22
+ docs = response[:docs]
23
+ assert metadata[:total_count] > 0
24
+ end
25
+
26
+ def test_sort
27
+ Document.index(Document.new(:id => 3, :title => "solar city",:review_count => 10))
28
+ Document.index(Document.new(:id => 4, :title => "city solar", :review_count => 5))
29
+
30
+ q = "solar"
31
+ response = Document.search(:q => q, :sort => "review_count asc")
32
+ docs = response[:docs]
33
+
34
+ assert_not_nil docs[0], "Not enough docs for #{q} to test."
35
+ first_result_name = docs[0][:title]
36
+ assert_not_nil docs[1], "Not enough docs for #{q} to test."
37
+ second_result_name = docs[1][:title]
38
+
39
+ assert_equal [second_result_name, first_result_name].sort, [first_result_name, second_result_name]
40
+
41
+ response = Document.search(:q => q, :sort => "review_count desc")
42
+ docs = response[:docs]
43
+
44
+ assert_not_nil docs[0], "Not enough docs for #{q} to test."
45
+ first_result_name = docs[0][:title]
46
+ assert_not_nil docs[1], "Not enough docs for #{q} to test."
47
+ second_result_name = docs[1][:title]
48
+
49
+ assert_equal [second_result_name, first_result_name].sort.reverse, [first_result_name, second_result_name]
50
+ end
51
+
52
+ def test_invalid_query_should_return_error_message_in_metadata
53
+ response = Document.search(:q => "http://tommy.chheng.com")
54
+ docs = response[:docs]
55
+ metadata = response[:metadata]
56
+
57
+ assert_not_nil response[:metadata][:error]
58
+ assert_equal 400, response[:metadata][:error][:http_status_code]
59
+ end
60
+
61
+ def test_parse_facet_fields
62
+ facet_counts = {'facet_queries'=>{},
63
+ 'facet_fields' => {'language' => ["Scala", 2, "Ruby", 1, "Java", 0]},
64
+ 'facet_dates'=>{}}
65
+
66
+ facet_counts = Document.parse_facet_counts(facet_counts)
67
+
68
+ assert_equal ["Scala", "Ruby", "Java"], facet_counts['facet_fields']['language'].keys
69
+ end
70
+
71
+ def test_parse_facet_queries
72
+ facet_counts = {"facet_queries"=>{"funding:[0 TO 5000000]"=>1, "funding:[10000000 TO 50000000]"=>0}, "facet_fields"=>{}, "facet_dates"=>{}}
73
+
74
+ facet_counts = Document.parse_facet_counts(facet_counts)
75
+
76
+ expected = {"funding"=>{"[0 TO 5000000]"=>1, "[10000000 TO 50000000]"=>0}}
77
+ assert_equal expected, facet_counts['facet_queries']
78
+ end
79
+
80
+ def test_parse_fq_with_hash
81
+ params = {:fq => {:tags => ["ruby", "scala"]}}
82
+ filters = Document.parse_fq(params[:fq])
83
+
84
+ expected = ["tags:\"ruby\"", "tags:\"scala\""]
85
+ assert_equal expected, filters
86
+ end
87
+
88
+ def test_parse_fq_with_hash_multiple_entries
89
+ params = {:fq => {:tags => ["ruby", "scala"], :author => "Bert"}}
90
+ filters = Document.parse_fq(params[:fq])
91
+
92
+ expected = ["tags:\"ruby\"", "tags:\"scala\"", "author:\"Bert\""]
93
+ assert_equal expected, filters
94
+ end
95
+
96
+ def test_parse_fq_with_hash_array_args
97
+ params = {:fq => [{:tags => ["ruby", "scala"]}]}
98
+ filters = Document.parse_fq(params[:fq])
99
+
100
+ expected = ["tags:\"ruby\"", "tags:\"scala\""]
101
+ assert_equal expected, filters
102
+ end
103
+
104
+ def test_parse_fq_with_hash_string_args
105
+ params = {:fq => [{:tags => "ruby"}]}
106
+ filters = Document.parse_fq(params[:fq])
107
+
108
+ expected = ["tags:\"ruby\""]
109
+ assert_equal expected, filters
110
+ end
111
+
112
+ def test_parse_fq_with_string_args
113
+ params = {:fq => ["tags:ruby"]}
114
+ filters = Document.parse_fq(params[:fq])
115
+
116
+ expected = ["tags:ruby"]
117
+ assert_equal expected, filters
118
+ end
119
+
120
+ def test_parse_fq_with_empty
121
+ filters = Document.parse_fq([])
122
+ expected = []
123
+ assert_equal expected, filters
124
+ end
125
+
126
+
127
+ def test_filter_query_mulitple_filters
128
+ Document.index(Document.new(:id => 3, :author => "Bert", :title => "solr lucene",:review_count => 10, :tags => ['ruby']))
129
+ Document.index(Document.new(:id => 4, :author => "Ernie", :title => "lucene solr", :review_count => 5, :tags => ['ruby', 'scala']))
130
+
131
+ response = Document.search(:q => "solr", :fq => {:tags => ["scala"], :author => "Ernie"})
132
+ docs = response[:docs]
133
+ metadata = response[:metadata]
134
+
135
+ assert_equal 1, metadata[:total_count]
136
+
137
+ doc = docs.first
138
+ assert_not_nil doc['tags']
139
+ assert doc['tags'].include?("scala")
140
+ end
141
+
142
+ def test_filter_query
143
+ Document.index(Document.new(:id => 3, :author => "Bert", :title => "solr lucene",:review_count => 10, :tags => ['ruby']))
144
+ Document.index(Document.new(:id => 4, :author => "Ernie", :title => "lucene solr", :review_count => 5, :tags => ['ruby', 'scala']))
145
+
146
+ response = Document.search(:q => "solr", :fq => [{:tags => ["scala"]}])
147
+ docs = response[:docs]
148
+ metadata = response[:metadata]
149
+
150
+ assert_equal 1, metadata[:total_count]
151
+
152
+ doc = docs.first
153
+ assert_not_nil doc['tags']
154
+ assert doc['tags'].include?("scala")
155
+ end
156
+
157
+ def test_text_faceting
158
+ Document.index(Document.new(:id => 3, :author => "Bert", :title => "solr lucene",:review_count => 10))
159
+ Document.index(Document.new(:id => 4, :author => "Ernie", :title => "lucene solr", :review_count => 5))
160
+
161
+ response = Document.search(:q => "solr", :'facet.field' => ['author'])
162
+ docs = response[:docs]
163
+ facet_counts = response[:facet_counts]
164
+ assert_not_nil facet_counts["facet_fields"]["author"]
165
+
166
+ author_facet_entries = facet_counts["facet_fields"]["author"]
167
+ assert author_facet_entries.keys.include?("Bert") && author_facet_entries.keys.include?("Ernie")
168
+ end
169
+
170
+ def test_range_faceting
171
+ Document.index(Document.new(:id => 3, :author => "Bert", :title => "solr lucene",:review_count => 10))
172
+ Document.index(Document.new(:id => 4, :author => "Ernie", :title => "lucene solr", :review_count => 5))
173
+
174
+ response = Document.search(:q => "solr", :'facet.field' => ['author'], :'facet.query' => ["review_count:[1 TO 5]", "review_count:[6 TO 10]"])
175
+ docs = response[:docs]
176
+ facet_counts = response[:facet_counts]
177
+
178
+ assert_not_nil facet_counts["facet_fields"]["author"]
179
+ assert_not_nil facet_counts["facet_queries"]["review_count"]
180
+ assert_equal({"[1 TO 5]"=>1, "[6 TO 10]"=>1}, facet_counts["facet_queries"]["review_count"])
181
+ end
182
+
183
+ def test_highlighting_support
184
+ Document.index(Document.new(:id => 3, :author => "Bert", :title => "solr lucene",:review_count => 10, :tags => ["solr"]))
185
+
186
+ response = Document.search(:q => "solr",
187
+ :'hl.fl' => "*")
188
+ docs = response[:docs]
189
+ highlighting = response[:highlighting]
190
+
191
+ first_result = highlighting.first
192
+ assert first_result[1]['tags'].include?("<mark>solr</mark>")
193
+ end
194
+
195
+ def test_embed_highlighting
196
+ Document.index(Document.new(:id => 3, :author => "Bert", :title => "solr lucene",:review_count => 10, :tags => ["solr", "sphinx"]))
197
+
198
+ response = Document.search(:q => "solr",
199
+ :'hl.fl' => "*")
200
+ docs = response[:docs]
201
+ assert_equal ["<mark>solr</mark>","sphinx"], docs.first['tags']
202
+ end
203
+
204
+ def test_debug_response
205
+ Document.index(@document)
206
+ q = @document.attributes[:title]
207
+
208
+ response = Document.search({:q => q, :debugQuery => true})
209
+ metadata = response[:metadata]
210
+ assert_not_nil metadata[:debug]
211
+ end
212
+
213
+ def test_stats_response
214
+ Document.index(@document)
215
+ q = @document.attributes[:title]
216
+
217
+ response = Document.search({:q => q, :stats => true, :'stats.field' => 'review_count'})
218
+ assert_not_nil response[:stats]
219
+ assert_equal 1, response[:stats]['stats_fields']['review_count']['count']
220
+ end
221
+
222
+ def test_will_paginate_support
223
+ Document.index(Document.new(:id => 3, :author => "Bert", :title => "solr lucene",:review_count => 10))
224
+ Document.index(Document.new(:id => 4, :author => "Ernie", :title => "lucene solr", :review_count => 5))
225
+
226
+ response = Document.search(:q => "solr")
227
+ docs = response[:docs]
228
+ assert_not_nil docs.start
229
+ assert_not_nil docs.per_page
230
+ assert_not_nil docs.current_page
231
+ end
232
+
233
+ def test_solr_server_attribute_writable
234
+ begin
235
+ Document.solr_server = :example
236
+ assert_equal :example, Document.solr_server
237
+ ensure
238
+ Document.solr_server = nil
239
+ end
240
+ end
241
+
242
+ def test_rsolr_is_configured_with_the_set_solr_server
243
+ begin
244
+ Document.solr_server = :example
245
+ assert_equal "example.com/", Document.solr.options[:url]
246
+ ensure
247
+ Document.solr_server = nil
248
+ end
249
+ end
250
+ end
251
+