/* * call-seq: * patricia_trie.scan(string) -> Array * patricia_trie.scan(string) {|record, word, start, length| ... } * * _string_を走査し、_patricia_trie_内に格納されているキーに * マッチした部分文字列の情報をブロックに渡す。複数のキーが * マッチする場合は最長一致するキーを優先する。 * * [_record_] * マッチしたキーのGroonga::Record。 * * [_word_] * マッチした部分文字列。 * * [_start_] * _string_内での_word_の出現位置。(バイト単位) * * [_length_] * _word_の長さ。(バイト単位) * * ブロックを指定しない場合は、マッチした部分文字列の情報を * まとめて配列として返す。 * * words = Groonga::PatriciaTrie.create(:key_type => "ShortText", * :key_normalize => true) * words.add("リンク") * adventure_of_link = words.add('リンクの冒険') * words.add('冒険') * gaxtu = words.add('ガッ') * muteki = words.add('MUTEKI') * * text = 'muTEki リンクの冒険 ミリバール ガッ' * words.scan(text).each do |record, word, start, length| * p [record.key, word, start, length] * # -> ["MUTEKI", "muTEki", 0, 6] * # -> ["リンクの冒険", "リンクの冒険", 7, 18] * # -> ["ガッ", "ガッ", 42, 6] * end * * words.scan(text) * # -> [[muteki, "muTEki", 0, 6], * # [adventure_of_link, "リンクの冒険", 7, 18], * # [gaxtu, "ガッ", 42, 6]] */ static VALUE rb_grn_patricia_trie_scan (VALUE self, VALUE rb_string) { grn_ctx *context; grn_obj *table; VALUE rb_result = Qnil; grn_pat_scan_hit hits[1024]; const char *string; long string_length; grn_bool block_given; string = StringValuePtr(rb_string); string_length = RSTRING_LEN(rb_string); rb_grn_table_key_support_deconstruct(SELF(self), &table, &context, NULL, NULL, NULL, NULL, NULL, NULL, NULL); block_given = rb_block_given_p(); if (!block_given) rb_result = rb_ary_new(); while (string_length > 0) { const char *rest; int i, n_hits; unsigned int previous_offset = 0; n_hits = grn_pat_scan(context, (grn_pat *)table, string, string_length, hits, sizeof(hits) / sizeof(*hits), &rest); for (i = 0; i < n_hits; i++) { VALUE record, term, matched_info; if (hits[i].offset < previous_offset) continue; record = rb_grn_record_new(self, hits[i].id, Qnil); term = rb_grn_context_rb_string_new(context, string + hits[i].offset, hits[i].length); matched_info = rb_ary_new3(4, record, term, UINT2NUM(hits[i].offset), UINT2NUM(hits[i].length)); if (block_given) { rb_yield(matched_info); } else { rb_ary_push(rb_result, matched_info); } previous_offset = hits[i].offset; } string_length -= rest - string; string = rest; } return rb_result; }
VALUE rb_grn_record_new_added (VALUE table, grn_id id, VALUE values) { VALUE record; record = rb_grn_record_new(table, id, values); rb_funcall(record, rb_intern("added="), 1, Qtrue); return record; }
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; }
/* * _table_ の _key_ に対応する {Groonga::Record} を返す。 * * 0.9.0から値ではなく {Groonga::Record} を返すようになった。 * * @overload [](key) * @return [Groonga::Record] */ static VALUE rb_grn_table_key_support_array_reference (VALUE self, VALUE rb_key) { grn_id id; id = rb_grn_table_key_support_get(self, rb_key); if (id == GRN_ID_NIL) { return Qnil; } else { return rb_grn_record_new(self, id, Qnil); } }
/* * call-seq: * table.add(key, values=nil) -> Groonga::Recordまたはnil * * 主キーが_key_のレコード追加し、追加したレコードを返す。レ * コードの追加に失敗した場合は+nil+を返す。 * * _values_にはレコードのカラムに設定する値を指定する。省略 * した場合または+nil+を指定した場合はカラムは設定しない。カ * ラムの値は<tt>{:カラム名1 => 値1, :カラム名2 => 値2, * ...}</tt>と指定する。 */ static VALUE rb_grn_table_key_support_add (int argc, VALUE *argv, VALUE self) { grn_id id; VALUE key, values; rb_scan_args(argc, argv, "11", &key, &values); id = rb_grn_table_key_support_add_raw(self, key); if (GRN_ID_NIL == id) return Qnil; else return rb_grn_record_new(self, id, values); }
static VALUE yield_record (VALUE user_data) { YieldRecordCallbackData *data = (YieldRecordCallbackData *)user_data; volatile VALUE record; if (data->id == GRN_ID_NIL) { record = Qnil; } else { record = rb_grn_record_new(data->self, data->id, Qnil); } data->record = record; return rb_yield(record); }
/* * 主キーが _key_ のレコード追加し、追加したレコードを返す。レ * コードの追加に失敗した場合は +nil+ を返す。 * * すでに同じキーのレコードが存在する場合は追加せずに同じレ * コードを返す。追加されたかどうかは * {Groonga::Record#added?} で調べることができる。 +true+ を返 * したら追加されたということを示す。 * * _values_ にはレコードのカラムに設定する値を指定する。省略 * した場合または +nil+ を指定した場合はカラムは設定しない。カ * ラムの値は @{:カラム名1 => 値1, :カラム名2 => 値2, * ...}@ と指定する。 * * @overload add(key, values=nil) * @return [Groonga::Recordまたはnil] */ static VALUE rb_grn_table_key_support_add (int argc, VALUE *argv, VALUE self) { grn_id id; VALUE key, values; int added = GRN_FALSE; rb_scan_args(argc, argv, "11", &key, &values); id = rb_grn_table_key_support_add_raw(self, key, &added); if (GRN_ID_NIL == id) { return Qnil; } else { if (added) { return rb_grn_record_new_added(self, id, values); } else { return rb_grn_record_new(self, id, values); } } }
/* * call-seq: * table_cursor.each {|record| ...} * * カーソルの範囲内にあるレコードを順番にブロックに渡す。 */ static VALUE rb_grn_table_cursor_each (VALUE self) { grn_id record_id; grn_ctx *context; grn_table_cursor *cursor; rb_grn_table_cursor_deconstruct(SELF(self), &cursor, &context, NULL, NULL, NULL, NULL); if (context && cursor) { while ((record_id = grn_table_cursor_next(context, cursor))) { rb_yield(rb_grn_record_new(rb_iv_get(self, "@table"), record_id, Qnil)); } } return Qnil; }
/* * call-seq: * table_cursor.next -> Groonga::Record * * カレントレコードを一件進めてそのレコードを返す。 */ static VALUE rb_grn_table_cursor_next (VALUE self) { VALUE rb_record = Qnil; grn_ctx *context; grn_table_cursor *cursor; rb_grn_table_cursor_deconstruct(SELF(self), &cursor, &context, NULL, NULL, NULL, NULL); if (context && cursor) { grn_id record_id; record_id = grn_table_cursor_next(context, cursor); if (record_id != GRN_ID_NIL) /* FIXME: use grn_table_cursor_table */ rb_record = rb_grn_record_new(rb_iv_get(self, "@table"), record_id, Qnil); } return rb_record; }
/* * call-seq: * array.add(values=nil) -> Groonga::Recordまたはnil * * レコード追加し、追加したレコードを返す。レコードの追加に失 * 敗した場合は+nil+を返す。 * * _values_にはレコードのカラムに設定する値を指定する。省略 * した場合または+nil+を指定した場合はカラムは設定しない。カ * ラムの値は<tt>{:カラム名1 => 値1, :カラム名2 => 値2, * ...}</tt>と指定する。 * * 使用例では、以下のようなユーザを格納するGroonga::Arrayが * 定義されているものとする。 * users = Groonga::Array.create(:name => "Users") * users.define_column("name", "ShortText") * users.define_column("uri", "ShortText") * * ユーザを追加する。 * user = users.add * * daijiroユーザを追加する。 * daijiro = users.add(:name => "daijiro") * * gunyara-kunユーザを追加する。 * gunyara_kun = users.add(:name => "gunyara-kun", * :uri => "http://d.hatena.ne.jp/tasukuchan/") */ static VALUE rb_grn_array_add (int argc, VALUE *argv, VALUE self) { grn_ctx *context = NULL; grn_obj *table; grn_id id; VALUE values; rb_scan_args(argc, argv, "01", &values); table = SELF(self, &context); id = grn_table_add(context, table, NULL, 0, NULL); rb_grn_context_check(context, self); if (GRN_ID_NIL == id) return Qnil; else return rb_grn_record_new(self, id, values); }
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; }
static VALUE rb_grn_bulk_to_ruby_object_by_range_type (grn_ctx *context, grn_obj *bulk, grn_obj *range, grn_id range_id, VALUE related_object, VALUE *rb_value) { grn_bool success = GRN_TRUE; if (!range && range_id != GRN_ID_NIL) { range = grn_ctx_at(context, range_id); } if (!range) return GRN_FALSE; switch (range->header.type) { case GRN_TABLE_HASH_KEY: case GRN_TABLE_PAT_KEY: case GRN_TABLE_DAT_KEY: case GRN_TABLE_NO_KEY: { grn_id id; id = *((grn_id *)GRN_BULK_HEAD(bulk)); if (id == GRN_ID_NIL) { *rb_value = Qnil; } else { VALUE rb_range; rb_range = GRNOBJECT2RVAL(Qnil, context, range, GRN_FALSE); *rb_value = rb_grn_record_new(rb_range, id, Qnil); } break; } case GRN_TYPE: if (range->header.flags & GRN_OBJ_KEY_VAR_SIZE) { *rb_value = rb_grn_context_rb_string_new(context, GRN_BULK_HEAD(bulk), GRN_BULK_VSIZE(bulk)); } else { switch (range->header.flags & GRN_OBJ_KEY_MASK) { case GRN_OBJ_KEY_UINT: *rb_value = INT2NUM(GRN_UINT32_VALUE(bulk)); break; case GRN_OBJ_KEY_INT: *rb_value = INT2NUM(GRN_INT32_VALUE(bulk)); break; case GRN_OBJ_KEY_FLOAT: *rb_value = rb_float_new(GRN_FLOAT_VALUE(bulk)); break; default: success = GRN_FALSE; } break; } break; default: success = GRN_FALSE; break; } return success; }
/* * 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; }