Skip to content
Commits on Source (3)
...@@ -22,6 +22,21 @@ ...@@ -22,6 +22,21 @@
<arg type="u" name="fetch_size"/> <arg type="u" name="fetch_size"/>
<arg type="as" name="candidates" direction="out"/> <arg type="as" name="candidates" direction="out"/>
<arg type="au" name="matched_preedit_str_lengths" direction="out"/> <arg type="au" name="matched_preedit_str_lengths" direction="out"/>
<arg type="aa(ii)" name="candidates_pinyin_indices" direction="out"/>
</method>
<!-- CommitCandidate
@candidate: the candidate user committed
@candidate_pinyin_indices: the corresponding element in GetCandidates
candidates_pinyin_indices out param.
This will allow zero-pinyin-service to add candidate to user db and
revert DeleteCandidate effect.
-->
<method name="CommitCandidate">
<arg type="s" name="candidate"/>
<arg type="a(ii)" name="candidate_pinyin_indices"/>
</method> </method>
<!-- DeleteCandidate <!-- DeleteCandidate
......
...@@ -33,14 +33,16 @@ on_handle_get_candidates (ZeroPinyinService *object, ...@@ -33,14 +33,16 @@ on_handle_get_candidates (ZeroPinyinService *object,
GVariant *result = NULL; GVariant *result = NULL;
GVariantBuilder *candidates_builder = NULL; GVariantBuilder *candidates_builder = NULL;
GVariantBuilder *matched_lengths_builder = NULL; GVariantBuilder *matched_lengths_builder = NULL;
GVariantBuilder *candidates_pinyin_indices = NULL;
/* test data */ /* test data */
/* get_candidates_test (preedit_str, fetch_size, candidates_builder, matched_lengths_builder); */ /* get_candidates_test (preedit_str, fetch_size, candidates_builder, matched_lengths_builder); */
candidates_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); candidates_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
matched_lengths_builder = g_variant_builder_new (G_VARIANT_TYPE ("au")); matched_lengths_builder = g_variant_builder_new (G_VARIANT_TYPE ("au"));
get_candidates (appdata->db, preedit_str, fetch_size, candidates_builder, matched_lengths_builder); candidates_pinyin_indices = g_variant_builder_new (G_VARIANT_TYPE ("aa(ii)"));
get_candidates (appdata->db, preedit_str, fetch_size, candidates_builder, matched_lengths_builder, candidates_pinyin_indices);
result = g_variant_new ("(asau)", candidates_builder, matched_lengths_builder); result = g_variant_new ("(asauaa(ii))", candidates_builder, matched_lengths_builder, candidates_pinyin_indices);
g_assert_nonnull (result); g_assert_nonnull (result);
/* result is a GVarient tuple of two dbus arrays */ /* result is a GVarient tuple of two dbus arrays */
...@@ -48,10 +50,23 @@ on_handle_get_candidates (ZeroPinyinService *object, ...@@ -48,10 +50,23 @@ on_handle_get_candidates (ZeroPinyinService *object,
g_variant_builder_unref (candidates_builder); g_variant_builder_unref (candidates_builder);
g_variant_builder_unref (matched_lengths_builder); g_variant_builder_unref (matched_lengths_builder);
g_variant_builder_unref (candidates_pinyin_indices);
return TRUE; return TRUE;
} }
static gboolean
on_handle_commit_candidate (ZeroPinyinService *object,
GDBusMethodInvocation *invocation,
const gchar *candidate,
GVariant *candidate_pinyin_indices,
AppData *appdata)
{
commit_candidate (appdata->db, candidate, candidate_pinyin_indices);
g_dbus_method_invocation_return_value (invocation, NULL);
return TRUE;
}
static gboolean static gboolean
on_handle_delete_candidate (ZeroPinyinService *object, on_handle_delete_candidate (ZeroPinyinService *object,
GDBusMethodInvocation *invocation, GDBusMethodInvocation *invocation,
...@@ -118,6 +133,10 @@ on_bus_acquired (GDBusConnection *connection, ...@@ -118,6 +133,10 @@ on_bus_acquired (GDBusConnection *connection,
"handle-get-candidates", "handle-get-candidates",
G_CALLBACK (on_handle_get_candidates), G_CALLBACK (on_handle_get_candidates),
appdata); appdata);
g_signal_connect (appdata->interface,
"handle-commit-candidate",
G_CALLBACK (on_handle_commit_candidate),
appdata);
g_signal_connect (appdata->interface, g_signal_connect (appdata->interface,
"handle-delete-candidate", "handle-delete-candidate",
G_CALLBACK (on_handle_delete_candidate), G_CALLBACK (on_handle_delete_candidate),
...@@ -154,7 +173,14 @@ on_name_lost (GDBusConnection *connection, ...@@ -154,7 +173,14 @@ on_name_lost (GDBusConnection *connection,
const gchar *name, const gchar *name,
gpointer user_data) gpointer user_data)
{ {
g_message ("on_name_lost() name=%s", name); AppData *appdata = (AppData*) user_data;
/* this won't happen if this is the only app that tries to take the
* name, because GApplication already have primary instance
* concept. None primary instance will just send 'activate' signal to
* primary instance and exit. They will not try to register ibus at
* all. */
g_message ("on_name_lost() name=%s exiting", name);
g_application_quit (G_APPLICATION (appdata->app));
} }
static void static void
......
# -*- mode: conf -*- # -*- mode: conf -*-
project('zero-pinyin-service', ['c', 'cpp'], project('zero-pinyin-service', ['c', 'cpp'],
version: '0.5.0', version: '0.6.0',
license: 'GPL', license: 'GPL',
default_options: [ default_options: [
'warning_level=2', 'warning_level=2',
...@@ -44,17 +44,19 @@ shared_dep = [glib, gio, uuid, sqlite3] ...@@ -44,17 +44,19 @@ shared_dep = [glib, gio, uuid, sqlite3]
gen_inc = include_directories('.') gen_inc = include_directories('.')
gdbus_codegen = find_program('gdbus-codegen') gdbus_codegen = find_program('gdbus-codegen')
zero_panel_generated = custom_target('zero-panel-generated', zero_pinyin_generated = custom_target('zero-panel-generated',
input: 'com.emacsos.zero.ZeroPinyinService1.ZeroPinyinServiceInterface.xml', input: 'com.emacsos.zero.ZeroPinyinService1.ZeroPinyinServiceInterface.xml',
output: ['zero-pinyin-service-generated.h', 'zero-pinyin-service-generated.c'], output: ['zero-pinyin-service-generated.h', 'zero-pinyin-service-generated.c'],
command: [gdbus_codegen, '--generate-c-code', 'zero-pinyin-service-generated', '@INPUT@']) command: [gdbus_codegen, '--generate-c-code', 'zero-pinyin-service-generated', '@INPUT@'])
src = [ lib = [
'../PinyinParser.cc', '../PinyinParser.cc',
'../sqlite3_util.c', '../sqlite3_util.c',
'parse-pinyin.cpp', 'parse-pinyin.cpp',
'zero-pinyin-service.c', 'zero-pinyin-service.c']
zero_panel_generated, src = [
lib,
zero_pinyin_generated,
'main.c'] 'main.c']
executable('zero-pinyin-service', src, executable('zero-pinyin-service', src,
include_directories: gen_inc, include_directories: gen_inc,
...@@ -67,5 +69,5 @@ test('parse-pinyin', ...@@ -67,5 +69,5 @@ test('parse-pinyin',
dependencies: shared_dep)) dependencies: shared_dep))
test('zero-pinyin-service-test', test('zero-pinyin-service-test',
executable('zero-pinyin-service-test', executable('zero-pinyin-service-test',
['zero-pinyin-service-test.c'], [lib, 'zero-pinyin-service-test.c'],
dependencies: shared_dep)) dependencies: shared_dep))
...@@ -4,7 +4,7 @@ static void ...@@ -4,7 +4,7 @@ static void
test_GString () test_GString ()
{ {
GString* s = NULL; GString* s = NULL;
s = g_string_new (""); s = g_string_new (NULL);
g_string_append_printf (s, "s0=%d ", 1); g_string_append_printf (s, "s0=%d ", 1);
g_assert_cmpstr (s->str, ==, "s0=1 "); g_assert_cmpstr (s->str, ==, "s0=1 ");
...@@ -12,11 +12,24 @@ test_GString () ...@@ -12,11 +12,24 @@ test_GString ()
g_string_free (s, TRUE); g_string_free (s, TRUE);
} }
static void
test_build_s_y_fields ()
{
gchar* result = NULL;
result = build_s_y_fields (1);
g_assert_cmpstr (result, ==, ", s0, y0 ");
g_free (result);
result = build_s_y_fields (2);
g_assert_cmpstr (result, ==, ", s0, y0, s1, y1 ");
g_free (result);
}
int int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
g_test_init (&argc, &argv, NULL); g_test_init (&argc, &argv, NULL);
g_test_add_func ("/zero/test_GString", g_test_add_func ("/zero/test_GString", test_GString);
test_GString); g_test_add_func ("/zero/test_build_s_y_fields", test_build_s_y_fields);
return g_test_run (); return g_test_run ();
} }
#include "zero-pinyin-service.h" #include "zero-pinyin-service.h"
#include "parse-pinyin.h" #include "parse-pinyin.h"
#include "../sqlite3_util.h"
void void
get_candidates_test (const char* preedit_str, get_candidates_test (const char* preedit_str,
...@@ -47,7 +48,7 @@ build_where_clause (GList* pylist, ...@@ -47,7 +48,7 @@ build_where_clause (GList* pylist,
GList* iter = pylist; GList* iter = pylist;
gboolean first_condition_done = FALSE; gboolean first_condition_done = FALSE;
Pinyin* thispy = NULL; Pinyin* thispy = NULL;
s = g_string_new (""); s = g_string_new (NULL);
for (guint i = 0; i < n; ++i) { for (guint i = 0; i < n; ++i) {
g_assert_nonnull (iter); g_assert_nonnull (iter);
thispy = (Pinyin*) iter->data; thispy = (Pinyin*) iter->data;
...@@ -74,6 +75,26 @@ build_where_clause (GList* pylist, ...@@ -74,6 +75,26 @@ build_where_clause (GList* pylist,
return result; return result;
} }
/**
* return a string like ", s0, y0, s1, y1 "
*
* caller should g_free() result after use.
*/
char*
build_s_y_fields (const guint n)
{
GString *s = NULL;
g_assert_cmpint (n, >=, 1);
s = g_string_new (NULL);
for (guint i = 0; i < n; ++i) {
g_string_append_printf (s, ", s%u, y%u", i, i);
}
s = g_string_append (s, " ");
gchar *result = s->str;
g_string_free (s, FALSE);
return result;
}
/** /**
* build a SQL to query candidates for first n pinyin in pylist. * build a SQL to query candidates for first n pinyin in pylist.
* n can be from 1 to len(pylist). * n can be from 1 to len(pylist).
...@@ -88,33 +109,78 @@ build_sql_for_n_pinyin (GList* pylist, ...@@ -88,33 +109,78 @@ build_sql_for_n_pinyin (GList* pylist,
/* always keep one space after current term */ /* always keep one space after current term */
GString* sql = NULL; GString* sql = NULL;
gchar* where_clause = NULL; gchar* where_clause = NULL;
sql = g_string_new ("SELECT MAX(user_freq), phrase, MAX(freq) FROM ("); sql = g_string_new ("SELECT MAX(user_freq) AS user_freq, "
"phrase, MAX(freq) AS freq");
gchar* s_y_fields = build_s_y_fields (n);
g_string_append_printf (sql, s_y_fields);
g_string_append_printf (sql, "FROM (");
g_string_append_printf ( g_string_append_printf (
sql, "SELECT 0 AS user_freq, phrase, freq FROM " sql, "SELECT 0 AS user_freq, phrase, freq");
"maindb.py_phrase_%u WHERE ", n - 1); g_string_append_printf (sql, s_y_fields);
g_string_append_printf (
sql, "FROM maindb.py_phrase_%u WHERE ", n - 1);
where_clause = build_where_clause (pylist, n); where_clause = build_where_clause (pylist, n);
g_assert_nonnull (where_clause); g_assert_nonnull (where_clause);
g_message ("where_clause=%s", where_clause); g_debug ("where_clause=%s", where_clause);
sql = g_string_append (sql, where_clause); sql = g_string_append (sql, where_clause);
sql = g_string_append (sql, "UNION "); sql = g_string_append (sql, "UNION ");
g_string_append_printf ( g_string_append_printf (
sql, "SELECT user_freq, phrase, freq FROM " sql, "SELECT user_freq, phrase, freq");
"userdb.py_phrase_%u WHERE ", n - 1); g_string_append_printf (sql, s_y_fields);
g_string_append_printf (
sql, "FROM userdb.py_phrase_%u WHERE ", n - 1);
sql = g_string_append (sql, where_clause); sql = g_string_append (sql, where_clause);
sql = g_string_append ( sql = g_string_append (
sql, ") " sql, ") "
"WHERE phrase NOT IN (SELECT phrase FROM userdb.not_phrase) " "WHERE phrase NOT IN (SELECT phrase FROM userdb.not_phrase) "
"GROUP BY phrase ORDER BY user_freq DESC, freq DESC "); "GROUP BY phrase "
"ORDER BY user_freq DESC, freq DESC ");
g_string_append_printf (sql, "LIMIT %u;", limit); g_string_append_printf (sql, "LIMIT %u;", limit);
char* result = sql->str; char* result = sql->str;
g_free (s_y_fields);
g_free (where_clause); g_free (where_clause);
g_string_free (sql, FALSE); g_string_free (sql, FALSE);
return result; return result;
} }
/**
* For a candidate of length group_size, calculate the matched py length.
*
* This is part of get_candidates_for_n_pinyin().
*
* see param meaning there.
*/
static guint
get_matched_py_length (const char* preedit_str,
GList* pylist,
const guint group_size)
{
guint matched_py_length = 0;
GList* iter = pylist;
g_assert_cmpint (group_size, >=, 1);
/* For usual pinyin string, just add up the Pinyin length. But for
* pinyin that contains ', when a Pinyin in pylist is used, also take
* the ' before and after it. */
for (guint i = 0; i < group_size; ++i) {
while (preedit_str[matched_py_length] == '\'') {
matched_py_length++;
}
matched_py_length += ((Pinyin*) iter->data)->length;
while (preedit_str[matched_py_length] == '\'') {
matched_py_length++;
}
iter = iter->next;
}
return matched_py_length;
}
/** /**
* fetch candidates for a fixed word length. * fetch candidates for a fixed word length.
* *
* @db: sqlite3 db handler.
* @preedit_str: the pinyin preedit str. can contain '. This is needed to
* calculate matched_py_length.
* @pylist: the pinyin list. * @pylist: the pinyin list.
* @group_size: the fixed word length. use this many pinyin from pinyin list. * @group_size: the fixed word length. use this many pinyin from pinyin list.
* @limit: fetch this many result is enough for user. more is not a problem though. * @limit: fetch this many result is enough for user. more is not a problem though.
...@@ -124,6 +190,7 @@ build_sql_for_n_pinyin (GList* pylist, ...@@ -124,6 +190,7 @@ build_sql_for_n_pinyin (GList* pylist,
*/ */
static guint static guint
get_candidates_for_n_pinyin (sqlite3* db, get_candidates_for_n_pinyin (sqlite3* db,
const char* preedit_str,
GList* pylist, GList* pylist,
const guint group_size, const guint group_size,
const guint limit, const guint limit,
...@@ -141,14 +208,9 @@ get_candidates_for_n_pinyin (sqlite3* db, ...@@ -141,14 +208,9 @@ get_candidates_for_n_pinyin (sqlite3* db,
/* build SQL and run SQL query */ /* build SQL and run SQL query */
char* sql = NULL; char* sql = NULL;
sql = build_sql_for_n_pinyin (pylist, group_size, MAX (limit, DEFAULT_LIMIT)); sql = build_sql_for_n_pinyin (pylist, group_size, MAX (limit, DEFAULT_LIMIT));
g_message ("build_sql_for_n_pinyin result SQL:\n\n%s\n", sql); g_debug ("build_sql_for_n_pinyin result SQL:\n\n%s\n", sql);
guint matched_py_length = 0; guint matched_py_length = get_matched_py_length (preedit_str, pylist, group_size);
GList* iter = pylist;
for (guint i = 0; i < group_size; ++i) {
matched_py_length += ((Pinyin*) iter->data)->length;
iter = iter->next;
}
sqlite3_stmt* stmt = NULL; sqlite3_stmt* stmt = NULL;
const char* unused; const char* unused;
...@@ -166,12 +228,20 @@ get_candidates_for_n_pinyin (sqlite3* db, ...@@ -166,12 +228,20 @@ get_candidates_for_n_pinyin (sqlite3* db,
if (r == SQLITE_DONE) { if (r == SQLITE_DONE) {
break; break;
} else if (r == SQLITE_ROW) { } else if (r == SQLITE_ROW) {
c = g_new (Candidate, 1); c = g_new0 (Candidate, 1);
/* sql SELECT should select these 3 columns in order */ /* sql SELECT should select these columns in order */
c->user_freq = sqlite3_column_int (stmt, 0); c->user_freq = sqlite3_column_int (stmt, 0);
c->str = g_strdup ((const char*) sqlite3_column_text (stmt, 1)); c->str = g_strdup ((const char*) sqlite3_column_text (stmt, 1));
c->freq = sqlite3_column_int (stmt, 2); c->freq = sqlite3_column_int (stmt, 2);
c->matched_py_length = matched_py_length; c->matched_py_length = matched_py_length;
c->char_len = group_size;
c->py_indices = g_malloc0 (sizeof (Pinyin*) * group_size);
for (guint i = 0; i < group_size; ++i) {
c->py_indices[i] = g_new0 (Pinyin, 1);
c->py_indices[i]->shengmu_i = sqlite3_column_int (stmt, 3 + i * 2);
c->py_indices[i]->yunmu_i = sqlite3_column_int (stmt, 4 + i * 2);
/* we don't care about ->length field */
}
if (g_utf8_validate (c->str, -1, NULL)) { if (g_utf8_validate (c->str, -1, NULL)) {
result = g_list_prepend (result, c); result = g_list_prepend (result, c);
...@@ -198,12 +268,41 @@ get_candidates_for_n_pinyin (sqlite3* db, ...@@ -198,12 +268,41 @@ get_candidates_for_n_pinyin (sqlite3* db,
return candidates_count; return candidates_count;
} }
static void
add_candidate_to_builders (Candidate *c,
GVariantBuilder *candidates_builder,
GVariantBuilder *matched_lengths_builder,
GVariantBuilder *candidates_pinyin_indices)
{
g_variant_builder_add (candidates_builder, "s", c->str);
g_variant_builder_add (matched_lengths_builder, "u", c->matched_py_length);
GVariantBuilder *py_indices_builder = NULL;
py_indices_builder = g_variant_builder_new (G_VARIANT_TYPE ("a(ii)"));
for (guint i = 0; i < c->char_len; ++i) {
g_variant_builder_add (
py_indices_builder, "(ii)",
c->py_indices[i]->shengmu_i,
c->py_indices[i]->yunmu_i);
g_debug ("adding (ii) %d %d",
c->py_indices[i]->shengmu_i,
c->py_indices[i]->yunmu_i);
g_free (c->py_indices[i]);
}
g_debug ("adding a(ii) to aa(ii)");
g_variant_builder_add (candidates_pinyin_indices, "a(ii)",
py_indices_builder);
g_variant_builder_unref (py_indices_builder);
g_free (c->str);
g_free (c->py_indices);
}
void void
get_candidates (sqlite3* db, get_candidates (sqlite3* db,
const char* preedit_str, const char* preedit_str,
const guint fetch_size, const guint fetch_size,
GVariantBuilder *candidates_builder, GVariantBuilder *candidates_builder,
GVariantBuilder *matched_lengths_builder) GVariantBuilder *matched_lengths_builder,
GVariantBuilder *candidates_pinyin_indices)
{ {
if (! db) { if (! db) {
g_warning ("No db connection, can't get candidates."); g_warning ("No db connection, can't get candidates.");
...@@ -221,17 +320,16 @@ get_candidates (sqlite3* db, ...@@ -221,17 +320,16 @@ get_candidates (sqlite3* db,
GList* candidates = NULL; GList* candidates = NULL;
while (fetched_size < fetch_size && group_size > 0) { while (fetched_size < fetch_size && group_size > 0) {
g_message ("phrase length=%u", group_size); g_message ("phrase length=%u", group_size);
r = get_candidates_for_n_pinyin (db, pylist, group_size, fetch_size - fetched_size, &candidates); r = get_candidates_for_n_pinyin (db, preedit_str, pylist, group_size, fetch_size - fetched_size, &candidates);
if (candidates) { if (candidates) {
GList* iter = g_list_first (candidates); GList* iter = g_list_first (candidates);
Candidate* c = NULL; Candidate* c = NULL;
while (iter != NULL) { while (iter != NULL) {
c = (Candidate*) iter->data; c = (Candidate*) iter->data;
g_variant_builder_add (candidates_builder, "s", add_candidate_to_builders (
c->str); c, candidates_builder,
g_free (c->str); matched_lengths_builder,
g_variant_builder_add (matched_lengths_builder, "u", candidates_pinyin_indices);
c->matched_py_length);
iter = iter->next; iter = iter->next;
} }
g_list_free_full (candidates, g_free); g_list_free_full (candidates, g_free);
...@@ -243,3 +341,136 @@ get_candidates (sqlite3* db, ...@@ -243,3 +341,136 @@ get_candidates (sqlite3* db,
g_message ("returning %u candidates", fetched_size); g_message ("returning %u candidates", fetched_size);
g_list_free_full (pylist, g_free); g_list_free_full (pylist, g_free);
} }
/**
* sub function for commit_candidate()
*/
static void
_update_userdb_py_phrase (sqlite3 *db,
const gchar *candidate,
GVariant *candidate_pinyin_indices,
guint len) /* utf8 length of candidate char */
{
GString *sql = NULL;
GVariantIter iter = {0};
GVariant *child = NULL;
gint x = 0;
gint y = 0;
guint count = 0;
char *s = NULL;
gboolean rb = FALSE;
g_assert_nonnull (db);
g_assert_nonnull (candidate);
g_assert_nonnull (candidate_pinyin_indices);
/* insert candidate maybe */
sql = g_string_new (NULL);
g_string_append_printf (sql, "INSERT OR IGNORE INTO userdb.py_phrase_%u (user_freq, phrase, freq", len - 1);
gchar* s_y_fields = build_s_y_fields (len);
sql = g_string_append (sql, s_y_fields);
g_free (s_y_fields);
s = sqlite3_mprintf (") VALUES (0, %Q, 0", candidate);
sql = g_string_append (sql, s);
sqlite3_free (s);
/* iter over GVariant "a(ii)" */
g_variant_iter_init (&iter, candidate_pinyin_indices);
count = 0;
while ((child = g_variant_iter_next_value (&iter))) {
g_variant_get (child, "(ii)", &x, &y);
g_string_append_printf (sql, ", %d, %d", x, y);
count++;
}
if (count != len) {
g_warning ("candidate length=%u, a(ii) length=%u, mismatch!",
len, count);
g_string_free (sql, TRUE);
g_assert_not_reached ();
return;
}
g_string_append_printf (sql, ");");
rb = sqlite3_exec_simple (db, sql->str);
if (! rb) {
g_warning ("INSERT candidate to userdb failed");
} else {
if (sqlite3_changes (db) == 1) {
g_message ("candidate %s inserted to userdb", candidate);
}
}
g_string_free (sql, TRUE);
/* increment user_freq field for candidate */
sql = g_string_new (NULL);
g_string_append_printf (sql, "UPDATE userdb.py_phrase_%u "
"SET user_freq = user_freq + 1 ", len - 1);
s = sqlite3_mprintf ("WHERE phrase = %Q ", candidate);
sql = g_string_append (sql, s);
sqlite3_free (s);
g_variant_iter_init (&iter, candidate_pinyin_indices);
count = 0;
while ((child = g_variant_iter_next_value (&iter))) {
g_variant_get (child, "(ii)", &x, &y);
g_string_append_printf (sql, "AND s%d=%d AND y%d=%d ",
count, x, count, y);
count++;
}
sql = g_string_append (sql, ";");
rb = sqlite3_exec_simple (db, sql->str);
if (! rb) {
g_warning ("UPDATE candidate user_freq failed");
} else {
if (sqlite3_changes (db) == 1) {
g_message ("candidate %s user_freq incremented", candidate);
} else {
g_warning ("UPDATE candidate user_freq failed, no match");
}
}
g_string_free (sql, TRUE);
}
static void
_update_userdb_not_phrase (sqlite3 *db,
const gchar *candidate)
{
g_assert_nonnull (db);
g_assert_nonnull (candidate);
gboolean rb = FALSE;
char *sql = sqlite3_mprintf ("DELETE FROM userdb.not_phrase WHERE phrase = %Q;", candidate);
rb = sqlite3_exec_simple (db, sql);
if (! rb) {
g_warning ("DELETE candidate from not_phrase failed");
} else {
if (sqlite3_changes (db) == 1) {
g_message ("candidate %s removed from not_phrase", candidate);
}
}
sqlite3_free (sql);
}
void
commit_candidate (sqlite3 *db,
const gchar *candidate,
GVariant *candidate_pinyin_indices)
{
if (! db) {
g_warning ("No db connection, can't commit candidates.");
return;
}
if (! candidate) {
g_warning ("candidate should not be NULL. won't commit candidate.");
return;
}
if (! candidate_pinyin_indices) {
g_warning ("candidate_pinyin_indices should not be NULL. won't commit candidate.");
return;
}
guint len = g_utf8_strlen (candidate, -1);
if (len <= 1) {
g_message ("commit single character %s is a no-op", candidate);
return;
}
_update_userdb_py_phrase (db, candidate, candidate_pinyin_indices, len);
_update_userdb_not_phrase (db, candidate);
}
...@@ -18,16 +18,18 @@ typedef struct { ...@@ -18,16 +18,18 @@ typedef struct {
} Pinyin; } Pinyin;
typedef struct { typedef struct {
gchar* str; /* the candidate string */ gchar *str; /* the candidate string */
guint freq; /* word frequency [0, 65535] */ guint freq; /* word frequency [0, 65535] */
guint user_freq; /* user frequency [0, 65535] */ guint user_freq; /* user frequency [0, 65535] */
guint matched_py_length; /* matched preedit_str length */ guint matched_py_length; /* matched preedit_str length */
guint char_len; /* candidate Chinese character length */
Pinyin **py_indices; /* Pinyin for each character in this candidate */
} Candidate; } Candidate;
/** /**
* an implementation of get_candidates() with simple test data. * an implementation of get_candidates() with simple test data.
*/ */
void get_candidates_test (const char* preedit_str, void get_candidates_test (const char *preedit_str,
const guint fetch_size, const guint fetch_size,
GVariantBuilder *candidates_builder, GVariantBuilder *candidates_builder,
GVariantBuilder *matched_lengths_builder); GVariantBuilder *matched_lengths_builder);
...@@ -40,11 +42,25 @@ void get_candidates_test (const char* preedit_str, ...@@ -40,11 +42,25 @@ void get_candidates_test (const char* preedit_str,
* @candidates_builder candidates will be added to this builder * @candidates_builder candidates will be added to this builder
* @matched_lengths_builder matched preedit_str length will be added to this builder * @matched_lengths_builder matched preedit_str length will be added to this builder
*/ */
void get_candidates (sqlite3* db, void get_candidates (sqlite3 *db,
const char* preedit_str, const char *preedit_str,
const guint fetch_size, const guint fetch_size,
GVariantBuilder *candidates_builder, GVariantBuilder *candidates_builder,
GVariantBuilder *matched_lengths_builder); GVariantBuilder *matched_lengths_builder,
GVariantBuilder *candidates_pinyin_indices);
/**
* commit candidate.
*
* This will save candidate in user db so it's available in the future.
* It also update user_freq for given candidate.
*/
void commit_candidate (sqlite3 *db,
const gchar *candidate,
GVariant *candidate_pinyin_indices);
/* for test only */
char* build_s_y_fields (const guint n);
G_END_DECLS G_END_DECLS
......