riddle 1.5.2 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -10,7 +10,7 @@ class Riddle::AutoVersion
10
10
  require 'riddle/1.10'
11
11
  when /2.0.[12]/
12
12
  require 'riddle/2.0.1'
13
- when /2.0.3/, /2.0.4/, /2.1.\d/
13
+ when /2.0.[^12]/, /2.1.\d/
14
14
  require 'riddle/2.1.0'
15
15
  end
16
16
  end
@@ -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
@@ -73,6 +73,10 @@ module Riddle
73
73
  end
74
74
  end
75
75
 
76
+ def rotate
77
+ pid && Process.kill(:HUP, pid.to_i)
78
+ end
79
+
76
80
  def running?
77
81
  !!pid && !!Process.kill(0, pid.to_i)
78
82
  rescue
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module Riddle
2
- Version = '1.5.2'
2
+ Version = '1.5.3'
3
3
  end
@@ -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: 7
4
+ hash: 5
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 5
9
- - 2
10
- version: 1.5.2
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-05-14 00:00:00 Z
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