Пример #1
0
/*
 * 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;
}
Пример #2
0
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;
}
Пример #3
0
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;
}
Пример #4
0
/*
 * _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);
    }
}
Пример #5
0
/*
 * 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);
}
Пример #6
0
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);
}
Пример #7
0
/*
 * 主キーが _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);
        }
    }
}
Пример #8
0
/*
 * 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;
}
Пример #9
0
/*
 * 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;
}
Пример #10
0
/*
 * 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);
}
Пример #11
0
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;
}
Пример #12
0
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;
}
Пример #13
0
/*
 * 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;
}