/* * 新しくデータベースを作成する。 * _options_ にはハッシュでオプションを指定する。 * * @example * # 一時データベースを作成: * Groonga::Database.create * * # 永続データベースを作成: * Groonga::Database.create(:path => "/tmp/db.groonga") * * @overload create(options=nil) * @return [Groonga::Database] 作成されたデータベースを返す。 * @param [::Hash] options The name and value * pairs. Omitted names are initialized as the default value. * @option options :path * データベースを保存するパス。省略すると一時データベース * となる。 * @option options :context (Groonga::Context.default) * データベースを結びつけるコンテキスト。省略すると * {Groonga::Context.default} を利用する。 */ static VALUE rb_grn_database_s_create (int argc, VALUE *argv, VALUE klass) { grn_ctx *context; grn_obj *old_database, *database; grn_db_create_optarg create_args; const char *path = NULL; VALUE rb_database; VALUE rb_path, options, rb_context, builtin_type_names; grn_bool owner; rb_scan_args(argc, argv, "01", &options); rb_grn_scan_options(options, "path", &rb_path, "context", &rb_context, "builtin_type_names", &builtin_type_names, NULL); if (!NIL_P(rb_path)) path = StringValuePtr(rb_path); context = rb_grn_context_ensure(&rb_context); create_args.builtin_type_names = NULL; create_args.n_builtin_type_names = 0; old_database = grn_ctx_db(context); if (old_database) grn_obj_unlink(context, old_database); reset_floating_objects(rb_context); database = grn_db_create(context, path, &create_args); rb_grn_context_check(context, rb_ary_new_from_values(argc, argv)); owner = (context->flags & GRN_CTX_PER_DB) ? GRN_FALSE : GRN_TRUE; rb_database = GRNOBJECT2RVAL(klass, context, database, owner); rb_iv_set(rb_database, "@context", rb_context); if (!NIL_P(rb_context)) rb_iv_set(rb_context, "database", rb_database); rb_grn_context_check(context, rb_ary_new_from_values(argc, argv)); if (rb_block_given_p()) return rb_ensure(rb_yield, rb_database, rb_grn_database_close, rb_database); else return rb_database; }
static inline void args_copy(struct args_info *args) { if (args->rest != Qfalse) { int argc = args->argc; args->argc = 0; args->rest = rb_ary_dup(args->rest); /* make dup */ /* * argv: [m0, m1, m2, m3] * rest: [a0, a1, a2, a3, a4, a5] * ^ * rest_index * * #=> first loop * * argv: [m0, m1] * rest: [m2, m3, a2, a3, a4, a5] * ^ * rest_index * * #=> 2nd loop * * argv: [] (argc == 0) * rest: [m0, m1, m2, m3, a2, a3, a4, a5] * ^ * rest_index */ while (args->rest_index > 0 && argc > 0) { RARRAY_ASET(args->rest, --args->rest_index, args->argv[--argc]); } while (argc > 0) { rb_ary_unshift(args->rest, args->argv[--argc]); } } else if (args->argc > 0) { args->rest = rb_ary_new_from_values(args->argc, args->argv); args->rest_index = 0; args->argc = 0; } }
/* * _expression_ から {Groonga::Snippet} を生成する。 _tags_ には * キーワードの前後に挿入するタグの配列を以下のような形式で指定 * する。 * * <pre> * !!!ruby * [ * ["キーワード前に挿入する文字列1", "キーワード後に挿入する文字列1"], * ["キーワード前に挿入する文字列2", "キーワード後に挿入する文字列2"], * # ..., * ] * </pre> * * もし、1つのスニペットの中に _tags_ で指定したタグより多くの * キーワードが含まれている場合は、以下のように、また、先頭 * のタグから順番に使われる。 * * <pre> * !!!ruby * expression.parse("Ruby groonga 検索") * tags = [["<tag1>", "</tag1>"], ["<tag2>", "</tag2>"]] * snippet = expression.snippet(tags) * p snippet.execute("Rubyでgroonga使って全文検索、高速検索。") * # => ["<tag1>Ruby</tag1>で<tag2>groonga</tag2>" * # => "使って全文<tag1>検索</tag1>、高速<tag2>検索</tag2>。"] * </pre> * * @overload snippet(tags, options) * @param tags [Array<string>] キーワードの前後に挿入するタグの配列 * (詳細は上記を参照) * @param options [::Hash] The name and value * pairs. Omitted names are initialized as the default value. * @option options :normalize (false) * キーワード文字列・スニペット元の文字列を正規化するかど * うか。省略した場合は +false+ で正規化しない。 * @option options :skip_leading_spaces (false) * 先頭の空白を無視するかどうか。省略した場合は +false+ で無 * 視しない。 * @option options :width (100) * スニペット文字列の長さ。省略した場合は100文字。 * @option options :max_results (3) * 生成するスニペットの最大数。省略した場合は3。 * @option options :html_escape (false) * スニペット内の +<+, +>+, +&+, +"+ をHTMLエスケープするか * どうか。省略した場合は +false+ で、HTMLエスケープしない。 * @return [Groonga::Snippet] */ static VALUE rb_grn_expression_snippet (int argc, VALUE *argv, VALUE self) { grn_ctx *context = NULL; grn_obj *expression; grn_obj *snippet; VALUE options; VALUE rb_normalize, rb_skip_leading_spaces; VALUE rb_width, rb_max_results, rb_tags; VALUE rb_html_escape; VALUE *rb_tag_values; VALUE related_object; unsigned int i; int flags = GRN_SNIP_COPY_TAG; unsigned int width = 100; unsigned int max_results = 3; unsigned int n_tags = 0; char **open_tags = NULL; unsigned int *open_tag_lengths = NULL; char **close_tags = NULL; unsigned int *close_tag_lengths = NULL; grn_snip_mapping *mapping = NULL; rb_grn_expression_deconstruct(SELF(self), &expression, &context, NULL, NULL, NULL, NULL, NULL); rb_scan_args(argc, argv, "11", &rb_tags, &options); rb_grn_scan_options(options, "normalize", &rb_normalize, "skip_leading_spaces", &rb_skip_leading_spaces, "width", &rb_width, "max_results", &rb_max_results, "html_escape", &rb_html_escape, NULL); if (TYPE(rb_tags) != T_ARRAY) { rb_raise(rb_eArgError, "tags should be " "[\"open_tag\", \"close_tag\"] or " "[[\"open_tag1\", \"close_tag1\"], ...]: %s", rb_grn_inspect(rb_tags)); } if (TYPE(RARRAY_PTR(rb_tags)[0]) == T_STRING) { rb_tags = rb_ary_new_from_args(1, rb_tags); } rb_tag_values = RARRAY_PTR(rb_tags); n_tags = RARRAY_LEN(rb_tags); open_tags = ALLOCA_N(char *, n_tags); open_tag_lengths = ALLOCA_N(unsigned int, n_tags); close_tags = ALLOCA_N(char *, n_tags); close_tag_lengths = ALLOCA_N(unsigned int, n_tags); for (i = 0; i < n_tags; i++) { VALUE *tag_pair; if (TYPE(rb_tag_values[i]) != T_ARRAY || RARRAY_LEN(rb_tag_values[i]) != 2) { rb_raise(rb_eArgError, "tags should be " "[\"open_tag\", \"close_tag\"] or" "[[\"open_tag1\", \"close_tag1\"], ...]: %s", rb_grn_inspect(rb_tags)); } tag_pair = RARRAY_PTR(rb_tag_values[i]); open_tags[i] = StringValuePtr(tag_pair[0]); open_tag_lengths[i] = RSTRING_LEN(tag_pair[0]); close_tags[i] = StringValuePtr(tag_pair[1]); close_tag_lengths[i] = RSTRING_LEN(tag_pair[1]); } if (RVAL2CBOOL(rb_normalize)) flags |= GRN_SNIP_NORMALIZE; if (RVAL2CBOOL(rb_skip_leading_spaces)) flags |= GRN_SNIP_SKIP_LEADING_SPACES; if (!NIL_P(rb_width)) width = NUM2UINT(rb_width); if (!NIL_P(rb_max_results)) max_results = NUM2UINT(rb_max_results); if (RVAL2CBOOL(rb_html_escape)) mapping = (grn_snip_mapping *)-1; snippet = grn_expr_snip(context, expression, flags, width, max_results, n_tags, (const char **)open_tags, open_tag_lengths, (const char **)close_tags, close_tag_lengths, mapping); related_object = rb_ary_new_from_args(2, self, rb_ary_new_from_values(argc, argv)); rb_grn_context_check(context, related_object); return GRNOBJECT2RVAL(Qnil, context, snippet, GRN_TRUE); }
/* * 文字列 _query_ をパースする。 * @overload parse(query, options={}) * @param [String] query パースする文字列 * @param [::Hash] options The name and value * pairs. Omitted names are initialized as the default value. * @option options :default_column * "column_name:hoge"ではなく"hoge"のようにcolumn_nameが指 * 定されない条件の検索対象となるカラムを指定する。 * @option options :default_operator (Groonga::Operator::AND) * "+"や"OR"で繋がれず、ただ列挙された複数の条件があった時、 * _expression_ 全体として各レコードをヒットとみなすかの論理 * 条件を指定する。省略した場合はGroonga::Operator::AND。 * * - Groonga::Operator::OR := * レコードはいずれかの条件にマッチすればいい。 =: * - Groonga::Operator::AND := * レコードは全ての条件にマッチしなければならない。 =: * - Groonga::Operator::AND_NOT := * 最初の条件にレコードはマッチし、残りの条件にレコードは * マッチしてはならない。 =: * * @option options :default_mode (Groonga::Operator::MATCH) * 検索時のモードを指定する。省略した場合はGroonga::Operator::MATCH。 * (FIXME: モードによってどういう動作になるかを書く。) * @option options :syntax (:query) * _query_ の構文を指定する。指定可能な値は以下の通り。省略 * した場合は +:query+ 。 * * - +:query+ := * 「文字列1 OR 文字列2」で「"文字列1"あるいは"文字列2" * にマッチという検索エンジンで利用できるような構文を使 * う。 * 参考: "Groongaのクエリ構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/query_syntax.html =: * - +nil+ := * +:query+と同様 =: * - +:script+ := * 「[カラム名] == [値]」というようにECMAScript風の構文を使う。 * 参考: "Groongaのscript構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/script_syntax.html =: * @option options :allow_pragma * _query_ の構文に query を用いているとき( +:syntax+ * オプション参照)、「*E-1」というようにクエリの先頭で * pragmaを利用できるようにする。script構文を用いている * ときはこのオプションを利用できない。 * * デフォルトではプラグマを利用できる。 * * 参考: "Groongaのクエリ構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/query_syntax.html * @option options :allow_column * _query_ の構文にqueryを用いているとき( +:syntax+ オプショ * ン参照)、「カラム名:値」というようにカラム名を指定した * 条件式を利用できるようにする。script構文を用いていると * きはこのオプションを利用できない。 * * デフォルトではカラム名を指定した条件式を利用できる。 * * 参考: "Groongaのクエリ構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/query_syntax.html * @option options :allow_update * _query_ の構文にscriptを用いているとき( +:syntax+ オプショ * ン参照)、「カラム名 = 値」というように更新操作を利用で * きるようにする。query構文を用いているときはこのオプショ * ンを利用できない。 * * デフォルトでは更新操作を利用できる。 * * 参考: "Groongaのクエリ構文のドキュメント":http://groonga.org/ja/docs/reference/grn_expr/query_syntax.html */ static VALUE rb_grn_expression_parse (int argc, VALUE *argv, VALUE self) { grn_ctx *context = NULL; grn_obj *expression, *default_column; grn_bool default_column_is_created = GRN_FALSE; grn_operator default_operator = GRN_OP_AND; grn_operator default_mode = GRN_OP_MATCH; grn_rc rc; char *query = NULL; unsigned query_size = 0; grn_expr_flags flags = 0; VALUE options, rb_query, rb_default_column, rb_default_operator; VALUE rb_default_mode, rb_syntax; VALUE rb_allow_pragma, rb_allow_column, rb_allow_update, rb_allow_leading_not; VALUE exception = Qnil; rb_scan_args(argc, argv, "11", &rb_query, &options); rb_grn_scan_options(options, "default_column", &rb_default_column, "default_operator", &rb_default_operator, "default_mode", &rb_default_mode, "syntax", &rb_syntax, "allow_pragma", &rb_allow_pragma, "allow_column", &rb_allow_column, "allow_update", &rb_allow_update, "allow_leading_not", &rb_allow_leading_not, NULL); query = StringValuePtr(rb_query); query_size = RSTRING_LEN(rb_query); rb_grn_expression_deconstruct(SELF(self), &expression, &context, NULL, NULL, NULL, NULL, NULL); if (NIL_P(rb_default_column)) { default_column = NULL; } else if (RVAL2CBOOL(rb_obj_is_kind_of(rb_default_column, rb_cGrnObject))) { default_column = RVAL2GRNOBJECT(rb_default_column, &context); } else { default_column = RVAL2GRNBULK(rb_default_column, context, NULL); default_column_is_created = GRN_TRUE; } if (!NIL_P(rb_default_mode)) default_mode = RVAL2GRNOPERATOR(rb_default_mode); if (!NIL_P(rb_default_operator)) default_operator = RVAL2GRNSETOPERATOR(rb_default_operator); if (NIL_P(rb_syntax) || rb_grn_equal_option(rb_syntax, "query")) { flags = GRN_EXPR_SYNTAX_QUERY; } else if (rb_grn_equal_option(rb_syntax, "script")) { flags = GRN_EXPR_SYNTAX_SCRIPT; } else { rb_raise(rb_eArgError, "syntax should be one of " "[nil, :query, :script]: %s", rb_grn_inspect(rb_syntax)); } if (NIL_P(rb_allow_pragma)) { if (!(flags & GRN_EXPR_SYNTAX_SCRIPT)) flags |= GRN_EXPR_ALLOW_PRAGMA; } else { if ((flags & GRN_EXPR_SYNTAX_SCRIPT)) rb_raise(rb_eArgError, ":allow_pragma isn't allowed in script syntax"); if (RVAL2CBOOL(rb_allow_pragma)) flags |= GRN_EXPR_ALLOW_PRAGMA; } if (NIL_P(rb_allow_column)) { if (!(flags & GRN_EXPR_SYNTAX_SCRIPT)) flags |= GRN_EXPR_ALLOW_COLUMN; } else { if ((flags & GRN_EXPR_SYNTAX_SCRIPT)) rb_raise(rb_eArgError, ":allow_column isn't allowed in script syntax"); if (RVAL2CBOOL(rb_allow_column)) flags |= GRN_EXPR_ALLOW_COLUMN; } if (NIL_P(rb_allow_update)) { flags |= GRN_EXPR_ALLOW_UPDATE; } else { if (RVAL2CBOOL(rb_allow_update)) flags |= GRN_EXPR_ALLOW_UPDATE; } if (!NIL_P(rb_allow_leading_not)) { if (RVAL2CBOOL(rb_allow_leading_not)) flags |= GRN_EXPR_ALLOW_LEADING_NOT; } rc = grn_expr_parse(context, expression, query, query_size, default_column, default_mode, default_operator, flags); if (rc != GRN_SUCCESS) { VALUE related_object; related_object = rb_ary_new_from_args(2, self, rb_ary_new_from_values(argc, argv)); exception = rb_grn_context_to_exception(context, related_object); } if (default_column_is_created) grn_obj_unlink(context, default_column); if (!NIL_P(exception)) rb_exc_raise(exception); return Qnil; }
/* * 各レコードをキーで管理するテーブルを生成する。ブロックを指 * 定すると、そのブロックに生成したテーブルが渡され、ブロック * を抜けると自動的にテーブルが破棄される。 * * @example * #無名一時テーブルを生成する。 * Groonga::Hash.create * * #無名永続テーブルを生成する。 * Groonga::Hash.create(:path => "/tmp/hash.grn") * * #名前付き永続テーブルを生成する。ただし、ファイル名は気にしない。 * Groonga::Hash.create(:name => "Bookmarks", * :persistent => true) * * #それぞれのレコードに512バイトの値を格納できる無名一時テーブルを生成する。 * Groonga::Hash.create(:value => 512) * * #キーとして文字列を使用する無名一時テーブルを生成する。 * Groonga::Hash.create(:key_type => Groonga::Type::SHORT_TEXT) * * #キーとして文字列を使用する無名一時テーブルを生成する。 * (キーの種類を表すオブジェクトは文字列で指定。) * Groonga::Hash.create(:key_type => "ShortText") * * #キーとしてBookmarksテーブルのレコードを使用す * る無名一時テーブルを生成する。 * bookmarks = Groonga::Hash.create(:name => "Bookmarks") * Groonga::Hash.create(:key_type => bookmarks) * * #キーとしてBookmarksテーブルのレコードを使用す * #る無名一時テーブルを生成する。(テーブルは文字列で指定。) * Groonga::Hash.create(:name => "Bookmarks") * Groonga::Hash.create(:key_type => "Bookmarks") * * #全文検索用のトークンをバイグラムで切り出す無名一時テーブ * #ルを生成する。 * bookmarks = Groonga::Hash.create(:name => "Bookmarks") * bookmarks.define_column("comment", "Text") * terms = Groonga::Hash.create(:name => "Terms", * :default_tokenizer => "TokenBigram") * terms.define_index_column("content", bookmarks, * :source => "Bookmarks.comment") * * @overload create(options={}) * @return [Groonga::Hash] * @!macro hash.create.options * @param [::Hash] options The name and value * pairs. Omitted names are initialized as the default value * @option options [Groonga::Context] :context (Groonga::Context.default) * テーブルが利用する {Groonga::Context} 。省略すると * {Groonga::Context.default} を用いる。 * @option options [Groonga::Context#[]] :name * テーブルの名前。名前をつけると、 {Groonga::Context#[]} に名 * 前を指定してテーブルを取得することができる。省略すると * 無名テーブルになり、テーブルIDでのみ取得できる。 * @option options [Groonga::Context#[]] :path * テーブルを保存するパス。パスを指定すると永続テーブルとな * り、プロセス終了後もレコードは保持される。次回起動時に * {Groonga::Context#[]} で保存されたレコードを利用することが * できる。省略すると一時テーブルになり、プロセスが終了する * とレコードは破棄される。 * @option options :persistent * +true+ を指定すると永続テーブルとなる。 +path+ を省略した * 場合は自動的にパスが付加される。 +:context+ で指定した * {Groonga::Context} に結びついているデータベースが一時デー * タベースの場合は例外が発生する。 * * @option options :key_normalize (false) Keys are normalized * if this value is @true@. * * @deprecated Use @:normalizer => "NormalizerAuto"@ instead. * * @option options [Boolean] :key_large (false) * It specifies whether total key size is large or not. The * default total key size is 4GiB. Large total key size is 1TiB. * * @since 6.0.1 * * @option options :key_type * キーの種類を示すオブジェクトを指定する。キーの種類には型 * 名("Int32"や"ShortText"など)または {Groonga::Type} または * テーブル( {Groonga::Array} 、 {Groonga::Hash} 、 * {Groonga::PatriciaTrie} のどれか)を指定する。 * * {Groonga::Type} を指定した場合は、その型が示す範囲の値をキー * として使用する。ただし、キーの最大サイズは4096バイトで * あるため、 {Groonga::Type::TEXT} や {Groonga::Type::LONG_TEXT} * は使用できない。 * * テーブルを指定した場合はレコードIDをキーとして使用する。 * 指定したテーブルの {Groonga::Record} をキーとして使用するこ * ともでき、その場合は自動的に {Groonga::Record} からレコード * IDを取得する。 * * 省略した場合はShortText型をキーとして使用する。この場合、 * 4096バイトまで使用可能である。 * @option options :value_type * 値の型を指定する。省略すると値のための領域を確保しない。 * 値を保存したい場合は必ず指定すること。 * * 参考: {Groonga::Type.new} * @option options [Groonga::IndexColumn] :default_tokenizer * {Groonga::IndexColumn} で使用するトークナイザを指定する。 * デフォルトでは何も設定されていないので、テーブルに * {Groonga::IndexColumn} を定義する場合は * @"TokenBigram"@ などを指定する必要がある。 * * @option options [::Array<String, Groonga::Procedure>, nil] * :token_filters (nil) The token filters to be used in the * table. * * @option options [Groonga::Record#n_sub_records] :sub_records * +true+ を指定すると {#group} でグループ化したときに、 * {Groonga::Record#n_sub_records} でグループに含まれるレコー * ドの件数を取得できる。 * * @option options [String, Groonga::Procedure, nil] :normalizer * The normalizer that is used by {Groonga::IndexColumn}. You * can specify this by normalizer name as String such as * @"NormalizerAuto"@ or normalizer object. * * @!macro hash.create.options * @overload create(options={}) * @yield [table] * @!macro hash.create.options */ static VALUE rb_grn_hash_s_create (int argc, VALUE *argv, VALUE klass) { grn_ctx *context; grn_obj *key_type = NULL, *value_type = NULL, *table; const char *name = NULL, *path = NULL; unsigned name_size = 0; grn_table_flags flags = GRN_OBJ_TABLE_HASH_KEY; VALUE rb_table; VALUE options, rb_context, rb_name, rb_path, rb_persistent; VALUE rb_key_normalize; VALUE rb_key_large; VALUE rb_key_type, rb_value_type, rb_default_tokenizer; VALUE rb_token_filters; VALUE rb_sub_records; VALUE rb_normalizer; rb_scan_args(argc, argv, "01", &options); rb_grn_scan_options(options, "context", &rb_context, "name", &rb_name, "path", &rb_path, "persistent", &rb_persistent, "key_normalize", &rb_key_normalize, "key_large", &rb_key_large, "key_type", &rb_key_type, "value_type", &rb_value_type, "default_tokenizer", &rb_default_tokenizer, "token_filters", &rb_token_filters, "sub_records", &rb_sub_records, "normalizer", &rb_normalizer, NULL); context = rb_grn_context_ensure(&rb_context); if (!NIL_P(rb_name)) { name = StringValuePtr(rb_name); name_size = RSTRING_LEN(rb_name); flags |= GRN_OBJ_PERSISTENT; } if (!NIL_P(rb_path)) { path = StringValueCStr(rb_path); flags |= GRN_OBJ_PERSISTENT; } if (RVAL2CBOOL(rb_persistent)) flags |= GRN_OBJ_PERSISTENT; if (RVAL2CBOOL(rb_key_normalize)) flags |= GRN_OBJ_KEY_NORMALIZE; if (RVAL2CBOOL(rb_key_large)) flags |= GRN_OBJ_KEY_LARGE; if (NIL_P(rb_key_type)) { key_type = grn_ctx_at(context, GRN_DB_SHORT_TEXT); } else { key_type = RVAL2GRNOBJECT(rb_key_type, &context); } if (!NIL_P(rb_value_type)) value_type = RVAL2GRNOBJECT(rb_value_type, &context); if (RVAL2CBOOL(rb_sub_records)) flags |= GRN_OBJ_WITH_SUBREC; table = grn_table_create(context, name, name_size, path, flags, key_type, value_type); if (!table) rb_grn_context_check(context, rb_ary_new_from_values(argc, argv)); rb_table = GRNOBJECT2RVAL(klass, context, table, GRN_TRUE); if (!NIL_P(rb_default_tokenizer)) rb_funcall(rb_table, rb_intern("default_tokenizer="), 1, rb_default_tokenizer); if (!NIL_P(rb_token_filters)) rb_funcall(rb_table, rb_intern("token_filters="), 1, rb_token_filters); if (!NIL_P(rb_normalizer)) rb_funcall(rb_table, rb_intern("normalizer="), 1, rb_normalizer); if (rb_block_given_p()) return rb_ensure(rb_yield, rb_table, rb_grn_object_close, rb_table); else return rb_table; }