static mrb_value mrb_grn_expression_parse(mrb_state *mrb, mrb_value self) { grn_ctx *ctx = (grn_ctx *)mrb->ud; grn_obj *expr; char *query; mrb_int query_size; grn_obj *default_column = NULL; grn_operator default_mode = GRN_OP_MATCH; grn_operator default_operator = GRN_OP_AND; grn_expr_flags flags = GRN_EXPR_SYNTAX_SCRIPT; mrb_value mrb_options = mrb_nil_value(); expr = DATA_PTR(self); mrb_get_args(mrb, "s|H", &query, &query_size, &mrb_options); if (!mrb_nil_p(mrb_options)) { mrb_value mrb_flags; mrb_flags = grn_mrb_options_get_lit(mrb, mrb_options, "flags"); if (!mrb_nil_p(mrb_flags)) { flags = mrb_fixnum(mrb_flags); } } grn_expr_parse(ctx, expr, query, query_size, default_column, default_mode, default_operator, flags); grn_mrb_ctx_check(mrb); return mrb_nil_value(); }
void test_expression_lifetime_over_database(void) { const gchar *path; gint i, n_tries = 100; grn_obj *expression; cut_omit("will be SEGVed."); path = cut_build_path(tmp_directory, "database.groonga", NULL); for (i = 0; i < n_tries; i++) { gint j, n_records = 100; const gchar *query; grn_obj *table, *variable; grn_obj default_column; database = grn_db_create(context, path, NULL); grn_test_assert_context(context); assert_send_command("table_create Sites 0 ShortText"); assert_send_command("column_create Sites point COLUMN_SCALAR Int32"); for (j = 0; j < n_records; j++) { gchar *command; command = g_strdup_printf("load '" "[[\"_key\", \"point\"]," "[\"http://groonga.org/version/%d\",%d]]' " "Sites", j, j); assert_send_command(command); g_free(command); } table = get_object("Sites"); GRN_EXPR_CREATE_FOR_QUERY(context, table, expression, variable); grn_obj_unlink(context, table); GRN_TEXT_INIT(&default_column, 0); GRN_TEXT_PUTS(context, &default_column, "point"); query = "point:50"; grn_expr_parse(context, expression, query, strlen(query), &default_column, GRN_OP_MATCH, GRN_OP_AND, GRN_EXPR_SYNTAX_QUERY | GRN_EXPR_ALLOW_COLUMN); grn_test_assert_context(context); grn_obj_unlink(context, &default_column); grn_expr_compile(context, expression); grn_obj_remove(context, database); database = NULL; remove_tmp_directory(); g_mkdir_with_parents(tmp_directory, 0700); } grn_ctx_fin(context); g_free(context); context = NULL; }
static grn_obj * query(const gchar *string) { GRN_EXPR_CREATE_FOR_QUERY(context, comments, expression, variable); grn_test_assert(grn_expr_parse(context, expression, string, strlen(string), content, GRN_OP_MATCH, GRN_OP_AND, GRN_EXPR_SYNTAX_QUERY | GRN_EXPR_ALLOW_PRAGMA | GRN_EXPR_ALLOW_COLUMN)); grn_test_assert_context(context); return expression; }
/* * 文字列 _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; }
void test_setoperation(gconstpointer data) { grn_operator operator; grn_obj *entries; grn_obj *result1; grn_obj *result2; const char *dump; operator = gcut_data_get_int(data, "operator"); assert_send_command("table_create Entries TABLE_HASH_KEY ShortText"); send_command( "load " "--table Entries " "--values '[{\"_key\": \"a\"}, {\"_key\": \"b\"}, {\"_key\": \"c\"}]'"); entries = grn_ctx_get(context, "Entries", -1); { const char *condition = "_id < 3"; grn_obj *expr; grn_obj *variable; GRN_EXPR_CREATE_FOR_QUERY(context, entries, expr, variable); grn_expr_parse(context, expr, condition, strlen(condition), NULL, GRN_OP_AND, GRN_OP_MATCH, GRN_EXPR_SYNTAX_SCRIPT); result1 = grn_table_select(context, entries, expr, NULL, GRN_OP_OR); grn_obj_unlink(context, expr); } { const char *condition = "_id > 1"; grn_obj *expr; grn_obj *variable; GRN_EXPR_CREATE_FOR_QUERY(context, entries, expr, variable); grn_expr_parse(context, expr, condition, strlen(condition), NULL, GRN_OP_AND, GRN_OP_MATCH, GRN_EXPR_SYNTAX_SCRIPT); result2 = grn_table_select(context, entries, expr, NULL, GRN_OP_OR); grn_obj_unlink(context, expr); } grn_table_setoperation(context, result1, result2, result1, operator); { grn_bool first_record = GRN_TRUE; grn_obj buffer; grn_obj *score_accessor; grn_obj score; GRN_TEXT_INIT(&buffer, 0); GRN_TEXT_PUTS(context, &buffer, "["); score_accessor = grn_obj_column(context, result1, GRN_COLUMN_NAME_SCORE, GRN_COLUMN_NAME_SCORE_LEN); GRN_FLOAT_INIT(&score, 0); GRN_TABLE_EACH_BEGIN(context, result1, cursor, id) { void *result_key; grn_id entry_id; char entry_key[GRN_TABLE_MAX_KEY_SIZE]; int entry_key_size; if (first_record) { first_record = GRN_FALSE; } else { GRN_TEXT_PUTS(context, &buffer, ", "); } GRN_TEXT_PUTS(context, &buffer, "["); grn_table_cursor_get_key(context, cursor, &result_key); entry_id = *((grn_id *)result_key); entry_key_size = grn_table_get_key(context, entries, entry_id, entry_key, GRN_TABLE_MAX_KEY_SIZE); GRN_TEXT_PUT(context, &buffer, entry_key, entry_key_size); GRN_TEXT_PUTS(context, &buffer, ", "); GRN_BULK_REWIND(&score); grn_obj_get_value(context, score_accessor, id, &score); grn_text_printf(context, &buffer, "%.1f", GRN_FLOAT_VALUE(&score)); GRN_TEXT_PUTS(context, &buffer, "]"); } GRN_TABLE_EACH_END(context, cursor); GRN_OBJ_FIN(context, &score); grn_obj_unlink(context, score_accessor); GRN_TEXT_PUTS(context, &buffer, "]"); dump = cut_take_strndup(GRN_TEXT_VALUE(&buffer), GRN_TEXT_LEN(&buffer)); GRN_OBJ_FIN(context, &buffer); }
void test_mroonga_index_score(void) { grn_obj *t1,*c1,*lc,*ft; grn_obj buff; grn_id r1,r2,r3,r4; const gchar *mrn_dir; mrn_dir = cut_build_path(tmp_directory, "mrn", NULL); g_mkdir_with_parents(mrn_dir, 0700); grn_obj_close(context, db); db = grn_db_create(context, cut_build_path(mrn_dir, "mroonga.grn", NULL), NULL); cut_assert_not_null(db); /* actual table */ t1 = grn_table_create(context, "t1", 2, cut_build_path(mrn_dir, "t1.grn", NULL), GRN_OBJ_TABLE_NO_KEY|GRN_OBJ_PERSISTENT, NULL, 0); cut_assert_not_null(t1); /* lexicon table */ lc = grn_table_create(context, "lc", 2, cut_build_path(mrn_dir, "lc.grn", NULL), GRN_OBJ_TABLE_PAT_KEY|GRN_OBJ_PERSISTENT, grn_ctx_at(context, GRN_DB_SHORT_TEXT), 0); cut_assert_not_null(lc); grn_test_assert(grn_obj_set_info(context, lc, GRN_INFO_DEFAULT_TOKENIZER, grn_ctx_at(context, GRN_DB_BIGRAM))); /* actual column */ c1 = grn_column_create(context, t1, "c1", 2, cut_build_path(mrn_dir, "t1.c1.grn", NULL), GRN_OBJ_COLUMN_SCALAR|GRN_OBJ_PERSISTENT, grn_ctx_at(context, GRN_DB_TEXT)); cut_assert_not_null(c1); /* fulltext index */ ft = grn_column_create(context, lc, "ft", 2, cut_build_path(mrn_dir, "lc.ft.grn", NULL), GRN_OBJ_COLUMN_INDEX|GRN_OBJ_PERSISTENT, t1); cut_assert_not_null(ft); GRN_TEXT_INIT(&buff,0); /* link between actual column and fulltext index */ GRN_UINT32_SET(context, &buff, grn_obj_id(context, c1)); grn_obj_set_info(context, ft, GRN_INFO_SOURCE, &buff); /* need to use grn_id */ /* insert row */ r1 = grn_table_add(context, t1, NULL, 0, NULL); cut_assert_equal_int(1,r1); GRN_TEXT_SETS(context, &buff, "abcde"); grn_test_assert(grn_obj_set_value(context, c1, r1, &buff, GRN_OBJ_SET)); r2 = grn_table_add(context, t1, NULL, 0, NULL); cut_assert_equal_int(2,r2); GRN_TEXT_SETS(context, &buff, "fghij"); grn_test_assert(grn_obj_set_value(context, c1, r2, &buff, GRN_OBJ_SET)); r3 = grn_table_add(context, t1, NULL, 0, NULL); cut_assert_equal_int(3,r3); GRN_TEXT_SETS(context, &buff, "11 22 33"); grn_test_assert(grn_obj_set_value(context, c1, r3, &buff, GRN_OBJ_SET)); r4 = grn_table_add(context, t1, NULL, 0, NULL); cut_assert_equal_int(4,r4); GRN_TEXT_SETS(context, &buff, "44 22 55"); grn_test_assert(grn_obj_set_value(context, c1, r4, &buff, GRN_OBJ_SET)); /* confirm record are inserted in both column and index */ cut_assert_equal_int(4,grn_table_size(context,t1)); cut_assert_equal_int(23,grn_table_size(context,lc)); /* nlq search */ { grn_id id, docid; grn_obj *res; grn_table_cursor *tc; grn_obj score, *score_column; res = grn_table_create(context, NULL, 0, NULL, GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, t1, 0); GRN_FLOAT_INIT(&score, 0); GRN_BULK_REWIND(&buff); GRN_TEXT_SETS(context, &buff, "hij"); grn_obj_search(context, ft, &buff, res, GRN_OP_OR, NULL); cut_assert_equal_int(1, grn_table_size(context, res)); score_column = grn_obj_column(context, res, "_score", 6); tc = grn_table_cursor_open(context, res, NULL, 0, NULL, 0, 0, -1, 0); while ((id = grn_table_cursor_next(context, tc))) { GRN_BULK_REWIND(&buff); grn_table_get_key(context, res, id, &docid, sizeof(grn_id)); cut_assert_equal_int(2, docid); cut_assert_not_null(grn_obj_get_value(context, c1, docid, &buff)); cut_assert_equal_int(5 ,GRN_TEXT_LEN(&buff)); cut_assert_equal_substring("fghij", (char*) GRN_BULK_HEAD(&buff),GRN_TEXT_LEN(&buff)); grn_obj_get_value(context, score_column, id, &score); cut_assert_equal_double(1.0, DBL_EPSILON, GRN_FLOAT_VALUE(&score)); } grn_table_cursor_close(context, tc); grn_obj_close(context, score_column); grn_obj_close(context, res); } /* boolean search */ { grn_id id, docid; grn_obj *res; grn_obj *match_columns, *match_columns_variable; grn_obj *expression, *expression_variable; grn_table_cursor *tc; grn_obj score, *score_column; const char *match_columns_expression = "c1 * 5"; const char *qstr = "+22 -55"; GRN_EXPR_CREATE_FOR_QUERY(context, t1, match_columns, match_columns_variable); grn_expr_parse(context, match_columns, match_columns_expression, strlen(match_columns_expression), NULL, GRN_OP_MATCH, GRN_OP_AND, GRN_EXPR_SYNTAX_SCRIPT); GRN_EXPR_CREATE_FOR_QUERY(context, t1, expression, expression_variable); res = grn_table_create(context, NULL, 0, NULL, GRN_TABLE_HASH_KEY|GRN_OBJ_WITH_SUBREC, t1, 0); grn_test_assert(grn_expr_parse(context, expression, qstr, strlen(qstr), match_columns, GRN_OP_MATCH, GRN_OP_OR, GRN_EXPR_SYNTAX_QUERY)); grn_table_select(context, t1, expression, res, GRN_OP_OR); cut_assert_equal_int(1, grn_table_size(context, res)); GRN_FLOAT_INIT(&score, 0); score_column = grn_obj_column(context, res, "_score", 6); tc = grn_table_cursor_open(context, res, NULL, 0, NULL, 0, 0, -1, 0); while ((id = grn_table_cursor_next(context, tc))) { GRN_BULK_REWIND(&buff); grn_table_get_key(context, res, id, &docid, sizeof(grn_id)); cut_assert_equal_int(3, docid); cut_assert_not_null(grn_obj_get_value(context, c1, docid, &buff)); cut_assert_equal_int(8, GRN_TEXT_LEN(&buff)); cut_assert_equal_substring("11 22 33", (char*) GRN_BULK_HEAD(&buff),GRN_TEXT_LEN(&buff)); grn_obj_get_value(context, score_column, id, &score); cut_assert_equal_double(5, DBL_EPSILON, GRN_FLOAT_VALUE(&score)); } grn_obj_close(context, expression); grn_obj_close(context, match_columns); grn_table_cursor_close(context ,tc); grn_obj_close(context, score_column); grn_obj_close(context, res); } grn_obj_close(context, &buff); grn_obj_close(context, ft); grn_obj_close(context, c1); grn_obj_close(context, lc); grn_obj_close(context, t1); }