solrsam 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +22 -0
- data/LICENSE +23 -0
- data/README.rdoc +115 -0
- data/Rakefile +18 -0
- data/config/solr/conf/elevate.xml +31 -0
- data/config/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
- data/config/solr/conf/protwords.txt +22 -0
- data/config/solr/conf/schema.xml +237 -0
- data/config/solr/conf/solrconfig.xml +430 -0
- data/config/solr/conf/spellings.txt +2 -0
- data/config/solr/conf/stopwords.txt +56 -0
- data/config/solr/conf/synonyms.txt +24 -0
- data/config/solr/conf/xslt/example.xsl +132 -0
- data/config/solr/conf/xslt/example_atom.xsl +67 -0
- data/config/solr/conf/xslt/example_rss.xsl +66 -0
- data/config/solr/conf/xslt/luke.xsl +337 -0
- data/config/solr.yml +12 -0
- data/config/solr.yml.example +13 -0
- data/lib/rails/generators/solrsan/config/config_generator.rb +23 -0
- data/lib/rails/generators/solrsan/config/templates/solr.yml +13 -0
- data/lib/rails/generators/solrsan/config/templates/solrsan.rb +5 -0
- data/lib/rails/generators/solrsan_generator.rb +11 -0
- data/lib/solrsam/capistrano.rb +31 -0
- data/lib/solrsam/config.rb +25 -0
- data/lib/solrsam/indexer.rb +83 -0
- data/lib/solrsam/search.rb +195 -0
- data/lib/solrsam/version.rb +3 -0
- data/lib/solrsam.rb +12 -0
- data/lib/tasks/solr.rake +71 -0
- data/solrsam.gemspec +25 -0
- data/test/models/document.rb +11 -0
- data/test/search_test_helper.rb +13 -0
- data/test/test_helper.rb +27 -0
- data/test/unit/indexer_test.rb +25 -0
- data/test/unit/search_test.rb +251 -0
- metadata +124 -0
@@ -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
|
data/lib/solrsam.rb
ADDED
data/lib/tasks/solr.rake
ADDED
@@ -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,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
|
+
|
data/test/test_helper.rb
ADDED
@@ -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
|
+
|