ext/rb-grn-column.c in groonga-0.0.7 vs ext/rb-grn-column.c in groonga-0.9.0
- old
+ new
@@ -1,8 +1,9 @@
/* -*- c-file-style: "ruby" -*- */
+/* vim: set sts=4 sw=4 ts=8 noet: */
/*
- Copyright (C) 2009 Kouhei Sutou <kou@clear-code.com>
+ Copyright (C) 2009-2010 Kouhei Sutou <kou@clear-code.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 2.1 as published by the Free Software Foundation.
@@ -69,19 +70,21 @@
grn_ctx *context, grn_obj *column)
{
RbGrnObject *rb_grn_object;
rb_grn_object = RB_GRN_OBJECT(rb_column);
-
+ rb_grn_named_object_bind(RB_GRN_NAMED_OBJECT(rb_column), context, column);
rb_column->value = grn_obj_open(context, GRN_BULK, 0,
rb_grn_object->range_id);
}
void
rb_grn_column_finalizer (grn_ctx *context, grn_obj *grn_object,
RbGrnColumn *rb_column)
{
+ rb_grn_named_object_finalizer(context, grn_object,
+ RB_GRN_NAMED_OBJECT(rb_column));
if (context && rb_column->value)
grn_obj_close(context, rb_column->value);
rb_column->value = NULL;
}
@@ -104,11 +107,10 @@
if (value)
*value = rb_column->value;
}
-
/*
* call-seq:
* column.table -> Groonga::Table
*
* カラムが所属するテーブルを返す。
@@ -133,24 +135,26 @@
* call-seq:
* column.local_name
*
* テーブル名を除いたカラム名を返す。
*
- * items = Groonga::Array.create(:name => "<items>")
- * title = items.define_column("title", "<shorttext>")
- * title.name # => "<items>.title"
+ * items = Groonga::Array.create(:name => "Items")
+ * title = items.define_column("title", "ShortText")
+ * title.name # => "Items.title"
* title.local_name # => "title"
*/
static VALUE
rb_grn_column_get_local_name (VALUE self)
{
+ RbGrnColumn *rb_grn_column;
grn_ctx *context = NULL;
grn_obj *column;
int name_size;
VALUE rb_name;
- rb_grn_object_deconstruct((RbGrnObject *)(SELF(self)), &column, &context,
+ rb_grn_column = SELF(self);
+ rb_grn_object_deconstruct(RB_GRN_OBJECT(rb_grn_column), &column, &context,
NULL, NULL,
NULL, NULL);
name_size = grn_column_name(context, column, NULL, 0);
if (name_size == 0)
return Qnil;
@@ -159,45 +163,165 @@
rb_str_set_len(rb_name, name_size);
grn_column_name(context, column, RSTRING_PTR(rb_name), name_size);
return rb_name;
}
+/*
+ * call-seq:
+ * column.select(options) {|record| ...} -> Groonga::Hash
+ * column.select(query, options) -> Groonga::Hash
+ * column.select(expression, options) -> Groonga::Hash
+ *
+ * カラムが所属するテーブルからブロックまたは文字列で指定し
+ * た条件にマッチするレコードを返す。返されたテーブルには
+ * +expression+という特異メソッドがあり、指定した条件を表し
+ * ているGroonga::Expressionを取得できる。
+ * Groonga::Expression#snippetを使うことにより、指定した条件
+ * 用のスニペットを簡単に生成できる。
+ *
+ * results = description_column.select do |column|
+ * column =~ "groonga"
+ * end
+ * snippet = results.expression.snippet([["<em>", "</em>"]])
+ * results.each do |record|
+ * puts "#{record['name']}の説明文の中で「groonga」が含まれる部分"
+ * snippet.execute(record["description"].each do |snippet|
+ * puts "---"
+ * puts "#{snippet}..."
+ * puts "---"
+ * end
+ * end
+ *
+ * 出力例
+ * Ruby/groongaの説明文の中で「groonga」が含まれる部分
+ * ---
+ * Ruby/<em>groonga</em>は<em>groonga</em>のいわゆるDB-APIの層の...
+ * ---
+ *
+ * _query_には「[カラム名]:[演算子][値]」という書式で条件を
+ * 指定する。演算子は以下の通り。
+ *
+ * [なし]
+ * [カラム値] == [値]
+ * [<tt>!</tt>]
+ * [カラム値] != [値]
+ * [<tt><</tt>]
+ * [カラム値] < [値]
+ * [<tt>></tt>]
+ * [カラム値] > [値]
+ * [<tt><=</tt>]
+ * [カラム値] <= [値]
+ * [<tt>>=</tt>]
+ * [カラム値] >= [値]
+ * [<tt>@</tt>]
+ * [カラム値]が[値]を含んでいるかどうか
+ *
+ * 例:
+ * "groonga" # _column_カラムの値が"groonga"のレコードにマッチ
+ * "name:daijiro" # _column_カラムが属しているテーブルの
+ * # "name"カラムの値が"daijiro"のレコードにマッチ
+ * "description:@groonga" # _column_カラムが属しているテーブルの
+ * # "description"カラムが
+ * # "groonga"を含んでいるレコードにマッチ
+ *
+ * _expression_には既に作成済みのGroonga::Expressionを渡す
+ *
+ * ブロックで条件を指定する場合は
+ * Groonga::ColumnExpressionBuilderを参照。
+ *
+ * _options_に指定可能な値は以下の通り。
+ *
+ * [+:operator+]
+ * マッチしたレコードをどのように扱うか。指定可能な値は以
+ * 下の通り。省略した場合はGroonga::Operation::OR。
+ *
+ * [Groonga::Operation::OR]
+ * マッチしたレコードを追加。すでにレコードが追加され
+ * ている場合は何もしない。
+ * [Groonga::Operation::AND]
+ * マッチしたレコードのスコアを増加。マッチしなかった
+ * レコードを削除。
+ * [Groonga::Operation::BUT]
+ * マッチしたレコードを削除。
+ * [Groonga::Operation::ADJUST]
+ * マッチしたレコードのスコアを増加。
+ *
+ * [+:result+]
+ * 検索結果を格納するテーブル。マッチしたレコードが追加さ
+ * れていく。省略した場合は新しくテーブルを作成して返す。
+ *
+ * [+:name+]
+ * 条件の名前。省略した場合は名前を付けない。
+ *
+ * [+:syntax+]
+ * _query_の構文。省略した場合は+:query+。
+ *
+ * 参考: Groonga::Expression#parse.
+ *
+ * [+:allow_pragma+]
+ * query構文時にプラグマを利用するかどうか。省略した場合は
+ * 利用する。
+ *
+ * 参考: Groonga::Expression#parse.
+ *
+ * [+:allow_column+]
+ * query構文時にカラム指定を利用するかどうか。省略した場合
+ * は利用する。
+ *
+ * 参考: Groonga::Expression#parse.
+ *
+ * [+:allow_update+]
+ * script構文時に更新操作を利用するかどうか。省略した場合
+ * は利用する。
+ *
+ * 参考: Groonga::Expression#parse.
+ */
static VALUE
rb_grn_column_select (int argc, VALUE *argv, VALUE self)
{
grn_ctx *context;
grn_obj *table, *column, *result, *expression;
grn_operator operator = GRN_OP_OR;
VALUE options;
- VALUE rb_query, rb_query_or_options, rb_name, rb_operator, rb_result;
+ VALUE rb_query, condition_or_options;
+ VALUE rb_name, rb_operator, rb_result, rb_syntax;
+ VALUE rb_allow_pragma, rb_allow_column, rb_allow_update;
VALUE builder;
- VALUE rb_expression;
-
+ VALUE rb_expression = Qnil;
+
rb_query = Qnil;
- rb_scan_args(argc, argv, "02", &rb_query_or_options, &options);
+ rb_scan_args(argc, argv, "02", &condition_or_options, &options);
rb_grn_column_deconstruct(SELF(self), &column, &context,
NULL, NULL,
NULL, NULL, NULL);
table = grn_column_table(context, column);
- if (RVAL2CBOOL(rb_obj_is_kind_of(rb_query_or_options, rb_cString))) {
- rb_query = rb_query_or_options;
+ if (RVAL2CBOOL(rb_obj_is_kind_of(condition_or_options, rb_cString))) {
+ rb_query = condition_or_options;
+ } else if (RVAL2CBOOL(rb_obj_is_kind_of(condition_or_options,
+ rb_cGrnExpression))) {
+ rb_expression = condition_or_options;
} else {
if (!NIL_P(options))
rb_raise(rb_eArgError,
- "should be [query_string, option_hash] "
+ "should be [query_string, option_hash], "
+ "[expression, opion_hash] "
"or [option_hash]: %s",
rb_grn_inspect(rb_ary_new4(argc, argv)));
- options = rb_query_or_options;
+ options = condition_or_options;
}
-
+
rb_grn_scan_options(options,
"operator", &rb_operator,
"result", &rb_result,
"name", &rb_name,
+ "syntax", &rb_syntax,
+ "allow_pragma", &rb_allow_pragma,
+ "allow_column", &rb_allow_column,
+ "allow_update", &rb_allow_update,
NULL);
if (!NIL_P(rb_operator))
operator = NUM2INT(rb_operator);
@@ -209,31 +333,237 @@
rb_result = GRNTABLE2RVAL(context, result, RB_GRN_TRUE);
} else {
result = RVAL2GRNTABLE(rb_result, &context);
}
- builder = rb_grn_column_expression_builder_new(self, rb_name, rb_query);
- rb_expression = rb_grn_column_expression_builder_build(builder);
+ if (NIL_P(rb_expression)) {
+ builder = rb_grn_column_expression_builder_new(self, rb_name, rb_query);
+ rb_funcall(builder, rb_intern("syntax="), 1, rb_syntax);
+ rb_funcall(builder, rb_intern("allow_pragma="), 1, rb_allow_pragma);
+ rb_funcall(builder, rb_intern("allow_column="), 1, rb_allow_column);
+ rb_funcall(builder, rb_intern("allow_update="), 1, rb_allow_update);
+ rb_expression = rb_grn_column_expression_builder_build(builder);
+ }
rb_grn_object_deconstruct(RB_GRN_OBJECT(DATA_PTR(rb_expression)),
&expression, NULL,
NULL, NULL, NULL, NULL);
grn_table_select(context, table, expression, result, operator);
rb_grn_context_check(context, self);
+ rb_attr(rb_singleton_class(rb_result),
+ rb_intern("expression"),
+ RB_GRN_TRUE, RB_GRN_FALSE, RB_GRN_FALSE);
+ rb_iv_set(rb_result, "@expression", rb_expression);
+
return rb_result;
}
+/*
+ * Document-method: unlock
+ *
+ * call-seq:
+ * column.unlock(options={})
+ *
+ * _column_のロックを解除する。
+ *
+ * 利用可能なオプションは以下の通り。
+ *
+ * [_:id_]
+ * _:id_で指定したレコードのロックを解除する。(注:
+ * groonga側が未実装のため、現在は無視される)
+ */
+static VALUE
+rb_grn_column_unlock (int argc, VALUE *argv, VALUE self)
+{
+ grn_id id = GRN_ID_NIL;
+ grn_ctx *context;
+ grn_obj *column;
+ grn_rc rc;
+ VALUE options, rb_id;
+
+ rb_scan_args(argc, argv, "01", &options);
+
+ rb_grn_column_deconstruct(SELF(self), &column, &context,
+ NULL, NULL,
+ NULL, NULL, NULL);
+
+ rb_grn_scan_options(options,
+ "id", &rb_id,
+ NULL);
+
+ if (!NIL_P(rb_id))
+ id = NUM2UINT(rb_id);
+
+ rc = grn_obj_unlock(context, column, id);
+ rb_grn_context_check(context, self);
+ rb_grn_rc_check(rc, self);
+
+ return Qnil;
+}
+
+static VALUE
+rb_grn_column_unlock_ensure (VALUE self)
+{
+ return rb_grn_column_unlock(0, NULL, self);
+}
+
+/*
+ * Document-method: lock
+ *
+ * call-seq:
+ * column.lock(options={})
+ * column.lock(options={}) {...}
+ *
+ * _column_をロックする。ロックに失敗した場合は
+ * Groonga::ResourceDeadlockAvoided例外が発生する。
+ *
+ * ブロックを指定した場合はブロックを抜けたときにunlockする。
+ *
+ * 利用可能なオプションは以下の通り。
+ *
+ * [_:timeout_]
+ * ロックを獲得できなかった場合は_:timeout_秒間ロックの獲
+ * 得を試みる。_:timeout_秒以内にロックを獲得できなかった
+ * 場合は例外が発生する。
+ * [_:id_]
+ * _:id_で指定したレコードをロックする。(注: groonga側が
+ * 未実装のため、現在は無視される)
+ */
+static VALUE
+rb_grn_column_lock (int argc, VALUE *argv, VALUE self)
+{
+ grn_id id = GRN_ID_NIL;
+ grn_ctx *context;
+ grn_obj *column;
+ int timeout = 0;
+ grn_rc rc;
+ VALUE options, rb_timeout, rb_id;
+
+ rb_scan_args(argc, argv, "01", &options);
+
+ rb_grn_column_deconstruct(SELF(self), &column, &context,
+ NULL, NULL,
+ NULL, NULL, NULL);
+
+ rb_grn_scan_options(options,
+ "timeout", &rb_timeout,
+ "id", &rb_id,
+ NULL);
+
+ if (!NIL_P(rb_timeout))
+ timeout = NUM2UINT(rb_timeout);
+
+ if (!NIL_P(rb_id))
+ id = NUM2UINT(rb_id);
+
+ rc = grn_obj_lock(context, column, id, timeout);
+ rb_grn_context_check(context, self);
+ rb_grn_rc_check(rc, self);
+
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, Qnil, rb_grn_column_unlock_ensure, self);
+ } else {
+ return Qnil;
+ }
+}
+
+/*
+ * Document-method: clear_lock
+ *
+ * call-seq:
+ * column.clear_lock(options={})
+ *
+ * _column_のロックを強制的に解除する。
+ *
+ * 利用可能なオプションは以下の通り。
+ *
+ * [_:id_]
+ * _:id_で指定したレコードのロックを強制的に解除する。
+ * (注: groonga側が未実装のため、現在は無視される。実装さ
+ * れるのではないかと思っているが、実装されないかもしれな
+ * い。)
+ */
+static VALUE
+rb_grn_column_clear_lock (int argc, VALUE *argv, VALUE self)
+{
+ grn_id id = GRN_ID_NIL;
+ grn_ctx *context;
+ grn_obj *column;
+ VALUE options, rb_id;
+
+ rb_scan_args(argc, argv, "01", &options);
+
+ rb_grn_column_deconstruct(SELF(self), &column, &context,
+ NULL, NULL,
+ NULL, NULL, NULL);
+
+ rb_grn_scan_options(options,
+ "id", &rb_id,
+ NULL);
+
+ if (!NIL_P(rb_id))
+ id = NUM2UINT(rb_id);
+
+ grn_obj_clear_lock(context, column);
+
+ return Qnil;
+}
+
+/*
+ * Document-method: locked?
+ *
+ * call-seq:
+ * column.locked?(options={})
+ *
+ * _column_がロックされていれば+true+を返す。
+ *
+ * 利用可能なオプションは以下の通り。
+ *
+ * [_:id_]
+ * _:id_で指定したレコードがロックされていれば+true+を返す。
+ * (注: groonga側が未実装のため、現在は無視される。実装さ
+ * れるのではないかと思っているが、実装されないかもしれな
+ * い。)
+ */
+static VALUE
+rb_grn_column_is_locked (int argc, VALUE *argv, VALUE self)
+{
+ grn_id id = GRN_ID_NIL;
+ grn_ctx *context;
+ grn_obj *column;
+ VALUE options, rb_id;
+
+ rb_scan_args(argc, argv, "01", &options);
+
+ rb_grn_column_deconstruct(SELF(self), &column, &context,
+ NULL, NULL,
+ NULL, NULL, NULL);
+
+ rb_grn_scan_options(options,
+ "id", &rb_id,
+ NULL);
+
+ if (!NIL_P(rb_id))
+ id = NUM2UINT(rb_id);
+
+ return CBOOL2RVAL(grn_obj_is_locked(context, column));
+}
+
void
rb_grn_init_column (VALUE mGrn)
{
rb_cGrnColumn = rb_define_class_under(mGrn, "Column", rb_cGrnObject);
rb_define_method(rb_cGrnColumn, "table", rb_grn_column_get_table, 0);
rb_define_method(rb_cGrnColumn, "local_name",
rb_grn_column_get_local_name, 0);
rb_define_method(rb_cGrnColumn, "select", rb_grn_column_select, -1);
+ rb_define_method(rb_cGrnColumn, "lock", rb_grn_column_lock, -1);
+ rb_define_method(rb_cGrnColumn, "unlock", rb_grn_column_unlock, -1);
+ rb_define_method(rb_cGrnColumn, "clear_lock", rb_grn_column_clear_lock, -1);
+ rb_define_method(rb_cGrnColumn, "locked?", rb_grn_column_is_locked, -1);
rb_grn_init_fix_size_column(mGrn);
rb_grn_init_variable_size_column(mGrn);
rb_grn_init_index_column(mGrn);
}