riddle 1.5.2 → 1.5.3
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.
- data/HISTORY +7 -0
- data/lib/riddle/auto_version.rb +1 -1
- data/lib/riddle/configuration.rb +6 -0
- data/lib/riddle/configuration/index.rb +43 -27
- data/lib/riddle/configuration/parser.rb +159 -0
- data/lib/riddle/controller.rb +4 -0
- data/lib/riddle/query/select.rb +25 -3
- data/lib/riddle/version.rb +1 -1
- data/spec/riddle/auto_version_spec.rb +14 -0
- data/spec/riddle/query/select_spec.rb +10 -0
- metadata +5 -4
data/HISTORY
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
1.5.3 - August 10th 2012
|
2
|
+
- Sphinx 2.0.5 support.
|
3
|
+
- :with_all and :without_all support for SphinxQL.
|
4
|
+
- Allow setting of prefix and infix fields directly.
|
5
|
+
- Configuration parser
|
6
|
+
- Adding rotate command to the controller.
|
7
|
+
|
1
8
|
1.5.2 - May 14th 2012
|
2
9
|
- Fixing 64-bit MVA support.
|
3
10
|
- Grouped searches now sort by weight instead of by group as a default. You can change this setting via Riddle::Client#group_clause.
|
data/lib/riddle/auto_version.rb
CHANGED
data/lib/riddle/configuration.rb
CHANGED
@@ -10,6 +10,8 @@ require 'riddle/configuration/source'
|
|
10
10
|
require 'riddle/configuration/sql_source'
|
11
11
|
require 'riddle/configuration/xml_source'
|
12
12
|
|
13
|
+
require 'riddle/configuration/parser'
|
14
|
+
|
13
15
|
module Riddle
|
14
16
|
class Configuration
|
15
17
|
class ConfigurationError < StandardError #:nodoc:
|
@@ -18,6 +20,10 @@ module Riddle
|
|
18
20
|
attr_reader :indices, :searchd
|
19
21
|
attr_accessor :indexer
|
20
22
|
|
23
|
+
def self.parse!(input)
|
24
|
+
Riddle::Configuration::Parser.new(input).parse!
|
25
|
+
end
|
26
|
+
|
21
27
|
def initialize
|
22
28
|
@indexer = Riddle::Configuration::Indexer.new
|
23
29
|
@searchd = Riddle::Configuration::Searchd.new
|
@@ -16,7 +16,7 @@ module Riddle
|
|
16
16
|
:overshort_step, :stopwords_step, :hitless_words
|
17
17
|
]
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
attr_accessor :name, :parent, :type, :sources, :path, :docinfo, :mlock,
|
21
21
|
:morphologies, :dict, :index_sp, :index_zones, :min_stemming_len,
|
22
22
|
:stopword_files, :wordform_files, :exception_files, :min_word_len,
|
@@ -29,7 +29,7 @@ module Riddle
|
|
29
29
|
:inplace_hit_gap, :inplace_docinfo_gap, :inplace_reloc_factor,
|
30
30
|
:inplace_write_factor, :index_exact_words, :overshort_step,
|
31
31
|
:stopwords_step, :hitless_words
|
32
|
-
|
32
|
+
|
33
33
|
def initialize(name, *sources)
|
34
34
|
@name = name
|
35
35
|
@sources = sources
|
@@ -44,86 +44,102 @@ module Riddle
|
|
44
44
|
@phrase_boundaries = []
|
45
45
|
@html_remove_element_tags = []
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def source
|
49
49
|
@sources.collect { |s| s.name }
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
def morphology
|
53
53
|
nil_join @morphologies, ", "
|
54
54
|
end
|
55
|
-
|
55
|
+
|
56
56
|
def morphology=(morphology)
|
57
57
|
@morphologies = nil_split morphology, /,\s?/
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
def stopwords
|
61
61
|
nil_join @stopword_files, " "
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
def stopwords=(stopwords)
|
65
65
|
@stopword_files = nil_split stopwords, ' '
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
def wordforms
|
69
69
|
nil_join @wordform_files, " "
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
def wordforms=(wordforms)
|
73
73
|
@wordform_files = nil_split wordforms, ' '
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
def exceptions
|
77
77
|
nil_join @exception_files, " "
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
def exceptions=(exceptions)
|
81
81
|
@exception_files = nil_split exceptions, ' '
|
82
82
|
end
|
83
|
-
|
83
|
+
|
84
84
|
def ignore_chars
|
85
85
|
nil_join @ignore_characters, ", "
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
def ignore_chars=(ignore_chars)
|
89
89
|
@ignore_characters = nil_split ignore_chars, /,\s?/
|
90
90
|
end
|
91
|
-
|
91
|
+
|
92
92
|
def prefix_fields
|
93
93
|
nil_join @prefix_field_names, ", "
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
|
+
def prefix_fields=(fields)
|
97
|
+
if fields.is_a?(Array)
|
98
|
+
@prefix_field_names = fields
|
99
|
+
else
|
100
|
+
@prefix_field_names = fields.split(/,\s*/)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
96
104
|
def infix_fields
|
97
105
|
nil_join @infix_field_names, ", "
|
98
106
|
end
|
99
|
-
|
107
|
+
|
108
|
+
def infix_fields=(fields)
|
109
|
+
if fields.is_a?(Array)
|
110
|
+
@infix_field_names = fields
|
111
|
+
else
|
112
|
+
@infix_field_names = fields.split(/,\s*/)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
100
116
|
def ngram_chars
|
101
117
|
nil_join @ngram_characters, ", "
|
102
118
|
end
|
103
|
-
|
119
|
+
|
104
120
|
def ngram_chars=(ngram_chars)
|
105
121
|
@ngram_characters = nil_split ngram_chars, /,\s?/
|
106
122
|
end
|
107
|
-
|
123
|
+
|
108
124
|
def phrase_boundary
|
109
125
|
nil_join @phrase_boundaries, ", "
|
110
126
|
end
|
111
|
-
|
127
|
+
|
112
128
|
def phrase_boundary=(phrase_boundary)
|
113
129
|
@phrase_boundaries = nil_split phrase_boundary, /,\s?/
|
114
130
|
end
|
115
|
-
|
131
|
+
|
116
132
|
def html_remove_elements
|
117
133
|
nil_join @html_remove_element_tags, ", "
|
118
134
|
end
|
119
|
-
|
135
|
+
|
120
136
|
def html_remove_elements=(html_remove_elements)
|
121
137
|
@html_remove_element_tags = nil_split html_remove_elements, /,\s?/
|
122
138
|
end
|
123
|
-
|
139
|
+
|
124
140
|
def render
|
125
141
|
raise ConfigurationError, "#{@name} #{@sources.inspect} #{@path} #{@parent}" unless valid?
|
126
|
-
|
142
|
+
|
127
143
|
inherited_name = "#{name}"
|
128
144
|
inherited_name << " : #{parent}" if parent
|
129
145
|
(
|
@@ -133,17 +149,17 @@ module Riddle
|
|
133
149
|
["}", ""]
|
134
150
|
).join("\n")
|
135
151
|
end
|
136
|
-
|
152
|
+
|
137
153
|
def valid?
|
138
154
|
(!@name.nil?) && (!( @sources.length == 0 || @path.nil? ) || !@parent.nil?)
|
139
155
|
end
|
140
|
-
|
156
|
+
|
141
157
|
private
|
142
|
-
|
158
|
+
|
143
159
|
def nil_split(string, pattern)
|
144
160
|
(string || "").split(pattern)
|
145
161
|
end
|
146
|
-
|
162
|
+
|
147
163
|
def nil_join(array, delimiter)
|
148
164
|
if array.length == 0
|
149
165
|
nil
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class Riddle::Configuration::Parser
|
5
|
+
SOURCE_CLASSES = {
|
6
|
+
'mysql' => Riddle::Configuration::SQLSource,
|
7
|
+
'pgsql' => Riddle::Configuration::SQLSource,
|
8
|
+
'mssql' => Riddle::Configuration::SQLSource,
|
9
|
+
'xmlpipe' => Riddle::Configuration::XMLSource,
|
10
|
+
'xmlpipe2' => Riddle::Configuration::XMLSource,
|
11
|
+
'odbc' => Riddle::Configuration::SQLSource
|
12
|
+
}
|
13
|
+
|
14
|
+
INDEX_CLASSES = {
|
15
|
+
'plain' => Riddle::Configuration::Index,
|
16
|
+
'distributed' => Riddle::Configuration::DistributedIndex,
|
17
|
+
'rt' => Riddle::Configuration::RealtimeIndex
|
18
|
+
}
|
19
|
+
|
20
|
+
def initialize(input)
|
21
|
+
@input = input
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse!
|
25
|
+
set_indexer
|
26
|
+
set_searchd
|
27
|
+
set_sources
|
28
|
+
set_indices
|
29
|
+
|
30
|
+
configuration
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def inner
|
36
|
+
@inner ||= InnerParser.new(@input).parse!
|
37
|
+
end
|
38
|
+
|
39
|
+
def configuration
|
40
|
+
@configuration ||= Riddle::Configuration.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def sources
|
44
|
+
@sources ||= {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def each_with_prefix(prefix)
|
48
|
+
inner.keys.select { |key| key[/^#{prefix}\s+/] }.each do |key|
|
49
|
+
yield key.gsub(/^#{prefix}\s+/, ''), inner[key]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_indexer
|
54
|
+
set_settings configuration.indexer, inner['indexer']
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_searchd
|
58
|
+
set_settings configuration.searchd, inner['searchd']
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_sources
|
62
|
+
each_with_prefix 'source' do |name, settings|
|
63
|
+
names = name.split(/\s*:\s*/)
|
64
|
+
type = settings.delete('type').first
|
65
|
+
source = SOURCE_CLASSES[type].new names.first, type
|
66
|
+
source.parent = names.last if names.length > 1
|
67
|
+
|
68
|
+
set_settings source, settings
|
69
|
+
|
70
|
+
sources[source.name] = source
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_indices
|
75
|
+
each_with_prefix 'index' do |name, settings|
|
76
|
+
names = name.split(/\s*:\s*/)
|
77
|
+
type = (settings.delete('type') || ['plain']).first
|
78
|
+
index = INDEX_CLASSES[type].new names.first
|
79
|
+
index.parent = names.last if names.length > 1
|
80
|
+
|
81
|
+
(settings.delete('source') || []).each do |source_name|
|
82
|
+
index.sources << sources[source_name]
|
83
|
+
end
|
84
|
+
|
85
|
+
set_settings index, settings
|
86
|
+
|
87
|
+
configuration.indices << index
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def set_settings(object, hash)
|
92
|
+
hash.each do |key, values|
|
93
|
+
values.each do |value|
|
94
|
+
set_setting object, key, value
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def set_setting(object, key, value)
|
100
|
+
if object.send(key).is_a?(Array)
|
101
|
+
object.send(key) << value
|
102
|
+
else
|
103
|
+
object.send "#{key}=", value
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class InnerParser
|
108
|
+
SETTING_PATTERN = /^(\w+)\s*=\s*(.*)$/
|
109
|
+
|
110
|
+
def initialize(input)
|
111
|
+
@stream = StringIO.new(input)
|
112
|
+
@sections = {}
|
113
|
+
end
|
114
|
+
|
115
|
+
def parse!
|
116
|
+
while label = next_line do
|
117
|
+
@sections[label] = next_settings
|
118
|
+
end
|
119
|
+
|
120
|
+
@sections
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def next_line
|
126
|
+
line = @stream.gets
|
127
|
+
return line if line.nil?
|
128
|
+
|
129
|
+
line = line.strip
|
130
|
+
line.empty? ? next_line : line
|
131
|
+
end
|
132
|
+
|
133
|
+
def next_settings
|
134
|
+
settings = Hash.new { |hash, key| hash[key] = [] }
|
135
|
+
line = ''
|
136
|
+
while line.empty? || line == '{' do
|
137
|
+
line = next_line
|
138
|
+
end
|
139
|
+
|
140
|
+
while line != '}' do
|
141
|
+
begin
|
142
|
+
key, value = *SETTING_PATTERN.match(line).captures
|
143
|
+
settings[key] << value
|
144
|
+
while value[/\\$/] do
|
145
|
+
value = next_line
|
146
|
+
settings[key].last << "\n" << value
|
147
|
+
end
|
148
|
+
rescue => error
|
149
|
+
raise error, "Error handling line '#{line}': #{error.message}",
|
150
|
+
error.backtrace
|
151
|
+
end
|
152
|
+
|
153
|
+
line = next_line
|
154
|
+
end
|
155
|
+
|
156
|
+
settings
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/riddle/controller.rb
CHANGED
data/lib/riddle/query/select.rb
CHANGED
@@ -4,7 +4,9 @@ class Riddle::Query::Select
|
|
4
4
|
@indices = []
|
5
5
|
@matching = nil
|
6
6
|
@wheres = {}
|
7
|
+
@where_alls = {}
|
7
8
|
@where_nots = {}
|
9
|
+
@where_not_alls = {}
|
8
10
|
@group_by = nil
|
9
11
|
@order_by = nil
|
10
12
|
@order_within_group_by = nil
|
@@ -33,11 +35,21 @@ class Riddle::Query::Select
|
|
33
35
|
self
|
34
36
|
end
|
35
37
|
|
38
|
+
def where_all(filters = {})
|
39
|
+
@where_alls.merge!(filters)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
36
43
|
def where_not(filters = {})
|
37
44
|
@where_nots.merge!(filters)
|
38
45
|
self
|
39
46
|
end
|
40
47
|
|
48
|
+
def where_not_all(filters = {})
|
49
|
+
@where_not_alls.merge!(filters)
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
41
53
|
def group_by(attribute)
|
42
54
|
@group_by = attribute
|
43
55
|
self
|
@@ -85,13 +97,13 @@ class Riddle::Query::Select
|
|
85
97
|
private
|
86
98
|
|
87
99
|
def wheres?
|
88
|
-
!(@wheres.empty? && @where_nots.empty? && @matching.nil?)
|
100
|
+
!(@wheres.empty? && @where_alls.empty? && @where_nots.empty? && @where_not_alls.empty? && @matching.nil?)
|
89
101
|
end
|
90
102
|
|
91
103
|
def combined_wheres
|
92
104
|
if @matching.nil?
|
93
105
|
wheres_to_s
|
94
|
-
elsif @wheres.empty? && @where_nots.empty?
|
106
|
+
elsif @wheres.empty? && @where_nots.empty? && @where_alls.empty? && @where_not_alls.empty?
|
95
107
|
"MATCH('#{@matching}')"
|
96
108
|
else
|
97
109
|
"MATCH('#{@matching}') AND #{wheres_to_s}"
|
@@ -103,10 +115,20 @@ class Riddle::Query::Select
|
|
103
115
|
@wheres.keys.collect { |key|
|
104
116
|
filter_comparison_and_value key, @wheres[key]
|
105
117
|
} +
|
118
|
+
@where_alls.collect { |key, values|
|
119
|
+
values.collect { |value|
|
120
|
+
filter_comparison_and_value key, value
|
121
|
+
}
|
122
|
+
} +
|
106
123
|
@where_nots.keys.collect { |key|
|
107
124
|
exclusive_filter_comparison_and_value key, @where_nots[key]
|
125
|
+
} +
|
126
|
+
@where_not_alls.collect { |key, values|
|
127
|
+
'(' + values.collect { |value|
|
128
|
+
exclusive_filter_comparison_and_value key, value
|
129
|
+
}.join(' OR ') + ')'
|
108
130
|
}
|
109
|
-
).join(' AND ')
|
131
|
+
).flatten.join(' AND ')
|
110
132
|
end
|
111
133
|
|
112
134
|
def filter_comparison_and_value(attribute, value)
|
data/lib/riddle/version.rb
CHANGED
@@ -64,6 +64,20 @@ describe Riddle::AutoVersion do
|
|
64
64
|
Riddle::AutoVersion.configure
|
65
65
|
end
|
66
66
|
|
67
|
+
it "should require 2.1.0 if 2.0.4 is being used" do
|
68
|
+
Riddle::AutoVersion.should_receive(:require).with('riddle/2.1.0')
|
69
|
+
|
70
|
+
@controller.stub!(:sphinx_version => '2.0.4-release')
|
71
|
+
Riddle::AutoVersion.configure
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should require 2.1.0 if 2.0.5 is being used" do
|
75
|
+
Riddle::AutoVersion.should_receive(:require).with('riddle/2.1.0')
|
76
|
+
|
77
|
+
@controller.stub!(:sphinx_version => '2.0.5-release')
|
78
|
+
Riddle::AutoVersion.configure
|
79
|
+
end
|
80
|
+
|
67
81
|
it "should require 2.1.0 if that is the known version" do
|
68
82
|
Riddle::AutoVersion.should_receive(:require).with('riddle/2.1.0')
|
69
83
|
|
@@ -84,6 +84,16 @@ describe Riddle::Query::Select do
|
|
84
84
|
should == "SELECT * FROM foo_core WHERE MATCH('foo') AND bar BETWEEN 1 AND 5"
|
85
85
|
end
|
86
86
|
|
87
|
+
it "handles filters expecting matches on all values" do
|
88
|
+
query.from('foo_core').where_all(:bars => [1, 2]).to_sql.
|
89
|
+
should == "SELECT * FROM foo_core WHERE bars = 1 AND bars = 2"
|
90
|
+
end
|
91
|
+
|
92
|
+
it "handles exclusive filters expecting matches on none of the values" do
|
93
|
+
query.from('foo_core').where_not_all(:bars => [1, 2]).to_sql.
|
94
|
+
should == "SELECT * FROM foo_core WHERE (bars <> 1 OR bars <> 2)"
|
95
|
+
end
|
96
|
+
|
87
97
|
it 'handles grouping' do
|
88
98
|
query.from('foo_core').group_by('bar_id').to_sql.
|
89
99
|
should == "SELECT * FROM foo_core GROUP BY bar_id"
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: riddle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 1.5.
|
9
|
+
- 3
|
10
|
+
version: 1.5.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Pat Allan
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-08-10 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
type: :development
|
@@ -102,6 +102,7 @@ files:
|
|
102
102
|
- lib/riddle/configuration/distributed_index.rb
|
103
103
|
- lib/riddle/configuration/index.rb
|
104
104
|
- lib/riddle/configuration/indexer.rb
|
105
|
+
- lib/riddle/configuration/parser.rb
|
105
106
|
- lib/riddle/configuration/realtime_index.rb
|
106
107
|
- lib/riddle/configuration/remote_index.rb
|
107
108
|
- lib/riddle/configuration/searchd.rb
|