groonga 0.0.7 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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);