Skip to content

Commit

Permalink
proc_highlight: add highlight()
Browse files Browse the repository at this point in the history
  • Loading branch information
naoa committed Feb 20, 2016
1 parent db9fb72 commit fed4adb
Show file tree
Hide file tree
Showing 15 changed files with 394 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/grn_proc.h
Expand Up @@ -37,6 +37,7 @@ void grn_proc_init_config_set(grn_ctx *ctx);
void grn_proc_init_config_delete(grn_ctx *ctx);
void grn_proc_init_edit_distance(grn_ctx *ctx);
void grn_proc_init_fuzzy_search(grn_ctx *ctx);
void grn_proc_init_highlight(grn_ctx *ctx);
void grn_proc_init_highlight_full(grn_ctx *ctx);
void grn_proc_init_highlight_html(grn_ctx *ctx);
void grn_proc_init_inspect(grn_ctx *ctx);
Expand Down
1 change: 1 addition & 0 deletions lib/proc.c
Expand Up @@ -6364,4 +6364,5 @@ grn_db_init_builtin_query(grn_ctx *ctx)
grn_proc_init_object_remove(ctx);

grn_proc_init_snippet(ctx);
grn_proc_init_highlight(ctx);
}
117 changes: 117 additions & 0 deletions lib/proc/proc_highlight.c
Expand Up @@ -235,6 +235,123 @@ highlight_keywords(grn_ctx *ctx, grn_user_data *user_data,
return highlighted;
}

static grn_obj *
func_highlight(grn_ctx *ctx, int nargs, grn_obj **args,
grn_user_data *user_data)
{
grn_obj *highlighted = NULL;

#define N_REQUIRED_ARGS 1
if (nargs > N_REQUIRED_ARGS) {
grn_obj *string = args[0];
grn_bool use_html_escape = GRN_FALSE;
grn_obj *keywords;
const char *normalizer_name = "NormalizerAuto";
unsigned int normalizer_name_length = 14;
const char *default_open_tag = NULL;
unsigned int default_open_tag_length = 0;
const char *default_close_tag = NULL;
unsigned int default_close_tag_length = 0;
grn_obj *end_arg = args[nargs - 1];
int n_args_without_option = nargs;

if (end_arg->header.type == GRN_PTR) {
grn_obj *hash;
hash = GRN_PTR_VALUE(end_arg);

if (hash) {
grn_hash_cursor *cursor;
void *key;
grn_obj *value;
int key_size;
if (hash->header.type != GRN_TABLE_HASH_KEY) {
GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT,
"highlight(): "
"end argument must be object literal: <%.*s>",
(int)GRN_TEXT_LEN(end_arg),
GRN_TEXT_VALUE(end_arg));
goto exit;
}
n_args_without_option--;

cursor = grn_hash_cursor_open(ctx, (grn_hash *)hash,
NULL, 0, NULL, 0,
0, -1, 0);
if (!cursor) {
GRN_PLUGIN_ERROR(ctx, GRN_NO_MEMORY_AVAILABLE,
"highlight(): couldn't open cursor");
goto exit;
}
while (grn_hash_cursor_next(ctx, cursor) != GRN_ID_NIL) {
grn_hash_cursor_get_key_value(ctx, cursor, &key, &key_size, (void **)&value);
if (key_size == 10 && !memcmp(key, "normalizer", 10)) {
normalizer_name = GRN_TEXT_VALUE(value);
normalizer_name_length = GRN_TEXT_LEN(value);
} else if (key_size == 11 && !memcmp(key, "html_escape", 11)) {
if (GRN_BOOL_VALUE(value)) {
use_html_escape = GRN_TRUE;
}
} else if (key_size == 16 && !memcmp(key, "default_open_tag", 16)) {
default_open_tag = GRN_TEXT_VALUE(value);
default_open_tag_length = GRN_TEXT_LEN(value);
} else if (key_size == 17 && !memcmp(key, "default_close_tag", 17)) {
default_close_tag = GRN_TEXT_VALUE(value);
default_close_tag_length = GRN_TEXT_LEN(value);
} else {
GRN_PLUGIN_ERROR(ctx, GRN_INVALID_ARGUMENT, "invalid option name: %.*s",
key_size, (char *)key);
grn_hash_cursor_close(ctx, cursor);
goto exit;
}
}
grn_hash_cursor_close(ctx, cursor);
}
}

keywords =
func_highlight_create_keywords_table(ctx, user_data,
normalizer_name,
normalizer_name_length);

if (keywords) {
grn_obj **keyword_args = args + N_REQUIRED_ARGS;
unsigned int n_keyword_args = n_args_without_option - N_REQUIRED_ARGS;
if (default_open_tag_length == 0 && default_close_tag_length == 0) {
highlighted = highlight_keyword_sets(ctx, user_data,
keyword_args, n_keyword_args,
string, keywords, use_html_escape);
} else {
unsigned int i;
for (i = 0; i < n_keyword_args; i++) {
grn_table_add(ctx, keywords,
GRN_TEXT_VALUE(keyword_args[i]),
GRN_TEXT_LEN(keyword_args[i]),
NULL);
}
highlighted = highlight_keywords(ctx, user_data,
string, keywords, use_html_escape,
default_open_tag, default_open_tag_length,
default_close_tag, default_close_tag_length);
}
}
}
#undef N_REQUIRED_ARGS

exit :
if (!highlighted) {
highlighted = grn_plugin_proc_alloc(ctx, user_data, GRN_DB_VOID, 0);
}

return highlighted;
}

void
grn_proc_init_highlight(grn_ctx *ctx)
{
grn_proc_create(ctx, "highlight", -1, GRN_PROC_FUNCTION,
func_highlight, NULL, NULL, 0, NULL);
}

static grn_obj *
func_highlight_full(grn_ctx *ctx, int nargs, grn_obj **args,
grn_user_data *user_data)
Expand Down
33 changes: 33 additions & 0 deletions test/command/suite/select/function/highlight/default.expected
@@ -0,0 +1,33 @@
table_create Entries TABLE_NO_KEY
[[0,0.0,0.0],true]
column_create Entries body COLUMN_SCALAR ShortText
[[0,0.0,0.0],true]
load --table Entries
[
{"body": "<b>Rroonga</b> is a Ruby binding of Groonga."}
]
[[0,0.0,0.0],1]
select Entries --output_columns 'highlight(body, "groonga", "<span class=\\"keyword1\\">", "</span>" )' --command_version 2
[
[
0,
0.0,
0.0
],
[
[
[
1
],
[
[
"highlight",
"null"
]
],
[
"<b>Rroonga</b> is a Ruby binding of <span class=\"keyword1\">Groonga</span>."
]
]
]
]
12 changes: 12 additions & 0 deletions test/command/suite/select/function/highlight/default.test
@@ -0,0 +1,12 @@
table_create Entries TABLE_NO_KEY
column_create Entries body COLUMN_SCALAR ShortText

load --table Entries
[
{"body": "<b>Rroonga</b> is a Ruby binding of Groonga."}
]

select Entries --output_columns \
'highlight(body, \
"groonga", "<span class=\\"keyword1\\">", "</span>" \
)' --command_version 2
33 changes: 33 additions & 0 deletions test/command/suite/select/function/highlight/default_tag.expected
@@ -0,0 +1,33 @@
table_create Entries TABLE_NO_KEY
[[0,0.0,0.0],true]
column_create Entries body COLUMN_SCALAR ShortText
[[0,0.0,0.0],true]
load --table Entries
[
{"body": "Mroonga is a MySQL storage engine based on Groonga."}
]
[[0,0.0,0.0],1]
select Entries --output_columns 'highlight(body, "Groonga", "Mroonga", {"default_open_tag": "<span>", "default_close_tag": "</span>"} )' --command_version 2
[
[
0,
0.0,
0.0
],
[
[
[
1
],
[
[
"highlight",
"null"
]
],
[
"<span>Mroonga</span> is a MySQL storage engine based on <span>Groonga</span>."
]
]
]
]
13 changes: 13 additions & 0 deletions test/command/suite/select/function/highlight/default_tag.test
@@ -0,0 +1,13 @@
table_create Entries TABLE_NO_KEY
column_create Entries body COLUMN_SCALAR ShortText

load --table Entries
[
{"body": "Mroonga is a MySQL storage engine based on Groonga."}
]

select Entries --output_columns \
'highlight(body, \
"Groonga", "Mroonga", \
{"default_open_tag": "<span>", "default_close_tag": "</span>"} \
)' --command_version 2
33 changes: 33 additions & 0 deletions test/command/suite/select/function/highlight/html_escape.expected
@@ -0,0 +1,33 @@
table_create Entries TABLE_NO_KEY
[[0,0.0,0.0],true]
column_create Entries body COLUMN_SCALAR ShortText
[[0,0.0,0.0],true]
load --table Entries
[
{"body": "<b>Rroonga</b> is a Ruby binding of Groonga."}
]
[[0,0.0,0.0],1]
select Entries --output_columns 'highlight(body, "groonga", "<span class=\\"keyword1\\">", "</span>", {"html_escape": true} )' --command_version 2
[
[
0,
0.0,
0.0
],
[
[
[
1
],
[
[
"highlight",
"null"
]
],
[
"&lt;b&gt;Rroonga&lt;/b&gt; is a Ruby binding of <span class=\"keyword1\">Groonga</span>."
]
]
]
]
13 changes: 13 additions & 0 deletions test/command/suite/select/function/highlight/html_escape.test
@@ -0,0 +1,13 @@
table_create Entries TABLE_NO_KEY
column_create Entries body COLUMN_SCALAR ShortText

load --table Entries
[
{"body": "<b>Rroonga</b> is a Ruby binding of Groonga."}
]

select Entries --output_columns \
'highlight(body, \
"groonga", "<span class=\\"keyword1\\">", "</span>", \
{"html_escape": true} \
)' --command_version 2
@@ -0,0 +1,33 @@
table_create Entries TABLE_NO_KEY
[[0,0.0,0.0],true]
column_create Entries body COLUMN_SCALAR ShortText
[[0,0.0,0.0],true]
load --table Entries
[
{"body": "PGroonga is a PostgreSQL plugin to use groonga as index."}
]
[[0,0.0,0.0],1]
select Entries --output_columns 'highlight(body, "groonga", "<span class=\\"keyword1\\">", "</span>", {"normalizer": ""} )' --command_version 2
[
[
0,
0.0,
0.0
],
[
[
[
1
],
[
[
"highlight",
"null"
]
],
[
"PGroonga is a PostgreSQL plugin to use <span class=\"keyword1\">groonga</span> as index."
]
]
]
]
13 changes: 13 additions & 0 deletions test/command/suite/select/function/highlight/no_normalizer.test
@@ -0,0 +1,13 @@
table_create Entries TABLE_NO_KEY
column_create Entries body COLUMN_SCALAR ShortText

load --table Entries
[
{"body": "PGroonga is a PostgreSQL plugin to use groonga as index."}
]

select Entries --output_columns \
'highlight(body, \
"groonga", "<span class=\\"keyword1\\">", "</span>", \
{"normalizer": ""} \
)' --command_version 2
33 changes: 33 additions & 0 deletions test/command/suite/select/function/highlight/normalizer.expected
@@ -0,0 +1,33 @@
table_create Entries TABLE_NO_KEY
[[0,0.0,0.0],true]
column_create Entries body COLUMN_SCALAR ShortText
[[0,0.0,0.0],true]
load --table Entries
[
{"body": "PGroonga is a PostgreSQL plugin to use groonga as index."}
]
[[0,0.0,0.0],1]
select Entries --output_columns 'highlight(body, "groonga", "<span class=\\"keyword1\\">", "</span>", {"normalizer": "NormalizerAuto"} )' --command_version 2
[
[
0,
0.0,
0.0
],
[
[
[
1
],
[
[
"highlight",
"null"
]
],
[
"P<span class=\"keyword1\">Groonga</span> is a PostgreSQL plugin to use <span class=\"keyword1\">groonga</span> as index."
]
]
]
]
13 changes: 13 additions & 0 deletions test/command/suite/select/function/highlight/normalizer.test
@@ -0,0 +1,13 @@
table_create Entries TABLE_NO_KEY
column_create Entries body COLUMN_SCALAR ShortText

load --table Entries
[
{"body": "PGroonga is a PostgreSQL plugin to use groonga as index."}
]

select Entries --output_columns \
'highlight(body, \
"groonga", "<span class=\\"keyword1\\">", "</span>", \
{"normalizer": "NormalizerAuto"} \
)' --command_version 2

0 comments on commit fed4adb

Please sign in to comment.