dm-groonga-adapter 0.1.0.pre → 0.1.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ begin
8
8
  gem.summary = %Q{datamapper adapter for groonga search engine}
9
9
  gem.description = gem.summary
10
10
  gem.email = "hello@hryk.info"
11
- gem.homepage = "http://github.com/hryk/dm-groonga-adapter"
11
+ gem.homepage = "http://hryk.github.com/dm-groonga-adapter"
12
12
  gem.authors = ["hiroyuki"]
13
13
 
14
14
  gem.add_dependency "groonga", ">= 0.9"
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- :minor: 1
3
2
  :patch: 0
4
- :build: pre
5
3
  :major: 0
4
+ :build: pre2
5
+ :minor: 1
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{dm-groonga-adapter}
8
- s.version = "0.1.0.pre"
8
+ s.version = "0.1.0.pre2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["hiroyuki"]
12
- s.date = %q{2010-04-08}
12
+ s.date = %q{2010-04-15}
13
13
  s.description = %q{datamapper adapter for groonga search engine}
14
14
  s.email = %q{hello@hryk.info}
15
15
  s.extra_rdoc_files = [
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
27
27
  "examples/basic.rb",
28
28
  "lib/groonga_adapter.rb",
29
29
  "lib/groonga_adapter/adapter.rb",
30
+ "lib/groonga_adapter/jsonparser.rb",
30
31
  "lib/groonga_adapter/local_index.rb",
31
32
  "lib/groonga_adapter/model_ext.rb",
32
33
  "lib/groonga_adapter/remote_index.rb",
@@ -42,7 +43,7 @@ Gem::Specification.new do |s|
42
43
  "spec/specs/remote_result_spec.rb",
43
44
  "spec/specs/search_spec.rb"
44
45
  ]
45
- s.homepage = %q{http://github.com/hryk/dm-groonga-adapter}
46
+ s.homepage = %q{http://hryk.github.com/dm-groonga-adapter}
46
47
  s.rdoc_options = ["--charset=UTF-8"]
47
48
  s.require_paths = ["lib"]
48
49
  s.rubygems_version = %q{1.3.6}
@@ -3,6 +3,10 @@ module DataMapper
3
3
  module Adapters
4
4
  class GroongaAdapter < AbstractAdapter
5
5
 
6
+ # Initialize adapter
7
+ #
8
+ # @param [String] name
9
+ # @param [Hash] options the options passed to RemoteIndex or LocalIndex.
6
10
  def initialize(name, options)
7
11
  super
8
12
  Groonga::Context.default = nil # Reset Groonga::Context
@@ -0,0 +1,336 @@
1
+ # = Simple JSON parser & builder
2
+ #
3
+ # Author:: Chihiro Ito
4
+ # License:: Public domain (unlike other files)
5
+ # Support:: http://groups.google.com/group/webos-goodies/
6
+ #
7
+ # This file contains two simple JSON processing classes. JsonParser
8
+ # converts a JSON string to an array or a hash. JsonBuilder performs
9
+ # vice versa. These classes are standard compliant and are designed for
10
+ # stability and reliability. Especially JsonParser has UTF-8 validation
11
+ # functionality so you can avoid some kind of security attack.
12
+
13
+ require 'strscan'
14
+ require 'json' if RUBY_VERSION >= '1.9.0'
15
+
16
+
17
+ # = Simple JSON parser
18
+ #
19
+ # This class converts a JSON string to an array or a hash. If *json_str*
20
+ # contains a JSON form string, you can convert it like below.
21
+ #
22
+ # ruby_obj = JsonParser.new.parse(json_str)
23
+ #
24
+ # If *json_str* has one or more invalid UTF-8 sequence, JsonParser throws
25
+ # exception by default. You can change this behavior to replacing with an
26
+ # arbitrary unicode character. See below for details.
27
+ class JsonParser
28
+
29
+ #:stopdoc:
30
+ RUBY19 = RUBY_VERSION >= '1.9.0'
31
+ Debug = false
32
+ Name = 'JsonParser'
33
+ ERR_IllegalSyntax = "[#{Name}] Syntax error"
34
+ ERR_IllegalUnicode = "[#{Name}] Illegal unicode sequence"
35
+
36
+ StringRegex = /\s*"((?:\\.|[^"\\])*)"/n
37
+ ValueRegex = /\s*(?:
38
+ (true)|(false)|(null)| # 1:true, 2:false, 3:null
39
+ (?:\"((?:\\.|[^\"\\])*)\")| # 4:String
40
+ ([-+]?\d+\.\d+(?:[eE][-+]?\d+)?)| # 5:Float
41
+ ([-+]?\d+)| # 6:Integer
42
+ (\{)|(\[))/xn # 7:Hash, 8:Array
43
+ #:startdoc:
44
+
45
+ # Create a new instance of JsonParser. *options* can contain these values.
46
+ # [:validation]
47
+ # If set to false, UTF-8 validation is disabled. true by default.
48
+ # [:surrogate]
49
+ # If set to false, surrogate pair support is disabled. true by default.
50
+ # [:malformed_chr]
51
+ # An invalid sequence in JSON string will be replaced with this value.
52
+ # If set to nil, An exception will be thrown in this situation.
53
+ # nil by default.
54
+ # [:compatible]
55
+ # If set to true, Ruby1.9's JSON module is never used. false by default.
56
+ def initialize(options = {})
57
+ @default_validation = options.has_key?(:validation) ? options[:validation] : true
58
+ @default_surrogate = options.has_key?(:surrogate) ? options[:surrogate] : true
59
+ @default_malformed_chr = options.has_key?(:malformed_chr) ? options[:malformed_chr] : nil
60
+ @default_compatible = options.has_key?(:compatible) ? options[:compatible] : false
61
+ end
62
+
63
+ # Convert *str* to an array or hash.
64
+ # [str]
65
+ # A JSON form string. This must be encoded using UTF-8.
66
+ # [options]
67
+ # Same as new.
68
+ def parse(str, options = {})
69
+ @enable_validation = options.has_key?(:validation) ? options[:validation] : @default_validation
70
+ @enable_surrogate = options.has_key?(:surrogate) ? options[:surrogate] : @default_surrogate
71
+ @malformed_chr = options.has_key?(:malformed_chr) ? options[:malformed_chr] : @default_malformed_chr
72
+ @compatible = options.has_key?(:compatible) ? options[:compatible] : @default_compatible
73
+ @malformed_chr = @malformed_chr[0].ord if String === @malformed_chr
74
+ if RUBY19
75
+ str = (str.encode('UTF-8') rescue str.dup)
76
+ if @enable_validation && !@malformed_chr
77
+ raise err_msg(ERR_IllegalUnicode) unless str.valid_encoding?
78
+ @enable_validation = false
79
+ end
80
+ if !@enable_validation && @enable_surrogate && !@malformed_chr && !@compatible
81
+ begin
82
+ return JSON.parse(str, :max_nesting => false)
83
+ rescue JSON::JSONError => e
84
+ exception = RuntimeError.new(e.message)
85
+ exception.set_backtrace(e.backtrace)
86
+ raise exception
87
+ end
88
+ end
89
+ str.force_encoding('ASCII-8BIT')
90
+ end
91
+ @scanner = StringScanner.new(str)
92
+ obj = case get_symbol[0]
93
+ when ?{ then parse_hash
94
+ when ?[ then parse_array
95
+ else raise err_msg(ERR_IllegalSyntax)
96
+ end
97
+ @scanner = nil
98
+ obj
99
+ end
100
+
101
+ private #---------------------------------------------------------
102
+
103
+ def validate_string(str, malformed_chr = nil)
104
+ code = 0
105
+ rest = 0
106
+ range = nil
107
+ ucs = []
108
+ str.each_byte do |c|
109
+ if rest <= 0
110
+ case c
111
+ when 0x01..0x7f then rest = 0 ; ucs << c
112
+ when 0xc0..0xdf then rest = 1 ; code = c & 0x1f ; range = 0x00080..0x0007ff
113
+ when 0xe0..0xef then rest = 2 ; code = c & 0x0f ; range = 0x00800..0x00ffff
114
+ when 0xf0..0xf7 then rest = 3 ; code = c & 0x07 ; range = 0x10000..0x10ffff
115
+ else ucs << handle_malformed_chr(malformed_chr)
116
+ end
117
+ elsif (0x80..0xbf) === c
118
+ code = (code << 6) | (c & 0x3f)
119
+ if (rest -= 1) <= 0
120
+ if !(range === code) || (0xd800..0xdfff) === code
121
+ code = handle_malformed_chr(malformed_chr)
122
+ end
123
+ ucs << code
124
+ end
125
+ else
126
+ ucs << handle_malformed_chr(malformed_chr)
127
+ rest = 0
128
+ end
129
+ end
130
+ ucs << handle_malformed_chr(malformed_chr) if rest > 0
131
+ ucs.pack('U*')
132
+ end
133
+
134
+ def handle_malformed_chr(chr)
135
+ raise err_msg(ERR_IllegalUnicode) unless chr
136
+ chr
137
+ end
138
+
139
+ def err_msg(err)
140
+ err + (Debug ? " #{@scanner.string[[0, @scanner.pos - 8].max,16].inspect}" : "")
141
+ end
142
+
143
+ def unescape_string(str)
144
+ str = str.gsub(/\\(["\\\/bfnrt])/n) do
145
+ $1.tr('"\\/bfnrt', "\"\\/\b\f\n\r\t")
146
+ end.gsub(/(\\u[0-9a-fA-F]{4})+/n) do |matched|
147
+ seq = matched.scan(/\\u([0-9a-fA-F]{4})/n).flatten.map { |c| c.hex }
148
+ if @enable_surrogate
149
+ seq.each_index do |index|
150
+ if seq[index] && (0xd800..0xdbff) === seq[index]
151
+ n = index + 1
152
+ raise err_msg(ERR_IllegalUnicode) unless seq[n] && 0xdc00..0xdfff === seq[n]
153
+ seq[index] = 0x10000 + ((seq[index] & 0x03ff) << 10) + (seq[n] & 0x03ff)
154
+ seq[n] = nil
155
+ end
156
+ end.compact!
157
+ end
158
+ seq.pack('U*')
159
+ end
160
+ str = validate_string(str, @malformed_chr) if @enable_validation
161
+ RUBY19 ? str.force_encoding('UTF-8') : str
162
+ end
163
+
164
+ def get_symbol
165
+ raise err_msg(ERR_IllegalSyntax) unless @scanner.scan(/\s*(.)/n)
166
+ @scanner[1]
167
+ end
168
+
169
+ def peek_symbol
170
+ @scanner.match?(/\s*(.)/n) ? @scanner[1] : nil
171
+ end
172
+
173
+ def parse_string
174
+ raise err_msg(ERR_IllegalSyntax) unless @scanner.scan(StringRegex)
175
+ unescape_string(@scanner[1])
176
+ end
177
+
178
+ def parse_value
179
+ raise err_msg(ERR_IllegalSyntax) unless @scanner.scan(ValueRegex)
180
+ case
181
+ when @scanner[1] then true
182
+ when @scanner[2] then false
183
+ when @scanner[3] then nil
184
+ when @scanner[4] then unescape_string(@scanner[4])
185
+ when @scanner[5] then @scanner[5].to_f
186
+ when @scanner[6] then @scanner[6].to_i
187
+ when @scanner[7] then parse_hash
188
+ when @scanner[8] then parse_array
189
+ else raise err_msg(ERR_IllegalSyntax)
190
+ end
191
+ end
192
+
193
+ def parse_hash
194
+ obj = {}
195
+ if peek_symbol[0] == ?} then get_symbol ; return obj ; end
196
+ while true
197
+ index = parse_string
198
+ raise err_msg(ERR_IllegalSyntax) unless get_symbol[0] == ?:
199
+ value = parse_value
200
+ obj[index] = value
201
+ case get_symbol[0]
202
+ when ?} then return obj
203
+ when ?, then next
204
+ else raise err_msg(ERR_IllegalSyntax)
205
+ end
206
+ end
207
+ end
208
+
209
+ def parse_array
210
+ obj = []
211
+ if peek_symbol[0] == ?] then get_symbol ; return obj ; end
212
+ while true
213
+ obj << parse_value
214
+ case get_symbol[0]
215
+ when ?] then return obj
216
+ when ?, then next
217
+ else raise err_msg(ERR_IllegalSyntax)
218
+ end
219
+ end
220
+ end
221
+
222
+ end
223
+
224
+ # = Simple JSON builder
225
+ #
226
+ # This class converts an Ruby object to a JSON string. you can convert
227
+ # *ruby_obj* like below.
228
+ #
229
+ # json_str = JsonBuilder.new.build(ruby_obj)
230
+ #
231
+ # *ruby_obj* must satisfy these conditions.
232
+ # - It must support to_s method, otherwise must be an array, a hash or nil.
233
+ # - All keys of a hash must support to_s method.
234
+ # - All values of an array or a hash must satisfy all conditions mentioned above.
235
+ #
236
+ # If the *ruby_obj* is not an array or a hash, it will be converted to an array
237
+ # with a single element.
238
+ class JsonBuilder
239
+
240
+ #:stopdoc:
241
+ RUBY19 = RUBY_VERSION >= '1.9.0'
242
+ Name = 'JsonBuilder'
243
+ ERR_NestIsTooDeep = "[#{Name}] Array / Hash nested too deep."
244
+ ERR_NaN = "[#{Name}] NaN and Infinite are not permitted in JSON."
245
+ #:startdoc:
246
+
247
+ # Create a new instance of JsonBuilder. *options* can contain these values.
248
+ # [:max_nest]
249
+ # If Array / Hash is nested more than this value, an exception would be thrown.
250
+ # 19 by default.
251
+ # [:nan]
252
+ # NaN is replaced with this value. If nil or false, an exception will be thrown.
253
+ # nil by default.
254
+ def initialize(options = {})
255
+ @default_max_nest = options.has_key?(:max_nest) ? options[:max_nest] : 19
256
+ @default_nan = options.has_key?(:nan) ? options[:nan] : nil
257
+ end
258
+
259
+ # Convert *obj* to a JSON form string.
260
+ # [obj]
261
+ # A ruby object. this object must satisfy all conditions mentioned above.
262
+ # [options]
263
+ # Same as new.
264
+ def build(obj, options = {})
265
+ @max_nest = options.has_key?(:max_nest) ? options[:max_nest] : @default_max_nest
266
+ @nan = options.has_key?(:nan) ? options[:nan] : @default_nan
267
+ if RUBY19 && !@nan
268
+ begin
269
+ JSON.generate(obj, :max_nesting => @max_nest, :check_circular => false)
270
+ rescue JSON::JSONError => e
271
+ exception = RuntimeError.new(e.message)
272
+ exception.set_backtrace(e.backtrace)
273
+ raise exception
274
+ end
275
+ else
276
+ case obj
277
+ when Array then build_array(obj, 0)
278
+ when Hash then build_object(obj, 0)
279
+ else build_array([obj], 0)
280
+ end
281
+ end
282
+ end
283
+
284
+ private #---------------------------------------------------------
285
+
286
+ ESCAPE_CONVERSION = {
287
+ '"' => '\"', '\\' => '\\\\', '/' => '/', "\x08" => '\b',
288
+ "\x0c" => '\f', "\x0a" => '\n', "\x0d" => '\r', "\x09" => '\t'
289
+ }
290
+ if RUBY19
291
+ def escape(str)
292
+ str = str.to_s.encode('UTF-8')
293
+ str.force_encoding('ASCII-8BIT')
294
+ str = str.gsub(/[^\x20-\x21\x23-\x2e\x30-\x5b\x5d-\xff]/n) do |chr|
295
+ escaped = ESCAPE_CONVERSION[chr]
296
+ escaped = sprintf("\\u%04X", chr[0].ord) unless escaped
297
+ escaped
298
+ end
299
+ str.force_encoding('UTF-8')
300
+ "\"#{str}\""
301
+ end
302
+ else
303
+ def escape(str)
304
+ str = str.gsub(/[^\x20-\x21\x23-\x2e\x30-\x5b\x5d-\xff]/n) do |chr|
305
+ escaped = ESCAPE_CONVERSION[chr]
306
+ escaped = sprintf("\\u%04x", chr[0]) unless escaped
307
+ escaped
308
+ end
309
+ "\\\"#{str}\\\""
310
+ end
311
+ end
312
+
313
+ def build_value(obj, level)
314
+ case obj
315
+ when Integer, TrueClass, FalseClass then obj.to_s
316
+ when Float then raise ERR_NaN unless obj.finite? || (obj = @nan) ; obj.to_s
317
+ when NilClass then 'null'
318
+ when Array then build_array(obj, level + 1)
319
+ when Hash then build_object(obj, level + 1)
320
+ else escape(obj)
321
+ end
322
+ end
323
+
324
+ def build_array(obj, level)
325
+ raise ERR_NestIsTooDeep if level >= @max_nest
326
+ '[' + obj.map { |item| build_value(item, level) }.join(',') + ']'
327
+ end
328
+
329
+ def build_object(obj, level)
330
+ raise ERR_NestIsTooDeep if level >= @max_nest
331
+ '{' + obj.map do |item|
332
+ "#{build_value(item[0].to_s,level)}:#{build_value(item[1],level)}"
333
+ end.join(',') + '}'
334
+ end
335
+
336
+ end
@@ -141,6 +141,8 @@ module DataMapper
141
141
  return Groonga::Type::BOOL
142
142
  when "Serial"
143
143
  return Groonga::Type::UINT32
144
+ when "Text"
145
+ return Groonga::Type::TEXT
144
146
  end
145
147
  else
146
148
  return Groonga::Type::SHORT_TEXT
@@ -1,4 +1,5 @@
1
1
  require 'json'
2
+ require 'nkf'
2
3
 
3
4
  module DataMapper
4
5
  module Adapters
@@ -9,6 +10,7 @@ module DataMapper
9
10
  def initialize(options)
10
11
  @context = Groonga::Context.default
11
12
  @context.connect(:host => options[:host], :port => options[:port])
13
+ @jsonbuilder = JsonBuilder.new
12
14
  # request "status" # <- TODO check connection with status command
13
15
  end
14
16
 
@@ -17,8 +19,8 @@ module DataMapper
17
19
  doc_id = doc[:id] #doc.delete(:id)
18
20
  record = []
19
21
  record << doc.update("_key" => doc_id)
20
- json = JSON.generate record
21
- res = request "load --table #{table_name} --values #{Unicode.unescape(json.gsub(/"/, '\"').gsub(/\s/, '\ '))}"
22
+ values = escape_value record
23
+ res = request "load --table #{table_name} --values #{values}"
22
24
  result = GroongaResult::Count.new res
23
25
  if result.success? && result.count > 0
24
26
  return doc
@@ -37,9 +39,20 @@ module DataMapper
37
39
  # [offset [limit [drilldown [drilldown_sortby [drilldown_output_columns
38
40
  # [drilldown_offset [drilldown_limit [output_type]]]]]]]]]]]]]]
39
41
  def search(table_name, grn_query, grn_sort=[], options={})
42
+ # In case of columns contain long string, groonga return 10 results with no-limit..
43
+ # --limit #{num} option is needed to get all result.
44
+ # because of this, current remote_index request at twice.
40
45
  sort_by, offset, limit = parse_grn_sort grn_sort
41
- remote_query = (grn_query.empty?) ? "" : "--query #{grn_query}"
42
- remote_sort_by = (sort_by.empty?) ? "" : "--sort-by #{sort_by}"
46
+ remote_query = (grn_query.empty?) ? "" : "--query #{grn_query}"
47
+ remote_sort_by = (sort_by.empty?) ? "" : "--sortby #{sort_by}"
48
+ # 1st request for get count.
49
+
50
+ if limit.nil? or limit == -1
51
+ count = GroongaResult::List.new(request "select #{table_name} #{remote_query} #{remote_sort_by} --output_columns _id")
52
+ limit = count.size
53
+ end
54
+
55
+ # 2nd request for get ids.
43
56
  res = request "select #{table_name} #{remote_query} #{remote_sort_by} --offset #{offset} --limit #{limit}"
44
57
  list = GroongaResult::List.new res
45
58
  if list.success?
@@ -109,12 +122,66 @@ module DataMapper
109
122
 
110
123
  protected
111
124
 
125
+ def escape_value record
126
+ record.map! {|r|
127
+ tmp = {}
128
+ r.each {|k,v|
129
+ if !v.nil?
130
+ if v.is_a? String
131
+ tmp[k] = NKF.nkf('-wZ1',v)
132
+ tmp[k].gsub!(/\\/, '\\\\\\')
133
+ tmp[k].gsub!(/"/) { |c| "\\\\\\#{c}" }
134
+ else
135
+ tmp[k] = v.to_s
136
+ end
137
+ end
138
+ }
139
+ tmp
140
+ }
141
+ json = @jsonbuilder.build record
142
+ json.gsub!(/'|\s|\(|\)|\t/) { |c| "\\#{c}" }
143
+ json
144
+ end
145
+
112
146
  def err_code(res)
113
147
  return if res.nil?
114
148
  code = res[0][0]
115
149
  code
116
150
  end
117
151
 
152
+ def trans_type(dmtype)
153
+ case dmtype.to_s
154
+ when 'String'
155
+ return 'ShortText'
156
+ when 'Text'
157
+ return 'Text'
158
+ when 'Float'
159
+ return 'Float'
160
+ when 'Bool'
161
+ return 'Bool'
162
+ when 'Boolean'
163
+ return 'Bool'
164
+ when 'Integer'
165
+ return 'Int32'
166
+ when 'BigDecimal'
167
+ return 'Int64'
168
+ when 'Time'
169
+ return 'Time'
170
+ when /^DataMapper::Types::(.+)$/
171
+ case $1
172
+ when "Boolean"
173
+ return 'Bool'
174
+ when "Serial"
175
+ return 'Int32'
176
+ when "Text"
177
+ return "Text"
178
+ end
179
+ else
180
+ return 'ShortText'
181
+ end
182
+ end
183
+
184
+
118
185
  def create_term_table(table_name, key_prop="ShortText", tokenizer="TokenBigram")
119
186
  res = request "table_create #{table_name} TABLE_PAT_KEY|KEY_NORMALIZE #{key_prop} Void #{tokenizer}"
120
187
  throw "Fale to create term table." unless err_code(res) == 0 || err_code(res) == -22
@@ -159,35 +226,6 @@ module DataMapper
159
226
  [ sort_str, options[:offset], options[:limit] ]
160
227
  end
161
228
 
162
- def trans_type(dmtype)
163
- case dmtype.to_s
164
- when 'String'
165
- return 'ShortText'
166
- when 'Text'
167
- return 'LongText'
168
- when 'Float'
169
- return 'Float'
170
- when 'Bool'
171
- return 'Bool'
172
- when 'Boolean'
173
- return 'Bool'
174
- when 'Integer'
175
- return 'Int32'
176
- when 'BigDecimal'
177
- return 'Int64'
178
- when 'Time'
179
- return 'Time'
180
- when /^DataMapper::Types::(.+)$/
181
- case $1
182
- when "Boolean"
183
- return 'Bool'
184
- when "Serial"
185
- return 'Int32'
186
- end
187
- else
188
- return 'ShortText'
189
- end
190
- end
191
229
  end # class GroongaAdapter::RemoteIndex
192
230
  end # module Adapters
193
231
  end # module DataMapper
@@ -7,3 +7,4 @@ require 'groonga_adapter/remote_result'
7
7
  require 'groonga_adapter/repository_ext'
8
8
  require 'groonga_adapter/model_ext'
9
9
  require 'groonga_adapter/unicode_ext'
10
+ require 'groonga_adapter/jsonparser'
@@ -3,13 +3,13 @@ shared_examples_for 'as is_search plugin' do
3
3
  before(:each) do
4
4
  DataMapper.setup(:default, "sqlite3::memory:")
5
5
  DataMapper.setup(:search, "groonga://#{index_path}")
6
- DataMapper::Logger.new($stderr, :debug)
6
+ # DataMapper::Logger.new($stderr, :debug)
7
7
  Object.send(:remove_const, :Image) if defined?(Image)
8
8
  class ::Image
9
9
  include DataMapper::Resource
10
10
  property :id, Serial
11
11
  property :title, String
12
-
12
+ property :description, Text
13
13
  is :searchable # this defaults to :search repository, you could also do
14
14
  end
15
15
 
@@ -61,4 +61,15 @@ shared_examples_for 'as is_search plugin' do
61
61
  # Story.fulltext_search("John").should == [story, story2] # <--- Crash on local index.
62
62
  Story.fulltext_search("author:@John").should == [story, story2]
63
63
  end
64
+
65
+ it 'should return all result when there is more than 10 result' do
66
+ suffix = (0..10).map {|i| "long description." }
67
+ (0..157).each do |num|
68
+ img = Image.create(:title => "Picture_#{num}", :description => "#{suffix}")
69
+ img.save
70
+ end
71
+ results = Image.search(:title.like => "Picture")
72
+ results.size.should == 158
73
+ end
64
74
  end
75
+
metadata CHANGED
@@ -6,8 +6,8 @@ version: !ruby/object:Gem::Version
6
6
  - 0
7
7
  - 1
8
8
  - 0
9
- - pre
10
- version: 0.1.0.pre
9
+ - pre2
10
+ version: 0.1.0.pre2
11
11
  platform: ruby
12
12
  authors:
13
13
  - hiroyuki
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-04-08 00:00:00 +09:00
18
+ date: 2010-04-15 00:00:00 +09:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -105,6 +105,7 @@ files:
105
105
  - examples/basic.rb
106
106
  - lib/groonga_adapter.rb
107
107
  - lib/groonga_adapter/adapter.rb
108
+ - lib/groonga_adapter/jsonparser.rb
108
109
  - lib/groonga_adapter/local_index.rb
109
110
  - lib/groonga_adapter/model_ext.rb
110
111
  - lib/groonga_adapter/remote_index.rb
@@ -120,7 +121,7 @@ files:
120
121
  - spec/specs/remote_result_spec.rb
121
122
  - spec/specs/search_spec.rb
122
123
  has_rdoc: true
123
- homepage: http://github.com/hryk/dm-groonga-adapter
124
+ homepage: http://hryk.github.com/dm-groonga-adapter
124
125
  licenses: []
125
126
 
126
127
  post_install_message: