grn_mini 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in grn_mini.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 ongaeshi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,368 @@
1
+ # GrnMini
2
+
3
+ Groonga(Rroonga) wrapper for using easily. It is the KVS so easy to use.
4
+
5
+ ## Installation
6
+
7
+ $ gem install grn_mini
8
+
9
+ ## Basic Usage
10
+
11
+ ### Create a database with the name "test.db".
12
+
13
+ ```ruby
14
+ require 'grn_mini'
15
+ array = GrnMini::Array.new("test.db")
16
+ ```
17
+
18
+ ### Add the record with the number column and text column.
19
+
20
+ Determine the column type when you first call "GrnMini::Array#add". (Add inverted index if data type is "string".)
21
+
22
+ ```ruby
23
+ array.add(text: "aaa", number: 1)
24
+ ```
25
+
26
+ ### It is also possible to use the '<<'
27
+
28
+ ```ruby
29
+ array << {text: "bbb", number: 2}
30
+ array << {text: "ccc", number: 3}
31
+ array.size #=> 3
32
+ ```
33
+
34
+ ### Create a temporary database. (Useful for testing)
35
+
36
+ ```ruby
37
+ GrnMini::Array.tmpdb do |array|
38
+ array << {text: "aaa", number: 1}
39
+ array << {text: "bbb", number: 2}
40
+ array << {text: "ccc", number: 3}
41
+ end
42
+ # Delete temporary database
43
+ ```
44
+
45
+ ## Data Type
46
+
47
+ ```ruby
48
+ GrnMini::Array.tmpdb do |array|
49
+ array << {filename: "a.txt", int: 1, float: 1.5, time: Time.at(1999)}
50
+ array << {filename: "b.doc", int: 2, float: 2.5, time: Time.at(2000)}
51
+
52
+ # ShortText
53
+ array[1].filename #=> "a.txt"
54
+ array[2].filename #=> "b.doc"
55
+
56
+ # Int32
57
+ array[1].int #=> 1
58
+ array[2].int #=> 2
59
+
60
+ # Float
61
+ array[1].float #=> 1.5
62
+ array[2].float #=> 2.5
63
+
64
+ # Time
65
+ array[1].time #=> 1999-01-01
66
+ array[2].time #=> 2000-01-01
67
+ end
68
+ ```
69
+
70
+ See also [8.4. Data type — Groonga documentation](http://groonga.org/docs/reference/types.html).
71
+
72
+ ## Access Record
73
+
74
+ ### Add
75
+
76
+ ```ruby
77
+ require 'grn_mini'
78
+
79
+ array = GrnMini::Array.new("test2.db")
80
+ array << {name:"Tanaka", age: 11, height: 162.5}
81
+ array << {name:"Suzuki", age: 31, height: 170.0}
82
+ ```
83
+
84
+ ### Read
85
+
86
+ ```ruby
87
+ record = array[1] # Read from id (> 0)
88
+ record.id #=> 1
89
+ ```
90
+
91
+ Access function with the same name as the column name
92
+
93
+ ```ruby
94
+ record.name #=> "Tanaka
95
+ record.age #=> 11
96
+ record.height #=> 162.5
97
+ ```
98
+
99
+ Groonga::Record#attributes is useful for debug
100
+
101
+ ```ruby
102
+ record.attributes #=> {"_id"=>1, "age"=>11, "height"=>162.5, "name"=>"Tanaka"}
103
+ ```
104
+
105
+ ### Update
106
+
107
+ ```ruby
108
+ array[2].name = "Hayashi"
109
+ array[2].attributes #=> {"_id"=>2, "age"=>31, "height"=>170.0, "name"=>"Hayashi"}
110
+ ```
111
+
112
+ ### Delete
113
+
114
+ Delete by passing id.
115
+
116
+ ```ruby
117
+ array.delete(1)
118
+
119
+ # It returns 'nil' value when you access a deleted record
120
+ array[1].attributes #=> {"_id"=>1, "age"=>0, "height"=>0.0, "name"=>nil}
121
+
122
+ # Can't see deleted records if access from Enumerable
123
+ array.first.id #=> 2
124
+ array.first.attributes #=> {"_id"=>2, "age"=>31, "height"=>170.0, "name"=>"Hayashi"}
125
+ ```
126
+
127
+ It is also possible to pass the block.
128
+
129
+ ```ruby
130
+ GrnMini::Array.tmpdb do |array|
131
+ array << {name:"Tanaka", age: 11, height: 162.5}
132
+ array << {name:"Suzuki", age: 31, height: 170.0}
133
+ array << {name:"Hayashi", age: 20, height: 165.0}
134
+
135
+ array.delete do |record|
136
+ record.age <= 20
137
+ end
138
+
139
+ array.size #=> 1
140
+ array.first.attributes #=> {"_id"=>2, "age"=>31, "height"=>170.0, "name"=>"Suzuki"}
141
+ end
142
+ ```
143
+
144
+ ## Search
145
+
146
+ Use GrnMini::Array#select method.
147
+ `:text` column is set to the `:default_column` implicitly.
148
+
149
+ ```ruby
150
+ GrnMini::Array.tmpdb do |array|
151
+ array << {text:"aaa", number:1}
152
+ array << {text:"bbb", number:20}
153
+ array << {text:"bbb ccc", number:2}
154
+ array << {text:"bbb", number:15}
155
+ array << {text:"ccc", number:3}
156
+
157
+ results = array.select("aaa")
158
+ results.map {|record| record.attributes} #=> [{"_id"=>1, "_key"=>{"_id"=>1, "number"=>1, "text"=>"aaa"}, "_score"=>1}]
159
+
160
+ # AND
161
+ results = array.select("bbb ccc")
162
+ results.map {|record| record.attributes} #=> [{"_id"=>2, "_key"=>{"_id"=>3, "number"=>2, "text"=>"bbb ccc"}, "_score"=>2}]
163
+
164
+ # Specify column
165
+ results = array.select("bbb number:<10")
166
+ results.map {|record| record.attributes} #=> [{"_id"=>2, "_key"=>{"_id"=>3, "number"=>2, "text"=>"bbb ccc"}, "_score"=>2}]
167
+
168
+ # AND, OR, Grouping
169
+ results = array.select("bbb (number:<= 10 OR number:>=20)")
170
+ results.map {|record| record.attributes} #=> [{"_id"=>2, "_key"=>{"_id"=>3, "number"=>2, "text"=>"bbb ccc"}, "_score"=>2}, {"_id"=>4, "_key"=>{"_id"=>2, "number"=>20, "text"=>"bbb"}, "_score"=>2}]
171
+
172
+ # NOT
173
+ results = array.select("bbb - ccc")
174
+ results.map {|record| record.attributes} #=> [{"_id"=>1, "_key"=>{"_id"=>2, "number"=>20, "text"=>"bbb"}, "_score"=>1}, {"_id"=>3, "_key"=>{"_id"=>4, "number"=>15, "text"=>"bbb"}, "_score"=>1}]
175
+ end
176
+ ```
177
+
178
+ Change `:default_column` to `:filename` column.
179
+
180
+ ```ruby
181
+ GrnMini::Array.tmpdb do |array|
182
+ array << {text: "txt", filename:"a.txt"}
183
+ array << {text: "txt", filename:"a.doc"}
184
+ array << {text: "txt", filename:"a.rb"}
185
+
186
+ # Specify column
187
+ results = array.select("filename:@txt")
188
+ results.first.attributes #=> {"_id"=>1, "_key"=>{"_id"=>1, "filename"=>"a.txt", "text"=>"txt"}, "_score"=>1}
189
+
190
+ # Change default_column
191
+ results = array.select("txt", default_column: "filename")
192
+ results.first.attributes #=> {"_id"=>1, "_key"=>{"_id"=>1, "filename"=>"a.txt", "text"=>"txt"}, "_score"=>1}
193
+ end
194
+ ```
195
+
196
+ See also [8.10.1. Query syntax](http://groonga.org/docs/reference/grn_expr/query_syntax.html), [Groonga::Table#select](http://ranguba.org/rroonga/en/Groonga/Table.html#select-instance_method)
197
+
198
+ ## Sort
199
+
200
+ Specify column name to sort.
201
+
202
+ ```ruby
203
+ GrnMini::Array.tmpdb do |array|
204
+ array << {name:"Tanaka", age: 11, height: 162.5}
205
+ array << {name:"Suzuki", age: 31, height: 170.0}
206
+ array << {name:"Hayashi", age: 21, height: 175.4}
207
+ array << {name:"Suzuki", age: 5, height: 110.0}
208
+
209
+ sorted = array.sort(["age"])
210
+
211
+ sorted.map {|r| {name: r.name, age: r.age}}
212
+ #=> [{:name=>"Suzuki", :age=> 5},
213
+ # {:name=>"Tanaka", :age=>11},
214
+ # {:name=>"Hayashi", :age=>21},
215
+ # {:name=>"Suzuki", :age=>31}]
216
+ end
217
+ ```
218
+
219
+ Combination sort.
220
+
221
+ ```ruby
222
+ sorted = array.sort([{key: "name", order: :ascending},
223
+ {key: "age" , order: :descending}])
224
+
225
+ sorted.map {|r| {name: r.name, age: r.age}}
226
+ #=> [{:name=>"Hayashi", :age=>21},
227
+ # {:name=>"Suzuki", :age=>31},
228
+ # {:name=>"Suzuki", :age=> 5},
229
+ # {:name=>"Tanaka", :age=>11}]
230
+ ```
231
+
232
+ ## Grouping
233
+
234
+ Drill down aka.
235
+
236
+ ```ruby
237
+ GrnMini::Array.tmpdb do |array|
238
+ array << {text:"aaaa.txt", suffix:"txt", type:1}
239
+ array << {text:"aaaa.doc", suffix:"doc", type:2}
240
+ array << {text:"aabb.txt", suffix:"txt", type:2}
241
+
242
+ groups = GrnMini::Util::group_with_sort(array, "suffix")
243
+
244
+ groups.size #=> 2
245
+ [groups[0].key, groups[0].n_sub_records] #=> ["txt", 2]
246
+ [groups[1].key, groups[1].n_sub_records] #=> ["doc", 1]
247
+ end
248
+ ```
249
+
250
+ Grouping from selection results.
251
+
252
+ ```ruby
253
+ GrnMini::Array.tmpdb do |array|
254
+ array << {text:"aaaa", suffix:"txt"}
255
+ array << {text:"aaaa", suffix:"doc"}
256
+ array << {text:"aaaa", suffix:"txt"}
257
+ array << {text:"cccc", suffix:"txt"}
258
+
259
+ results = array.select("aa")
260
+ groups = GrnMini::Util::group_with_sort(results, "suffix")
261
+
262
+ groups.size #=> 2
263
+ [groups[0].key, groups[0].n_sub_records] #=> ["txt", 2]
264
+ [groups[1].key, groups[1].n_sub_records] #=> ["doc", 1]
265
+ end
266
+ ```
267
+
268
+ ## Snippet
269
+
270
+ Display of keyword surrounding text. It is often used in search engine.
271
+ Use `GrnMini::Util::text_snippet_from_selection_results`.
272
+
273
+ ```ruby
274
+ GrnMini::Array.tmpdb do |array|
275
+ array << {text: <<EOF, filename: "aaa.txt"}
276
+ [1] This is a pen pep pea pek pet.
277
+ ------------------------------
278
+ ------------------------------
279
+ ------------------------------
280
+ ------------------------------
281
+ [2] This is a pen pep pea pek pet.
282
+ ------------------------------
283
+ ------------------------------
284
+ ------------------------------
285
+ ------------------------------
286
+ EOF
287
+
288
+ results = array.select("This pen")
289
+ snippet = GrnMini::Util::text_snippet_from_selection_results(results)
290
+
291
+ record = results.first
292
+ segments = snippet.execute(record.text)
293
+ segments.size #=> 2
294
+ segments[0] #=> "[1] <<This>> is a <<pen>> pep pea pek pet.\n------------------------------\n------------------------------\n---"
295
+ segments[1] #=> "--------\n------------------------------\n[2] <<This>> is a <<pen>> pep pea pek pet.\n-------------------------"
296
+ end
297
+ ```
298
+
299
+ `GrnMini::Util::html_snippet_from_selection_results` is HTML escaped.
300
+
301
+ ```ruby
302
+ GrnMini::Array.tmpdb do |array|
303
+ array << {text: <<EOF, filename: "aaa.txt"}
304
+ <html>
305
+ <div>This is a pen pep pea pek pet.</div>
306
+ </html>
307
+ EOF
308
+
309
+ results = array.select("This pen")
310
+ snippet = GrnMini::Util::html_snippet_from_selection_results(results, '<span class="strong">', '</span>') # Default value is '<strong>', '</strong>'
311
+
312
+ record = results.first
313
+ segments = snippet.execute(record.text)
314
+ segments.size #=> 1
315
+ segments.first #=> "&lt;html&gt;\n &lt;div&gt;<span class=\"strong\">This</span> is a <span class=\"strong\">pen</span> pep pea pek pet.&lt;/div&gt;\n&lt;/html&gt;\n"
316
+ end
317
+ ```
318
+
319
+ See also [Groonga::Expression#snippet](http://ranguba.org/rroonga/en/Groonga/Expression.html#snippet-instance_method)
320
+
321
+ ## Pagination
322
+
323
+ #paginate is more convenient than #sort if you want a pagination.
324
+
325
+ ```ruby
326
+ GrnMini::Array.tmpdb do |array|
327
+ array << {text: "aaaa", filename: "1.txt"}
328
+ array << {text: "aaaa aaaa", filename: "2a.txt"}
329
+ array << {text: "aaaa aaaa aaaa", filename: "3.txt"}
330
+ array << {text: "aaaa aaaa", filename: "2b.txt"}
331
+ array << {text: "aaaa aaaa", filename: "2c.txt"}
332
+ array << {text: "aaaa aaaa", filename: "2d.txt"}
333
+ array << {text: "aaaa aaaa", filename: "2e.txt"}
334
+ array << {text: "aaaa aaaa", filename: "2f.txt"}
335
+
336
+ results = array.select("aaaa")
337
+
338
+ # -- page1 --
339
+ page_entries = results.paginate([["_score", :desc]], :page => 1, :size => 5)
340
+
341
+ # Total number of record
342
+ page_entries.n_records #=> 8
343
+
344
+ # Page offset
345
+ page_entries.start_offset #=> 1
346
+ page_entries.end_offset #=> 5
347
+
348
+ # Page entries
349
+ page_entries.size #=> 5
350
+
351
+ # -- page2 --
352
+ page_entries = results.paginate([["_score", :desc]], :page => 2, :size => 5)
353
+
354
+ # Sample page content display
355
+ puts "#{page_entries.n_records} hit. (#{page_entries.start_offset} - #{page_entries.end_offset})"
356
+ page_entries.each do |record|
357
+ puts "#{record.filename}: #{record.text}"
358
+ end
359
+
360
+ #=> 8 hit. (6 - 8)
361
+ # 2b.txt: aaaa aaaa
362
+ # 2f.txt: aaaa aaaa
363
+ # 1.txt: aaaa
364
+ end
365
+ ```
366
+
367
+ See also [Groonga::Table#pagenate](http://ranguba.org/rroonga/en/Groonga/Table.html#paginate-instance_method)
368
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ end
7
+
8
+ task :default => :test
data/grn_mini.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grn_mini/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "grn_mini"
8
+ spec.version = GrnMini::VERSION
9
+ spec.authors = ["ongaeshi"]
10
+ spec.email = ["ongaeshi0621@gmail.com"]
11
+ spec.description = %q{Groonga(Rroonga) wrapper for using easily.}
12
+ spec.summary = %q{It is the KVS it's so easy to use.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "minitest"
24
+
25
+ spec.add_dependency "rroonga"
26
+ end
data/lib/grn_mini.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "grn_mini/version"
2
+ require "grn_mini/array"
3
+ require "grn_mini/util"
4
+
5
+ module GrnMini
6
+ # Your code goes here...
7
+ end
@@ -0,0 +1,101 @@
1
+ require 'grn_mini/util'
2
+ require 'groonga'
3
+ require 'tmpdir'
4
+
5
+ module GrnMini
6
+ class Array
7
+ attr_accessor :grn
8
+ include Enumerable
9
+
10
+ def self.tmpdb
11
+ Dir.mktmpdir do |dir|
12
+ # p dir
13
+ yield self.new(File.join(dir, "tmp.db"))
14
+ end
15
+ end
16
+
17
+ def initialize(path)
18
+ unless File.exist?(path)
19
+ Groonga::Database.create(path: path)
20
+ else
21
+ Groonga::Database.open(path)
22
+ end
23
+
24
+ unless Groonga["Array"]
25
+ @grn = Groonga::Array.create(name: "Array", persistent: true)
26
+ @terms = Groonga::PatriciaTrie.create(name: "Terms", key_normalize: true, default_tokenizer: "TokenBigramSplitSymbolAlphaDigit")
27
+ else
28
+ @grn = Groonga["Array"]
29
+ @terms = Groonga["Terms"]
30
+ end
31
+ end
32
+
33
+ def add(hash)
34
+ if @grn.empty?
35
+ hash.each do |key, value|
36
+ column = key.to_s
37
+
38
+ # @todo Need define_index_column ?
39
+ if value.is_a?(Time)
40
+ @grn.define_column(column, "Time")
41
+ elsif value.is_a?(Float)
42
+ @grn.define_column(column, "Float")
43
+ elsif value.is_a?(Numeric)
44
+ @grn.define_column(column, "Int32")
45
+ else
46
+ @grn.define_column(column, "ShortText")
47
+ @terms.define_index_column("array_#{column}", @grn, source: "Array.#{column}", with_position: true)
48
+ end
49
+ end
50
+ end
51
+
52
+ @grn.add(hash)
53
+ end
54
+
55
+ alias << add
56
+
57
+ def select(query, options = {default_column: "text"})
58
+ @grn.select(query, options)
59
+ end
60
+
61
+ def size
62
+ @grn.size
63
+ end
64
+
65
+ alias length size
66
+
67
+ def empty?
68
+ size == 0
69
+ end
70
+
71
+ def each
72
+ @grn.each do |record|
73
+ yield record
74
+ end
75
+ end
76
+
77
+ class IdIsGreaterThanZero < RuntimeError; end
78
+
79
+ def [](id)
80
+ raise IdIsGreaterThanZero if id == 0
81
+ @grn[id]
82
+ end
83
+
84
+ def delete(id = nil, &block)
85
+ if block_given?
86
+ @grn.delete(&block)
87
+ else
88
+ raise IdIsGreaterThanZero if id == 0
89
+ @grn.delete(id)
90
+ end
91
+ end
92
+
93
+ def sort(keys, options = {})
94
+ @grn.sort(keys, options)
95
+ end
96
+
97
+ def group(key, options = {})
98
+ @grn.group(key, options)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,19 @@
1
+ require 'groonga'
2
+
3
+ module GrnMini
4
+ module Util
5
+ module_function
6
+
7
+ def group_with_sort(table, column)
8
+ table.group(column).sort_by {|record| record.n_sub_records }.reverse
9
+ end
10
+
11
+ def text_snippet_from_selection_results(table, open_tag = '<<', close_tag = ">>")
12
+ table.expression.snippet([[open_tag, close_tag]])
13
+ end
14
+
15
+ def html_snippet_from_selection_results(table, open_tag = '<strong>', close_tag = "</strong>")
16
+ table.expression.snippet([[open_tag, close_tag]], {html_escape: true})
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module GrnMini
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'grn_mini'
3
+
4
+ require 'minitest/autorun'
@@ -0,0 +1,7 @@
1
+ require 'minitest_helper'
2
+
3
+ class TestGrnMini < MiniTest::Unit::TestCase
4
+ def test_that_it_has_a_version_number
5
+ refute_nil ::GrnMini::VERSION
6
+ end
7
+ end
@@ -0,0 +1,377 @@
1
+ require 'minitest_helper'
2
+
3
+ class TestGrnMiniArray < MiniTest::Unit::TestCase
4
+ def test_initialize
5
+ Dir.mktmpdir do |dir|
6
+ array = GrnMini::Array.new(File.join(dir, "test.db"))
7
+ end
8
+ end
9
+
10
+ def test_add
11
+ GrnMini::Array.tmpdb do |array|
12
+ array.add(text:"aaa", number:1)
13
+ assert_equal 1, array.size
14
+
15
+ # alias << add
16
+ array << {text:"bbb", number:2}
17
+ assert_equal 2, array.size
18
+ end
19
+ end
20
+
21
+ def test_select
22
+ GrnMini::Array.tmpdb do |array|
23
+ array << {text:"aaa", number:1}
24
+ array << {text:"bbb", number:2}
25
+ array << {text:"ccc", number:3}
26
+
27
+ results = array.select("bb")
28
+
29
+ assert_equal 1, results.size
30
+ assert_equal "bbb", results.first.text
31
+ end
32
+ end
33
+
34
+ def test_select2
35
+ GrnMini::Array.tmpdb do |array|
36
+ array << {text:"aaa", number:1}
37
+ array << {text:"bbb", number:2}
38
+ array << {text:"bbb", number:20}
39
+ array << {text:"ccc", number:3}
40
+
41
+ results = array.select("bb number:<10")
42
+
43
+ assert_equal 1, results.size
44
+ assert_equal "bbb", results.first.text
45
+ assert_equal 2, results.first.number
46
+ end
47
+ end
48
+
49
+ def test_size
50
+ GrnMini::Array.tmpdb do |array|
51
+ assert_equal 0, array.size
52
+ assert_equal 0, array.length
53
+
54
+ array << {text:"aaa", number:1}
55
+ array << {text:"bbb", number:2}
56
+ assert_equal 2, array.size
57
+ assert_equal 2, array.length
58
+ end
59
+ end
60
+
61
+ def test_empty?
62
+ GrnMini::Array.tmpdb do |array|
63
+ assert_equal true, array.empty?
64
+
65
+ array << {text:"aaa", number:1}
66
+ assert_equal false, array.empty?
67
+ end
68
+ end
69
+
70
+ def test_each
71
+ GrnMini::Array.tmpdb do |array|
72
+ array << {text:"aaa", number:1}
73
+ array << {text:"bbb", number:2}
74
+ array << {text:"ccc", number:3}
75
+
76
+ array.each_with_index do |v, index|
77
+ case index
78
+ when 0
79
+ assert_equal "aaa", v.text
80
+ assert_equal 1, v.number
81
+ when 1
82
+ assert_equal "bbb", v.text
83
+ assert_equal 2, v.number
84
+ when 2
85
+ assert_equal "ccc", v.text
86
+ assert_equal 3, v.number
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def test_read_by_id
93
+ GrnMini::Array.tmpdb do |array|
94
+ array << {text:"aaa", number:1}
95
+ array << {text:"bbb", number:2}
96
+ array << {text:"ccc", number:3}
97
+
98
+ # id > 0
99
+ assert_raises(GrnMini::Array::IdIsGreaterThanZero) { array[0] }
100
+
101
+ record = array[1]
102
+ assert_equal 1, record.id
103
+
104
+ assert_equal "aaa", array[1].text
105
+ assert_equal "bbb", array[2].text
106
+ assert_equal "ccc", array[3].text
107
+
108
+ assert_equal nil, array[4].text
109
+ end
110
+ end
111
+
112
+ def test_write_by_id
113
+ GrnMini::Array.tmpdb do |array|
114
+ array << {text:"aaa", number:1}
115
+ array << {text:"bbb", number:2}
116
+ array << {text:"ccc", number:3}
117
+
118
+ assert_equal "bbb", array[2].text
119
+ assert_equal 2, array[2].number
120
+
121
+ array[2].text = "BBB"
122
+ array[2].number = 22
123
+
124
+ assert_equal "BBB", array[2].text
125
+ assert_equal 22, array[2].number
126
+ end
127
+ end
128
+
129
+ def test_delete_by_id
130
+ GrnMini::Array.tmpdb do |array|
131
+ array << {text:"aaa", number:1}
132
+ array << {text:"bbb", number:2}
133
+ array << {text:"ccc", number:3}
134
+
135
+ assert_equal 3, array.size
136
+
137
+ array.delete(2)
138
+
139
+ assert_equal 2, array.size
140
+
141
+ # Deleted elements are not stuffed between
142
+ assert_equal nil, array[2].text
143
+ assert_equal 0, array[2].number
144
+
145
+ # Can be accessed properly if use GrnMini::Array#each
146
+ array.each_with_index do |v, index|
147
+ case index
148
+ when 0
149
+ assert_equal "aaa", v.text
150
+ assert_equal 1, v.number
151
+ when 1
152
+ assert_equal "ccc", v.text
153
+ assert_equal 3, v.number
154
+ end
155
+ # p v.attributes
156
+ end
157
+
158
+ # New member id is '4'
159
+ array << {text:"ddd", number:1}
160
+ assert_equal "ddd", array[4].text
161
+ end
162
+ end
163
+
164
+ def test_delete_by_block
165
+ GrnMini::Array.tmpdb do |array|
166
+ array << {text:"bbb", number:2}
167
+ array << {text:"ccc", number:3}
168
+ array << {text:"aaa", number:1}
169
+
170
+ array.delete do |record|
171
+ record.number >= 2
172
+ end
173
+
174
+ assert_equal 1, array.size
175
+ assert_equal "aaa", array.first.text
176
+ assert_equal 1, array.first.number
177
+ end
178
+ end
179
+
180
+ def test_float_column
181
+ GrnMini::Array.tmpdb do |array|
182
+ array << {text:"aaaa", float: 1.5}
183
+ array << {text:"bbbb", float: 2.5}
184
+ array << {text:"cccc", float: 3.5}
185
+
186
+ assert_equal 2.5, array[2].float
187
+
188
+ results = array.select("float:>2.6")
189
+ assert_equal 3.5, results.first.float
190
+ end
191
+ end
192
+
193
+ def test_time_column
194
+ GrnMini::Array.tmpdb do |array|
195
+ array << {text:"aaaa", timestamp: Time.new(2013)} # 2013-01-01
196
+ array << {text:"bbbb", timestamp: Time.new(2014)} # 2014-01-01
197
+ array << {text:"cccc", timestamp: Time.new(2015)} # 2015-01-01
198
+
199
+ assert_equal Time.new(2014), array[2].timestamp
200
+
201
+ results = array.select("timestamp:<=#{Time.new(2013,12).to_i}")
202
+ assert_equal 1, results.size
203
+ assert_equal Time.new(2013), results.first.timestamp
204
+ end
205
+ end
206
+
207
+ def test_record_attributes
208
+ GrnMini::Array.tmpdb do |array|
209
+ array << {text:"aaaa", int: 1}
210
+ array << {text:"bbbb", int: 2}
211
+ array << {text:"cccc", int: 3}
212
+
213
+ assert_equal({"_id"=>1, "int"=>1, "text"=>"aaaa"}, array[1].attributes)
214
+ assert_equal({"_id"=>2, "int"=>2, "text"=>"bbbb"}, array[2].attributes)
215
+ assert_equal({"_id"=>3, "int"=>3, "text"=>"cccc"}, array[3].attributes)
216
+ end
217
+ end
218
+
219
+ def test_assign_long_text_to_short_text
220
+ GrnMini::Array.tmpdb do |array|
221
+ array << {filename:"a.txt"}
222
+ array << {filename:"a"*4095 + ".txt" } # Over 4095 byte (ShortText limit)
223
+
224
+ results = array.select("txt", default_column: "filename")
225
+ assert_equal 2, results.size
226
+ end
227
+ end
228
+
229
+ def test_grn_object
230
+ GrnMini::Array.tmpdb do |array|
231
+ array << {text: "aaaa", filename:"a.txt", int: 1, float: 1.5, time: Time.at(2001)}
232
+
233
+ raw = array.grn
234
+
235
+ assert_equal true, raw.have_column?("filename")
236
+ assert_equal true, raw.have_column?("int")
237
+ assert_equal true, raw.have_column?("float")
238
+ assert_equal true, raw.have_column?("time")
239
+ assert_equal false, raw.have_column?("timeee")
240
+
241
+ assert_equal "ShortText", raw.column("text").range.name
242
+ assert_equal "ShortText", raw.column("filename").range.name
243
+ assert_equal "Int32" , raw.column("int").range.name
244
+ assert_equal "Float" , raw.column("float").range.name
245
+ assert_equal "Time" , raw.column("time").range.name
246
+
247
+ assert_equal false, raw.support_key?
248
+ assert_equal false, raw.support_sub_records?
249
+ end
250
+ end
251
+
252
+ def test_sort
253
+ GrnMini::Array.tmpdb do |array|
254
+ array << {name:"Tanaka", age: 11, height: 162.5}
255
+ array << {name:"Suzuki", age: 31, height: 170.0}
256
+ array << {name:"Hayashi", age: 21, height: 175.4}
257
+ array << {name:"Suzuki", age: 5, height: 110.0}
258
+
259
+ sorted_by_age = array.sort(["age"])
260
+ sorted_array = sorted_by_age.map {|r| {name: r.name, age: r.age}}
261
+ assert_equal [{:name=>"Suzuki", :age=>5},
262
+ {:name=>"Tanaka", :age=>11},
263
+ {:name=>"Hayashi", :age=>21},
264
+ {:name=>"Suzuki", :age=>31}], sorted_array
265
+
266
+ sorted_by_combination = array.sort([{key: "name", order: :ascending},
267
+ {key: "age" , order: :descending}])
268
+ sorted_array = sorted_by_combination.map {|r| {name: r.name, age: r.age}}
269
+ assert_equal [{:name=>"Hayashi", :age=>21},
270
+ {:name=>"Suzuki", :age=>31},
271
+ {:name=>"Suzuki", :age=>5},
272
+ {:name=>"Tanaka", :age=>11}], sorted_array
273
+ end
274
+ end
275
+
276
+ def test_group_from_array
277
+ GrnMini::Array.tmpdb do |array|
278
+ array << {text:"aaaa.txt", suffix:"txt", type:1}
279
+ array << {text:"aaaa.doc", suffix:"doc", type:2}
280
+ array << {text:"aabb.txt", suffix:"txt", type:2}
281
+
282
+ groups = GrnMini::Util::group_with_sort(array, "suffix")
283
+
284
+ assert_equal 2, groups.size
285
+ assert_equal ["txt", 2], [groups[0].key, groups[0].n_sub_records]
286
+ assert_equal ["doc", 1], [groups[1].key, groups[1].n_sub_records]
287
+ end
288
+ end
289
+
290
+ def test_group_from_selection_results
291
+ GrnMini::Array.tmpdb do |array|
292
+ array << {text:"aaaa", suffix:"txt"}
293
+ array << {text:"aaaa", suffix:"doc"}
294
+ array << {text:"aaaa", suffix:"txt"}
295
+ array << {text:"cccc", suffix:"txt"}
296
+
297
+ results = array.select("aa")
298
+ groups = GrnMini::Util::group_with_sort(results, "suffix")
299
+
300
+ assert_equal 2, groups.size
301
+ assert_equal ["txt", 2], [groups[0].key, groups[0].n_sub_records]
302
+ assert_equal ["doc", 1], [groups[1].key, groups[1].n_sub_records]
303
+ end
304
+ end
305
+
306
+ def test_text_snippet_from_selection_results
307
+ GrnMini::Array.tmpdb do |array|
308
+ array << {text: <<EOF, filename: "aaa.txt"}
309
+ [1] This is a pen pep pea pek pet.
310
+ ------------------------------
311
+ ------------------------------
312
+ ------------------------------
313
+ ------------------------------
314
+ [2] This is a pen pep pea pek pet.
315
+ ------------------------------
316
+ ------------------------------
317
+ ------------------------------
318
+ ------------------------------
319
+ ------------------------------
320
+ EOF
321
+
322
+ results = array.select("This pen")
323
+ snippet = GrnMini::Util::text_snippet_from_selection_results(results)
324
+
325
+ record = results.first
326
+ segments = snippet.execute(record.text)
327
+ assert_equal 2, segments.size
328
+ assert_match /\[1\] <<This>> is a <<pen>> pep pea pek pet./, segments[0]
329
+ assert_match /\[2\] <<This>> is a <<pen>> pep pea pek pet./, segments[1]
330
+ end
331
+ end
332
+
333
+ def test_html_snippet_from_selection_results
334
+ GrnMini::Array.tmpdb do |array|
335
+ array << {text: <<EOF, filename: "aaa.txt"}
336
+ <html>
337
+ <div>This is a pen pep pea pek pet.</div>
338
+ </html>
339
+ EOF
340
+
341
+ results = array.select("This pen")
342
+ snippet = GrnMini::Util::html_snippet_from_selection_results(results)
343
+
344
+ record = results.first
345
+ segments = snippet.execute(record.text)
346
+ assert_equal 1, segments.size
347
+ assert_equal "&lt;html&gt;\n &lt;div&gt;<strong>This</strong> is a <strong>pen</strong> pep pea pek pet.&lt;/div&gt;\n&lt;/html&gt;\n", segments.first
348
+ end
349
+ end
350
+
351
+ def test_paginate
352
+ GrnMini::Array.tmpdb do |array|
353
+ array << {text: "aaaa", filename: "1.txt"}
354
+ array << {text: "aaaa aaaa", filename: "2a.txt"}
355
+ array << {text: "aaaa aaaa aaaa", filename: "3.txt"}
356
+ array << {text: "aaaa aaaa", filename: "2b.txt"}
357
+ array << {text: "aaaa aaaa", filename: "2c.txt"}
358
+ array << {text: "aaaa aaaa", filename: "2d.txt"}
359
+
360
+ results = array.select("aaaa")
361
+
362
+ # page1
363
+ page_entries = results.paginate([["_score", :desc]], :page => 1, :size => 5)
364
+ assert_equal 6, page_entries.n_records
365
+ assert_equal 1, page_entries.start_offset
366
+ assert_equal 5, page_entries.end_offset
367
+ assert_equal "3.txt", page_entries.first.filename
368
+
369
+ # page2
370
+ page_entries = results.paginate([["_score", :desc]], :page => 2, :size => 5)
371
+ assert_equal 6, page_entries.n_records
372
+ assert_equal 6, page_entries.start_offset
373
+ assert_equal 6, page_entries.end_offset
374
+ assert_equal "1.txt", page_entries.first.filename
375
+ end
376
+ end
377
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grn_mini
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ongaeshi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-01-05 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: &2156695780 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2156695780
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &2156695360 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2156695360
36
+ - !ruby/object:Gem::Dependency
37
+ name: minitest
38
+ requirement: &2156694900 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2156694900
47
+ - !ruby/object:Gem::Dependency
48
+ name: rroonga
49
+ requirement: &2156694480 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *2156694480
58
+ description: Groonga(Rroonga) wrapper for using easily.
59
+ email:
60
+ - ongaeshi0621@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - .travis.yml
67
+ - Gemfile
68
+ - LICENSE.txt
69
+ - README.md
70
+ - Rakefile
71
+ - grn_mini.gemspec
72
+ - lib/grn_mini.rb
73
+ - lib/grn_mini/array.rb
74
+ - lib/grn_mini/util.rb
75
+ - lib/grn_mini/version.rb
76
+ - test/minitest_helper.rb
77
+ - test/test_grn_mini.rb
78
+ - test/test_grn_mini_array.rb
79
+ homepage: ''
80
+ licenses:
81
+ - MIT
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 1.8.6
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: It is the KVS it's so easy to use.
104
+ test_files:
105
+ - test/minitest_helper.rb
106
+ - test/test_grn_mini.rb
107
+ - test/test_grn_mini_array.rb