groonga 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/NEWS.ja.rdoc +11 -0
  2. data/NEWS.rdoc +11 -0
  3. data/README.ja.rdoc +4 -3
  4. data/README.rdoc +4 -3
  5. data/Rakefile +1 -1
  6. data/TUTORIAL.ja.rdoc +168 -44
  7. data/benchmark/common.rb +49 -0
  8. data/benchmark/read-write-small-many-items.rb +156 -0
  9. data/benchmark/write-small-many-items.rb +145 -0
  10. data/example/bookmark.rb +68 -20
  11. data/ext/rb-grn-array-cursor.c +8 -0
  12. data/ext/rb-grn-array.c +40 -11
  13. data/ext/rb-grn-column.c +38 -209
  14. data/ext/rb-grn-context.c +203 -56
  15. data/ext/rb-grn-database.c +119 -5
  16. data/ext/rb-grn-encoding-support.c +64 -0
  17. data/ext/rb-grn-encoding.c +58 -1
  18. data/ext/rb-grn-fix-size-column.c +220 -0
  19. data/ext/rb-grn-hash-cursor.c +8 -0
  20. data/ext/rb-grn-hash.c +244 -2
  21. data/ext/rb-grn-index-column.c +474 -0
  22. data/ext/rb-grn-object.c +143 -265
  23. data/ext/rb-grn-patricia-trie.c +148 -2
  24. data/ext/rb-grn-query.c +5 -3
  25. data/ext/rb-grn-record.c +3 -2
  26. data/ext/rb-grn-snippet.c +5 -3
  27. data/ext/rb-grn-table-cursor-key-support.c +3 -3
  28. data/ext/rb-grn-table-cursor.c +106 -112
  29. data/ext/rb-grn-table-key-support.c +220 -118
  30. data/ext/rb-grn-table.c +336 -80
  31. data/ext/rb-grn-type.c +5 -4
  32. data/ext/rb-grn-utils.c +62 -63
  33. data/ext/rb-grn.h +215 -14
  34. data/ext/rb-groonga.c +7 -16
  35. data/extconf.rb +3 -1
  36. data/html/favicon.ico +0 -0
  37. data/html/favicon.xcf +0 -0
  38. data/html/index.html +1 -7
  39. data/lib/groonga/record.rb +6 -1
  40. data/test/groonga-test-utils.rb +1 -0
  41. data/test/test-array.rb +81 -0
  42. data/test/test-column.rb +22 -12
  43. data/test/test-context.rb +1 -29
  44. data/test/test-database.rb +30 -0
  45. data/test/test-hash.rb +194 -0
  46. data/test/test-index-column.rb +57 -0
  47. data/test/test-patricia-trie.rb +82 -0
  48. data/test/test-record.rb +10 -10
  49. data/test/test-table.rb +37 -130
  50. data/test/test-type.rb +4 -3
  51. metadata +15 -4
  52. data/benchmark/small-many-items.rb +0 -175
@@ -0,0 +1,474 @@
1
+ /* -*- c-file-style: "ruby" -*- */
2
+ /*
3
+ Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
4
+
5
+ This library is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU Lesser General Public
7
+ License version 2.1 as published by the Free Software Foundation.
8
+
9
+ This library is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ Lesser General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Lesser General Public
15
+ License along with this library; if not, write to the Free Software
16
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ */
18
+
19
+ #include "rb-grn.h"
20
+
21
+ #define SELF(object) ((RbGrnIndexColumn *)DATA_PTR(object))
22
+
23
+ VALUE rb_cGrnIndexColumn;
24
+
25
+ /*
26
+ * Document-class: Groonga::IndexColumn < Groonga::Column
27
+ *
28
+ * 転置索引エントリを格納するカラム。このカラムを利用するこ
29
+ * とにより高速な全文検索を実現できる。
30
+ *
31
+ * テーブルにGroonga::IndexColumnを定義する方法は
32
+ * Groonga::Table#define_index_columnを参照。
33
+ */
34
+
35
+ void
36
+ rb_grn_index_column_unbind (RbGrnIndexColumn *rb_grn_index_column)
37
+ {
38
+ RbGrnObject *rb_grn_object;
39
+ grn_ctx *context;
40
+
41
+ rb_grn_object = RB_GRN_OBJECT(rb_grn_index_column);
42
+ context = rb_grn_object->context;
43
+
44
+ if (context) {
45
+ grn_obj_close(context, rb_grn_index_column->id_query);
46
+ grn_obj_close(context, rb_grn_index_column->string_query);
47
+ grn_obj_close(context, rb_grn_index_column->value);
48
+ grn_obj_close(context, rb_grn_index_column->old_value);
49
+ }
50
+
51
+ rb_grn_object_unbind(rb_grn_object);
52
+ }
53
+
54
+ static void
55
+ rb_grn_index_column_free (void *object)
56
+ {
57
+ RbGrnIndexColumn *rb_grn_index_column = object;
58
+
59
+ rb_grn_index_column_unbind(rb_grn_index_column);
60
+ xfree(rb_grn_index_column);
61
+ }
62
+
63
+ VALUE
64
+ rb_grn_index_column_alloc (VALUE klass)
65
+ {
66
+ return Data_Wrap_Struct(klass, NULL, rb_grn_index_column_free, NULL);
67
+ }
68
+
69
+ void
70
+ rb_grn_index_column_bind (RbGrnIndexColumn *rb_grn_index_column,
71
+ grn_ctx *context, grn_obj *column,
72
+ rb_grn_boolean owner)
73
+ {
74
+ RbGrnObject *rb_grn_object;
75
+
76
+ rb_grn_object = RB_GRN_OBJECT(rb_grn_index_column);
77
+ rb_grn_object_bind(rb_grn_object, context, column, owner);
78
+ rb_grn_object->unbind = RB_GRN_UNBIND_FUNCTION(rb_grn_index_column_unbind);
79
+
80
+ rb_grn_index_column->value = grn_obj_open(context, GRN_BULK, 0,
81
+ rb_grn_object->range_id);
82
+ rb_grn_index_column->old_value = grn_obj_open(context, GRN_BULK, 0,
83
+ rb_grn_object->range_id);
84
+
85
+ rb_grn_index_column->id_query = grn_obj_open(context, GRN_BULK, 0,
86
+ rb_grn_object->domain_id);
87
+ rb_grn_index_column->string_query = grn_obj_open(context, GRN_BULK,
88
+ GRN_OBJ_DO_SHALLOW_COPY,
89
+ GRN_ID_NIL);
90
+ }
91
+
92
+ void
93
+ rb_grn_index_column_assign (VALUE self, VALUE rb_context,
94
+ grn_ctx *context, grn_obj *column,
95
+ rb_grn_boolean owner)
96
+ {
97
+ RbGrnIndexColumn *rb_grn_index_column;
98
+
99
+ rb_grn_index_column = ALLOC(RbGrnIndexColumn);
100
+ DATA_PTR(self) = rb_grn_index_column;
101
+ rb_grn_index_column_bind(rb_grn_index_column, context, column, owner);
102
+
103
+ rb_iv_set(self, "context", rb_context);
104
+ }
105
+
106
+ void
107
+ rb_grn_index_column_deconstruct (RbGrnIndexColumn *rb_grn_index_column,
108
+ grn_obj **column,
109
+ grn_ctx **context,
110
+ grn_id *domain_id,
111
+ grn_obj **domain,
112
+ grn_obj **value,
113
+ grn_obj **old_value,
114
+ grn_id *range_id,
115
+ grn_obj **range,
116
+ grn_obj **id_query,
117
+ grn_obj **string_query)
118
+ {
119
+ RbGrnObject *rb_grn_object;
120
+
121
+ rb_grn_object = RB_GRN_OBJECT(rb_grn_index_column);
122
+ rb_grn_object_deconstruct(rb_grn_object, column, context,
123
+ domain_id, domain,
124
+ range_id, range);
125
+
126
+ if (value)
127
+ *value = rb_grn_index_column->value;
128
+ if (old_value)
129
+ *old_value = rb_grn_index_column->old_value;
130
+ if (id_query)
131
+ *id_query = rb_grn_index_column->id_query;
132
+ if (string_query)
133
+ *string_query = rb_grn_index_column->string_query;
134
+ }
135
+
136
+ /*
137
+ * call-seq:
138
+ * column[id] = value
139
+ * column[id] = options
140
+ *
141
+ * IDが_id_であるレコードを高速に全文検索するため転置索引を作
142
+ * 成する。多くの場合、Groonga::Table#define_index_columnで
143
+ * +:source+オプションを指定することにより、自動的に全文検索
144
+ * 用の索引は更新されるので、明示的にこのメソッドを使うこと
145
+ * は少ない。
146
+ *
147
+ * _value_には文字列を指定する。
148
+ *
149
+ * _options_を指定することにより、より索引の作成を制御できる。
150
+ * _options_に指定可能な値は以下の通り。
151
+ *
152
+ * [+:section+]
153
+ * 段落番号を指定する。省略した場合は1を指定したとみなされ
154
+ * る。
155
+ *
156
+ * Groonga::Table#define_index_columnで
157
+ * <tt>{:with_section => true}</tt>を指定していなければい
158
+ * けない。
159
+ *
160
+ * [+:old_value+]
161
+ * 以前の値を指定する。省略した場合は現在の値が用いられる。
162
+ * 通常は指定する必要はない。
163
+ *
164
+ * [+:value+]
165
+ * 新しい値を指定する。_value_を指定した場合と_options_で
166
+ * <tt>{:value => value}</tt>を指定した場合は同じ動作とな
167
+ * る。
168
+ *
169
+ * 記事の段落毎に索引を作成する。
170
+ * articles = Groonga::Array.create(:name => "<articles>")
171
+ * articles.define_column("title", "<shottext>")
172
+ * articles.define_column("content", "<text>")
173
+ *
174
+ * terms = Groonga::Hash.create(:name => "<terms>",
175
+ * :with_section => true,
176
+ * :default_tokenizer => "<token:bigram>")
177
+ * content_index = terms.define_index_column("content", articles)
178
+ *
179
+ * content = <<-EOC
180
+ * groonga は組み込み型の全文検索エンジンライブラリです。
181
+ * DBMSやスクリプト言語処理系等に組み込むことによって、その
182
+ * 全文検索機能を強化することができます。また、リレーショナ
183
+ * ルモデルに基づくデータストア機能を内包しており、groonga
184
+ * 単体でも高速なデータストアサーバとして使用することができ
185
+ * ます。
186
+ *
187
+ * ■全文検索方式
188
+ * 転置索引型の全文検索エンジンです。転置索引は圧縮されてファ
189
+ * イルに格納され、検索時のディスク読み出し量を小さく、かつ
190
+ * 局所的に抑えるように設計されています。用途に応じて以下の
191
+ * 索引タイプを選択できます。
192
+ * EOC
193
+ *
194
+ * groonga = articles.add(:title => "groonga", :content => content)
195
+ *
196
+ * content.split(/\n{2,}/).each_with_index do |sentence, i|
197
+ * content_index[groonga] = {:value => sentence, :section => i + 1}
198
+ * end
199
+ *
200
+ * content.search("エンジン").collect do |record|
201
+ * p record.key["title"] # -> "groonga"
202
+ * end
203
+ */
204
+ static VALUE
205
+ rb_grn_index_column_array_set (VALUE self, VALUE rb_id, VALUE rb_value)
206
+ {
207
+ grn_ctx *context = NULL;
208
+ grn_obj *column, *range;
209
+ grn_rc rc;
210
+ grn_id id;
211
+ unsigned int section;
212
+ grn_obj *old_value, *new_value;
213
+ VALUE original_rb_value, rb_section, rb_old_value, rb_new_value;
214
+
215
+ original_rb_value = rb_value;
216
+
217
+ rb_grn_index_column_deconstruct(SELF(self), &column, &context,
218
+ NULL, NULL,
219
+ &new_value, &old_value,
220
+ NULL, &range,
221
+ NULL, NULL);
222
+
223
+ id = RVAL2GRNID(rb_id, context, range, self);
224
+
225
+ if (!RVAL2CBOOL(rb_obj_is_kind_of(rb_value, rb_cHash))) {
226
+ VALUE hash_value;
227
+ hash_value = rb_hash_new();
228
+ rb_hash_aset(hash_value, RB_GRN_INTERN("value"), rb_value);
229
+ rb_value = hash_value;
230
+ }
231
+
232
+ rb_grn_scan_options(rb_value,
233
+ "section", &rb_section,
234
+ "old_value", &rb_old_value,
235
+ "value", &rb_new_value,
236
+ NULL);
237
+
238
+ if (NIL_P(rb_section))
239
+ section = 1;
240
+ else
241
+ section = NUM2UINT(rb_section);
242
+
243
+ if (NIL_P(rb_old_value)) {
244
+ old_value = NULL;
245
+ } else {
246
+ GRN_BULK_REWIND(old_value);
247
+ RVAL2GRNBULK(rb_old_value, context, old_value);
248
+ }
249
+
250
+ if (NIL_P(rb_new_value)) {
251
+ new_value = NULL;
252
+ } else {
253
+ GRN_BULK_REWIND(new_value);
254
+ RVAL2GRNBULK(rb_new_value, context, new_value);
255
+ }
256
+
257
+ rc = grn_column_index_update(context, column,
258
+ id, section, old_value, new_value);
259
+ rb_grn_context_check(context, self);
260
+ rb_grn_rc_check(rc, self);
261
+
262
+ return original_rb_value;
263
+ }
264
+
265
+ static VALUE
266
+ rb_grn_index_column_get_sources (VALUE self)
267
+ {
268
+ grn_ctx *context = NULL;
269
+ grn_obj *column;
270
+ grn_obj sources;
271
+ grn_id *source_ids;
272
+ VALUE rb_sources;
273
+ int i, n;
274
+
275
+ rb_grn_index_column_deconstruct(SELF(self), &column, &context,
276
+ NULL, NULL,
277
+ NULL, NULL, NULL, NULL,
278
+ NULL, NULL);
279
+
280
+ GRN_OBJ_INIT(&sources, GRN_BULK, 0, GRN_ID_NIL);
281
+ grn_obj_get_info(context, column, GRN_INFO_SOURCE, &sources);
282
+ rb_grn_context_check(context, self);
283
+
284
+ n = GRN_BULK_VSIZE(&sources) / sizeof(grn_id);
285
+ source_ids = (grn_id *)GRN_BULK_HEAD(&sources);
286
+ rb_sources = rb_ary_new2(n);
287
+ for (i = 0; i < n; i++) {
288
+ grn_obj *source;
289
+ VALUE rb_source;
290
+
291
+ source = grn_ctx_at(context, *source_ids);
292
+ rb_source = GRNOBJECT2RVAL(Qnil, context, source, RB_GRN_FALSE);
293
+ rb_ary_push(rb_sources, rb_source);
294
+ source_ids++;
295
+ }
296
+ grn_obj_close(context, &sources);
297
+
298
+ return rb_sources;
299
+ }
300
+
301
+ static VALUE
302
+ rb_grn_index_column_set_sources (VALUE self, VALUE rb_sources)
303
+ {
304
+ VALUE exception;
305
+ grn_ctx *context = NULL;
306
+ grn_obj *column;
307
+ int i, n;
308
+ VALUE *rb_source_values;
309
+ grn_id *sources;
310
+ grn_rc rc;
311
+
312
+ rb_grn_index_column_deconstruct(SELF(self), &column, &context,
313
+ NULL, NULL,
314
+ NULL, NULL, NULL, NULL,
315
+ NULL, NULL);
316
+
317
+ n = RARRAY_LEN(rb_sources);
318
+ rb_source_values = RARRAY_PTR(rb_sources);
319
+ sources = ALLOCA_N(grn_id, n);
320
+ for (i = 0; i < n; i++) {
321
+ VALUE rb_source_id;
322
+ grn_obj *source;
323
+ grn_id source_id;
324
+
325
+ rb_source_id = rb_source_values[i];
326
+ if (CBOOL2RVAL(rb_obj_is_kind_of(rb_source_id, rb_cInteger))) {
327
+ source_id = NUM2UINT(rb_source_id);
328
+ } else {
329
+ source = RVAL2GRNOBJECT(rb_source_id, &context);
330
+ rb_grn_context_check(context, rb_source_id);
331
+ source_id = grn_obj_id(context, source);
332
+ }
333
+ sources[i] = source_id;
334
+ }
335
+
336
+ {
337
+ grn_obj bulk_sources;
338
+ GRN_OBJ_INIT(&bulk_sources, GRN_BULK, 0, GRN_ID_NIL);
339
+ GRN_TEXT_SET(context, &bulk_sources, sources, n * sizeof(grn_id));
340
+ rc = grn_obj_set_info(context, column, GRN_INFO_SOURCE, &bulk_sources);
341
+ exception = rb_grn_context_to_exception(context, self);
342
+ grn_obj_close(context, &bulk_sources);
343
+ }
344
+
345
+ if (!NIL_P(exception))
346
+ rb_exc_raise(exception);
347
+ rb_grn_rc_check(rc, self);
348
+
349
+ return Qnil;
350
+ }
351
+
352
+ static VALUE
353
+ rb_grn_index_column_set_source (VALUE self, VALUE rb_source)
354
+ {
355
+ if (!RVAL2CBOOL(rb_obj_is_kind_of(rb_source, rb_cArray)))
356
+ rb_source = rb_ary_new3(1, rb_source);
357
+
358
+ return rb_grn_index_column_set_sources(self, rb_source);
359
+ }
360
+
361
+ /*
362
+ * Document-method: search
363
+ *
364
+ * call-seq:
365
+ * column.search(query, options={}) -> Groonga::Hash
366
+ *
367
+ * _object_から_query_に対応するオブジェクトを検索し、見つかっ
368
+ * たオブジェクトのIDがキーになっているGroonga::Hashを返す。
369
+ *
370
+ * 利用可能なオプションは以下の通り。
371
+ *
372
+ * [_:result_]
373
+ * 結果を格納するGroonga::Hash。指定しない場合は新しく
374
+ * Groonga::Hashを生成し、それに結果を格納して返す。
375
+ * [_:operator_]
376
+ * 以下のどれかの値を指定する。+nil+, <tt>"or"</tt>, <tt>"||"</tt>,
377
+ * <tt>"and"</tt>, <tt>"+"</tt>, <tt>"&&"</tt>, <tt>"but"</tt>,
378
+ * <tt>"not"</tt>, <tt>"-"</tt>, <tt>"adjust"</tt>, <tt>">"</tt>。
379
+ * それぞれ以下のようになる。(FIXME: 「以下」)
380
+ * [_:exact_]
381
+ * +true+を指定すると完全一致で検索する
382
+ * [_:longest_common_prefix_]
383
+ * +true+を指定すると_query_と同じ接頭辞をもつエントリのう
384
+ * ち、もっとも長いエントリを検索する
385
+ * [_:suffix_]
386
+ * +true+を指定すると_query_が後方一致するエントリを検索す
387
+ * る
388
+ * [_:prefix_]
389
+ * +true+を指定すると_query_が前方一致するレコードを検索す
390
+ * る
391
+ * [_:near_]
392
+ * +true+を指定すると_query_に指定した複数の語が近傍に含ま
393
+ * れるレコードを検索する
394
+ * [...]
395
+ * ...
396
+ */
397
+ static VALUE
398
+ rb_grn_index_column_search (int argc, VALUE *argv, VALUE self)
399
+ {
400
+ grn_ctx *context;
401
+ grn_obj *column;
402
+ grn_obj *range;
403
+ grn_obj *query = NULL, *id_query = NULL, *string_query = NULL;
404
+ grn_obj *result;
405
+ grn_sel_operator operator;
406
+ grn_rc rc;
407
+ VALUE rb_query, options, rb_result, rb_operator;
408
+
409
+ rb_grn_index_column_deconstruct(SELF(self), &column, &context,
410
+ NULL, NULL,
411
+ NULL, NULL, NULL, &range,
412
+ &id_query, &string_query);
413
+
414
+ rb_scan_args(argc, argv, "11", &rb_query, &options);
415
+
416
+ if (CBOOL2RVAL(rb_obj_is_kind_of(rb_query, rb_cGrnQuery))) {
417
+ grn_query *_query;
418
+ _query = RVAL2GRNQUERY(rb_query);
419
+ query = (grn_obj *)_query;
420
+ } else if (CBOOL2RVAL(rb_obj_is_kind_of(rb_query, rb_cInteger))) {
421
+ grn_id id;
422
+ id = NUM2UINT(rb_query);
423
+ GRN_TEXT_SET(context, id_query, &id, sizeof(grn_id));
424
+ query = id_query;
425
+ } else {
426
+ const char *_query;
427
+ _query = StringValuePtr(rb_query);
428
+ GRN_TEXT_SET(context, string_query, _query, RSTRING_LEN(rb_query));
429
+ query = string_query;
430
+ }
431
+
432
+ rb_grn_scan_options(options,
433
+ "result", &rb_result,
434
+ "operator", &rb_operator,
435
+ NULL);
436
+
437
+ if (NIL_P(rb_result)) {
438
+ result = grn_table_create(context, NULL, 0, NULL,
439
+ GRN_OBJ_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC,
440
+ range, 0);
441
+ rb_grn_context_check(context, self);
442
+ rb_result = GRNOBJECT2RVAL(Qnil, context, result, RB_GRN_TRUE);
443
+ } else {
444
+ result = RVAL2GRNOBJECT(rb_result, &context);
445
+ }
446
+
447
+ operator = RVAL2GRNSELECTOPERATOR(rb_operator);
448
+
449
+ rc = grn_obj_search(context, column, query, result, operator, NULL);
450
+ rb_grn_rc_check(rc, self);
451
+
452
+ return rb_result;
453
+ }
454
+
455
+ void
456
+ rb_grn_init_index_column (VALUE mGrn)
457
+ {
458
+ rb_cGrnIndexColumn =
459
+ rb_define_class_under(mGrn, "IndexColumn", rb_cGrnColumn);
460
+ rb_define_alloc_func(rb_cGrnIndexColumn, rb_grn_index_column_alloc);
461
+
462
+ rb_define_method(rb_cGrnIndexColumn, "[]=",
463
+ rb_grn_index_column_array_set, 2);
464
+
465
+ rb_define_method(rb_cGrnIndexColumn, "sources",
466
+ rb_grn_index_column_get_sources, 0);
467
+ rb_define_method(rb_cGrnIndexColumn, "sources=",
468
+ rb_grn_index_column_set_sources, 1);
469
+ rb_define_method(rb_cGrnIndexColumn, "source=",
470
+ rb_grn_index_column_set_source, 1);
471
+
472
+ rb_define_method(rb_cGrnIndexColumn, "search",
473
+ rb_grn_index_column_search, -1);
474
+ }