commit 01f5a4102691f1e0eae4a8233859c1da0363e92a
Author: Tristan Van Berkom <tristan.van.berkom@gmail.com>
Date:   Sun Jun 26 12:05:54 2011 -0400

    Handle fields-of-interest for local addressbook backend.
    
    This patch refactors the local addressbook backend to use
    the new sqlitedb api instead of the old summary apis.
    The result is that vcards are virtually built from the
    sqlitedb cache when fields-of-interest is set.
    
    Additionally, the patch adds a filtering algorithm in
    e_book_backend_notify_update() to automatically filter vcard
    notifications in response to a client adding or modifying a contact.
    
    This patch should address both bugs:
      https://bugzilla.gnome.org/show_bug.cgi?id=652179
      https://bugzilla.gnome.org/show_bug.cgi?id=652172

diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index 596c829..b0c8acd 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -48,7 +48,7 @@
 #include "libebook/e-contact.h"
 
 #include "libedata-book/e-book-backend-sexp.h"
-#include "libedata-book/e-book-backend-summary.h"
+#include "libedata-book/e-book-backend-sqlitedb.h"
 #include "libedata-book/e-data-book.h"
 #include "libedata-book/e-data-book-view.h"
 
@@ -62,7 +62,11 @@
 #define E_BOOK_BACKEND_FILE_VERSION "0.2"
 
 #define PAS_ID_PREFIX "pas-id-"
-#define SUMMARY_FLUSH_TIMEOUT 5000
+
+#define SQLITEDB_EMAIL_ID    "addressbook@localbackend.com"
+#define SQLITEDB_FOLDER_ID   "folder_id"
+#define SQLITEDB_FOLDER_NAME "folder"
+
 
 #define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL)
 #define EDB_ERROR_EX(_code, _msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg)
@@ -72,10 +76,11 @@ G_DEFINE_TYPE (EBookBackendFile, e_book_backend_file, E_TYPE_BOOK_BACKEND_SYNC)
 struct _EBookBackendFilePrivate {
 	gchar     *dirname;
 	gchar     *filename;
-	gchar     *summary_filename;
-	DB       *file_db;
-	DB_ENV   *env;
-	EBookBackendSummary *summary;
+	DB        *file_db;
+	DB_ENV    *env;
+
+	EBookBackendSqliteDB *sqlitedb;
+
 	/* for future use */
 	gpointer reserved1;
 	gpointer reserved2;
@@ -120,7 +125,7 @@ string_to_dbt (const gchar *str, DBT *dbt)
 }
 
 static EContact*
-create_contact (gchar *uid, const gchar *vcard)
+create_contact (const gchar *uid, const gchar *vcard)
 {
 	EContact *contact = e_contact_new_from_vcard (vcard);
 	if (!e_contact_get_const (contact, E_CONTACT_UID))
@@ -130,12 +135,14 @@ create_contact (gchar *uid, const gchar *vcard)
 }
 
 static gboolean
-build_summary (EBookBackendFilePrivate *bfpriv)
+build_sqlitedb (EBookBackendFilePrivate *bfpriv)
 {
 	DB             *db = bfpriv->file_db;
 	DBC            *dbc;
 	gint            db_error;
-	DBT  id_dbt, vcard_dbt;
+	DBT             id_dbt, vcard_dbt;
+	GSList         *contacts = NULL;
+	GError         *error = NULL;
 
 	db_error = db->cursor (db, NULL, &dbc, 0);
 
@@ -154,8 +161,8 @@ build_summary (EBookBackendFilePrivate *bfpriv)
 		if (id_dbt.size != strlen (E_BOOK_BACKEND_FILE_VERSION_NAME) + 1
 		    || strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
 			EContact *contact = create_contact (id_dbt.data, vcard_dbt.data);
-			e_book_backend_summary_add_contact (bfpriv->summary, contact);
-			g_object_unref (contact);
+
+			contacts = g_slist_prepend (contacts, contact);
 		}
 
 		db_error = dbc->c_get (dbc, &id_dbt, &vcard_dbt, DB_NEXT);
@@ -164,6 +171,25 @@ build_summary (EBookBackendFilePrivate *bfpriv)
 
 	dbc->c_close (dbc);
 
+	contacts = g_slist_reverse (contacts);
+
+	if (!e_book_backend_sqlitedb_add_contacts (bfpriv->sqlitedb,
+						   SQLITEDB_FOLDER_ID,
+						   contacts, FALSE, &error)) {
+		g_warning ("Failed to build contact summary: %s", error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
+	g_slist_foreach (contacts, (GFunc)g_object_unref, NULL);
+	g_slist_free (contacts);
+
+	if (!e_book_backend_sqlitedb_set_is_populated (bfpriv->sqlitedb, SQLITEDB_FOLDER_ID, TRUE, &error)) {
+		g_warning ("Failed to set the sqlitedb populated flag: %s", error->message);
+		g_error_free (error);
+		return FALSE;
+	}
+
 	return TRUE;
 }
 
@@ -255,7 +281,14 @@ e_book_backend_file_create_contact (EBookBackendSync *backend,
 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
 
 	if (do_create (bf, vcard, contact, perror)) {
-		e_book_backend_summary_add_contact (bf->priv->summary, *contact);
+		GError *error = NULL;
+
+		if (!e_book_backend_sqlitedb_add_contact (bf->priv->sqlitedb,
+							  SQLITEDB_FOLDER_ID,
+							  *contact, FALSE, &error)) {
+			g_warning ("Failed to add contact to summary: %s", error->message);
+			g_error_free (error);
+		}
 	}
 }
 
@@ -268,12 +301,13 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 				     GError **perror)
 {
 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
-	DB             *db = bf->priv->file_db;
-	DBT            id_dbt;
-	gint            db_error;
-	const gchar    *id;
-	const GSList   *l;
-	GSList         *removed_cards = NULL;
+	DB               *db = bf->priv->file_db;
+	DBT               id_dbt;
+	gint              db_error;
+	const gchar      *id;
+	GSList           *removed_cards = NULL;
+	const GSList     *l;
+	GError           *error = NULL;
 
 	for (l = id_list; l; l = l->next) {
 		id = l->data;
@@ -297,12 +331,14 @@ e_book_backend_file_remove_contacts (EBookBackendSync *backend,
 			g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error));
 	}
 
-	*ids = removed_cards;
-
-	for (l = removed_cards; l; l = l->next) {
-		id = l->data;
-		e_book_backend_summary_remove_contact (bf->priv->summary, id);
+	if (!e_book_backend_sqlitedb_remove_contacts (bf->priv->sqlitedb,
+						      SQLITEDB_FOLDER_ID,
+						      removed_cards, &error)) {
+		g_warning ("Failed to remove contacts from the summary: %s", error->message);
+		g_error_free (error);
 	}
+
+	*ids = removed_cards;
 }
 
 static void
@@ -353,8 +389,19 @@ e_book_backend_file_modify_contact (EBookBackendSync *backend,
 		if (db_error != 0) {
 			g_warning (G_STRLOC ": db->sync failed with %s", db_strerror (db_error));
 		} else {
-			e_book_backend_summary_remove_contact (bf->priv->summary, id);
-			e_book_backend_summary_add_contact (bf->priv->summary, *contact);
+			GError *error = NULL;
+
+			if (!e_book_backend_sqlitedb_remove_contact (bf->priv->sqlitedb,
+								     SQLITEDB_FOLDER_ID,
+								     id, &error)) {
+				g_warning ("Failed to remove contact from the summary: %s", error->message);
+				g_error_free (error);
+			} else if (!e_book_backend_sqlitedb_add_contact (bf->priv->sqlitedb,
+									 SQLITEDB_FOLDER_ID,
+									 *contact, FALSE, &error)) {
+				g_warning ("Failed to add contact to summary: %s", error->message);
+				g_error_free (error);
+			}
 		}
 	} else {
 		g_warning (G_STRLOC ": db->put failed with %s", db_strerror(db_error));
@@ -412,36 +459,27 @@ e_book_backend_file_get_contact_list (EBookBackendSync *backend,
 	EBookBackendSExp *card_sexp = NULL;
 	gboolean search_needed;
 	const gchar *search = query;
-	GSList *contact_list = NULL;
+	GSList *contact_list = NULL, *l;
+	GSList *summary_list = NULL;
 
 	d(printf ("e_book_backend_file_get_contact_list (%s)\n", search));
-	if (e_book_backend_summary_is_summary_query (bf->priv->summary, search)) {
 
-		/* do a summary query */
-		GPtrArray *ids = e_book_backend_summary_search (bf->priv->summary, search);
-		gint i;
+	summary_list = e_book_backend_sqlitedb_search (bf->priv->sqlitedb,
+						       SQLITEDB_FOLDER_ID,
+						       search, NULL, NULL);
 
-		if (!ids) {
-			g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND));
-			return;
-		}
+	if (summary_list) {
 
-		for (i = 0; i < ids->len; i++) {
-			gchar *id = g_ptr_array_index (ids, i);
-			string_to_dbt (id, &id_dbt);
-			memset (&vcard_dbt, 0, sizeof (vcard_dbt));
-			vcard_dbt.flags = DB_DBT_MALLOC;
+		for (l = summary_list; l; l = l->next) {
+			EbSdbSearchData *data = l->data;
 
-			db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
-			if (db_error == 0) {
-				contact_list = g_slist_prepend (contact_list, vcard_dbt.data);
-			} else {
-				g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
-				db_error_to_gerror (db_error, perror);
-				break;
-			}
+			contact_list = g_slist_prepend (contact_list, data->vcard);
+			data->vcard  = NULL;
 		}
-		g_ptr_array_free (ids, TRUE);
+
+		g_slist_foreach (summary_list, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+		g_slist_free (summary_list);
+
 	} else {
 		search_needed = TRUE;
 		if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")"))
@@ -522,24 +560,12 @@ e_book_backend_file_get_contact_list_uids (EBookBackendSync *backend,
 	GSList *uids = NULL;
 
 	d(printf ("e_book_backend_file_get_contact_list (%s)\n", search));
-	if (e_book_backend_summary_is_summary_query (bf->priv->summary, search)) {
-		/* do a summary query */
-		GPtrArray *ids = e_book_backend_summary_search (bf->priv->summary, search);
-		gint i;
-
-		if (!ids) {
-			g_propagate_error (perror, EDB_ERROR (CONTACT_NOT_FOUND));
-			return;
-		}
 
-		for (i = 0; i < ids->len; i++) {
-			gchar *id = g_ptr_array_index (ids, i);
+	uids = e_book_backend_sqlitedb_search_uids (bf->priv->sqlitedb,
+						    SQLITEDB_FOLDER_ID,
+						    search, NULL);
 
-			uids = g_slist_prepend (uids, g_strdup (id));
-		}
-
-		g_ptr_array_free (ids, TRUE);
-	} else {
+	if (!uids) {
 		search_needed = TRUE;
 		if (!strcmp (search, "(contains \"x-evolution-any-field\" \"\")"))
 			search_needed = FALSE;
@@ -633,6 +659,18 @@ get_closure (EDataBookView *book_view)
 	return g_object_get_data (G_OBJECT (book_view), "EBookBackendFile.BookView::closure");
 }
 
+static void
+notify_update_vcard (EDataBookView *book_view,
+		     gboolean       prefiltered,
+		     const gchar   *id,
+		     gchar         *vcard)
+{
+	if (prefiltered)
+		e_data_book_view_notify_update_prefiltered_vcard (book_view, id, vcard);
+	else
+		e_data_book_view_notify_update_vcard (book_view, vcard);
+}
+
 static gpointer
 book_view_thread (gpointer data)
 {
@@ -644,6 +682,8 @@ book_view_thread (gpointer data)
 	DBT id_dbt, vcard_dbt;
 	gint db_error;
 	gboolean allcontacts;
+	GSList *summary_list, *l;
+	GHashTable *fields_of_interest;
 
 	g_return_val_if_fail (E_IS_DATA_BOOK_VIEW (data), NULL);
 
@@ -661,8 +701,9 @@ book_view_thread (gpointer data)
 	   when/if it's stopped */
 	e_data_book_view_ref (book_view);
 
-	db = bf->priv->file_db;
-	query = e_data_book_view_get_card_query (book_view);
+	db                 = bf->priv->file_db;
+	query              = e_data_book_view_get_card_query (book_view);
+	fields_of_interest = e_data_book_view_get_fields_of_interest (book_view);
 
 	if ( !strcmp (query, "(contains \"x-evolution-any-field\" \"\")")) {
 		e_data_book_view_notify_progress (book_view, -1, _("Loading..."));
@@ -675,37 +716,21 @@ book_view_thread (gpointer data)
 	d(printf ("signalling parent thread\n"));
 	e_flag_set (closure->running);
 
-	if (e_book_backend_summary_is_summary_query (bf->priv->summary, query)) {
-		/* do a summary query */
-		GPtrArray *ids = e_book_backend_summary_search (bf->priv->summary, e_data_book_view_get_card_query (book_view));
-		gint i;
-
-		if (!ids)
-			goto done;
-
-		for (i = 0; i < ids->len; i++) {
-			gchar *id = g_ptr_array_index (ids, i);
+	summary_list = e_book_backend_sqlitedb_search (bf->priv->sqlitedb,
+						       SQLITEDB_FOLDER_ID,
+						       query, fields_of_interest, NULL);
+	if (summary_list) {
 
-			if (!e_flag_is_set (closure->running))
-				break;
+		for (l = summary_list; l; l = l->next) {
+			EbSdbSearchData *data = l->data;
 
-			string_to_dbt (id, &id_dbt);
-			memset (&vcard_dbt, 0, sizeof (vcard_dbt));
-			vcard_dbt.flags = DB_DBT_MALLOC;
-
-			db_error = db->get (db, NULL, &id_dbt, &vcard_dbt, 0);
-
-			if (db_error == 0) {
-				e_data_book_view_notify_update_prefiltered_vcard (book_view, id, vcard_dbt.data);
-			}
-			else {
-				g_warning (G_STRLOC ": db->get failed with %s", db_strerror (db_error));
-			}
+			notify_update_vcard (book_view, TRUE, data->uid, data->vcard);
+			data->vcard = NULL;
 		}
 
-		g_ptr_array_free (ids, TRUE);
-	}
-	else {
+		g_slist_foreach (summary_list, (GFunc)e_book_backend_sqlitedb_search_data_free, NULL);
+		g_slist_free (summary_list);
+	} else {
 		/* iterate over the db and do the query there */
 		DBC    *dbc;
 
@@ -724,10 +749,8 @@ book_view_thread (gpointer data)
 
 				/* don't include the version in the list of cards */
 				if (strcmp (id_dbt.data, E_BOOK_BACKEND_FILE_VERSION_NAME)) {
-					if (allcontacts)
-						e_data_book_view_notify_update_prefiltered_vcard (book_view, id_dbt.data, vcard_dbt.data);
-					else
-						e_data_book_view_notify_update_vcard (book_view, vcard_dbt.data);
+					notify_update_vcard (book_view, allcontacts, 
+							     id_dbt.data, vcard_dbt.data);
 				} else {
 					g_free (vcard_dbt.data);
 				}
@@ -747,7 +770,7 @@ book_view_thread (gpointer data)
 		}
 
 	}
-done:
+
 	if (e_flag_is_set (closure->running))
 		e_data_book_view_notify_complete (book_view, NULL /* Success */);
 
@@ -997,14 +1020,13 @@ e_book_backend_file_open (EBookBackendSync       *backend,
 			  GError	       **perror)
 {
 	EBookBackendFile *bf = E_BOOK_BACKEND_FILE (backend);
-	gchar           *dirname, *filename;
-	gboolean        readonly = TRUE;
-	ESource *source = e_book_backend_get_source (E_BOOK_BACKEND (backend));
-	gint             db_error;
-	DB *db;
-	DB_ENV *env;
-	time_t db_mtime;
-	struct stat sb;
+	gchar            *dirname, *filename;
+	gboolean          readonly = TRUE;
+	ESource          *source = e_book_backend_get_source (E_BOOK_BACKEND (backend));
+	gint              db_error;
+	DB               *db;
+	DB_ENV           *env;
+	GError           *local_error = NULL;
 
 #ifdef CREATE_DEFAULT_VCARD
 	gboolean create_default_vcard = FALSE;
@@ -1200,23 +1222,25 @@ e_book_backend_file_open (EBookBackendSync       *backend,
 	bf->priv->dirname = dirname;
 	bf->priv->filename = filename;
 
-	if (g_stat (bf->priv->filename, &sb) == -1) {
-		db->close (db, 0);
-		bf->priv->file_db = NULL;
-		g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, "stat(%s) failed", bf->priv->filename));
+	bf->priv->sqlitedb = e_book_backend_sqlitedb_new (bf->priv->dirname,
+							  SQLITEDB_EMAIL_ID,
+							  SQLITEDB_FOLDER_ID,
+							  SQLITEDB_FOLDER_NAME,
+							  FALSE,
+							  perror);
+	if (!bf->priv->sqlitedb)
 		return;
-	}
-	db_mtime = sb.st_mtime;
-
-	g_free (bf->priv->summary_filename);
-	bf->priv->summary_filename = g_strconcat (bf->priv->filename, ".summary", NULL);
-	bf->priv->summary = e_book_backend_summary_new (bf->priv->summary_filename, SUMMARY_FLUSH_TIMEOUT);
 
-	if (e_book_backend_summary_is_up_to_date (bf->priv->summary, db_mtime) == FALSE
-	    || e_book_backend_summary_load (bf->priv->summary) == FALSE ) {
-		if (!bf->priv->summary || !build_summary (bf->priv)) {
-			g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, "Failed to build summary for an address book %s", bf->priv->filename));
+	if (!e_book_backend_sqlitedb_get_is_populated (bf->priv->sqlitedb,
+						       SQLITEDB_FOLDER_ID,
+						       &local_error)) {
+		if (local_error) {
+			g_propagate_error (perror, local_error);
 			return;
+		} else if (!build_sqlitedb (bf->priv)) {
+			g_propagate_error (perror, e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
+                                    "Failed to build summary for an address book %s",
+                                     bf->priv->filename));
 		}
 	}
 
@@ -1261,11 +1285,12 @@ e_book_backend_file_remove (EBookBackendSync *backend,
 		return;
 	}
 
-	/* unref the summary before we remove the file so it's not written out again */
-	g_object_unref (bf->priv->summary);
-	bf->priv->summary = NULL;
-	if (-1 == g_unlink (bf->priv->summary_filename))
-		g_warning ("failed to remove summary file `%s`: %s", bf->priv->summary_filename, g_strerror (errno));
+	if (!e_book_backend_sqlitedb_remove (bf->priv->sqlitedb, perror))
+		return;
+
+	/* unref the sqlitedb before we remove the file so it's not written out again */
+	g_object_unref (bf->priv->sqlitedb);
+	bf->priv->sqlitedb = NULL;
 
 	dir = g_dir_open (bf->priv->dirname, 0, NULL);
 	if (dir) {
@@ -1377,9 +1402,9 @@ e_book_backend_file_dispose (GObject *object)
 	}
 	G_UNLOCK (global_env);
 
-	if (bf->priv->summary) {
-		g_object_unref (bf->priv->summary);
-		bf->priv->summary = NULL;
+	if (bf->priv->sqlitedb) {
+		g_object_unref (bf->priv->sqlitedb);
+		bf->priv->sqlitedb = NULL;
 	}
 
 	G_OBJECT_CLASS (e_book_backend_file_parent_class)->dispose (object);
@@ -1394,7 +1419,6 @@ e_book_backend_file_finalize (GObject *object)
 
 	g_free (bf->priv->filename);
 	g_free (bf->priv->dirname);
-	g_free (bf->priv->summary_filename);
 
 	g_free (bf->priv);
 
diff --git a/addressbook/libebook/e-contact.c b/addressbook/libebook/e-contact.c
index 8ea4904..048d159 100644
--- a/addressbook/libebook/e-contact.c
+++ b/addressbook/libebook/e-contact.c
@@ -1719,6 +1719,44 @@ e_contact_set_attributes (EContact *contact, EContactField field_id, GList *attr
 }
 
 /**
+ * e_contact_copy_attributes:
+ * @source: an #EContact
+ * @dest: another #EContact
+ * @field_id: an #EContactField
+ * @attributes: a #GList of pointers to #EVCardAttribute
+ *
+ * Copies the attributes for @field_id from @source into @dest
+ **/
+void
+e_contact_copy_attributes (EContact *source, EContact *dest, EContactField field_id)
+{
+	GList *attrs, *a;
+	const EContactFieldInfo *info = NULL;
+
+	g_return_if_fail (source && E_IS_CONTACT (source));
+	g_return_if_fail (dest && E_IS_CONTACT (dest));
+	g_return_if_fail (field_id >= 1 && field_id <= E_CONTACT_FIELD_LAST);
+
+	info = &field_info[field_id];
+
+	attrs = e_vcard_get_attributes (E_VCARD (source));
+
+	e_vcard_remove_attributes (E_VCARD (dest), NULL, info->vcard_field_name);
+
+	for (a = attrs; a; a = a->next) {
+		EVCardAttribute *attr = a->data;
+		const gchar *name;
+
+		name = e_vcard_attribute_get_name (attr);
+
+		if (!g_ascii_strcasecmp (name, info->vcard_field_name)) {
+			e_vcard_append_attribute (E_VCARD (dest),
+						  e_vcard_attribute_copy (attr));
+		}
+	}
+}
+
+/**
  * e_contact_name_new:
  *
  * Creates a new #EContactName struct.
diff --git a/addressbook/libebook/e-contact.h b/addressbook/libebook/e-contact.h
index 729eeeb..64ed5b8 100644
--- a/addressbook/libebook/e-contact.h
+++ b/addressbook/libebook/e-contact.h
@@ -314,6 +314,7 @@ void                    e_contact_set              (EContact *contact, EContactF
    EVCardAttribute*'s. */
 GList*                  e_contact_get_attributes   (EContact *contact, EContactField field_id);
 void                    e_contact_set_attributes   (EContact *contact, EContactField field_id, GList *attributes);
+void                    e_contact_copy_attributes  (EContact *source, EContact *dest, EContactField field_id);
 
 /* misc functions for structured values */
 GType                   e_contact_date_get_type    (void);
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index 358f489..3ae958e 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -804,7 +804,9 @@ e_book_backend_remove_client (EBookBackend *backend,
  * @callback returns %FALSE to stop further processing.
  **/
 void
-e_book_backend_foreach_view (EBookBackend *backend, gboolean (* callback) (EDataBookView *view, gpointer user_data), gpointer user_data)
+e_book_backend_foreach_view (EBookBackend *backend, 
+			     gboolean (* callback) (EDataBookView *view, gpointer user_data), 
+			     gpointer user_data)
 {
 	const GSList *views;
 	EDataBookView *view;
@@ -1026,10 +1028,51 @@ e_book_backend_sync (EBookBackend *backend)
 
 
 
+
+typedef struct {	
+	EContact   *contact;
+	EContact   *shallow;
+} FilterContactData;
+
+static void
+foreach_filter_contact (const gchar       *field,
+			gpointer           present,
+			FilterContactData *data)
+{
+	EContactField   field_id;
+
+	field_id = e_contact_field_id (field);
+	e_contact_copy_attributes (data->contact, data->shallow, field_id);
+}
+
 static gboolean
-view_notify_update (EDataBookView *view, gpointer contact)
+view_notify_update (EDataBookView *view, gpointer data)
 {
-	e_data_book_view_notify_update (view, contact);
+	EContact   *contact = data;
+	GHashTable *fields;
+
+	fields = e_data_book_view_get_fields_of_interest (view);
+	if (fields && g_hash_table_size (fields) > 0) {
+		FilterContactData data = { 0, };
+		EContact         *shallow = e_contact_new ();
+		gchar            *vcard;
+
+		/* Set the UID unconditionally */
+		e_contact_set (shallow, E_CONTACT_UID, e_contact_get_const (contact, E_CONTACT_UID));
+
+		/* Transfer any attributes for the fields-of-interest */
+		data.contact = contact;
+		data.shallow = shallow;
+		g_hash_table_foreach (fields, (GHFunc)foreach_filter_contact, &data);
+
+		vcard = e_vcard_to_string (E_VCARD (shallow), EVC_FORMAT_VCARD_30);
+		g_object_unref (shallow);
+
+		/* Notify with a stripped vcard holding only the fields-of-interest */
+		e_data_book_view_notify_update_vcard (view, vcard);
+	} else {
+		e_data_book_view_notify_update (view, contact);
+	}
 
 	return TRUE;
 }
