VALUE rb_grn_value_to_ruby_object (grn_ctx *context, grn_obj *value, grn_obj *range, VALUE related_object) { if (!value) return Qnil; switch (value->header.type) { case GRN_VOID: return Qnil; break; case GRN_BULK: if (GRN_BULK_EMPTYP(value)) return Qnil; if (value->header.domain == GRN_ID_NIL && range) value->header.domain = grn_obj_id(context, range); return GRNBULK2RVAL(context, value, range, related_object); break; case GRN_UVECTOR: { VALUE rb_value, rb_range = Qnil; grn_id *uvector, *uvector_end; rb_value = rb_ary_new(); if (range) rb_range = GRNTABLE2RVAL(context, range, GRN_FALSE); uvector = (grn_id *)GRN_BULK_HEAD(value); uvector_end = (grn_id *)GRN_BULK_CURR(value); for (; uvector < uvector_end; uvector++) { VALUE record = Qnil; if (*uvector != GRN_ID_NIL) record = rb_grn_record_new(rb_range, *uvector, Qnil); rb_ary_push(rb_value, record); } return rb_value; } break; case GRN_VECTOR: return GRNVECTOR2RVAL(context, value); break; default: rb_raise(rb_eGrnError, "unsupported value type: %s(%#x): %s", rb_grn_inspect_type(value->header.type), value->header.type, rb_grn_inspect(related_object)); break; } if (!range) return GRNOBJECT2RVAL(Qnil, context, value, GRN_FALSE); return Qnil; }
/* * 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, 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 = Qnil; rb_query = Qnil; 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(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], " "[expression, option_hash] " "or [option_hash]: %s", rb_grn_inspect(rb_ary_new4(argc, argv))); 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); if (NIL_P(rb_result)) { result = grn_table_create(context, NULL, 0, NULL, GRN_TABLE_HASH_KEY | GRN_OBJ_WITH_SUBREC, table, 0); rb_result = GRNTABLE2RVAL(context, result, RB_GRN_TRUE); } else { result = RVAL2GRNTABLE(rb_result, &context); } 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; }
VALUE rb_grn_uvector_to_ruby_object (grn_ctx *context, grn_obj *uvector, grn_obj *range, VALUE related_object) { VALUE array = Qnil; if (!uvector) return Qnil; if (!range) { rb_raise(rb_eTypeError, "unknown range uvector can't be converted: <%s>", rb_grn_inspect(related_object)); } switch (range->header.type) { case GRN_TYPE: { const char *current, *end; grn_id range_id; grn_obj value; int value_size; value_size = grn_obj_get_range(context, range); array = rb_ary_new(); current = GRN_BULK_HEAD(uvector); end = GRN_BULK_CURR(uvector); range_id = grn_obj_id(context, range); GRN_OBJ_INIT(&value, GRN_BULK, GRN_OBJ_DO_SHALLOW_COPY, range_id); while (current < end) { VALUE rb_value; GRN_TEXT_SET(context, &value, current, value_size); rb_value = GRNBULK2RVAL(context, &value, range, related_object); rb_ary_push(array, rb_value); current += value_size; } GRN_OBJ_FIN(context, &value); break; } case GRN_TABLE_HASH_KEY: case GRN_TABLE_PAT_KEY: case GRN_TABLE_DAT_KEY: case GRN_TABLE_NO_KEY: { grn_id *current, *end; VALUE rb_range = Qnil; array = rb_ary_new(); rb_range = GRNTABLE2RVAL(context, range, GRN_FALSE); current = (grn_id *)GRN_BULK_HEAD(uvector); end = (grn_id *)GRN_BULK_CURR(uvector); while (current < end) { VALUE record = Qnil; if (*current != GRN_ID_NIL) { record = rb_grn_record_new(rb_range, *current, Qnil); } rb_ary_push(array, record); current++; } break; } default: rb_raise(rb_eTypeError, "unknown range uvector can't be converted: %s(%#x): <%s>", rb_grn_inspect_type(range->header.type), range->header.type, rb_grn_inspect(related_object)); break; } return array; }
/* * It gets a value of variable size column value for the record that * ID is _id_. * * @example Gets weight vector value * Groonga::Schema.define do |schema| * schema.create_table("Products", * :type => :patricia_trie, * :key_type => "ShortText") do |table| * # This is weight vector. * # ":with_weight => true" is important to store weight value. * table.short_text("tags", * :type => :vector, * :with_weight => true) * end * end * * products = Groonga["Products"] * rroonga = products.add("Rroonga") * rroonga.tags = [ * { * :value => "ruby", * :weight => 100, * }, * { * :value => "groonga", * :weight => 10, * }, * ] * * p rroonga.tags * # => [ * # {:value => "ruby", :weight => 100}, * # {:value => "groonga", :weight => 10} * # ] * * @overload [](id) * @param [Integer, Record] id The record ID. * @return [Array<Hash<Symbol, String>>] An array of value if the column * is a weight vector column. * Each value is a Hash like the following form: * * <pre> * { * :value => [KEY], * :weight => [WEIGHT], * } * </pre> * * @[KEY]@ is the key of the table that is specified as range on * creating the weight vector. * * @[WEIGHT]@ is a positive integer. * * @return [::Object] See {Groonga::Object#[]} for columns except * weight vector column. * * @since 4.0.1. */ static VALUE rb_grn_variable_size_column_array_reference (VALUE self, VALUE rb_id) { grn_ctx *context = NULL; grn_obj *column, *range; grn_id id; grn_obj *value; VALUE rb_value; VALUE rb_range; unsigned int i, n; rb_grn_variable_size_column_deconstruct(SELF(self), &column, &context, NULL, NULL, &value, NULL, NULL, &range); if (!(column->header.flags & GRN_OBJ_WITH_WEIGHT)) { return rb_call_super(1, &rb_id); } id = RVAL2GRNID(rb_id, context, range, self); grn_obj_reinit(context, value, value->header.domain, value->header.flags | GRN_OBJ_VECTOR); grn_obj_get_value(context, column, id, value); rb_grn_context_check(context, self); rb_range = GRNTABLE2RVAL(context, range, GRN_FALSE); n = grn_vector_size(context, value); rb_value = rb_ary_new2(n); for (i = 0; i < n; i++) { VALUE rb_element_value; unsigned int weight = 0; grn_id domain; VALUE rb_element; if (value->header.type == GRN_UVECTOR) { grn_id id; id = grn_uvector_get_element(context, value, i, &weight); rb_element_value = rb_grn_record_new(rb_range, id, Qnil); } else { const char *element_value; unsigned int element_value_length; element_value_length = grn_vector_get_element(context, value, i, &element_value, &weight, &domain); rb_element_value = rb_str_new(element_value, element_value_length); } rb_element = rb_hash_new(); rb_hash_aset(rb_element, ID2SYM(rb_intern("value")), rb_element_value); rb_hash_aset(rb_element, ID2SYM(rb_intern("weight")), UINT2NUM(weight)); rb_ary_push(rb_value, rb_element); } return rb_value; }