ebeigarts-thinking-sphinx 1.1.21 → 1.1.22

Sign up to get free protection for your applications and to get access to all the features.
@@ -39,7 +39,7 @@ module ThinkingSphinx
39
39
  module Version #:nodoc:
40
40
  Major = 1
41
41
  Minor = 1
42
- Tiny = 21
42
+ Tiny = 22
43
43
 
44
44
  String = [Major, Minor, Tiny].join('.')
45
45
  end
@@ -0,0 +1,122 @@
1
+ module ThinkingSphinx
2
+ class OracleAdapter < AbstractAdapter
3
+ def setup
4
+ create_group_concat_function
5
+ create_crc32_function
6
+ end
7
+
8
+ def sphinx_identifier
9
+ "odbc"
10
+ end
11
+
12
+ def concatenate(clause, separator = ' ')
13
+ clause.split(', ').collect { |field| field }.join(" || '#{separator}' || ")
14
+ end
15
+
16
+ def group_concatenate(clause, separator = ' ')
17
+ "TO_STRING(CAST(COLLECT(TO_CHAR(#{clause})) AS SYS.ODCIVARCHAR2LIST), '#{separator}')"
18
+ end
19
+
20
+ def cast_to_string(clause)
21
+ "TO_CHAR(#{clause})"
22
+ end
23
+
24
+ def cast_to_datetime(clause)
25
+ "(TO_DATE(TO_CHAR(#{clause}, 'YYYY-MON-DD HH24.MI.SS'), 'YYYY-MON-DD HH24.MI.SS') - TO_DATE('01-JAN-1970','DD-MON-YYYY')) * (86400)"
26
+ end
27
+
28
+ def cast_to_unsigned(clause)
29
+ "CAST(#{clause} AS NUMBER(10,0))"
30
+ end
31
+
32
+ def convert_nulls(clause, default = '')
33
+ return clause if default == ''
34
+ default = "'#{default}'" if default.is_a?(String)
35
+ "COALESCE(#{clause}, #{default})"
36
+ end
37
+
38
+ def boolean(value)
39
+ value ? '1' : '0'
40
+ end
41
+
42
+ # TODO
43
+ def crc(clause, blank_to_null = false)
44
+ "CRC32(#{clause})"
45
+ end
46
+
47
+ def utf8_query_pre
48
+ nil
49
+ end
50
+
51
+ def time_difference(diff)
52
+ "SYSDATE - #{diff}/(86400)"
53
+ end
54
+
55
+ def select_each(query)
56
+ cursor = connection.raw_connection.exec(query)
57
+ col_names = cursor.get_col_names.collect(&:downcase)
58
+ while values = cursor.fetch
59
+ hash_values = Hash[*col_names.zip(values).flatten]
60
+ yield hash_values
61
+ end
62
+ cursor.close
63
+ end
64
+
65
+ private
66
+
67
+ # Requires Oracle 10g+
68
+ # Return only first 4000 bytes
69
+ def create_group_concat_function
70
+ connection.execute <<-SQL
71
+ CREATE OR REPLACE FUNCTION to_string (
72
+ nt_in IN SYS.ODCIVARCHAR2LIST,
73
+ delimiter_in IN VARCHAR2 DEFAULT ','
74
+ ) RETURN VARCHAR2 IS
75
+ v_idx PLS_INTEGER;
76
+ v_str VARCHAR2(4000);
77
+ v_dlm VARCHAR2(1);
78
+ BEGIN
79
+ v_idx := nt_in.FIRST;
80
+ WHILE v_idx IS NOT NULL LOOP
81
+ v_str := SUBSTRB(v_str || v_dlm || nt_in(v_idx), 1, 4000);
82
+ v_dlm := delimiter_in;
83
+ v_idx := nt_in.NEXT(v_idx);
84
+ END LOOP;
85
+ RETURN v_str;
86
+ END to_string;
87
+ SQL
88
+ end
89
+
90
+ # Requires Oracle 10g+
91
+ def create_crc32_function
92
+ connection.execute <<-SQL
93
+ CREATE OR REPLACE FUNCTION crc32(
94
+ word IN VARCHAR2
95
+ ) RETURN NUMBER IS
96
+ code NUMBER(4,0);
97
+ i NUMBER(10,0);
98
+ j NUMBER(1,0);
99
+ tmp NUMBER(10,0);
100
+ tmp_a NUMBER(10,0);
101
+ tmp_b NUMBER(10,0);
102
+ BEGIN
103
+ tmp := 4294967295;
104
+ i := 0;
105
+ WHILE i < length(word) LOOP
106
+ code := ascii(SUBSTR(word, i + 1, 1));
107
+ tmp := tmp - 2 * to_number(bitand(tmp, code)) + code;
108
+ j := 0;
109
+ WHILE j < 8 LOOP
110
+ tmp_a := floor(tmp / 2);
111
+ tmp_b := 3988292384 * to_number(bitand(tmp, 1));
112
+ tmp := tmp_a - 2 * to_number(bitand(tmp_a, tmp_b)) + tmp_b;
113
+ j := j + 1;
114
+ END LOOP;
115
+ i := i + 1;
116
+ END LOOP;
117
+ RETURN tmp - 2 * to_number(bitand(tmp, 4294967295)) + 4294967295;
118
+ END crc32;
119
+ SQL
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,91 @@
1
+ module ThinkingSphinx
2
+ class SQLite3Adapter < AbstractAdapter
3
+ def setup
4
+ create_group_concat_function
5
+ create_crc32
6
+ end
7
+
8
+ def sphinx_identifier
9
+ "sqlite3"
10
+ end
11
+
12
+ def concatenate(clause, separator = ' ')
13
+ clause.split(', ').collect { |field| field }.join(" || '#{separator}' || ")
14
+ end
15
+
16
+ def group_concatenate(clause, separator = ' ')
17
+ "GROUP_CONCAT(#{clause}, '#{separator}')"
18
+ end
19
+
20
+ def cast_to_string(clause)
21
+ "CAST(#{clause} AS TEXT)"
22
+ end
23
+
24
+ def cast_to_datetime(clause)
25
+ "STRFTIME('%s', #{clause})"
26
+ end
27
+
28
+ def cast_to_unsigned(clause)
29
+ "CAST(#{clause} AS INTEGER)"
30
+ end
31
+
32
+ def convert_nulls(clause, default = '')
33
+ return clause if default == ''
34
+ default = "'#{default}'" if default.is_a?(String)
35
+ "COALESCE(#{clause}, #{default})"
36
+ end
37
+
38
+ def boolean(value)
39
+ value ? "'t'" : "'f'"
40
+ end
41
+
42
+ def crc(clause, blank_to_null = false)
43
+ "CRC32(#{clause})"
44
+ end
45
+
46
+ def utf8_query_pre
47
+ nil
48
+ end
49
+
50
+ def time_difference(diff)
51
+ "STRFTIME('%s','now') - STRFTIME('%s', #{diff})"
52
+ end
53
+
54
+ private
55
+
56
+ def create_group_concat_function
57
+ connection.raw_connection.create_aggregate("group_concat", 2) do
58
+ step do |func, value, separator|
59
+ value = value.respond_to?(:to_s) ? value.to_s : ''
60
+ if String(func[:concat]).empty? then
61
+ func[:concat] = value
62
+ else
63
+ func[:concat] = func[:concat].to_s + separator.to_s + value
64
+ end
65
+ end
66
+ finalize do |func|
67
+ func.result = func[:concat]
68
+ end
69
+ end
70
+ end
71
+
72
+ def create_crc32
73
+ connection.raw_connection.create_function("crc32", 1) do |func, value|
74
+ c = value.respond_to?(:to_s) ? value.to_s : ''
75
+ n = c.length
76
+ r = 0xFFFFFFFF
77
+ n.times do |i|
78
+ r ^= c[i]
79
+ 8.times do
80
+ if (r & 1) != 0
81
+ r = (r >> 1) ^ 0xEDB88320
82
+ else
83
+ r >>= 1
84
+ end
85
+ end
86
+ end
87
+ func.result = r ^ 0xFFFFFFFF
88
+ end
89
+ end
90
+ end
91
+ end
@@ -128,7 +128,7 @@ module ThinkingSphinx
128
128
  def config_value(offset = nil)
129
129
  if type == :multi && ThinkingSphinx::Configuration.instance.type != "xml"
130
130
  multi_config = include_as_association? ? "field" :
131
- source_value(offset).gsub(/\n\s*/, " ").strip
131
+ source_value(offset).gsub(/\s+/m, " ").strip
132
132
  "uint #{unique_name} from #{multi_config}"
133
133
  else
134
134
  unique_name
@@ -195,24 +195,25 @@ module ThinkingSphinx
195
195
  end
196
196
 
197
197
  def query(offset)
198
- assoc = association_for_mva
199
- raise "Could not determine SQL for MVA" if assoc.nil?
198
+ base_assoc = base_association_for_mva
199
+ end_assoc = end_association_for_mva
200
+ raise "Could not determine SQL for MVA" if base_assoc.nil?
200
201
 
201
202
  <<-SQL
202
- SELECT #{foreign_key_for_mva assoc}
203
+ SELECT #{foreign_key_for_mva base_assoc}
203
204
  #{ThinkingSphinx.unique_id_expression(offset)} AS #{quote_column('id')},
204
- #{primary_key_for_mva(assoc)} AS #{quote_column(unique_name)}
205
- FROM #{quote_table_name assoc.table}
205
+ #{primary_key_for_mva(end_assoc)} AS #{quote_column(unique_name)}
206
+ FROM #{quote_table_name base_assoc.table} #{association_joins}
206
207
  SQL
207
208
  end
208
209
 
209
210
  def query_clause
210
- foreign_key = foreign_key_for_mva association_for_mva
211
+ foreign_key = foreign_key_for_mva base_association_for_mva
211
212
  "WHERE #{foreign_key} >= $start AND #{foreign_key} <= $end"
212
213
  end
213
214
 
214
215
  def range_query
215
- assoc = association_for_mva
216
+ assoc = base_association_for_mva
216
217
  foreign_key = foreign_key_for_mva assoc
217
218
  "SELECT MIN(#{foreign_key}), MAX(#{foreign_key}) FROM #{quote_table_name assoc.table}"
218
219
  end
@@ -227,12 +228,34 @@ FROM #{quote_table_name assoc.table}
227
228
  quote_with_table assoc.table, assoc.reflection.primary_key_name
228
229
  end
229
230
 
230
- def association_for_mva
231
+ def end_association_for_mva
231
232
  @association_for_mva ||= associations[columns.first].detect { |assoc|
232
233
  assoc.has_column?(columns.first.__name)
233
234
  }
234
235
  end
235
236
 
237
+ def base_association_for_mva
238
+ @first_association_for_mva ||= begin
239
+ assoc = end_association_for_mva
240
+ while !assoc.parent.nil?
241
+ assoc = assoc.parent
242
+ end
243
+
244
+ assoc
245
+ end
246
+ end
247
+
248
+ def association_joins
249
+ joins = []
250
+ assoc = end_association_for_mva
251
+ while assoc != base_association_for_mva
252
+ joins << assoc.to_sql
253
+ assoc = assoc.parent
254
+ end
255
+
256
+ joins.join(' ')
257
+ end
258
+
236
259
  def is_many_ints?
237
260
  concat_ws? && all_ints?
238
261
  end
@@ -321,6 +321,26 @@ describe ThinkingSphinx::Attribute do
321
321
  end
322
322
  end
323
323
 
324
+ describe "MVA via two has-many associations with a ranged source query" do
325
+ before :each do
326
+ @index = ThinkingSphinx::Index.new(Alpha)
327
+ @source = ThinkingSphinx::Source.new(@index)
328
+ @attribute = ThinkingSphinx::Attribute.new(@source,
329
+ [ThinkingSphinx::Index::FauxColumn.new(:betas, :gammas, :value)],
330
+ :as => :gamma_values, :source => :ranged_query
331
+ )
332
+ end
333
+
334
+ it "should use a ranged query" do
335
+ @attribute.type_to_config.should == :sql_attr_multi
336
+
337
+ declaration, query, range_query = @attribute.config_value.split('; ')
338
+ declaration.should == "uint gamma_values from ranged-query"
339
+ query.should == "SELECT `betas`.`alpha_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `gammas`.`value` AS `gamma_values` FROM `betas` LEFT OUTER JOIN `gammas` ON gammas.beta_id = betas.id WHERE `betas`.`alpha_id` >= $start AND `betas`.`alpha_id` <= $end"
340
+ range_query.should == "SELECT MIN(`betas`.`alpha_id`), MAX(`betas`.`alpha_id`) FROM `betas`"
341
+ end
342
+ end
343
+
324
344
  describe "with custom queries" do
325
345
  before :each do
326
346
  index = CricketTeam.sphinx_indexes.first
@@ -12,7 +12,7 @@ describe ThinkingSphinx::Configuration do
12
12
  unless defined?(Merb)
13
13
  module ::Merb; end
14
14
  end
15
-
15
+
16
16
  ThinkingSphinx::Configuration.stub_method(:defined? => true)
17
17
  Merb.stub!(:environment => "merb_production")
18
18
  ThinkingSphinx::Configuration.environment.should == "merb_production"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ebeigarts-thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.21
4
+ version: 1.1.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Allan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-11 00:00:00 -07:00
12
+ date: 2009-06-22 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -30,6 +30,8 @@ files:
30
30
  - lib/thinking_sphinx/active_record.rb
31
31
  - lib/thinking_sphinx/adapters/abstract_adapter.rb
32
32
  - lib/thinking_sphinx/adapters/mysql_adapter.rb
33
+ - lib/thinking_sphinx/adapters/sqlite3_adapter.rb
34
+ - lib/thinking_sphinx/adapters/oracle_adapter.rb
33
35
  - lib/thinking_sphinx/adapters/postgresql_adapter.rb
34
36
  - lib/thinking_sphinx/association.rb
35
37
  - lib/thinking_sphinx/attribute.rb
@@ -128,7 +130,7 @@ has_rdoc: true
128
130
  homepage: http://ts.freelancing-gods.com
129
131
  licenses:
130
132
  post_install_message: |+
131
- As of Thinking Sphinx 1.1.18, there is one important change to
133
+ With the release of Thinking Sphinx 1.1.18, there is one important change to
132
134
  note: previously, the default morphology for indexing was 'stem_en'. The new
133
135
  default is nil, to avoid any unexpected behavior. If you wish to keep the old
134
136
  value though, you will need to add the following settings to your