groonga 0.0.7 → 0.9.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.
Files changed (102) hide show
  1. data/NEWS.ja.rdoc +56 -0
  2. data/NEWS.rdoc +58 -0
  3. data/Rakefile +2 -3
  4. data/benchmark/read-write-many-small-items.rb +16 -32
  5. data/benchmark/write-many-small-items.rb +14 -28
  6. data/example/bookmark.rb +19 -17
  7. data/example/index-html.rb +11 -1
  8. data/example/search/config.ru +14 -9
  9. data/ext/rb-grn-array.c +6 -6
  10. data/ext/rb-grn-column.c +348 -18
  11. data/ext/rb-grn-context.c +8 -4
  12. data/ext/rb-grn-database.c +6 -7
  13. data/ext/rb-grn-exception.c +101 -5
  14. data/ext/rb-grn-expression.c +206 -23
  15. data/ext/rb-grn-fix-size-column.c +6 -39
  16. data/ext/rb-grn-hash.c +24 -24
  17. data/ext/rb-grn-index-column.c +74 -19
  18. data/ext/rb-grn-logger.c +48 -0
  19. data/ext/rb-grn-object.c +281 -67
  20. data/ext/rb-grn-operation.c +1 -1
  21. data/ext/rb-grn-patricia-trie-cursor.c +10 -1
  22. data/ext/rb-grn-patricia-trie.c +268 -7
  23. data/ext/rb-grn-query.c +52 -1
  24. data/ext/rb-grn-record.c +8 -2
  25. data/ext/rb-grn-snippet.c +63 -1
  26. data/ext/rb-grn-table-cursor-key-support.c +15 -1
  27. data/ext/rb-grn-table-cursor.c +57 -0
  28. data/ext/rb-grn-table-key-support.c +382 -46
  29. data/ext/rb-grn-table.c +729 -192
  30. data/ext/rb-grn-type.c +63 -12
  31. data/ext/rb-grn-utils.c +156 -158
  32. data/ext/rb-grn-variable.c +18 -0
  33. data/ext/rb-grn.h +85 -21
  34. data/ext/rb-groonga.c +13 -3
  35. data/extconf.rb +19 -4
  36. data/html/developer.html +1 -1
  37. data/html/header.html.erb +1 -1
  38. data/html/index.html +4 -4
  39. data/lib/groonga.rb +10 -0
  40. data/lib/groonga/expression-builder.rb +81 -42
  41. data/lib/groonga/patricia-trie.rb +13 -0
  42. data/lib/groonga/record.rb +158 -13
  43. data/lib/groonga/schema.rb +339 -33
  44. data/pkg-config.rb +6 -1
  45. data/test-unit/lib/test/unit.rb +23 -42
  46. data/test-unit/lib/test/unit/assertionfailederror.rb +11 -0
  47. data/test-unit/lib/test/unit/assertions.rb +87 -9
  48. data/test-unit/lib/test/unit/autorunner.rb +20 -11
  49. data/test-unit/lib/test/unit/collector.rb +1 -8
  50. data/test-unit/lib/test/unit/collector/load.rb +2 -3
  51. data/test-unit/lib/test/unit/color-scheme.rb +13 -1
  52. data/test-unit/lib/test/unit/diff.rb +223 -37
  53. data/test-unit/lib/test/unit/error.rb +4 -0
  54. data/test-unit/lib/test/unit/failure.rb +31 -5
  55. data/test-unit/lib/test/unit/notification.rb +8 -4
  56. data/test-unit/lib/test/unit/omission.rb +51 -3
  57. data/test-unit/lib/test/unit/pending.rb +4 -0
  58. data/test-unit/lib/test/unit/testcase.rb +55 -4
  59. data/test-unit/lib/test/unit/ui/console/testrunner.rb +190 -4
  60. data/test-unit/lib/test/unit/ui/emacs/testrunner.rb +14 -0
  61. data/test-unit/lib/test/unit/ui/testrunner.rb +8 -0
  62. data/test-unit/lib/test/unit/version.rb +1 -1
  63. data/test-unit/sample/{tc_adder.rb → test_adder.rb} +3 -1
  64. data/test-unit/sample/{tc_subtracter.rb → test_subtracter.rb} +3 -1
  65. data/test-unit/sample/test_user.rb +1 -0
  66. data/test-unit/test/collector/test-descendant.rb +2 -4
  67. data/test-unit/test/collector/test_objectspace.rb +7 -5
  68. data/test-unit/test/run-test.rb +2 -0
  69. data/test-unit/test/test-color-scheme.rb +7 -0
  70. data/test-unit/test/test-diff.rb +48 -7
  71. data/test-unit/test/test-omission.rb +1 -1
  72. data/test-unit/test/test-testcase.rb +47 -0
  73. data/test-unit/test/test_assertions.rb +79 -10
  74. data/test/groonga-test-utils.rb +6 -1
  75. data/test/test-array.rb +29 -14
  76. data/test/test-column.rb +107 -55
  77. data/test/test-context.rb +5 -0
  78. data/test/test-database.rb +2 -37
  79. data/test/test-exception.rb +9 -1
  80. data/test/test-expression-builder.rb +23 -5
  81. data/test/test-expression.rb +44 -8
  82. data/test/test-fix-size-column.rb +16 -5
  83. data/test/test-gqtp.rb +70 -0
  84. data/test/test-hash.rb +142 -43
  85. data/test/test-index-column.rb +9 -9
  86. data/test/test-patricia-trie.rb +79 -20
  87. data/test/test-procedure.rb +4 -2
  88. data/test/test-record.rb +32 -20
  89. data/test/test-remote.rb +3 -2
  90. data/test/test-schema.rb +226 -92
  91. data/test/test-table-cursor.rb +103 -1
  92. data/test/test-table-offset-and-limit.rb +102 -0
  93. data/test/test-table-select-normalize.rb +4 -4
  94. data/test/test-table-select.rb +52 -8
  95. data/test/test-table.rb +235 -116
  96. data/test/test-type.rb +2 -2
  97. data/test/test-variable-size-column.rb +21 -5
  98. data/test/test-vector-column.rb +76 -0
  99. data/{TUTORIAL.ja.rdoc → text/TUTORIAL.ja.rdoc} +52 -52
  100. data/text/expression.rdoc +284 -0
  101. metadata +11 -7
  102. data/test-unit/sample/ts_examples.rb +0 -7
@@ -1,6 +1,6 @@
1
1
  /* -*- c-file-style: "ruby" -*- */
2
2
  /*
3
- Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
3
+ Copyright (C) 2009-2010 Kouhei Sutou <kou@clear-code.com>
4
4
 
5
5
  This library is free software; you can redistribute it and/or
6
6
  modify it under the terms of the GNU Lesser General Public
@@ -22,6 +22,17 @@
22
22
 
23
23
  VALUE rb_cGrnTable;
24
24
 
25
+ static ID id_array_reference;
26
+ static ID id_array_set;
27
+
28
+ /*
29
+ * Document-class: Groonga::Table < Groonga::Object
30
+ *
31
+ * Ruby/groongaが提供するテーブルのベースとなるクラス。このクラス
32
+ * からGroonga::Array, Groonga::Hash, Groonga::PatriciaTrie
33
+ * が継承されている。
34
+ */
35
+
25
36
  grn_obj *
26
37
  rb_grn_table_from_ruby_object (VALUE object, grn_ctx **context)
27
38
  {
@@ -46,6 +57,7 @@ rb_grn_table_finalizer (grn_ctx *context, grn_obj *object,
46
57
  if (context && rb_grn_table->value)
47
58
  grn_obj_close(context, rb_grn_table->value);
48
59
  rb_grn_table->value = NULL;
60
+ rb_grn_table->columns = Qnil;
49
61
  }
50
62
 
51
63
  void
@@ -57,6 +69,7 @@ rb_grn_table_bind (RbGrnTable *rb_grn_table,
57
69
  rb_grn_object = RB_GRN_OBJECT(rb_grn_table);
58
70
  rb_grn_table->value = grn_obj_open(context, GRN_BULK, 0,
59
71
  rb_grn_object->range_id);
72
+ rb_grn_table->columns = rb_ary_new();
60
73
  }
61
74
 
62
75
  void
@@ -67,7 +80,8 @@ rb_grn_table_deconstruct (RbGrnTable *rb_grn_table,
67
80
  grn_obj **domain,
68
81
  grn_obj **value,
69
82
  grn_id *range_id,
70
- grn_obj **range)
83
+ grn_obj **range,
84
+ VALUE *columns)
71
85
  {
72
86
  RbGrnObject *rb_grn_object;
73
87
 
@@ -78,17 +92,20 @@ rb_grn_table_deconstruct (RbGrnTable *rb_grn_table,
78
92
 
79
93
  if (value)
80
94
  *value = rb_grn_table->value;
95
+ if (columns)
96
+ *columns = rb_grn_table->columns;
81
97
  }
82
98
 
83
99
  static void
84
100
  rb_grn_table_mark (void *data)
85
101
  {
86
102
  RbGrnObject *rb_grn_object = data;
103
+ RbGrnTable *rb_grn_table = data;
87
104
  grn_ctx *context;
88
105
  grn_obj *table;
89
- grn_obj *column_ids;
90
- int n;
91
- grn_table_cursor *cursor;
106
+
107
+ if (!rb_grn_object)
108
+ return;
92
109
 
93
110
  context = rb_grn_object->context;
94
111
  table = rb_grn_object->object;
@@ -101,31 +118,7 @@ rb_grn_table_mark (void *data)
101
118
  if (grn_obj_name(context, table, NULL, 0) == 0)
102
119
  return;
103
120
 
104
- column_ids = grn_table_create(context, NULL, 0, NULL,
105
- GRN_TABLE_HASH_KEY, NULL, 0);
106
- n = grn_table_columns(context, table, NULL, 0, column_ids);
107
- if (n == 0) {
108
- grn_obj_close(context, column_ids);
109
- return;
110
- }
111
-
112
- cursor = grn_table_cursor_open(context, column_ids, NULL, 0, NULL, 0,
113
- 0, 0, GRN_CURSOR_ASCENDING);
114
- while (grn_table_cursor_next(context, cursor) != GRN_ID_NIL) {
115
- void *key;
116
- grn_id *column_id;
117
- grn_obj *column;
118
- RbGrnObject *rb_grn_column;
119
-
120
- grn_table_cursor_get_key(context, cursor, &key);
121
- column_id = key;
122
- column = grn_ctx_at(context, *column_id);
123
- rb_grn_column = grn_obj_user_data(context, column)->ptr;
124
- if (rb_grn_column)
125
- rb_gc_mark(rb_grn_column->self);
126
- }
127
- grn_table_cursor_close(context, cursor);
128
- grn_obj_close(context, column_ids);
121
+ rb_gc_mark(rb_grn_table->columns);
129
122
  }
130
123
 
131
124
  static VALUE
@@ -134,76 +127,6 @@ rb_grn_table_alloc (VALUE klass)
134
127
  return Data_Wrap_Struct(klass, rb_grn_table_mark, rb_grn_object_free, NULL);
135
128
  }
136
129
 
137
- VALUE
138
- rb_grn_table_s_create (int argc, VALUE *argv, VALUE klass,
139
- grn_obj_flags key_store)
140
- {
141
- grn_ctx *context;
142
- grn_obj *key_type = NULL, *value_type = NULL, *table;
143
- const char *name = NULL, *path = NULL;
144
- unsigned name_size = 0;
145
- grn_obj_flags flags = key_store;
146
- VALUE rb_table;
147
- VALUE options, rb_context, rb_name, rb_path, rb_persistent;
148
- VALUE rb_key_normalize, rb_key_with_sis, rb_key_type;
149
- VALUE rb_value_type;
150
-
151
- rb_scan_args(argc, argv, "01", &options);
152
-
153
- rb_grn_scan_options(options,
154
- "context", &rb_context,
155
- "name", &rb_name,
156
- "path", &rb_path,
157
- "persistent", &rb_persistent,
158
- "key_normalize", &rb_key_normalize,
159
- "key_with_sis", &rb_key_with_sis,
160
- "key_type", &rb_key_type,
161
- "value_type", &rb_value_type,
162
- NULL);
163
-
164
- context = rb_grn_context_ensure(&rb_context);
165
-
166
- if (!NIL_P(rb_name)) {
167
- name = StringValuePtr(rb_name);
168
- name_size = RSTRING_LEN(rb_name);
169
- flags |= GRN_OBJ_PERSISTENT;
170
- }
171
-
172
- if (!NIL_P(rb_path)) {
173
- path = StringValueCStr(rb_path);
174
- flags |= GRN_OBJ_PERSISTENT;
175
- }
176
-
177
- if (RVAL2CBOOL(rb_persistent))
178
- flags |= GRN_OBJ_PERSISTENT;
179
-
180
- if (RVAL2CBOOL(rb_key_normalize))
181
- flags |= GRN_OBJ_KEY_NORMALIZE;
182
-
183
- if (RVAL2CBOOL(rb_key_with_sis))
184
- flags |= GRN_OBJ_KEY_WITH_SIS;
185
-
186
- if (NIL_P(rb_key_type)) {
187
- flags |= GRN_OBJ_KEY_VAR_SIZE;
188
- } else {
189
- key_type = RVAL2GRNOBJECT(rb_key_type, &context);
190
- }
191
-
192
- if (!NIL_P(rb_value_type))
193
- value_type = RVAL2GRNOBJECT(rb_value_type, &context);
194
-
195
- table = grn_table_create(context, name, name_size, path,
196
- flags, key_type, value_type);
197
- rb_table = rb_grn_object_alloc(klass);
198
- rb_grn_table_assign(rb_table, rb_context, context, table, RB_GRN_TRUE);
199
- rb_grn_context_check(context, rb_table);
200
-
201
- if (rb_block_given_p())
202
- return rb_ensure(rb_yield, rb_table, rb_grn_object_close, rb_table);
203
- else
204
- return rb_table;
205
- }
206
-
207
130
  grn_obj *
208
131
  rb_grn_table_open_raw (int argc, VALUE *argv,
209
132
  grn_ctx **context, VALUE *rb_context)
@@ -249,6 +172,27 @@ rb_grn_table_initialize (int argc, VALUE *argv, VALUE self)
249
172
  return Qnil;
250
173
  }
251
174
 
175
+ /*
176
+ * call-seq:
177
+ * Groonga::Table.open(options={}) -> Groonga::Table
178
+ * Groonga::Table.open(options={}) {|table| ... }
179
+ *
180
+ * 既存のテーブルを開く。ブロックを指定すると、そのブロック
181
+ * に開かれたテーブルが渡され、ブロックを抜けると自動的にテ
182
+ * ーブルが破棄される。
183
+ *
184
+ * _options_に指定可能な値は以下の通り。
185
+ *
186
+ * [+:context+]
187
+ * テーブルが利用するGroonga::Context。省略すると
188
+ * Groonga::Context.defaultを用いる。
189
+ *
190
+ * [+:name+]
191
+ * 開こうとするテーブルの名前。
192
+ *
193
+ * [+:path+]
194
+ * 開こうとするテーブルのパス。
195
+ */
252
196
  static VALUE
253
197
  rb_grn_table_s_open (int argc, VALUE *argv, VALUE klass)
254
198
  {
@@ -285,8 +229,7 @@ rb_grn_table_s_open (int argc, VALUE *argv, VALUE klass)
285
229
  }
286
230
  }
287
231
 
288
- rb_table = rb_grn_object_alloc(klass);
289
- rb_grn_object_assign(klass, rb_table, rb_context, context, table);
232
+ rb_table = GRNOBJECT2RVAL(klass, context, table, RB_GRN_TRUE);
290
233
  }
291
234
 
292
235
  if (rb_block_given_p())
@@ -298,12 +241,19 @@ rb_grn_table_s_open (int argc, VALUE *argv, VALUE klass)
298
241
  static VALUE
299
242
  rb_grn_table_inspect_content (VALUE self, VALUE inspected)
300
243
  {
244
+ RbGrnTable *rb_grn_table;
301
245
  grn_ctx *context = NULL;
302
246
  grn_obj *table;
247
+ VALUE columns;
303
248
 
304
- rb_grn_table_deconstruct(SELF(self), &table, &context,
249
+ rb_grn_table = SELF(self);
250
+ if (!rb_grn_table)
251
+ return inspected;
252
+
253
+ rb_grn_table_deconstruct(rb_grn_table, &table, &context,
305
254
  NULL, NULL,
306
- NULL, NULL, NULL);
255
+ NULL, NULL, NULL,
256
+ &columns);
307
257
 
308
258
  if (!table)
309
259
  return inspected;
@@ -338,9 +288,22 @@ rb_grn_table_inspect_content (VALUE self, VALUE inspected)
338
288
  }
339
289
  rb_str_cat2(inspected, ">");
340
290
 
291
+ /*
292
+ rb_str_cat2(inspected, ", ");
293
+ rb_str_cat2(inspected, "columns: <");
294
+ rb_str_concat(inspected, rb_inspect(columns));
295
+ rb_str_cat2(inspected, ">");
296
+ */
297
+
341
298
  return inspected;
342
299
  }
343
300
 
301
+ /*
302
+ * call-seq:
303
+ * _table_.inspect -> String
304
+ *
305
+ * テーブルの中身を人に見やすい文字列で返す。
306
+ */
344
307
  static VALUE
345
308
  rb_grn_table_inspect (VALUE self)
346
309
  {
@@ -355,6 +318,43 @@ rb_grn_table_inspect (VALUE self)
355
318
  return inspected;
356
319
  }
357
320
 
321
+ /*
322
+ * call-seq:
323
+ * table.define_column(name, value_type, options={}) ->
324
+ * Groonga::FixSizeColumnかGroonga::VariableSizeColumn
325
+ *
326
+ * テーブルに名前が_name_で型が_value_type_のカラムを定義
327
+ * し、新しく定義されたカラムを返す。
328
+ *
329
+ * _options_に指定可能な値は以下の通り。
330
+ *
331
+ * [+:path+]
332
+ * カラムを保存するパス。
333
+ *
334
+ * [+:persistent+]
335
+ * +true+を指定すると永続カラムとなる。省略した場合は永
336
+ * 続カラムとなる。+:path+を省略した場合は自動的にパスが
337
+ * 付加される。
338
+ *
339
+ * [+:type+]
340
+ * カラムの値の格納方法について指定する。省略した場合は、
341
+ * +:scalar+になる。
342
+ *
343
+ * [+:scalar+]
344
+ * スカラ値(単独の値)を格納する。
345
+ *
346
+ * [+:vector+]
347
+ * 値の配列を格納する。
348
+ *
349
+ * [+:compress+]
350
+ * 値の圧縮方法を指定する。省略した場合は、圧縮しない。
351
+ *
352
+ * [+:zlib+]
353
+ * 値をzlib圧縮して格納する。
354
+ *
355
+ * [+:lzo+]
356
+ * 値をlzo圧縮して格納する。
357
+ */
358
358
  static VALUE
359
359
  rb_grn_table_define_column (int argc, VALUE *argv, VALUE self)
360
360
  {
@@ -366,10 +366,13 @@ rb_grn_table_define_column (int argc, VALUE *argv, VALUE self)
366
366
  grn_obj_flags flags = 0;
367
367
  VALUE rb_name, rb_value_type;
368
368
  VALUE options, rb_path, rb_persistent, rb_compress, rb_type;
369
+ VALUE columns;
370
+ VALUE rb_column;
369
371
 
370
372
  rb_grn_table_deconstruct(SELF(self), &table, &context,
371
373
  NULL, NULL,
372
- NULL, NULL, NULL);
374
+ NULL, NULL, NULL,
375
+ &columns);
373
376
 
374
377
  rb_scan_args(argc, argv, "21", &rb_name, &rb_value_type, &options);
375
378
 
@@ -385,13 +388,20 @@ rb_grn_table_define_column (int argc, VALUE *argv, VALUE self)
385
388
 
386
389
  value_type = RVAL2GRNOBJECT(rb_value_type, &context);
387
390
 
388
- if (!NIL_P(rb_path)) {
389
- path = StringValueCStr(rb_path);
391
+ if ((NIL_P(rb_persistent) && grn_obj_path(context, table)) ||
392
+ RVAL2CBOOL(rb_persistent)) {
390
393
  flags |= GRN_OBJ_PERSISTENT;
391
394
  }
392
395
 
393
- if (RVAL2CBOOL(rb_persistent))
396
+ if (!NIL_P(rb_path)) {
397
+ path = StringValueCStr(rb_path);
398
+ if ((flags & GRN_OBJ_PERSISTENT) != GRN_OBJ_PERSISTENT) {
399
+ rb_raise(rb_eArgError,
400
+ "should not pass :path if :persistent is false: <%s>",
401
+ path);
402
+ }
394
403
  flags |= GRN_OBJ_PERSISTENT;
404
+ }
395
405
 
396
406
  if (NIL_P(rb_type) ||
397
407
  (rb_grn_equal_option(rb_type, "scalar"))) {
@@ -421,9 +431,46 @@ rb_grn_table_define_column (int argc, VALUE *argv, VALUE self)
421
431
  path, flags, value_type);
422
432
  rb_grn_context_check(context, self);
423
433
 
424
- return GRNCOLUMN2RVAL(Qnil, context, column, RB_GRN_TRUE);
434
+ rb_column = GRNCOLUMN2RVAL(Qnil, context, column, RB_GRN_TRUE);
435
+ rb_ary_push(columns, rb_column);
436
+ rb_grn_named_object_set_name(RB_GRN_NAMED_OBJECT(DATA_PTR(rb_column)),
437
+ name, name_size);
438
+
439
+ return rb_column;
425
440
  }
426
441
 
442
+ /*
443
+ * call-seq:
444
+ * table.define_index_column(name, value_type, options={}) -> Groonga::IndexColumn
445
+ *
446
+ * テーブルに名前が_name_で型が_value_type_のインデックスカ
447
+ * ラムを定義し、新しく定義されたカラムを返す。
448
+ *
449
+ * _options_に指定可能な値は以下の通り。
450
+ *
451
+ * [+:path+]
452
+ * カラムを保存するパス。
453
+ *
454
+ * [+:persistent+]
455
+ * +true+を指定すると永続カラムとなる。省略した場合は永
456
+ * 続カラムとなる。+:path+を省略した場合は自動的にパスが
457
+ * 付加される。
458
+ *
459
+ * [+:with_section+]
460
+ * 転置索引にsection(段落情報)を合わせて格納する。
461
+ *
462
+ * [+:with_weight+]
463
+ * 転置索引にweight情報を合わせて格納する。
464
+ *
465
+ * [+:with_position+]
466
+ * 転置索引に出現位置情報を合わせて格納する。
467
+ *
468
+ * [+:source+]
469
+ * インデックス対象となるカラムを指定する。+:sources+との併用はできない。
470
+ *
471
+ * [+:sources+]
472
+ * インデックス対象となる複数のカラムを指定する。+:source+との併用はできない。
473
+ */
427
474
  static VALUE
428
475
  rb_grn_table_define_index_column (int argc, VALUE *argv, VALUE self)
429
476
  {
@@ -437,10 +484,12 @@ rb_grn_table_define_index_column (int argc, VALUE *argv, VALUE self)
437
484
  VALUE options, rb_path, rb_persistent;
438
485
  VALUE rb_with_section, rb_with_weight, rb_with_position;
439
486
  VALUE rb_column, rb_source, rb_sources;
487
+ VALUE columns;
440
488
 
441
489
  rb_grn_table_deconstruct(SELF(self), &table, &context,
442
490
  NULL, NULL,
443
- NULL, NULL, NULL);
491
+ NULL, NULL, NULL,
492
+ &columns);
444
493
 
445
494
  rb_scan_args(argc, argv, "21", &rb_name, &rb_value_type, &options);
446
495
 
@@ -459,13 +508,20 @@ rb_grn_table_define_index_column (int argc, VALUE *argv, VALUE self)
459
508
 
460
509
  value_type = RVAL2GRNOBJECT(rb_value_type, &context);
461
510
 
462
- if (!NIL_P(rb_path)) {
463
- path = StringValueCStr(rb_path);
511
+ if ((NIL_P(rb_persistent) && grn_obj_path(context, table)) ||
512
+ RVAL2CBOOL(rb_persistent)) {
464
513
  flags |= GRN_OBJ_PERSISTENT;
465
514
  }
466
515
 
467
- if (RVAL2CBOOL(rb_persistent))
516
+ if (!NIL_P(rb_path)) {
517
+ path = StringValueCStr(rb_path);
518
+ if ((flags & GRN_OBJ_PERSISTENT) != GRN_OBJ_PERSISTENT) {
519
+ rb_raise(rb_eArgError,
520
+ "should not pass :path if :persistent is false: <%s>",
521
+ path);
522
+ }
468
523
  flags |= GRN_OBJ_PERSISTENT;
524
+ }
469
525
 
470
526
  if (RVAL2CBOOL(rb_with_section))
471
527
  flags |= GRN_OBJ_WITH_SECTION;
@@ -505,9 +561,20 @@ rb_grn_table_define_index_column (int argc, VALUE *argv, VALUE self)
505
561
  if (!NIL_P(rb_sources))
506
562
  rb_funcall(rb_column, rb_intern("sources="), 1, rb_sources);
507
563
 
564
+ rb_ary_push(columns, rb_column);
565
+ rb_grn_named_object_set_name(RB_GRN_NAMED_OBJECT(DATA_PTR(rb_column)),
566
+ name, name_size);
567
+
508
568
  return rb_column;
509
569
  }
510
570
 
571
+ /*
572
+ * call-seq:
573
+ * table.add_column(name, value_type, path)
574
+ *
575
+ * _value_type_を値の型として、_path_に保存されている永続的
576
+ * なカラムを、テーブルの_name_に対応するカラムとして開く。
577
+ */
511
578
  static VALUE
512
579
  rb_grn_table_add_column (VALUE self, VALUE rb_name, VALUE rb_value_type,
513
580
  VALUE rb_path)
@@ -518,10 +585,12 @@ rb_grn_table_add_column (VALUE self, VALUE rb_name, VALUE rb_value_type,
518
585
  char *name = NULL, *path = NULL;
519
586
  unsigned name_size = 0;
520
587
  VALUE rb_column;
588
+ VALUE columns;
521
589
 
522
590
  rb_grn_table_deconstruct(SELF(self), &table, &context,
523
591
  NULL, NULL,
524
- NULL, NULL, NULL);
592
+ NULL, NULL, NULL,
593
+ &columns);
525
594
 
526
595
  name = StringValuePtr(rb_name);
527
596
  name_size = RSTRING_LEN(rb_name);
@@ -536,29 +605,48 @@ rb_grn_table_add_column (VALUE self, VALUE rb_name, VALUE rb_value_type,
536
605
 
537
606
  rb_column = GRNCOLUMN2RVAL(Qnil, context, column, RB_GRN_TRUE);
538
607
  rb_iv_set(rb_column, "table", self);
608
+ rb_ary_push(columns, rb_column);
609
+ rb_grn_named_object_set_name(RB_GRN_NAMED_OBJECT(DATA_PTR(rb_column)),
610
+ name, name_size);
611
+
539
612
  return rb_column;
540
613
  }
541
614
 
542
- static VALUE
615
+ /*
616
+ * call-seq:
617
+ * table.column(name) -> Groonga::Column or nil
618
+ *
619
+ * テーブルの_name_に対応するカラムを返す。カラムが存在しな
620
+ * い場合は+nil+を返す。
621
+ */
622
+ VALUE
543
623
  rb_grn_table_get_column (VALUE self, VALUE rb_name)
544
624
  {
625
+ grn_user_data *user_data;
545
626
  grn_ctx *context = NULL;
546
627
  grn_obj *table;
547
628
  grn_obj *column;
548
- char *name = NULL;
629
+ const char *name = NULL;
549
630
  unsigned name_size = 0;
550
631
  rb_grn_boolean owner;
551
632
  VALUE rb_column;
633
+ VALUE columns;
634
+ VALUE *raw_columns;
635
+ long i, n;
552
636
 
553
637
  rb_grn_table_deconstruct(SELF(self), &table, &context,
554
638
  NULL, NULL,
555
- NULL, NULL, NULL);
639
+ NULL, NULL, NULL,
640
+ &columns);
556
641
 
557
642
  switch (TYPE(rb_name)) {
558
643
  case T_SYMBOL:
559
- rb_name = rb_str_new2(rb_id2name(SYM2ID(rb_name)));
644
+ name = rb_id2name(SYM2ID(rb_name));
645
+ name_size = strlen(name);
560
646
  break;
561
647
  case T_STRING:
648
+ name = StringValuePtr(rb_name);
649
+ name_size = RSTRING_LEN(rb_name);
562
650
  break;
563
651
  default:
564
652
  rb_raise(rb_eArgError,
@@ -566,19 +654,66 @@ rb_grn_table_get_column (VALUE self, VALUE rb_name)
566
654
  rb_grn_inspect(rb_name));
567
655
  break;
568
656
  }
569
- name = StringValuePtr(rb_name);
570
- name_size = RSTRING_LEN(rb_name);
657
+
658
+ raw_columns = RARRAY_PTR(columns);
659
+ n = RARRAY_LEN(columns);
660
+ for (i = 0; i < n; i++) {
661
+ VALUE rb_column = raw_columns[i];
662
+ RbGrnNamedObject *rb_grn_named_object;
663
+
664
+ rb_grn_named_object = RB_GRN_NAMED_OBJECT(DATA_PTR(rb_column));
665
+ if (rb_grn_named_object->name_size > 0 &&
666
+ strncmp(name, rb_grn_named_object->name, name_size) == 0) {
667
+ return rb_column;
668
+ }
669
+ }
571
670
 
572
671
  column = grn_obj_column(context, table, name, name_size);
573
672
  rb_grn_context_check(context, self);
673
+ if (!column)
674
+ return Qnil;
574
675
 
575
- owner = (column && column->header.type == GRN_ACCESSOR);
676
+ user_data = grn_obj_user_data(context, column);
677
+ if (user_data) {
678
+ RbGrnObject *rb_grn_object;
679
+ rb_grn_object = user_data->ptr;
680
+ if (rb_grn_object) {
681
+ rb_ary_push(columns, rb_grn_object->self);
682
+ return rb_grn_object->self;
683
+ }
684
+ }
685
+
686
+ owner = column->header.type == GRN_ACCESSOR;
576
687
  rb_column = GRNCOLUMN2RVAL(Qnil, context, column, owner);
577
- if (owner)
688
+ if (owner) {
578
689
  rb_iv_set(rb_column, "table", self);
690
+ }
691
+ rb_ary_push(columns, rb_column);
692
+
579
693
  return rb_column;
580
694
  }
581
695
 
696
+ VALUE
697
+ rb_grn_table_get_column_surely (VALUE self, VALUE rb_name)
698
+ {
699
+ VALUE rb_column;
700
+
701
+ rb_column = rb_grn_table_get_column(self, rb_name);
702
+ if (NIL_P(rb_column)) {
703
+ rb_raise(rb_eGrnNoSuchColumn,
704
+ "no such column: <%s>: <%s>",
705
+ rb_grn_inspect(rb_name), rb_grn_inspect(self));
706
+ }
707
+ return rb_column;
708
+ }
709
+
710
+ /*
711
+ * call-seq:
712
+ * table.columns(name=nil) -> Groonga::Columnの配列
713
+ *
714
+ * テーブルの全てのカラムを返す。_name_が指定された場合はカ
715
+ * ラム名の先頭が_name_で始まるカラムを返す。
716
+ */
582
717
  static VALUE
583
718
  rb_grn_table_get_columns (int argc, VALUE *argv, VALUE self)
584
719
  {
@@ -594,7 +729,8 @@ rb_grn_table_get_columns (int argc, VALUE *argv, VALUE self)
594
729
 
595
730
  rb_grn_table_deconstruct(SELF(self), &table, &context,
596
731
  NULL, NULL,
597
- NULL, NULL, NULL);
732
+ NULL, NULL, NULL,
733
+ NULL);
598
734
 
599
735
  rb_scan_args(argc, argv, "01", &rb_name);
600
736
 
@@ -613,7 +749,7 @@ rb_grn_table_get_columns (int argc, VALUE *argv, VALUE self)
613
749
  return rb_columns;
614
750
 
615
751
  cursor = grn_table_cursor_open(context, columns, NULL, 0, NULL, 0,
616
- 0, 0, GRN_CURSOR_ASCENDING);
752
+ 0, -1, GRN_CURSOR_ASCENDING);
617
753
  rb_grn_context_check(context, self);
618
754
  while (grn_table_cursor_next(context, cursor) != GRN_ID_NIL) {
619
755
  void *key;
@@ -644,18 +780,23 @@ rb_grn_table_open_grn_cursor (int argc, VALUE *argv, VALUE self,
644
780
  grn_table_cursor *cursor;
645
781
  void *min_key = NULL, *max_key = NULL;
646
782
  unsigned min_key_size = 0, max_key_size = 0;
783
+ int offset = 0, limit = -1;
647
784
  int flags = 0;
648
785
  VALUE options, rb_min, rb_max, rb_order, rb_greater_than, rb_less_than;
786
+ VALUE rb_offset, rb_limit;
649
787
 
650
788
  rb_grn_table_deconstruct(SELF(self), &table, context,
651
789
  NULL, NULL,
652
- NULL, NULL, NULL);
790
+ NULL, NULL, NULL,
791
+ NULL);
653
792
 
654
793
  rb_scan_args(argc, argv, "01", &options);
655
794
 
656
795
  rb_grn_scan_options(options,
657
796
  "min", &rb_min,
658
797
  "max", &rb_max,
798
+ "offset", &rb_offset,
799
+ "limit", &rb_limit,
659
800
  "order", &rb_order,
660
801
  "greater_than", &rb_greater_than,
661
802
  "less_than", &rb_less_than,
@@ -669,6 +810,10 @@ rb_grn_table_open_grn_cursor (int argc, VALUE *argv, VALUE self,
669
810
  max_key = StringValuePtr(rb_max);
670
811
  max_key_size = RSTRING_LEN(rb_max);
671
812
  }
813
+ if (!NIL_P(rb_offset))
814
+ offset = NUM2INT(rb_offset);
815
+ if (!NIL_P(rb_limit))
816
+ limit = NUM2INT(rb_limit);
672
817
 
673
818
  if (NIL_P(rb_order)) {
674
819
  } else if (rb_grn_equal_option(rb_order, "asc") ||
@@ -689,16 +834,55 @@ rb_grn_table_open_grn_cursor (int argc, VALUE *argv, VALUE self,
689
834
  if (RVAL2CBOOL(rb_less_than))
690
835
  flags |= GRN_CURSOR_LT;
691
836
 
692
- /* FIXME: should support offset and limit */
693
837
  cursor = grn_table_cursor_open(*context, table,
694
838
  min_key, min_key_size,
695
839
  max_key, max_key_size,
696
- 0, 0, flags);
840
+ offset, limit, flags);
697
841
  rb_grn_context_check(*context, self);
698
842
 
699
843
  return cursor;
700
844
  }
701
845
 
846
+ /*
847
+ * call-seq:
848
+ * table.open_cursor(options={}) -> Groonga::TableCursor
849
+ * table.open_cursor(options={}) {|cursor| ... }
850
+ *
851
+ * カーソルを生成して返す。ブロックを指定すると、そのブロッ
852
+ * クに生成したカーソルが渡され、ブロックを抜けると自動的に
853
+ * カーソルが破棄される。
854
+ *
855
+ * _options_に指定可能な値は以下の通り。
856
+ *
857
+ * [+:min+]
858
+ * キーの下限
859
+ *
860
+ * [+:max+]
861
+ * キーの上限
862
+ *
863
+ * [+:offset+]
864
+ * 該当する範囲のレコードのうち、(0ベースで)_:offset_番目
865
+ * からレコードを取り出す。
866
+ *
867
+ * [+:limit+]
868
+ * 該当する範囲のレコードのうち、_:limit_件のみを取り出す。
869
+ * 省略された場合または-1が指定された場合は、全件が指定され
870
+ * たものとみなす。
871
+ *
872
+ * [+:order+]
873
+ * +:asc+または+:ascending+を指定すると昇順にレコードを取
874
+ * り出す。
875
+ * +:desc+または+:descending+を指定すると降順にレコードを
876
+ * 取り出す。
877
+ *
878
+ * [+:greater_than+]
879
+ * +true+を指定すると+:min+で指定した値に一致した[+key+]を
880
+ * 範囲に含まない。
881
+ *
882
+ * [+:less_than+]
883
+ * +true+を指定すると+:max+で指定した値に一致した[+key+]を
884
+ * 範囲に含まない。
885
+ */
702
886
  static VALUE
703
887
  rb_grn_table_open_cursor (int argc, VALUE *argv, VALUE self)
704
888
  {
@@ -715,6 +899,13 @@ rb_grn_table_open_cursor (int argc, VALUE *argv, VALUE self)
715
899
  return rb_cursor;
716
900
  }
717
901
 
902
+ /*
903
+ * call-seq:
904
+ * table.records -> Groonga::Recordの配列
905
+ *
906
+ * テーブルに登録されている全てのレコードが入っている配列を
907
+ * 返す。
908
+ */
718
909
  static VALUE
719
910
  rb_grn_table_get_records (int argc, VALUE *argv, VALUE self)
720
911
  {
@@ -733,6 +924,12 @@ rb_grn_table_get_records (int argc, VALUE *argv, VALUE self)
733
924
  return records;
734
925
  }
735
926
 
927
+ /*
928
+ * call-seq:
929
+ * table.size -> レコード数
930
+ *
931
+ * テーブルに登録されているレコード数を返す。
932
+ */
736
933
  static VALUE
737
934
  rb_grn_table_get_size (VALUE self)
738
935
  {
@@ -742,11 +939,18 @@ rb_grn_table_get_size (VALUE self)
742
939
 
743
940
  rb_grn_table_deconstruct(SELF(self), &table, &context,
744
941
  NULL, NULL,
745
- NULL, NULL, NULL);
942
+ NULL, NULL, NULL,
943
+ NULL);
746
944
  size = grn_table_size(context, table);
747
945
  return UINT2NUM(size);
748
946
  }
749
947
 
948
+ /*
949
+ * call-seq:
950
+ * table.truncate
951
+ *
952
+ * テーブルの全レコードを一括して削除する。
953
+ */
750
954
  static VALUE
751
955
  rb_grn_table_truncate (VALUE self)
752
956
  {
@@ -756,29 +960,42 @@ rb_grn_table_truncate (VALUE self)
756
960
 
757
961
  rb_grn_table_deconstruct(SELF(self), &table, &context,
758
962
  NULL, NULL,
759
- NULL, NULL, NULL);
963
+ NULL, NULL, NULL,
964
+ NULL);
760
965
  rc = grn_table_truncate(context, table);
761
966
  rb_grn_rc_check(rc, self);
762
967
 
763
968
  return Qnil;
764
969
  }
765
970
 
971
+ /*
972
+ * call-seq:
973
+ * table.each {|record| ...}
974
+ *
975
+ * テーブルに登録されているレコードを順番にブロックに渡す。
976
+ */
766
977
  static VALUE
767
978
  rb_grn_table_each (VALUE self)
768
979
  {
980
+ RbGrnTable *rb_table;
981
+ RbGrnObject *rb_grn_object;
769
982
  grn_ctx *context = NULL;
770
983
  grn_obj *table;
771
984
  grn_table_cursor *cursor;
772
985
  VALUE rb_cursor;
773
986
  grn_id id;
774
987
 
775
- rb_grn_table_deconstruct(SELF(self), &table, &context,
988
+ rb_table = SELF(self);
989
+ rb_grn_table_deconstruct(rb_table, &table, &context,
776
990
  NULL, NULL,
777
- NULL, NULL, NULL);
991
+ NULL, NULL, NULL,
992
+ NULL);
778
993
  cursor = grn_table_cursor_open(context, table, NULL, 0, NULL, 0,
779
- 0, 0, GRN_CURSOR_ASCENDING);
994
+ 0, -1, GRN_CURSOR_ASCENDING);
780
995
  rb_cursor = GRNTABLECURSOR2RVAL(Qnil, context, cursor);
781
- while ((id = grn_table_cursor_next(context, cursor)) != GRN_ID_NIL) {
996
+ rb_grn_object = RB_GRN_OBJECT(rb_table);
997
+ while (rb_grn_object->object &&
998
+ (id = grn_table_cursor_next(context, cursor)) != GRN_ID_NIL) {
782
999
  rb_yield(rb_grn_record_new(self, id, Qnil));
783
1000
  }
784
1001
  rb_grn_object_close(rb_cursor);
@@ -786,6 +1003,12 @@ rb_grn_table_each (VALUE self)
786
1003
  return Qnil;
787
1004
  }
788
1005
 
1006
+ /*
1007
+ * call-seq:
1008
+ * table.delete(id)
1009
+ *
1010
+ * テーブルの_id_に対応するレコードを削除する。
1011
+ */
789
1012
  VALUE
790
1013
  rb_grn_table_delete (VALUE self, VALUE rb_id)
791
1014
  {
@@ -796,7 +1019,8 @@ rb_grn_table_delete (VALUE self, VALUE rb_id)
796
1019
 
797
1020
  rb_grn_table_deconstruct(SELF(self), &table, &context,
798
1021
  NULL, NULL,
799
- NULL, NULL, NULL);
1022
+ NULL, NULL, NULL,
1023
+ NULL);
800
1024
 
801
1025
  id = NUM2UINT(rb_id);
802
1026
  rc = grn_table_delete_by_id(context, table, id);
@@ -805,6 +1029,32 @@ rb_grn_table_delete (VALUE self, VALUE rb_id)
805
1029
  return Qnil;
806
1030
  }
807
1031
 
1032
+ /*
1033
+ * call-seq:
1034
+ * table.sort(keys, options={}) -> Groonga::Recordの配列
1035
+ *
1036
+ * テーブルに登録されているレコードを_keys_で指定されたルー
1037
+ * ルに従ってソートしたレコードの配列を返す。
1038
+ *
1039
+ * [
1040
+ * {:key => "カラム名", :order => :asc, :ascending,
1041
+ * :desc, :descendingのいずれか},
1042
+ * {:key => "カラム名", :order => :asc, :ascending,
1043
+ * :desc, :descendingのいずれか},
1044
+ * ...,
1045
+ * ]
1046
+ *
1047
+ * _options_に指定可能な値は以下の通り。
1048
+ *
1049
+ * [+:offset+]
1050
+ * ソートされたレコードのうち、(0ベースで)_:offset_番目
1051
+ * からレコードを取り出す。
1052
+ *
1053
+ * [+:limit+]
1054
+ * ソートされたレコードのうち、_:limit_件のみを取り出す。
1055
+ * 省略された場合または-1が指定された場合は、全件が指定され
1056
+ * たものとみなす。
1057
+ */
808
1058
  static VALUE
809
1059
  rb_grn_table_sort (int argc, VALUE *argv, VALUE self)
810
1060
  {
@@ -819,10 +1069,12 @@ rb_grn_table_sort (int argc, VALUE *argv, VALUE self)
819
1069
  VALUE *rb_sort_keys;
820
1070
  grn_table_cursor *cursor;
821
1071
  VALUE rb_result;
1072
+ VALUE exception;
822
1073
 
823
1074
  rb_grn_table_deconstruct(SELF(self), &table, &context,
824
1075
  NULL, NULL,
825
- NULL, NULL, NULL);
1076
+ NULL, NULL, NULL,
1077
+ NULL);
826
1078
 
827
1079
  rb_scan_args(argc, argv, "11", &rb_keys, &options);
828
1080
 
@@ -889,10 +1141,15 @@ rb_grn_table_sort (int argc, VALUE *argv, VALUE self)
889
1141
  NULL, table);
890
1142
  n_records = grn_table_sort(context, table, offset, limit,
891
1143
  result, keys, n_keys);
1144
+ exception = rb_grn_context_to_exception(context, self);
1145
+ if (!NIL_P(exception)) {
1146
+ grn_obj_close(context, result);
1147
+ rb_exc_raise(exception);
1148
+ }
892
1149
 
893
1150
  rb_result = rb_ary_new();
894
1151
  cursor = grn_table_cursor_open(context, result, NULL, 0, NULL, 0,
895
- 0, 0, GRN_CURSOR_ASCENDING);
1152
+ 0, -1, GRN_CURSOR_ASCENDING);
896
1153
  while (grn_table_cursor_next(context, cursor) != GRN_ID_NIL) {
897
1154
  void *value;
898
1155
  grn_id *id;
@@ -904,11 +1161,20 @@ rb_grn_table_sort (int argc, VALUE *argv, VALUE self)
904
1161
  grn_table_cursor_close(context, cursor);
905
1162
  grn_obj_close(context, result);
906
1163
 
907
- rb_grn_context_check(context, self); /* FIXME: here is too late */
908
-
909
1164
  return rb_result;
910
1165
  }
911
1166
 
1167
+ /*
1168
+ * call-seq:
1169
+ * table.group(column, options={}) -> Groonga::Hash
1170
+ * table.group(column1, column2, ..., options={}) -> [Groonga::Hash, ...]
1171
+ *
1172
+ * _table_のレコードを_column1_, _column2_, _..._で指定したカ
1173
+ * ラムの値でグループ化する。カラムはカラム名(文字列)でも
1174
+ * 指定可能。
1175
+ *
1176
+ * このAPIは将来変更されます。
1177
+ */
912
1178
  static VALUE
913
1179
  rb_grn_table_group (int argc, VALUE *argv, VALUE self)
914
1180
  {
@@ -924,16 +1190,17 @@ rb_grn_table_group (int argc, VALUE *argv, VALUE self)
924
1190
 
925
1191
  rb_grn_table_deconstruct(SELF(self), &table, &context,
926
1192
  NULL, NULL,
927
- NULL, NULL, NULL);
1193
+ NULL, NULL, NULL,
1194
+ NULL);
928
1195
 
929
- rb_scan_args(argc, argv, "10", &rb_keys);
930
-
931
- if (!RVAL2CBOOL(rb_obj_is_kind_of(rb_keys, rb_cArray)))
932
- rb_raise(rb_eArgError, "keys should be an array of key: <%s>",
933
- rb_grn_inspect(rb_keys));
1196
+ rb_scan_args(argc, argv, "00*", &rb_keys);
934
1197
 
935
1198
  n_keys = RARRAY_LEN(rb_keys);
936
1199
  rb_sort_keys = RARRAY_PTR(rb_keys);
1200
+ if (n_keys == 1 && TYPE(rb_sort_keys[0]) == T_ARRAY) {
1201
+ n_keys = RARRAY_LEN(rb_sort_keys[0]);
1202
+ rb_sort_keys = RARRAY_PTR(rb_sort_keys[0]);
1203
+ }
937
1204
  keys = ALLOCA_N(grn_table_sort_key, n_keys);
938
1205
  for (i = 0; i < n_keys; i++) {
939
1206
  VALUE rb_sort_options, rb_key;
@@ -992,12 +1259,20 @@ rb_grn_table_group (int argc, VALUE *argv, VALUE self)
992
1259
  * Document-method: []
993
1260
  *
994
1261
  * call-seq:
995
- * table[id] ->
1262
+ * table[id] -> Groonga::Record
996
1263
  *
997
- * _table_の_id_に対応する値を返す。
1264
+ * _table_の_id_に対応するGroonga::Recordを返す。
1265
+ *
1266
+ * 0.9.0から値ではなくGroonga::Recordを返すようになった。
998
1267
  */
999
1268
  VALUE
1000
1269
  rb_grn_table_array_reference (VALUE self, VALUE rb_id)
1270
+ {
1271
+ return rb_grn_record_new_raw(self, rb_id, Qnil);
1272
+ }
1273
+
1274
+ VALUE
1275
+ rb_grn_table_get_value (VALUE self, VALUE rb_id)
1001
1276
  {
1002
1277
  grn_id id;
1003
1278
  grn_ctx *context;
@@ -1007,30 +1282,54 @@ rb_grn_table_array_reference (VALUE self, VALUE rb_id)
1007
1282
 
1008
1283
  rb_grn_table_deconstruct(SELF(self), &table, &context,
1009
1284
  NULL, NULL,
1010
- &value, NULL, &range);
1285
+ &value, NULL, &range,
1286
+ NULL);
1011
1287
 
1012
1288
  id = NUM2UINT(rb_id);
1013
1289
  GRN_BULK_REWIND(value);
1014
1290
  grn_obj_get_value(context, table, id, value);
1015
1291
  rb_grn_context_check(context, self);
1016
1292
 
1017
- if (GRN_BULK_EMPTYP(value))
1018
- return Qnil;
1019
- else
1020
- return rb_str_new(GRN_BULK_HEAD(value), GRN_BULK_VSIZE(value));
1293
+ return GRNBULK2RVAL(context, value, range, self);
1021
1294
  }
1022
1295
 
1023
1296
  /*
1024
- * Document-method: []=
1297
+ * Document-method: value
1025
1298
  *
1026
1299
  * call-seq:
1027
- * table[id] = value
1300
+ * table.value(id) ->
1301
+ * table.value(id, :id => true) -> 値
1302
+ *
1303
+ * _table_の_id_に対応する値を返す。
1028
1304
  *
1029
- * _table_の_id_に対応する値を設定する。既存の値は上書きさ
1030
- * れる。
1305
+ * <tt>:id => true</tt>が指定できるのは利便性のため。
1306
+ * Groonga::ArrayでもGroonga::HashやGroonga::PatriciaTrieと
1307
+ * 同じ引数で動くようになる。
1031
1308
  */
1309
+ static VALUE
1310
+ rb_grn_table_get_value_convenience (int argc, VALUE *argv, VALUE self)
1311
+ {
1312
+ VALUE rb_id, rb_options;
1313
+
1314
+ rb_scan_args(argc, argv, "11", &rb_id, &rb_options);
1315
+ if (!NIL_P(rb_options)) {
1316
+ VALUE rb_option_id;
1317
+ rb_grn_scan_options(rb_options,
1318
+ "id", &rb_option_id,
1319
+ NULL);
1320
+ if (!(NIL_P(rb_option_id) || RVAL2CBOOL(rb_option_id))) {
1321
+ rb_raise(rb_eArgError, ":id options must be true or nil: %s: %s",
1322
+ rb_grn_inspect(rb_option_id),
1323
+ rb_grn_inspect(rb_ary_new3(2,
1324
+ self, rb_ary_new4(argc, argv))));
1325
+ }
1326
+ }
1327
+
1328
+ return rb_grn_table_get_value(self, rb_id);
1329
+ }
1330
+
1032
1331
  VALUE
1033
- rb_grn_table_array_set (VALUE self, VALUE rb_id, VALUE rb_value)
1332
+ rb_grn_table_set_value (VALUE self, VALUE rb_id, VALUE rb_value)
1034
1333
  {
1035
1334
  grn_id id;
1036
1335
  grn_ctx *context;
@@ -1041,7 +1340,8 @@ rb_grn_table_array_set (VALUE self, VALUE rb_id, VALUE rb_value)
1041
1340
 
1042
1341
  rb_grn_table_deconstruct(SELF(self), &table, &context,
1043
1342
  NULL, NULL,
1044
- &value, NULL, &range);
1343
+ &value, NULL, &range,
1344
+ NULL);
1045
1345
 
1046
1346
  id = NUM2UINT(rb_id);
1047
1347
  GRN_BULK_REWIND(value);
@@ -1053,6 +1353,152 @@ rb_grn_table_array_set (VALUE self, VALUE rb_id, VALUE rb_value)
1053
1353
  return Qnil;
1054
1354
  }
1055
1355
 
1356
+ /*
1357
+ * Document-method: set_value
1358
+ *
1359
+ * call-seq:
1360
+ * table.set_value(id, value)
1361
+ * table.set_value(id, value, :id => true)
1362
+ *
1363
+ * _table_の_id_に対応する値として_value_設定する。既存の値は
1364
+ * 上書きされる。
1365
+ *
1366
+ * <tt>:id => true</tt>が指定できるのは利便性のため。
1367
+ * Groonga::ArrayでもGroonga::HashやGroonga::PatriciaTrieと
1368
+ * 同じ引数で動くようになる。
1369
+ */
1370
+ static VALUE
1371
+ rb_grn_table_set_value_convenience (int argc, VALUE *argv, VALUE self)
1372
+ {
1373
+ VALUE rb_id, rb_value, rb_options;
1374
+
1375
+ rb_scan_args(argc, argv, "21", &rb_id, &rb_value, &rb_options);
1376
+ if (!NIL_P(rb_options)) {
1377
+ VALUE rb_option_id;
1378
+ rb_grn_scan_options(rb_options,
1379
+ "id", &rb_option_id,
1380
+ NULL);
1381
+ if (!(NIL_P(rb_option_id) || RVAL2CBOOL(rb_option_id))) {
1382
+ rb_raise(rb_eArgError, ":id options must be true or nil: %s: %s",
1383
+ rb_grn_inspect(rb_option_id),
1384
+ rb_grn_inspect(rb_ary_new3(2,
1385
+ self, rb_ary_new4(argc, argv))));
1386
+ }
1387
+ }
1388
+
1389
+ return rb_grn_table_set_value(self, rb_id, rb_value);
1390
+ }
1391
+
1392
+ VALUE
1393
+ rb_grn_table_get_column_value_raw (VALUE self, grn_id id, VALUE rb_name)
1394
+ {
1395
+ VALUE rb_column;
1396
+
1397
+ rb_column = rb_grn_table_get_column_surely(self, rb_name);
1398
+
1399
+ /* TODO: improve speed. */
1400
+ return rb_funcall(rb_column, id_array_reference, 1, INT2NUM(id));
1401
+ }
1402
+
1403
+ VALUE
1404
+ rb_grn_table_get_column_value (VALUE self, VALUE rb_id, VALUE rb_name)
1405
+ {
1406
+ return rb_grn_table_get_column_value_raw(self, NUM2INT(rb_id), rb_name);
1407
+ }
1408
+
1409
+ /*
1410
+ * Document-method: column_value
1411
+ *
1412
+ * call-seq:
1413
+ * table.column_value(id, name) -> 値
1414
+ * table.column_value(id, name, :id => true) -> 値
1415
+ *
1416
+ * _table_の_id_に対応するカラム_name_の値を返す。
1417
+ *
1418
+ * <tt>:id => true</tt>が指定できるのは利便性のため。
1419
+ * Groonga::ArrayでもGroonga::HashやGroonga::PatriciaTrieと
1420
+ * 同じ引数で動くようになる。
1421
+ */
1422
+ static VALUE
1423
+ rb_grn_table_get_column_value_convenience (int argc, VALUE *argv, VALUE self)
1424
+ {
1425
+ VALUE rb_id, rb_name, rb_options;
1426
+
1427
+ rb_scan_args(argc, argv, "21", &rb_id, &rb_name, &rb_options);
1428
+ if (!NIL_P(rb_options)) {
1429
+ VALUE rb_option_id;
1430
+ rb_grn_scan_options(rb_options,
1431
+ "id", &rb_option_id,
1432
+ NULL);
1433
+ if (!(NIL_P(rb_option_id) || RVAL2CBOOL(rb_option_id))) {
1434
+ rb_raise(rb_eArgError, ":id options must be true or nil: %s: %s",
1435
+ rb_grn_inspect(rb_option_id),
1436
+ rb_grn_inspect(rb_ary_new3(2,
1437
+ self,
1438
+ rb_ary_new4(argc, argv))));
1439
+ }
1440
+ }
1441
+
1442
+ return rb_grn_table_get_column_value(self, rb_id, rb_name);
1443
+ }
1444
+
1445
+ VALUE
1446
+ rb_grn_table_set_column_value_raw (VALUE self, grn_id id,
1447
+ VALUE rb_name, VALUE rb_value)
1448
+ {
1449
+ VALUE rb_column;
1450
+
1451
+ rb_column = rb_grn_table_get_column_surely(self, rb_name);
1452
+
1453
+ /* TODO: improve speed. */
1454
+ return rb_funcall(rb_column, id_array_set, 2, INT2NUM(id), rb_value);
1455
+ }
1456
+
1457
+ VALUE
1458
+ rb_grn_table_set_column_value (VALUE self, VALUE rb_id,
1459
+ VALUE rb_name, VALUE rb_value)
1460
+ {
1461
+ return rb_grn_table_set_column_value_raw(self, NUM2INT(rb_id),
1462
+ rb_name, rb_value);
1463
+ }
1464
+
1465
+ /*
1466
+ * Document-method: set_column_value
1467
+ *
1468
+ * call-seq:
1469
+ * table.set_column_value(id, name, value)
1470
+ * table.set_column_value(id, name, value, :id => true)
1471
+ *
1472
+ * _table_の_id_に対応するカラム_name_の値として_value_設定す
1473
+ * る。既存の値は上書きされる。
1474
+ *
1475
+ * <tt>:id => true</tt>が指定できるのは利便性のため。
1476
+ * Groonga::ArrayでもGroonga::HashやGroonga::PatriciaTrieと
1477
+ * 同じ引数で動くようになる。
1478
+ */
1479
+ static VALUE
1480
+ rb_grn_table_set_column_value_convenience (int argc, VALUE *argv, VALUE self)
1481
+ {
1482
+ VALUE rb_id, rb_name, rb_value, rb_options;
1483
+
1484
+ rb_scan_args(argc, argv, "31", &rb_id, &rb_name, &rb_value, &rb_options);
1485
+ if (!NIL_P(rb_options)) {
1486
+ VALUE rb_option_id;
1487
+ rb_grn_scan_options(rb_options,
1488
+ "id", &rb_option_id,
1489
+ NULL);
1490
+ if (!(NIL_P(rb_option_id) || RVAL2CBOOL(rb_option_id))) {
1491
+ rb_raise(rb_eArgError, ":id options must be true or nil: %s: %s",
1492
+ rb_grn_inspect(rb_option_id),
1493
+ rb_grn_inspect(rb_ary_new3(2,
1494
+ self,
1495
+ rb_ary_new4(argc, argv))));
1496
+ }
1497
+ }
1498
+
1499
+ return rb_grn_table_set_column_value(self, rb_id, rb_name, rb_value);
1500
+ }
1501
+
1056
1502
  /*
1057
1503
  * Document-method: unlock
1058
1504
  *
@@ -1080,7 +1526,8 @@ rb_grn_table_unlock (int argc, VALUE *argv, VALUE self)
1080
1526
 
1081
1527
  rb_grn_table_deconstruct(SELF(self), &table, &context,
1082
1528
  NULL, NULL,
1083
- NULL, NULL, NULL);
1529
+ NULL, NULL, NULL,
1530
+ NULL);
1084
1531
 
1085
1532
  rb_grn_scan_options(options,
1086
1533
  "id", &rb_id,
@@ -1138,7 +1585,8 @@ rb_grn_table_lock (int argc, VALUE *argv, VALUE self)
1138
1585
 
1139
1586
  rb_grn_table_deconstruct(SELF(self), &table, &context,
1140
1587
  NULL, NULL,
1141
- NULL, NULL, NULL);
1588
+ NULL, NULL, NULL,
1589
+ NULL);
1142
1590
 
1143
1591
  rb_grn_scan_options(options,
1144
1592
  "timeout", &rb_timeout,
@@ -1190,7 +1638,8 @@ rb_grn_table_clear_lock (int argc, VALUE *argv, VALUE self)
1190
1638
 
1191
1639
  rb_grn_table_deconstruct(SELF(self), &table, &context,
1192
1640
  NULL, NULL,
1193
- NULL, NULL, NULL);
1641
+ NULL, NULL, NULL,
1642
+ NULL);
1194
1643
 
1195
1644
  rb_grn_scan_options(options,
1196
1645
  "id", &rb_id,
@@ -1232,7 +1681,8 @@ rb_grn_table_is_locked (int argc, VALUE *argv, VALUE self)
1232
1681
 
1233
1682
  rb_grn_table_deconstruct(SELF(self), &table, &context,
1234
1683
  NULL, NULL,
1235
- NULL, NULL, NULL);
1684
+ NULL, NULL, NULL,
1685
+ NULL);
1236
1686
 
1237
1687
  rb_grn_scan_options(options,
1238
1688
  "id", &rb_id,
@@ -1248,6 +1698,7 @@ rb_grn_table_is_locked (int argc, VALUE *argv, VALUE self)
1248
1698
  * call-seq:
1249
1699
  * table.select(options) {|record| ...} -> Groonga::Hash
1250
1700
  * table.select(query, options) -> Groonga::Hash
1701
+ * table.select(expression, options) -> Groonga::Hash
1251
1702
  *
1252
1703
  * _table_からブロックまたは文字列で指定した条件にマッチする
1253
1704
  * レコードを返す。返されたテーブルには+expression+という特
@@ -1257,7 +1708,7 @@ rb_grn_table_is_locked (int argc, VALUE *argv, VALUE self)
1257
1708
  * 用のスニペットを簡単に生成できる。
1258
1709
  *
1259
1710
  * results = table.select do |record|
1260
- * result["description"] =~ "groonga"
1711
+ * record["description"] =~ "groonga"
1261
1712
  * end
1262
1713
  * snippet = results.expression.snippet([["<em>", "</em>"]])
1263
1714
  * results.each do |record|
@@ -1280,26 +1731,28 @@ rb_grn_table_is_locked (int argc, VALUE *argv, VALUE self)
1280
1731
  *
1281
1732
  * [なし]
1282
1733
  * [カラム値] == [値]
1283
- * [+!+]
1734
+ * [<tt>!</tt>]
1284
1735
  * [カラム値] != [値]
1285
- * [+<+]
1736
+ * [<tt><</tt>]
1286
1737
  * [カラム値] < [値]
1287
- * [+>+]
1738
+ * [<tt>></tt>]
1288
1739
  * [カラム値] > [値]
1289
- * [+<=+]
1740
+ * [<tt><=</tt>]
1290
1741
  * [カラム値] <= [値]
1291
- * [+>=+]
1742
+ * [<tt>>=</tt>]
1292
1743
  * [カラム値] >= [値]
1293
- * [+@+]
1744
+ * [<tt>@</tt>]
1294
1745
  * [カラム値]が[値]を含んでいるかどうか
1295
1746
  *
1296
1747
  * 例:
1297
1748
  * "name:daijiro" # "name"カラムの値が"daijiro"のレコードにマッチ
1298
- * "description:@groonga" # "description"カラムに
1749
+ * "description:@groonga" # "description"カラムが
1299
1750
  * # "groonga"を含んでいるレコードにマッチ
1300
1751
  *
1301
- * ブロックで条件を指定する場合はGroonga::ExpressionBuilder
1302
- * を参照。
1752
+ * _expression_には既に作成済みのGroonga::Expressionを渡す
1753
+ *
1754
+ * ブロックで条件を指定する場合は
1755
+ * Groonga::RecordExpressionBuilderを参照。
1303
1756
  *
1304
1757
  * _options_に指定可能な値は以下の通り。
1305
1758
  *
@@ -1324,6 +1777,29 @@ rb_grn_table_is_locked (int argc, VALUE *argv, VALUE self)
1324
1777
  *
1325
1778
  * [+:name+]
1326
1779
  * 条件の名前。省略した場合は名前を付けない。
1780
+ *
1781
+ * [+:syntax+]
1782
+ * _query_の構文。省略した場合は+:query+。
1783
+ *
1784
+ * 参考: Groonga::Expression#parse.
1785
+ *
1786
+ * [+:allow_pragma+]
1787
+ * query構文時にプラグマを利用するかどうか。省略した場合は
1788
+ * 利用する。
1789
+ *
1790
+ * 参考: Groonga::Expression#parse.
1791
+ *
1792
+ * [+:allow_column+]
1793
+ * query構文時にカラム指定を利用するかどうか。省略した場合
1794
+ * は利用する。
1795
+ *
1796
+ * 参考: Groonga::Expression#parse.
1797
+ *
1798
+ * [+:allow_update+]
1799
+ * script構文時に更新操作を利用するかどうか。省略した場合
1800
+ * は利用する。
1801
+ *
1802
+ * 参考: Groonga::Expression#parse.
1327
1803
  */
1328
1804
  static VALUE
1329
1805
  rb_grn_table_select (int argc, VALUE *argv, VALUE self)
@@ -1331,31 +1807,41 @@ rb_grn_table_select (int argc, VALUE *argv, VALUE self)
1331
1807
  grn_ctx *context;
1332
1808
  grn_obj *table, *result, *expression;
1333
1809
  grn_operator operator = GRN_OP_OR;
1334
- VALUE rb_query = Qnil, query_or_options, options;
1335
- VALUE rb_name, rb_operator, rb_result;
1336
- VALUE rb_expression, builder;
1810
+ VALUE rb_query = Qnil, condition_or_options, options;
1811
+ VALUE rb_name, rb_operator, rb_result, rb_syntax;
1812
+ VALUE rb_allow_pragma, rb_allow_column, rb_allow_update;
1813
+ VALUE rb_expression = Qnil, builder;
1337
1814
 
1338
- rb_scan_args(argc, argv, "02", &query_or_options, &options);
1815
+ rb_scan_args(argc, argv, "02", &condition_or_options, &options);
1339
1816
 
1340
1817
  rb_grn_table_deconstruct(SELF(self), &table, &context,
1341
1818
  NULL, NULL,
1342
- NULL, NULL, NULL);
1343
-
1344
- if (RVAL2CBOOL(rb_obj_is_kind_of(query_or_options, rb_cString))) {
1345
- rb_query = query_or_options;
1819
+ NULL, NULL, NULL,
1820
+ NULL);
1821
+
1822
+ if (RVAL2CBOOL(rb_obj_is_kind_of(condition_or_options, rb_cString))) {
1823
+ rb_query = condition_or_options;
1824
+ } else if (RVAL2CBOOL(rb_obj_is_kind_of(condition_or_options,
1825
+ rb_cGrnExpression))) {
1826
+ rb_expression = condition_or_options;
1346
1827
  } else {
1347
1828
  if (!NIL_P(options))
1348
1829
  rb_raise(rb_eArgError,
1349
- "should be [query_string, option_hash] "
1830
+ "should be [query_string, option_hash], "
1831
+ "[expression, opion_hash] "
1350
1832
  "or [option_hash]: %s",
1351
1833
  rb_grn_inspect(rb_ary_new4(argc, argv)));
1352
- options = query_or_options;
1834
+ options = condition_or_options;
1353
1835
  }
1354
1836
 
1355
1837
  rb_grn_scan_options(options,
1356
1838
  "operator", &rb_operator,
1357
1839
  "result", &rb_result,
1358
1840
  "name", &rb_name,
1841
+ "syntax", &rb_syntax,
1842
+ "allow_pragma", &rb_allow_pragma,
1843
+ "allow_column", &rb_allow_column,
1844
+ "allow_update", &rb_allow_update,
1359
1845
  NULL);
1360
1846
 
1361
1847
  if (!NIL_P(rb_operator))
@@ -1371,11 +1857,15 @@ rb_grn_table_select (int argc, VALUE *argv, VALUE self)
1371
1857
  result = RVAL2GRNTABLE(rb_result, &context);
1372
1858
  }
1373
1859
 
1374
- builder = rb_grn_record_expression_builder_new(self, rb_name);
1375
- if (!NIL_P(rb_query)) {
1376
- rb_funcall(builder, rb_intern("query="), 1, rb_query);
1860
+ if (NIL_P(rb_expression)) {
1861
+ builder = rb_grn_record_expression_builder_new(self, rb_name);
1862
+ rb_funcall(builder, rb_intern("query="), 1, rb_query);
1863
+ rb_funcall(builder, rb_intern("syntax="), 1, rb_syntax);
1864
+ rb_funcall(builder, rb_intern("allow_pragma="), 1, rb_allow_pragma);
1865
+ rb_funcall(builder, rb_intern("allow_column="), 1, rb_allow_column);
1866
+ rb_funcall(builder, rb_intern("allow_update="), 1, rb_allow_update);
1867
+ rb_expression = rb_grn_record_expression_builder_build(builder);
1377
1868
  }
1378
- rb_expression = rb_grn_record_expression_builder_build(builder);
1379
1869
  rb_grn_object_deconstruct(RB_GRN_OBJECT(DATA_PTR(rb_expression)),
1380
1870
  &expression, NULL,
1381
1871
  NULL, NULL, NULL, NULL);
@@ -1401,10 +1891,12 @@ rb_grn_table_set_operation_bang (VALUE self, VALUE rb_other,
1401
1891
 
1402
1892
  rb_grn_table_deconstruct(SELF(self), &table, &context,
1403
1893
  NULL, NULL,
1404
- NULL, NULL, NULL);
1894
+ NULL, NULL, NULL,
1895
+ NULL);
1405
1896
  rb_grn_table_deconstruct(SELF(rb_other), &other, NULL,
1406
1897
  NULL, NULL,
1407
- NULL, NULL, NULL);
1898
+ NULL, NULL, NULL,
1899
+ NULL);
1408
1900
 
1409
1901
  rc = grn_table_setoperation(context, table, other, table, operator);
1410
1902
  rb_grn_context_check(context, self);
@@ -1413,33 +1905,69 @@ rb_grn_table_set_operation_bang (VALUE self, VALUE rb_other,
1413
1905
  return self;
1414
1906
  }
1415
1907
 
1908
+ /*
1909
+ * call-seq:
1910
+ * table.union!(other) -> Groonga::Table
1911
+ *
1912
+ * キーを比較し、_table_には登録されていない_other_のレコー
1913
+ * ドを_table_に作成する。
1914
+ *
1915
+ */
1416
1916
  static VALUE
1417
1917
  rb_grn_table_union_bang (VALUE self, VALUE rb_other)
1418
1918
  {
1419
- return rb_grn_table_set_operation_bang(self ,rb_other, GRN_OP_OR);
1919
+ return rb_grn_table_set_operation_bang(self, rb_other, GRN_OP_OR);
1420
1920
  }
1421
1921
 
1922
+
1923
+ /*
1924
+ * call-seq:
1925
+ * table.intersection!(other) -> Groonga::Table
1926
+ *
1927
+ * キーを比較し、_other_には登録されていないレコードを
1928
+ * _table_から削除する。
1929
+ *
1930
+ */
1422
1931
  static VALUE
1423
1932
  rb_grn_table_intersection_bang (VALUE self, VALUE rb_other)
1424
1933
  {
1425
- return rb_grn_table_set_operation_bang(self ,rb_other, GRN_OP_AND);
1934
+ return rb_grn_table_set_operation_bang(self, rb_other, GRN_OP_AND);
1426
1935
  }
1427
1936
 
1937
+ /*
1938
+ * call-seq:
1939
+ * table.difference!(other) -> Groonga::Table
1940
+ *
1941
+ * キーを比較し、_other_にも登録されているレコードを_table_
1942
+ * から削除する。
1943
+ *
1944
+ */
1428
1945
  static VALUE
1429
1946
  rb_grn_table_difference_bang (VALUE self, VALUE rb_other)
1430
1947
  {
1431
- return rb_grn_table_set_operation_bang(self ,rb_other, GRN_OP_BUT);
1948
+ return rb_grn_table_set_operation_bang(self, rb_other, GRN_OP_BUT);
1432
1949
  }
1433
1950
 
1951
+ /*
1952
+ * call-seq:
1953
+ * table.merge!(other) -> Groonga::Table
1954
+ *
1955
+ * キーを比較し、_other_にも登録されている_table_のレコード
1956
+ * のスコアを_other_のスコアと同値にする。
1957
+ *
1958
+ */
1434
1959
  static VALUE
1435
1960
  rb_grn_table_merge_bang (VALUE self, VALUE rb_other)
1436
1961
  {
1437
- return rb_grn_table_set_operation_bang(self ,rb_other, GRN_OP_ADJUST);
1962
+ return rb_grn_table_set_operation_bang(self, rb_other, GRN_OP_ADJUST);
1438
1963
  }
1439
1964
 
1440
1965
  void
1441
1966
  rb_grn_init_table (VALUE mGrn)
1442
1967
  {
1968
+ id_array_reference = rb_intern("[]");
1969
+ id_array_set = rb_intern("[]=");
1970
+
1443
1971
  rb_cGrnTable = rb_define_class_under(mGrn, "Table", rb_cGrnObject);
1444
1972
  rb_define_alloc_func(rb_cGrnTable, rb_grn_table_alloc);
1445
1973
 
@@ -1477,7 +2005,16 @@ rb_grn_init_table (VALUE mGrn)
1477
2005
  rb_define_method(rb_cGrnTable, "group", rb_grn_table_group, -1);
1478
2006
 
1479
2007
  rb_define_method(rb_cGrnTable, "[]", rb_grn_table_array_reference, 1);
1480
- rb_define_method(rb_cGrnTable, "[]=", rb_grn_table_array_set, 2);
2008
+ rb_undef_method(rb_cGrnTable, "[]=");
2009
+
2010
+ rb_define_method(rb_cGrnTable, "value",
2011
+ rb_grn_table_get_value_convenience, -1);
2012
+ rb_define_method(rb_cGrnTable, "set_value",
2013
+ rb_grn_table_set_value_convenience, -1);
2014
+ rb_define_method(rb_cGrnTable, "column_value",
2015
+ rb_grn_table_get_column_value_convenience, -1);
2016
+ rb_define_method(rb_cGrnTable, "set_column_value",
2017
+ rb_grn_table_set_column_value_convenience, -1);
1481
2018
 
1482
2019
  rb_define_method(rb_cGrnTable, "lock", rb_grn_table_lock, -1);
1483
2020
  rb_define_method(rb_cGrnTable, "unlock", rb_grn_table_unlock, -1);