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 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