riddle 0.9.8.1231.0 → 0.9.8.1533.10
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +89 -0
- data/lib/riddle.rb +11 -6
- data/lib/riddle/client.rb +53 -11
- data/lib/riddle/client/filter.rb +11 -2
- data/lib/riddle/client/message.rb +4 -3
- data/lib/riddle/configuration.rb +33 -0
- data/lib/riddle/configuration/distributed_index.rb +48 -0
- data/lib/riddle/configuration/index.rb +142 -0
- data/lib/riddle/configuration/indexer.rb +19 -0
- data/lib/riddle/configuration/remote_index.rb +17 -0
- data/lib/riddle/configuration/searchd.rb +25 -0
- data/lib/riddle/configuration/section.rb +43 -0
- data/lib/riddle/configuration/source.rb +23 -0
- data/lib/riddle/configuration/sql_source.rb +34 -0
- data/lib/riddle/configuration/xml_source.rb +28 -0
- data/lib/riddle/controller.rb +53 -0
- data/lib/tabtab_definitions.rb +15 -0
- data/spec/unit/client_spec.rb +29 -1
- data/spec/unit/configuration/distributed_index_spec.rb +56 -0
- data/spec/unit/configuration/index_spec.rb +108 -0
- data/spec/unit/configuration/indexer_spec.rb +36 -0
- data/spec/unit/configuration/searchd_spec.rb +48 -0
- data/spec/unit/configuration/source_spec.rb +5 -0
- data/spec/unit/configuration/sql_source_spec.rb +100 -0
- data/spec/unit/configuration/xml_source_spec.rb +51 -0
- data/spec/unit/configuration_spec.rb +25 -0
- data/spec/unit/filter_spec.rb +5 -0
- data/spec/unit/riddle_spec.rb +17 -0
- metadata +45 -5
- data/README +0 -74
@@ -0,0 +1,19 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Configuration
|
3
|
+
class Indexer < Riddle::Configuration::Section
|
4
|
+
self.settings = [:mem_limit, :max_iops, :max_iosize]
|
5
|
+
|
6
|
+
attr_accessor *self.settings
|
7
|
+
|
8
|
+
def render
|
9
|
+
raise ConfigurationError unless valid?
|
10
|
+
|
11
|
+
(
|
12
|
+
["indexer", "{"] +
|
13
|
+
settings_body +
|
14
|
+
["}", ""]
|
15
|
+
).join("\n")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Configuration
|
3
|
+
class RemoteIndex
|
4
|
+
attr_accessor :address, :port, :name
|
5
|
+
|
6
|
+
def initialize(address, port, name)
|
7
|
+
@address = address
|
8
|
+
@port = port
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def remote
|
13
|
+
"#{address}:#{port}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Configuration
|
3
|
+
class Searchd < Riddle::Configuration::Section
|
4
|
+
self.settings = [:address, :port, :log, :query_log, :read_timeout,
|
5
|
+
:max_children, :pid_file, :max_matches, :seamless_rotate,
|
6
|
+
:preopen_indexes, :unlink_old]
|
7
|
+
|
8
|
+
attr_accessor *self.settings
|
9
|
+
|
10
|
+
def render
|
11
|
+
raise ConfigurationError unless valid?
|
12
|
+
|
13
|
+
(
|
14
|
+
["searchd", "{"] +
|
15
|
+
settings_body +
|
16
|
+
["}", ""]
|
17
|
+
).join("\n")
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid?
|
21
|
+
!( @port.nil? || @pid_file.nil? )
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Configuration
|
3
|
+
class Section
|
4
|
+
class << self
|
5
|
+
attr_accessor :settings
|
6
|
+
end
|
7
|
+
|
8
|
+
settings = []
|
9
|
+
|
10
|
+
def valid?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def settings_body
|
17
|
+
self.class.settings.select { |setting|
|
18
|
+
!send(setting).nil?
|
19
|
+
}.collect { |setting|
|
20
|
+
if send(setting) == ""
|
21
|
+
conf = " #{setting} = "
|
22
|
+
else
|
23
|
+
conf = setting_to_array(setting).collect { |set|
|
24
|
+
" #{setting} = #{set}"
|
25
|
+
}
|
26
|
+
end
|
27
|
+
conf.length == 0 ? nil : conf
|
28
|
+
}.flatten.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
def setting_to_array(setting)
|
32
|
+
value = send(setting)
|
33
|
+
case value
|
34
|
+
when Array then value
|
35
|
+
when TrueClass then [1]
|
36
|
+
when FalseClass then [0]
|
37
|
+
else
|
38
|
+
[value]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Configuration
|
3
|
+
class Source < Riddle::Configuration::Section
|
4
|
+
attr_accessor :name, :parent, :type
|
5
|
+
|
6
|
+
def render
|
7
|
+
raise ConfigurationError unless valid?
|
8
|
+
|
9
|
+
inherited_name = "#{name}"
|
10
|
+
inherited_name << " : #{parent}" if parent
|
11
|
+
(
|
12
|
+
["source #{inherited_name}", "{"] +
|
13
|
+
settings_body +
|
14
|
+
["}", ""]
|
15
|
+
).join("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid?
|
19
|
+
!( @name.nil? || @type.nil? )
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Configuration
|
3
|
+
class SQLSource < Riddle::Configuration::Source
|
4
|
+
self.settings = [:type, :sql_host, :sql_user, :sql_pass, :sql_db,
|
5
|
+
:sql_port, :sql_sock, :mysql_connect_flags, :sql_query_pre, :sql_query,
|
6
|
+
:sql_query_range, :sql_range_step, :sql_attr_uint, :sql_attr_bool,
|
7
|
+
:sql_attr_timestamp, :sql_attr_str2ordinal, :sql_attr_float,
|
8
|
+
:sql_attr_multi, :sql_query_post, :sql_query_post_index,
|
9
|
+
:sql_ranged_throttle, :sql_query_info]
|
10
|
+
|
11
|
+
attr_accessor *self.settings
|
12
|
+
|
13
|
+
def initialize(name, type)
|
14
|
+
@name = name
|
15
|
+
@type = type
|
16
|
+
|
17
|
+
@sql_query_pre = []
|
18
|
+
@sql_attr_uint = []
|
19
|
+
@sql_attr_bool = []
|
20
|
+
@sql_attr_timestamp = []
|
21
|
+
@sql_attr_str2ordinal = []
|
22
|
+
@sql_attr_float = []
|
23
|
+
@sql_attr_multi = []
|
24
|
+
@sql_query_post = []
|
25
|
+
@sql_query_post_index = []
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
super && (!( @sql_host.nil? || @sql_user.nil? || @sql_db.nil? ||
|
30
|
+
@sql_query.nil? ) || !@parent.nil?)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Configuration
|
3
|
+
class XMLSource < Riddle::Configuration::Source
|
4
|
+
self.settings = [:type, :xmlpipe_command, :xmlpipe_field,
|
5
|
+
:xmlpipe_attr_uint, :xmlpipe_attr_bool, :xmlpipe_attr_timestamp,
|
6
|
+
:xmlpipe_attr_str2ordinal, :xmlpipe_attr_float, :xmlpipe_attr_multi]
|
7
|
+
|
8
|
+
attr_accessor *self.settings
|
9
|
+
|
10
|
+
def initialize(name, type)
|
11
|
+
@name = name
|
12
|
+
@type = type
|
13
|
+
|
14
|
+
@xmlpipe_field = []
|
15
|
+
@xmlpipe_attr_uint = []
|
16
|
+
@xmlpipe_attr_bool = []
|
17
|
+
@xmlpipe_attr_timestamp = []
|
18
|
+
@xmlpipe_attr_str2ordinal = []
|
19
|
+
@xmlpipe_attr_float = []
|
20
|
+
@xmlpipe_attr_multi = []
|
21
|
+
end
|
22
|
+
|
23
|
+
def valid?
|
24
|
+
super && ( !@xmlpipe_command.nil? || !parent.nil? )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Controller
|
3
|
+
def initialize(configuration, path)
|
4
|
+
@configuration = configuration
|
5
|
+
@path = path
|
6
|
+
end
|
7
|
+
|
8
|
+
def index
|
9
|
+
cmd = "indexer --config #{@path} --all"
|
10
|
+
cmd << " --rotate" if running?
|
11
|
+
`#{cmd}`
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
return if running?
|
16
|
+
|
17
|
+
cmd = "searchd --pidfile --config #{@path}"
|
18
|
+
|
19
|
+
if RUBY_PLATFORM =~ /mswin/
|
20
|
+
system("start /B #{cmd} 1> NUL 2>&1")
|
21
|
+
else
|
22
|
+
`#{cmd}`
|
23
|
+
end
|
24
|
+
|
25
|
+
sleep(1)
|
26
|
+
|
27
|
+
unless running?
|
28
|
+
puts "Failed to start searchd daemon. Check #{@configuration.searchd.log}."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop
|
33
|
+
return unless running?
|
34
|
+
Process.kill('SIGTERM', pid.to_i)
|
35
|
+
rescue Errno::EINVAL
|
36
|
+
Process.kill('SIGKILL', pid.to_i)
|
37
|
+
end
|
38
|
+
|
39
|
+
def pid
|
40
|
+
if File.exists?(@configuration.searchd.pid_file)
|
41
|
+
File.read(@configuration.searchd.pid_file)[/\d+/]
|
42
|
+
else
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def running?
|
48
|
+
!!pid && !!Process.kill(0, pid.to_i)
|
49
|
+
rescue
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
TabTab::Definition.register("searchd") do |c|
|
2
|
+
c.flag :help, :h
|
3
|
+
c.flag :config, :c
|
4
|
+
c.flag :stop
|
5
|
+
c.flag :iostats
|
6
|
+
c.flag :console
|
7
|
+
c.flag :port, :p
|
8
|
+
c.flag :index, :i
|
9
|
+
end
|
10
|
+
|
11
|
+
TabTab::Definition.register("indexer") do |c|
|
12
|
+
c.flag :config, :c
|
13
|
+
c.flag :all
|
14
|
+
c.flag :rotate
|
15
|
+
end
|
data/spec/unit/client_spec.rb
CHANGED
@@ -177,4 +177,32 @@ describe Riddle::Client do
|
|
177
177
|
true
|
178
178
|
).should == query_contents(:keywords_with_hits)
|
179
179
|
end
|
180
|
-
|
180
|
+
|
181
|
+
it "should timeout after a specified time" do
|
182
|
+
client = Riddle::Client.new
|
183
|
+
client.port = 3314
|
184
|
+
client.timeout = 1
|
185
|
+
|
186
|
+
server = TCPServer.new "localhost", 3314
|
187
|
+
|
188
|
+
lambda {
|
189
|
+
client.send(:connect) { |socket| }
|
190
|
+
}.should raise_error(Riddle::ConnectionError)
|
191
|
+
|
192
|
+
server.close
|
193
|
+
end
|
194
|
+
|
195
|
+
context "connection retrying" do
|
196
|
+
it "should try fives time when connection refused" do
|
197
|
+
client = Riddle::Client.new
|
198
|
+
client.port = 3314
|
199
|
+
|
200
|
+
TCPSocket.should_receive(:new).with('localhost', 3314).exactly(5).times.
|
201
|
+
and_raise(Errno::ECONNREFUSED)
|
202
|
+
|
203
|
+
lambda {
|
204
|
+
client.send(:connect) { |socket| }
|
205
|
+
}.should raise_error(Riddle::ConnectionError)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe Riddle::Configuration::DistributedIndex do
|
4
|
+
it "should not be valid without any indexes" do
|
5
|
+
index = Riddle::Configuration::DistributedIndex.new("dist1")
|
6
|
+
index.should_not be_valid
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should be valid with just local indexes" do
|
10
|
+
index = Riddle::Configuration::DistributedIndex.new("dist1")
|
11
|
+
index.local_indexes << "local_one"
|
12
|
+
index.should be_valid
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should be valid with just remote indexes" do
|
16
|
+
index = Riddle::Configuration::DistributedIndex.new("dist1")
|
17
|
+
index.remote_indexes << Riddle::Configuration::RemoteIndex.new("local", 3312, "remote_one")
|
18
|
+
index.should be_valid
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should be of type 'distributed'" do
|
22
|
+
index = Riddle::Configuration::DistributedIndex.new("dist1")
|
23
|
+
index.type.should == 'distributed'
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should raise a ConfigurationError if rendering when not valid" do
|
27
|
+
index = Riddle::Configuration::DistributedIndex.new("dist1")
|
28
|
+
lambda { index.render }.should raise_error(Riddle::Configuration::ConfigurationError)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should render correctly if supplied settings are valid" do
|
32
|
+
index = Riddle::Configuration::DistributedIndex.new("dist1")
|
33
|
+
|
34
|
+
index.local_indexes << "test1" << "test1stemmed"
|
35
|
+
index.remote_indexes <<
|
36
|
+
Riddle::Configuration::RemoteIndex.new("localhost", 3313, "remote1") <<
|
37
|
+
Riddle::Configuration::RemoteIndex.new("localhost", 3314, "remote2") <<
|
38
|
+
Riddle::Configuration::RemoteIndex.new("localhost", 3314, "remote3")
|
39
|
+
|
40
|
+
index.agent_connect_timeout = 1000
|
41
|
+
index.agent_query_timeout = 3000
|
42
|
+
|
43
|
+
index.render.should == <<-DISTINDEX
|
44
|
+
index dist1
|
45
|
+
{
|
46
|
+
type = distributed
|
47
|
+
local = test1
|
48
|
+
local = test1stemmed
|
49
|
+
agent = localhost:3313:remote1
|
50
|
+
agent = localhost:3314:remote2,remote3
|
51
|
+
agent_connect_timeout = 1000
|
52
|
+
agent_query_timeout = 3000
|
53
|
+
}
|
54
|
+
DISTINDEX
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe Riddle::Configuration::DistributedIndex do
|
4
|
+
it "should be invalid without a name, sources or path if there's no parent" do
|
5
|
+
index = Riddle::Configuration::Index.new(nil)
|
6
|
+
index.should_not be_valid
|
7
|
+
|
8
|
+
index.name = "test1"
|
9
|
+
index.should_not be_valid
|
10
|
+
|
11
|
+
index.sources << Riddle::Configuration::SQLSource.new("source", "mysql")
|
12
|
+
index.should_not be_valid
|
13
|
+
|
14
|
+
index.path = "a/path"
|
15
|
+
index.should be_valid
|
16
|
+
|
17
|
+
index.name = nil
|
18
|
+
index.should_not be_valid
|
19
|
+
|
20
|
+
index.name = "test1"
|
21
|
+
index.sources.clear
|
22
|
+
index.should_not be_valid
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be invalid without a name but not sources or path if it has a parent" do
|
26
|
+
index = Riddle::Configuration::Index.new(nil)
|
27
|
+
index.should_not be_valid
|
28
|
+
|
29
|
+
index.name = "test1stemmed"
|
30
|
+
index.should_not be_valid
|
31
|
+
|
32
|
+
index.parent = "test1"
|
33
|
+
index.should be_valid
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should raise a ConfigurationError if rendering when not valid" do
|
37
|
+
index = Riddle::Configuration::Index.new("test1")
|
38
|
+
lambda { index.render }.should raise_error(Riddle::Configuration::ConfigurationError)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should render correctly if supplied settings are valid" do
|
42
|
+
source = Riddle::Configuration::XMLSource.new("src1", "xmlpipe")
|
43
|
+
source.xmlpipe_command = "ls /dev/null"
|
44
|
+
|
45
|
+
index = Riddle::Configuration::Index.new("test1", source)
|
46
|
+
index.path = "/var/data/test1"
|
47
|
+
index.docinfo = "extern"
|
48
|
+
index.mlock = 0
|
49
|
+
index.morphologies << "stem_en" << "stem_ru" << "soundex"
|
50
|
+
index.stopword_files << "/var/data/stopwords.txt" << "/var/data/stopwords2.txt"
|
51
|
+
index.wordform_files << "/var/data/wordforms.txt"
|
52
|
+
index.exception_files << "/var/data/exceptions.txt"
|
53
|
+
index.min_word_len = 1
|
54
|
+
index.charset_type = "utf-8"
|
55
|
+
index.charset_table = "0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F"
|
56
|
+
index.ignore_characters << "U+00AD"
|
57
|
+
index.min_prefix_len = 0
|
58
|
+
index.min_infix_len = 0
|
59
|
+
index.prefix_field_names << "filename"
|
60
|
+
index.infix_field_names << "url" << "domain"
|
61
|
+
index.enable_star = true
|
62
|
+
index.ngram_len = 1
|
63
|
+
index.ngram_characters << "U+3000..U+2FA1F"
|
64
|
+
index.phrase_boundaries << "." << "?" << "!" << "U+2026"
|
65
|
+
index.phrase_boundary_step = 100
|
66
|
+
index.html_strip = 0
|
67
|
+
index.html_index_attrs = "img=alt,title; a=title"
|
68
|
+
index.html_remove_element_tags << "style" << "script"
|
69
|
+
index.preopen = 1
|
70
|
+
|
71
|
+
index.render.should == <<-INDEX
|
72
|
+
source src1
|
73
|
+
{
|
74
|
+
type = xmlpipe
|
75
|
+
xmlpipe_command = ls /dev/null
|
76
|
+
}
|
77
|
+
|
78
|
+
index test1
|
79
|
+
{
|
80
|
+
source = src1
|
81
|
+
path = /var/data/test1
|
82
|
+
docinfo = extern
|
83
|
+
mlock = 0
|
84
|
+
morphology = stem_en, stem_ru, soundex
|
85
|
+
stopwords = /var/data/stopwords.txt /var/data/stopwords2.txt
|
86
|
+
wordforms = /var/data/wordforms.txt
|
87
|
+
exceptions = /var/data/exceptions.txt
|
88
|
+
min_word_len = 1
|
89
|
+
charset_type = utf-8
|
90
|
+
charset_table = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
|
91
|
+
ignore_chars = U+00AD
|
92
|
+
min_prefix_len = 0
|
93
|
+
min_infix_len = 0
|
94
|
+
prefix_fields = filename
|
95
|
+
infix_fields = url, domain
|
96
|
+
enable_star = 1
|
97
|
+
ngram_len = 1
|
98
|
+
ngram_chars = U+3000..U+2FA1F
|
99
|
+
phrase_boundary = ., ?, !, U+2026
|
100
|
+
phrase_boundary_step = 100
|
101
|
+
html_strip = 0
|
102
|
+
html_index_attrs = img=alt,title; a=title
|
103
|
+
html_remove_elements = style, script
|
104
|
+
preopen = 1
|
105
|
+
}
|
106
|
+
INDEX
|
107
|
+
end
|
108
|
+
end
|