[gnome-software] search: Try to deduplicate the same logical app
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software] search: Try to deduplicate the same logical app
- Date: Tue, 9 May 2017 08:58:39 +0000 (UTC)
commit 2e87612c163b36309beb8103196c616c1b46efd6
Author: Iain Lane <iain orangesquash org uk>
Date: Mon May 8 21:04:08 2017 +0100
search: Try to deduplicate the same logical app
If there are several results for the same app from the 'same place' -
for example a distro package has an update the user hasn't yet installed
- only show one result in the search results list.
Do this by filtering on id/source/version, which should be stable across
updates ('version' is the installed version if the app is installed
[regardless of available updates] or the candidate version if not).
https://bugzilla.gnome.org/show_bug.cgi?id=782340
lib/gs-app-list-private.h | 13 ++++++---
lib/gs-app-list.c | 62 ++++++++++++++++++++++++++++----------------
lib/gs-plugin-loader.c | 17 +++++++-----
lib/gs-self-test.c | 33 +++++++++++++++++++++++-
4 files changed, 90 insertions(+), 35 deletions(-)
---
diff --git a/lib/gs-app-list-private.h b/lib/gs-app-list-private.h
index 61d5ff4..e88ef33 100644
--- a/lib/gs-app-list-private.h
+++ b/lib/gs-app-list-private.h
@@ -44,14 +44,19 @@ typedef enum {
/**
* GsAppListFilterFlags:
- * @GS_APP_LIST_FILTER_FLAG_NONE: No flags set
- * @GS_APP_LIST_FILTER_FLAG_PRIORITY: Filter by application priority
+ * @GS_APP_LIST_FILTER_FLAG_NONE: No flags set
+ * @GS_APP_LIST_FILTER_FLAG_KEY_ID: Filter by ID
+ * @GS_APP_LIST_FILTER_FLAG_KEY_SOURCE: Filter by default source
+ * @GS_APP_LIST_FILTER_FLAG_KEY_VERSION: Filter by version
*
- * Flags to use when filtering.
+ * Flags to use when filtering. The priority of eash #GsApp is used to choose
+ * which application object to keep.
**/
typedef enum {
GS_APP_LIST_FILTER_FLAG_NONE = 0,
- GS_APP_LIST_FILTER_FLAG_PRIORITY = 1 << 0,
+ GS_APP_LIST_FILTER_FLAG_KEY_ID = 1 << 0,
+ GS_APP_LIST_FILTER_FLAG_KEY_SOURCE = 1 << 1,
+ GS_APP_LIST_FILTER_FLAG_KEY_VERSION = 1 << 2,
/*< private >*/
GS_APP_LIST_FILTER_FLAG_LAST
} GsAppListFilterFlags;
diff --git a/lib/gs-app-list.c b/lib/gs-app-list.c
index c7de5be..f402bc6 100644
--- a/lib/gs-app-list.c
+++ b/lib/gs-app-list.c
@@ -479,14 +479,9 @@ gs_app_list_randomize (GsAppList *list)
void
gs_app_list_filter_duplicates (GsAppList *list, GsAppListFilterFlags flags)
{
- guint i;
- GsApp *app;
- GsApp *found;
- const gchar *id;
g_autoptr(GHashTable) hash = NULL;
g_autoptr(GList) values = NULL;
g_autoptr(GPtrArray) apps_no_id = NULL;
- GList *l;
g_autoptr(GMutexLocker) locker = g_mutex_locker_new (&list->mutex);
g_return_if_fail (GS_IS_APP_LIST (list));
@@ -497,60 +492,81 @@ gs_app_list_filter_duplicates (GsAppList *list, GsAppListFilterFlags flags)
/* an array to hold apps that have NULL app ids */
apps_no_id = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
- for (i = 0; i < list->array->len; i++) {
+ for (guint i = 0; i < list->array->len; i++) {
+ GsApp *app;
+ GsApp *found;
+ g_autoptr(GString) key = NULL;
+
app = gs_app_list_index (list, i);
- id = gs_app_get_unique_id (app);
- if (flags & GS_APP_LIST_FILTER_FLAG_PRIORITY)
- id = gs_app_get_id (app);
- if (id == NULL) {
+ if (flags == GS_APP_LIST_FILTER_FLAG_NONE) {
+ key = g_string_new (gs_app_get_unique_id (app));
+ } else {
+ key = g_string_new (NULL);
+ if (flags & GS_APP_LIST_FILTER_FLAG_KEY_ID) {
+ const gchar *tmp = gs_app_get_id (app);
+ if (tmp != NULL)
+ g_string_append (key, gs_app_get_id (app));
+ }
+ if (flags & GS_APP_LIST_FILTER_FLAG_KEY_SOURCE) {
+ const gchar *tmp = gs_app_get_source_default (app);
+ if (tmp != NULL)
+ g_string_append_printf (key, ":%s", tmp);
+ }
+ if (flags & GS_APP_LIST_FILTER_FLAG_KEY_VERSION) {
+ const gchar *tmp = gs_app_get_version (app);
+ if (tmp != NULL)
+ g_string_append_printf (key, ":%s", tmp);
+ }
+ }
+ if (key->len == 0) {
g_autofree gchar *str = gs_app_to_string (app);
- g_debug ("adding without deduplication as no app id: %s", str);
+ g_debug ("adding without deduplication as no app key: %s", str);
g_ptr_array_add (apps_no_id, g_object_ref (app));
continue;
}
- found = g_hash_table_lookup (hash, id);
+ found = g_hash_table_lookup (hash, key->str);
if (found == NULL) {
- g_debug ("found new %s", id);
+ g_debug ("found new %s", key->str);
g_hash_table_insert (hash,
- g_strdup (id),
+ g_strdup (key->str),
g_object_ref (app));
continue;
}
/* better? */
- if (flags & GS_APP_LIST_FILTER_FLAG_PRIORITY) {
+ if (flags != GS_APP_LIST_FILTER_FLAG_NONE) {
if (gs_app_get_priority (app) >
gs_app_get_priority (found)) {
g_debug ("using better %s (priority %u > %u)",
- id,
+ key->str,
gs_app_get_priority (app),
gs_app_get_priority (found));
g_hash_table_insert (hash,
- g_strdup (id),
+ g_strdup (key->str),
g_object_ref (app));
continue;
}
g_debug ("ignoring worse duplicate %s (priority %u > %u)",
- id,
+ key->str,
gs_app_get_priority (app),
gs_app_get_priority (found));
continue;
}
- g_debug ("ignoring duplicate %s", id);
+ g_debug ("ignoring duplicate %s", key->str);
continue;
}
/* add back the best results to the existing list */
gs_app_list_remove_all_safe (list);
values = g_hash_table_get_values (hash);
- for (l = values; l != NULL; l = l->next) {
- app = GS_APP (l->data);
+ for (GList *l = values; l != NULL; l = l->next) {
+ GsApp *app = GS_APP (l->data);
gs_app_list_add_safe (list, app);
}
/* add back apps with NULL app ids to the existing list */
- for (i = 0; i < apps_no_id->len; i++) {
- app = g_ptr_array_index (apps_no_id, i);
+ for (guint i = 0; i < apps_no_id->len; i++) {
+ GsApp *app = g_ptr_array_index (apps_no_id, i);
gs_app_list_add_safe (list, app);
}
}
diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c
index 7a93444..f0ac05d 100644
--- a/lib/gs-plugin-loader.c
+++ b/lib/gs-plugin-loader.c
@@ -1823,7 +1823,7 @@ gs_plugin_loader_get_popular_thread_cb (GTask *task,
/* filter duplicates with priority */
gs_app_list_filter (job->list, gs_plugin_loader_app_set_prio, plugin_loader);
- gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_PRIORITY);
+ gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_KEY_ID);
/* success */
g_task_return_pointer (task, g_object_ref (job->list), (GDestroyNotify) g_object_unref);
@@ -1914,7 +1914,7 @@ gs_plugin_loader_get_featured_thread_cb (GTask *task,
/* filter duplicates with priority */
gs_app_list_filter (job->list, gs_plugin_loader_app_set_prio, plugin_loader);
- gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_PRIORITY);
+ gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_KEY_ID);
/* success */
g_task_return_pointer (task, g_object_ref (job->list), (GDestroyNotify) g_object_unref);
@@ -2114,9 +2114,12 @@ gs_plugin_loader_search_thread_cb (GTask *task,
gs_app_list_filter (job->list, gs_plugin_loader_filter_qt_for_gtk, NULL);
gs_app_list_filter (job->list, gs_plugin_loader_get_app_is_compatible, plugin_loader);
- /* filter duplicates with priority */
+ /* filter duplicates with priority, taking into account the source name
+ * & version, so we combine available updates with the installed app */
gs_app_list_filter (job->list, gs_plugin_loader_app_set_prio, plugin_loader);
- gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_NONE);
+ gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_KEY_ID |
+ GS_APP_LIST_FILTER_FLAG_KEY_SOURCE |
+ GS_APP_LIST_FILTER_FLAG_KEY_VERSION);
/* sort these again as the refine may have added useful metadata */
gs_app_list_sort (job->list, job->sort_func, job->sort_func_data);
@@ -2553,7 +2556,7 @@ gs_plugin_loader_get_category_apps_thread_cb (GTask *task,
/* filter duplicates with priority */
gs_app_list_filter (job->list, gs_plugin_loader_app_set_prio, plugin_loader);
- gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_PRIORITY);
+ gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_KEY_ID);
/* sort, just in case the UI doesn't do this */
gs_app_list_sort (job->list, gs_plugin_loader_app_sort_name_cb, NULL);
@@ -4523,7 +4526,7 @@ gs_plugin_loader_file_to_app_thread_cb (GTask *task,
/* filter package list */
gs_app_list_filter (job->list, gs_plugin_loader_app_set_prio, plugin_loader);
- gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_PRIORITY);
+ gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_KEY_ID);
/* check the apps have an icon set */
for (guint j = 0; j < gs_app_list_length (job->list); j++) {
@@ -4674,7 +4677,7 @@ gs_plugin_loader_url_to_app_thread_cb (GTask *task,
/* filter package list */
gs_app_list_filter (job->list, gs_plugin_loader_app_is_valid, job);
gs_app_list_filter (job->list, gs_plugin_loader_app_set_prio, plugin_loader);
- gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_PRIORITY);
+ gs_app_list_filter_duplicates (job->list, GS_APP_LIST_FILTER_FLAG_KEY_ID);
/* success */
if (gs_app_list_length (job->list) != 1) {
diff --git a/lib/gs-self-test.c b/lib/gs-self-test.c
index 85ba013..af8a13b 100644
--- a/lib/gs-self-test.c
+++ b/lib/gs-self-test.c
@@ -286,11 +286,42 @@ gs_plugin_func (void)
gs_app_set_priority (app, 50);
g_object_unref (app);
g_assert_cmpint (gs_app_list_length (list), ==, 3);
- gs_app_list_filter_duplicates (list, GS_APP_LIST_FILTER_FLAG_PRIORITY);
+ gs_app_list_filter_duplicates (list, GS_APP_LIST_FILTER_FLAG_KEY_ID);
g_assert_cmpint (gs_app_list_length (list), ==, 1);
g_assert_cmpstr (gs_app_get_unique_id (gs_app_list_index (list, 0)), ==, "user/bar/*/*/e/*");
g_object_unref (list);
+ /* respect priority (using name and version) when deduplicating */
+ list = gs_app_list_new ();
+ app = gs_app_new ("e");
+ gs_app_add_source (app, "foo");
+ gs_app_set_version (app, "1.2.3");
+ gs_app_set_unique_id (app, "user/foo/repo/*/*/*");
+ gs_app_list_add (list, app);
+ gs_app_set_priority (app, 0);
+ g_object_unref (app);
+ app = gs_app_new ("e");
+ gs_app_add_source (app, "foo");
+ gs_app_set_version (app, "1.2.3");
+ gs_app_set_unique_id (app, "user/foo/repo-security/*/*/*");
+ gs_app_list_add (list, app);
+ gs_app_set_priority (app, 99);
+ g_object_unref (app);
+ app = gs_app_new ("e");
+ gs_app_add_source (app, "foo");
+ gs_app_set_version (app, "1.2.3");
+ gs_app_set_unique_id (app, "user/foo/repo-universe/*/*/*");
+ gs_app_list_add (list, app);
+ gs_app_set_priority (app, 50);
+ g_object_unref (app);
+ g_assert_cmpint (gs_app_list_length (list), ==, 3);
+ gs_app_list_filter_duplicates (list, GS_APP_LIST_FILTER_FLAG_KEY_ID |
+ GS_APP_LIST_FILTER_FLAG_KEY_SOURCE |
+ GS_APP_LIST_FILTER_FLAG_KEY_VERSION);
+ g_assert_cmpint (gs_app_list_length (list), ==, 1);
+ g_assert_cmpstr (gs_app_get_unique_id (gs_app_list_index (list, 0)), ==,
"user/foo/repo-security/*/*/*");
+ g_object_unref (list);
+
/* use globs when adding */
list = gs_app_list_new ();
app = gs_app_new ("b");
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]