[evolution-ews/gnome-3-36] evo-I#982 - 'Message contains'-search broken in 3.36.3



commit 52ad8b41e0f39ca440a611e8be02852987085e0d
Author: Milan Crha <mcrha redhat com>
Date:   Mon Jun 15 15:20:37 2020 +0200

    evo-I#982 - 'Message contains'-search broken in 3.36.3
    
    Optimize body-contains search by not re-invoking it for every message
    in the folder.
    
    Related to https://gitlab.gnome.org/GNOME/evolution/-/issues/982

 src/camel/camel-ews-folder.c |   6 ++
 src/camel/camel-ews-search.c | 206 +++++++++++++++++++++++++++++--------------
 src/camel/camel-ews-search.h |   2 +
 3 files changed, 147 insertions(+), 67 deletions(-)
---
diff --git a/src/camel/camel-ews-folder.c b/src/camel/camel-ews-folder.c
index a68b7d0c..9938343f 100644
--- a/src/camel/camel-ews-folder.c
+++ b/src/camel/camel-ews-folder.c
@@ -1141,11 +1141,13 @@ ews_folder_search_by_expression (CamelFolder *folder,
        ews_search = CAMEL_EWS_SEARCH (ews_folder->search);
 
        camel_folder_search_set_folder (ews_folder->search, folder);
+       camel_ews_search_clear_cached_results (ews_search);
        camel_ews_search_set_cancellable_and_error (ews_search, cancellable, error);
 
        matches = camel_folder_search_search (ews_folder->search, expression, NULL, cancellable, error);
 
        camel_ews_search_set_cancellable_and_error (ews_search, NULL, NULL);
+       camel_ews_search_clear_cached_results (ews_search);
 
        g_mutex_unlock (&priv->search_lock);
 
@@ -1171,11 +1173,13 @@ ews_folder_count_by_expression (CamelFolder *folder,
        ews_search = CAMEL_EWS_SEARCH (ews_folder->search);
 
        camel_folder_search_set_folder (ews_folder->search, folder);
+       camel_ews_search_clear_cached_results (ews_search);
        camel_ews_search_set_cancellable_and_error (ews_search, cancellable, error);
 
        matches = camel_folder_search_count (ews_folder->search, expression, cancellable, error);
 
        camel_ews_search_set_cancellable_and_error (ews_search, NULL, NULL);
+       camel_ews_search_clear_cached_results (ews_search);
 
        g_mutex_unlock (&priv->search_lock);
 
@@ -1205,11 +1209,13 @@ ews_folder_search_by_uids (CamelFolder *folder,
        ews_search = CAMEL_EWS_SEARCH (ews_folder->search);
 
        camel_folder_search_set_folder (ews_folder->search, folder);
+       camel_ews_search_clear_cached_results (ews_search);
        camel_ews_search_set_cancellable_and_error (ews_search, cancellable, error);
 
        matches = camel_folder_search_search (ews_folder->search, expression, uids, cancellable, error);
 
        camel_ews_search_set_cancellable_and_error (ews_search, NULL, NULL);
+       camel_ews_search_clear_cached_results (ews_search);
 
        g_mutex_unlock (&priv->search_lock);
 
diff --git a/src/camel/camel-ews-search.c b/src/camel/camel-ews-search.c
index 865f1d73..5d5130e3 100644
--- a/src/camel/camel-ews-search.c
+++ b/src/camel/camel-ews-search.c
@@ -33,6 +33,7 @@ struct _CamelEwsSearchPrivate {
        GWeakRef ews_store;
        gint *local_data_search; /* not NULL, if testing whether all used headers are all locally available */
 
+       GHashTable *cached_results; /* gchar * (search description) ~> GHashTable { gchar * (uid {from string 
pool}) ~> NULL } */
        GCancellable *cancellable; /* not referenced */
        GError **error; /* not referenced */
 };
@@ -103,6 +104,7 @@ ews_search_finalize (GObject *object)
        priv = CAMEL_EWS_SEARCH_GET_PRIVATE (object);
 
        g_weak_ref_clear (&priv->ews_store);
+       g_hash_table_destroy (priv->cached_results);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (camel_ews_search_parent_class)->finalize (object);
@@ -183,6 +185,31 @@ ews_search_items_to_ptr_array (const GSList *items)
        return matches;
 }
 
+static gchar *
+ews_search_describe_criteria (const GPtrArray *words)
+{
+       GString *desc;
+
+       desc = g_string_sized_new (64);
+
+       if (words && words->len) {
+               guint ii;
+
+               for (ii = 0; ii < words->len; ii++) {
+                       const gchar *word = words->pdata[ii];
+
+                       if (word) {
+                               g_string_append (desc, word);
+                               g_string_append_c (desc, '\n');
+                       }
+               }
+       }
+
+       g_string_append_c (desc, '\n');
+
+       return g_string_free (desc, FALSE);
+}
+
 static CamelSExpResult *
 ews_search_process_criteria (CamelSExp *sexp,
                             CamelFolderSearch *search,
@@ -191,108 +218,144 @@ ews_search_process_criteria (CamelSExp *sexp,
 {
        CamelSExpResult *result;
        CamelEwsSearch *ews_search = CAMEL_EWS_SEARCH (search);
-       CamelEwsFolder *ews_folder;
+       CamelMessageInfo *info;
+       GHashTable *cached_results;
+       gchar *criteria_desc;
        GPtrArray *uids = NULL;
        GError *local_error = NULL;
 
-       ews_folder = CAMEL_EWS_FOLDER (camel_folder_search_get_folder (search));
+       criteria_desc = ews_search_describe_criteria (words);
+       cached_results = g_hash_table_lookup (ews_search->priv->cached_results, criteria_desc);
 
-       /* Sanity check. */
-       g_return_val_if_fail (ews_folder != NULL, NULL);
+       if (!cached_results) {
+               CamelEwsFolder *ews_folder;
 
-       if (ews_folder != NULL) {
-               EEwsConnection *connection = NULL;
-               gchar *folder_id = NULL;
-               gboolean can_search;
+               ews_folder = CAMEL_EWS_FOLDER (camel_folder_search_get_folder (search));
 
-               /* there should always be one, held by one of the callers of this function */
-               g_warn_if_fail (ews_store != NULL);
+               /* Sanity check. */
+               g_return_val_if_fail (ews_folder != NULL, NULL);
 
-               can_search = ews_store != NULL && words != NULL;
+               if (ews_folder != NULL) {
+                       EEwsConnection *connection = NULL;
+                       gchar *folder_id = NULL;
+                       gboolean can_search;
 
-               if (can_search) {
-                       folder_id = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary,
-                               camel_folder_get_full_name (CAMEL_FOLDER (ews_folder)));
-                       if (!folder_id)
-                               can_search = FALSE;
-               }
+                       /* there should always be one, held by one of the callers of this function */
+                       g_warn_if_fail (ews_store != NULL);
 
-               if (can_search) {
-                       connection = camel_ews_store_ref_connection (ews_store);
-                       if (!connection)
-                               can_search = FALSE;
-               }
+                       can_search = ews_store != NULL && words != NULL;
 
-               if (can_search) {
-                       EwsFolderId *fid;
-                       GSList *found_items = NULL;
-                       gboolean includes_last_item = FALSE;
-                       GString *expression;
-                       guint ii;
+                       if (can_search) {
+                               folder_id = camel_ews_store_summary_get_folder_id_from_name 
(ews_store->summary,
+                                       camel_folder_get_full_name (CAMEL_FOLDER (ews_folder)));
+                               if (!folder_id)
+                                       can_search = FALSE;
+                       }
 
-                       fid = e_ews_folder_id_new (folder_id, NULL, FALSE);
-                       expression = g_string_new ("");
+                       if (can_search) {
+                               connection = camel_ews_store_ref_connection (ews_store);
+                               if (!connection)
+                                       can_search = FALSE;
+                       }
 
-                       if (words->len >= 2)
-                               g_string_append (expression, "(and ");
+                       if (can_search) {
+                               EwsFolderId *fid;
+                               GSList *found_items = NULL;
+                               gboolean includes_last_item = FALSE;
+                               GString *expression;
+                               guint ii;
 
-                       for (ii = 0; ii < words->len; ii++) {
-                               GString *word;
+                               fid = e_ews_folder_id_new (folder_id, NULL, FALSE);
+                               expression = g_string_new ("");
 
-                               word = e_str_replace_string (g_ptr_array_index (words, ii), "\"", "\\\"");
+                               if (words->len >= 2)
+                                       g_string_append (expression, "(and ");
 
-                               g_string_append (expression, "(body-contains \"");
-                               g_string_append (expression, word->str);
-                               g_string_append (expression, "\")");
+                               for (ii = 0; ii < words->len; ii++) {
+                                       GString *word;
 
-                               g_string_free (word, TRUE);
-                       }
+                                       word = e_str_replace_string (g_ptr_array_index (words, ii), "\"", 
"\\\"");
+
+                                       g_string_append (expression, "(body-contains \"");
+                                       g_string_append (expression, word->str);
+                                       g_string_append (expression, "\")");
+
+                                       g_string_free (word, TRUE);
+                               }
+
+                               /* Close the 'and' */
+                               if (words->len >= 2)
+                                       g_string_append_c (expression, ')');
 
-                       /* Close the 'and' */
-                       if (words->len >= 2)
-                               g_string_append_c (expression, ')');
+                               if (e_ews_connection_find_folder_items_sync (
+                                       connection, EWS_PRIORITY_MEDIUM,
+                                       fid, "IdOnly", NULL, NULL, expression->str, NULL,
+                                       E_EWS_FOLDER_TYPE_MAILBOX, &includes_last_item, &found_items,
+                                       e_ews_query_to_restriction,
+                                       ews_search->priv->cancellable, &local_error)) {
 
-                       if (e_ews_connection_find_folder_items_sync (
-                               connection, EWS_PRIORITY_MEDIUM,
-                               fid, "IdOnly", NULL, NULL, expression->str, NULL,
-                               E_EWS_FOLDER_TYPE_MAILBOX, &includes_last_item, &found_items,
-                               e_ews_query_to_restriction,
-                               ews_search->priv->cancellable, &local_error)) {
+                                       uids = ews_search_items_to_ptr_array (found_items);
+                               }
 
-                               uids = ews_search_items_to_ptr_array (found_items);
+                               g_slist_free_full (found_items, g_object_unref);
+                               g_string_free (expression, TRUE);
+                               e_ews_folder_id_free (fid);
                        }
 
-                       g_slist_free_full (found_items, g_object_unref);
-                       g_string_free (expression, TRUE);
-                       e_ews_folder_id_free (fid);
+                       g_clear_object (&connection);
+                       g_free (folder_id);
                }
 
-               g_clear_object (&connection);
-               g_free (folder_id);
+               if (local_error != NULL)
+                       g_propagate_error (ews_search->priv->error, local_error);
+
+               if (!uids) {
+                       /* Make like we've got an empty result */
+                       uids = g_ptr_array_new ();
+               }
        }
 
-       /* Sanity check. */
-       g_warn_if_fail (
-               ((uids != NULL) && (local_error == NULL)) ||
-               ((uids == NULL) && (local_error != NULL)));
+       if (!cached_results) {
+               cached_results = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) 
camel_pstring_free, NULL);
+
+               if (uids) {
+                       guint ii;
 
-       if (local_error != NULL)
-               g_propagate_error (ews_search->priv->error, local_error);
+                       for (ii = 0; ii < uids->len; ii++) {
+                               g_hash_table_insert (cached_results, (gpointer) camel_pstring_strdup 
(uids->pdata[ii]), NULL);
+                       }
+               }
 
-       if (!uids) {
-               /* Make like we've got an empty result */
-               uids = g_ptr_array_new ();
+               g_hash_table_insert (ews_search->priv->cached_results, criteria_desc, cached_results);
+       } else {
+               g_free (criteria_desc);
        }
 
-       if (camel_folder_search_get_current_message_info (search)) {
+       info = camel_folder_search_get_current_message_info (search);
+
+       if (info) {
                result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
-               result->value.boolean = (uids && uids->len > 0);
+               result->value.boolean = g_hash_table_contains (cached_results, camel_message_info_get_uid 
(info));
        } else {
+               if (!uids) {
+                       GHashTableIter iter;
+                       gpointer key;
+
+                       uids = g_ptr_array_sized_new (g_hash_table_size (cached_results));
+
+                       g_hash_table_iter_init (&iter, cached_results);
+
+                       while (g_hash_table_iter_next (&iter, &key, NULL)) {
+                               g_ptr_array_add (uids, (gpointer) camel_pstring_strdup (key));
+                       }
+               }
+
                result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_ARRAY_PTR);
                result->value.ptrarray = g_ptr_array_ref (uids);
        }
 
-       g_ptr_array_unref (uids);
+       if (uids)
+               g_ptr_array_unref (uids);
 
        return result;
 }
@@ -425,6 +488,7 @@ camel_ews_search_init (CamelEwsSearch *search)
 {
        search->priv = CAMEL_EWS_SEARCH_GET_PRIVATE (search);
        search->priv->local_data_search = NULL;
+       search->priv->cached_results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) g_hash_table_destroy);
 
        g_weak_ref_init (&search->priv->ews_store, NULL);
 }
@@ -504,6 +568,14 @@ camel_ews_search_set_store (CamelEwsSearch *search,
        g_object_notify (G_OBJECT (search), "store");
 }
 
+void
+camel_ews_search_clear_cached_results (CamelEwsSearch *search)
+{
+       g_return_if_fail (CAMEL_IS_EWS_SEARCH (search));
+
+       g_hash_table_remove_all (search->priv->cached_results);
+}
+
 /**
  * camel_ews_search_set_cancellable_and_error:
  * @search: a #CamelEwsSearch
diff --git a/src/camel/camel-ews-search.h b/src/camel/camel-ews-search.h
index 1d8152e6..daa001e6 100644
--- a/src/camel/camel-ews-search.h
+++ b/src/camel/camel-ews-search.h
@@ -73,6 +73,8 @@ CamelEwsStore *
                camel_ews_search_ref_store      (CamelEwsSearch *search);
 void           camel_ews_search_set_store      (CamelEwsSearch *search,
                                                 CamelEwsStore *ews_store);
+void           camel_ews_search_clear_cached_results
+                                               (CamelEwsSearch *search);
 void           camel_ews_search_set_cancellable_and_error
                                                (CamelEwsSearch *search,
                                                 GCancellable *cancellable,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]