grn_mini 0.1.0

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