[nautilus] Add a way to rank search results



commit 2156d246e45933d424decd447203e39c26482da7
Author: William Jon McCann <jmccann redhat com>
Date:   Thu Jul 19 16:16:43 2012 -0400

    Add a way to rank search results
    
    We give each search hit a relevance score computed from the sum
    of proximity, recent, and text match bonuses.
    
    The search results are ordered by this value by default.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=680163

 libnautilus-private/Makefile.am                    |    2 +
 libnautilus-private/nautilus-column-utilities.c    |   20 ++
 libnautilus-private/nautilus-file-private.h        |    2 +
 libnautilus-private/nautilus-file.c                |  102 ++++++-
 libnautilus-private/nautilus-file.h                |    6 +-
 libnautilus-private/nautilus-search-directory.c    |   18 +-
 .../nautilus-search-engine-simple.c                |   72 +++--
 .../nautilus-search-engine-tracker.c               |   38 ++-
 libnautilus-private/nautilus-search-engine.c       |   14 +-
 libnautilus-private/nautilus-search-hit.c          |  327 ++++++++++++++++++++
 libnautilus-private/nautilus-search-hit.h          |   63 ++++
 libnautilus-private/nautilus-search-provider.h     |    1 +
 12 files changed, 614 insertions(+), 51 deletions(-)
---
diff --git a/libnautilus-private/Makefile.am b/libnautilus-private/Makefile.am
index ee613e0..6512a1c 100644
--- a/libnautilus-private/Makefile.am
+++ b/libnautilus-private/Makefile.am
@@ -162,6 +162,8 @@ libnautilus_private_la_SOURCES = \
 	nautilus-search-engine.h \
 	nautilus-search-engine-simple.c \
 	nautilus-search-engine-simple.h \
+	nautilus-search-hit.c \
+	nautilus-search-hit.h \
 	nautilus-selection-canvas-item.c \
 	nautilus-selection-canvas-item.h \
 	nautilus-signaller.h \
diff --git a/libnautilus-private/nautilus-column-utilities.c b/libnautilus-private/nautilus-column-utilities.c
index 9302738..8380a94 100644
--- a/libnautilus-private/nautilus-column-utilities.c
+++ b/libnautilus-private/nautilus-column-utilities.c
@@ -191,6 +191,24 @@ get_trash_columns (void)
 	return nautilus_column_list_copy (columns);
 }
 
+static GList *
+get_search_columns (void)
+{
+	static GList *columns = NULL;
+
+	if (columns == NULL) {
+		columns = g_list_append (columns,
+					 g_object_new (NAUTILUS_TYPE_COLUMN,
+						       "name", "search_relevance",
+						       "attribute", "search_relevance",
+						       "label", _("Relevance"),
+						       "description", _("Relevance rank for search"),
+						       NULL));
+	}
+
+	return nautilus_column_list_copy (columns);
+}
+
 GList *
 nautilus_get_common_columns (void)
 {
@@ -211,6 +229,8 @@ nautilus_get_all_columns (void)
 
 	columns = g_list_concat (nautilus_get_common_columns (),
 	                         get_trash_columns ());
+	columns = g_list_concat (columns,
+				 get_search_columns ());
 
 	return columns;
 }
diff --git a/libnautilus-private/nautilus-file-private.h b/libnautilus-private/nautilus-file-private.h
index 09ecc8a..ff58386 100644
--- a/libnautilus-private/nautilus-file-private.h
+++ b/libnautilus-private/nautilus-file-private.h
@@ -220,6 +220,8 @@ struct NautilusFileDetails
 
 	time_t trash_time; /* 0 is unknown */
 
+	gdouble search_relevance;
+
 	guint64 free_space; /* (guint)-1 for unknown */
 	time_t free_space_read; /* The time free_space was updated, or 0 for never */
 };
diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c
index 01ffa78..6d704e4 100644
--- a/libnautilus-private/nautilus-file.c
+++ b/libnautilus-private/nautilus-file.c
@@ -130,6 +130,7 @@ static GQuark attribute_name_q,
 	attribute_deep_file_count_q,
 	attribute_deep_directory_count_q,
 	attribute_deep_total_count_q,
+	attribute_search_relevance_q,
 	attribute_trashed_on_q,
 	attribute_trashed_on_full_q,
 	attribute_trash_orig_path_q,
@@ -2986,6 +2987,49 @@ compare_by_type (NautilusFile *file_1, NautilusFile *file_2)
 	return result;
 }
 
+static Knowledge
+get_search_relevance (NautilusFile *file,
+		      gdouble      *relevance_out)
+{
+	if (!nautilus_file_is_in_search (file)) {
+		return UNKNOWABLE;
+	}
+
+	*relevance_out = file->details->search_relevance;
+
+	return KNOWN;
+}
+
+static int
+compare_by_search_relevance (NautilusFile *file_1, NautilusFile *file_2)
+{
+	gdouble r_1, r_2;
+	Knowledge known_1, known_2;
+
+	known_1 = get_search_relevance (file_1, &r_1);
+	known_2 = get_search_relevance (file_2, &r_2);
+
+	if (known_1 > known_2) {
+		return -1;
+	}
+	if (known_1 < known_2) {
+		return +1;
+	}
+
+	if (known_1 == UNKNOWABLE || known_1 == UNKNOWN) {
+		return 0;
+	}
+
+	if (r_1 < r_2) {
+		return -1;
+	}
+	if (r_1 > r_2) {
+		return +1;
+	}
+
+	return 0;
+}
+
 static int
 compare_by_time (NautilusFile *file_1, NautilusFile *file_2, NautilusDateType type)
 {
@@ -3145,6 +3189,12 @@ nautilus_file_compare_for_sort (NautilusFile *file_1,
 				result = compare_by_full_path (file_1, file_2);
 			}
 			break;
+		case NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE:
+			result = compare_by_search_relevance (file_1, file_2);
+			if (result == 0) {
+				result = compare_by_full_path (file_1, file_2);
+			}
+			break;
 		default:
 			g_return_val_if_reached (0);
 		}
@@ -3203,6 +3253,11 @@ nautilus_file_compare_for_sort_by_attribute_q   (NautilusFile
 						       NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
 						       directories_first,
 						       reversed);
+        } else if (attribute == attribute_search_relevance_q) {
+		return nautilus_file_compare_for_sort (file_1, file_2,
+						       NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE,
+						       directories_first,
+						       reversed);
 	}
 
 	/* it is a normal attribute, compare by strings */
@@ -3332,6 +3387,19 @@ nautilus_file_is_in_desktop (NautilusFile *file)
 	return FALSE;
 }
 
+gboolean
+nautilus_file_is_in_search (NautilusFile *file)
+{
+	char *uri;
+	gboolean ret;
+
+	uri = nautilus_file_get_uri (file);
+	ret = eel_uri_is_search (uri);
+	g_free (uri);
+
+	return ret;
+}
+
 static gboolean
 filter_hidden_partition_callback (gpointer data,
 				  gpointer callback_data)
@@ -4772,6 +4840,12 @@ nautilus_file_set_attributes (NautilusFile *file,
 	g_object_unref (location);
 }
 
+void
+nautilus_file_set_search_relevance (NautilusFile *file,
+				    gdouble       relevance)
+{
+	file->details->search_relevance = relevance;
+}
 
 /**
  * nautilus_file_can_get_permissions:
@@ -7415,13 +7489,15 @@ static gboolean
 get_attributes_for_default_sort_type (NautilusFile *file,
 				      gboolean *is_recent,
 				      gboolean *is_download,
-				      gboolean *is_trash)
+				      gboolean *is_trash,
+				      gboolean *is_search)
 {
-	gboolean is_recent_dir, is_download_dir, is_desktop_dir, is_trash_dir, retval;
+	gboolean is_recent_dir, is_download_dir, is_desktop_dir, is_trash_dir, is_search_dir, retval;
 
 	*is_recent = FALSE;
 	*is_download = FALSE;
 	*is_trash = FALSE;
+	*is_search = FALSE;
 	retval = FALSE;
 
 	/* special handling for certain directories */
@@ -7434,6 +7510,8 @@ get_attributes_for_default_sort_type (NautilusFile *file,
 			nautilus_file_is_user_special_directory (file, G_USER_DIRECTORY_DESKTOP);
 		is_trash_dir =
 			nautilus_file_is_in_trash (file);
+		is_search_dir =
+			nautilus_file_is_in_search (file);
 
 		if (is_download_dir && !is_desktop_dir) {
 			*is_download = TRUE;
@@ -7444,6 +7522,9 @@ get_attributes_for_default_sort_type (NautilusFile *file,
 		} else if (is_recent_dir) {
 			*is_recent = TRUE;
 			retval = TRUE;
+		} else if (is_search_dir) {
+			*is_search = TRUE;
+			retval = TRUE;
 		}
 	}
 
@@ -7455,17 +7536,19 @@ nautilus_file_get_default_sort_type (NautilusFile *file,
 				     gboolean *reversed)
 {
 	NautilusFileSortType retval;
-	gboolean is_recent, is_download, is_trash, res;
+	gboolean is_recent, is_download, is_trash, is_search, res;
 
 	retval = NAUTILUS_FILE_SORT_NONE;
-	is_recent = is_download = is_trash = FALSE;
-	res = get_attributes_for_default_sort_type (file, &is_recent, &is_download, &is_trash);
+	is_recent = is_download = is_trash = is_search = FALSE;
+	res = get_attributes_for_default_sort_type (file, &is_recent, &is_download, &is_trash, &is_search);
 
 	if (res) {
 		if (is_recent || is_download) {
 			retval = NAUTILUS_FILE_SORT_BY_MTIME;
 		} else if (is_trash) {
 			retval = NAUTILUS_FILE_SORT_BY_TRASHED_TIME;
+		} else if (is_search) {
+			retval = NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE;
 		}
 
 		if (reversed != NULL) {
@@ -7481,17 +7564,19 @@ nautilus_file_get_default_sort_attribute (NautilusFile *file,
 					  gboolean *reversed)
 {
 	const gchar *retval;
-	gboolean is_recent, is_download, is_trash, res;
+	gboolean is_recent, is_download, is_trash, is_search, res;
 
 	retval = NULL;
-	is_download = is_trash = FALSE;
-	res = get_attributes_for_default_sort_type (file, &is_recent, &is_download, &is_trash);
+	is_download = is_trash = is_search = FALSE;
+	res = get_attributes_for_default_sort_type (file, &is_recent, &is_download, &is_trash, &is_search);
 
 	if (res) {
 		if (is_recent || is_download) {
 			retval = g_quark_to_string (attribute_date_modified_q);
 		} else if (is_trash) {
 			retval = g_quark_to_string (attribute_trashed_on_q);
+		} else if (is_search) {
+			retval = g_quark_to_string (attribute_search_relevance_q);
 		}
 
 		if (reversed != NULL) {
@@ -7869,6 +7954,7 @@ nautilus_file_class_init (NautilusFileClass *class)
 	attribute_deep_file_count_q = g_quark_from_static_string ("deep_file_count");
 	attribute_deep_directory_count_q = g_quark_from_static_string ("deep_directory_count");
 	attribute_deep_total_count_q = g_quark_from_static_string ("deep_total_count");
+	attribute_search_relevance_q = g_quark_from_static_string ("search_relevance");
 	attribute_trashed_on_q = g_quark_from_static_string ("trashed_on");
 	attribute_trashed_on_full_q = g_quark_from_static_string ("trashed_on_full");
 	attribute_trash_orig_path_q = g_quark_from_static_string ("trash_orig_path");
diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h
index 7ab7e0c..a1bedf1 100644
--- a/libnautilus-private/nautilus-file.h
+++ b/libnautilus-private/nautilus-file.h
@@ -60,7 +60,8 @@ typedef enum {
 	NAUTILUS_FILE_SORT_BY_TYPE,
 	NAUTILUS_FILE_SORT_BY_MTIME,
         NAUTILUS_FILE_SORT_BY_ATIME,
-	NAUTILUS_FILE_SORT_BY_TRASHED_TIME
+	NAUTILUS_FILE_SORT_BY_TRASHED_TIME,
+	NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE
 } NautilusFileSortType;	
 
 typedef enum {
@@ -187,6 +188,7 @@ gboolean                nautilus_file_is_directory                      (Nautilu
 gboolean                nautilus_file_is_user_special_directory         (NautilusFile                   *file,
 									 GUserDirectory                 special_directory);
 gboolean		nautilus_file_is_archive			(NautilusFile			*file);
+gboolean                nautilus_file_is_in_search                      (NautilusFile                   *file);
 gboolean                nautilus_file_is_in_trash                       (NautilusFile                   *file);
 gboolean                nautilus_file_is_in_recent                      (NautilusFile                   *file);
 gboolean                nautilus_file_is_in_desktop                     (NautilusFile                   *file);
@@ -214,6 +216,8 @@ char *                  nautilus_file_peek_top_left_text                (Nautilu
 gboolean                nautilus_file_get_directory_item_mime_types     (NautilusFile                   *file,
 									 GList                         **mime_list);
 
+void                    nautilus_file_set_search_relevance              (NautilusFile                   *file,
+									 gdouble                         relevance);
 void                    nautilus_file_set_attributes                    (NautilusFile                   *file, 
 									 GFileInfo                      *attributes,
 									 NautilusFileOperationCallback   callback,
diff --git a/libnautilus-private/nautilus-search-directory.c b/libnautilus-private/nautilus-search-directory.c
index bb82337..a5733b4 100644
--- a/libnautilus-private/nautilus-search-directory.c
+++ b/libnautilus-private/nautilus-search-directory.c
@@ -468,22 +468,26 @@ search_engine_hits_added (NautilusSearchEngine *engine, GList *hits,
 	GList *hit_list;
 	GList *file_list;
 	NautilusFile *file;
-	char *uri;
 	SearchMonitor *monitor;
 	GList *monitor_list;
 
 	file_list = NULL;
 
 	for (hit_list = hits; hit_list != NULL; hit_list = hit_list->next) {
-		uri = hit_list->data;
+		NautilusSearchHit *hit = hit_list->data;
+		const char *uri;
 
+		uri = nautilus_search_hit_get_uri (hit);
 		if (g_str_has_suffix (uri, NAUTILUS_SAVED_SEARCH_EXTENSION)) {
 			/* Never return saved searches themselves as hits */
 			continue;
 		}
-		
+
+		nautilus_search_hit_compute_scores (hit, search->details->query);
+
 		file = nautilus_file_get_by_uri (uri);
-		
+		nautilus_file_set_search_relevance (file, nautilus_search_hit_get_relevance (hit));
+
 		for (monitor_list = search->details->monitor_list; monitor_list; monitor_list = monitor_list->next) {
 			monitor = monitor_list->data;
 
@@ -513,13 +517,15 @@ search_engine_hits_subtracted (NautilusSearchEngine *engine, GList *hits,
 	GList *monitor_list;
 	SearchMonitor *monitor;
 	GList *file_list;
-	char *uri;
 	NautilusFile *file;
 
 	file_list = NULL;
 
 	for (hit_list = hits; hit_list != NULL; hit_list = hit_list->next) {
-		uri = hit_list->data;
+		NautilusSearchHit *hit = hit_list->data;
+		const char *uri;
+
+		uri = nautilus_search_hit_get_uri (hit);
 		file = nautilus_file_get_by_uri (uri);
 
 		for (monitor_list = search->details->monitor_list; monitor_list; 
diff --git a/libnautilus-private/nautilus-search-engine-simple.c b/libnautilus-private/nautilus-search-engine-simple.c
index 6f875ee..09684c3 100644
--- a/libnautilus-private/nautilus-search-engine-simple.c
+++ b/libnautilus-private/nautilus-search-engine-simple.c
@@ -22,6 +22,7 @@
  */
 
 #include <config.h>
+#include "nautilus-search-hit.h"
 #include "nautilus-search-provider.h"
 #include "nautilus-search-engine-simple.h"
 
@@ -50,7 +51,7 @@ typedef struct {
 
 	gboolean recursive;
 	gint n_processed_files;
-	GList *uri_hits;
+	GList *hits;
 } SearchThreadData;
 
 
@@ -137,7 +138,7 @@ search_thread_data_free (SearchThreadData *data)
 	g_object_unref (data->cancellable);
 	g_strfreev (data->words);	
 	g_list_free_full (data->mime_types, g_free);
-	g_list_free_full (data->uri_hits, g_free);
+	g_list_free_full (data->hits, g_object_unref);
 	g_free (data);
 }
 
@@ -159,43 +160,41 @@ search_thread_done_idle (gpointer user_data)
 }
 
 typedef struct {
-	GList *uris;
+	GList *hits;
 	SearchThreadData *thread_data;
-} SearchHits;
+} SearchHitsData;
 
 
 static gboolean
 search_thread_add_hits_idle (gpointer user_data)
 {
-	SearchHits *hits;
+	SearchHitsData *data = user_data;
 
-	hits = user_data;
-
-	if (!g_cancellable_is_cancelled (hits->thread_data->cancellable)) {
-		nautilus_search_provider_hits_added (NAUTILUS_SEARCH_PROVIDER (hits->thread_data->engine),
-						     hits->uris);
+	if (!g_cancellable_is_cancelled (data->thread_data->cancellable)) {
+		nautilus_search_provider_hits_added (NAUTILUS_SEARCH_PROVIDER (data->thread_data->engine),
+						     data->hits);
 	}
 
-	g_list_free_full (hits->uris, g_free);
-	g_free (hits);
+	g_list_free_full (data->hits, g_object_unref);
+	g_free (data);
 	
 	return FALSE;
 }
 
 static void
-send_batch (SearchThreadData *data)
+send_batch (SearchThreadData *thread_data)
 {
-	SearchHits *hits;
+	SearchHitsData *data;
 	
-	data->n_processed_files = 0;
+	thread_data->n_processed_files = 0;
 	
-	if (data->uri_hits) {
-		hits = g_new (SearchHits, 1);
-		hits->uris = data->uri_hits;
-		hits->thread_data = data;
-		g_idle_add (search_thread_add_hits_idle, hits);
+	if (thread_data->hits) {
+		data = g_new (SearchHitsData, 1);
+		data->hits = thread_data->hits;
+		data->thread_data = thread_data;
+		g_idle_add (search_thread_add_hits_idle, data);
 	}
-	data->uri_hits = NULL;
+	thread_data->hits = NULL;
 }
 
 #define STD_ATTRIBUTES \
@@ -203,6 +202,7 @@ send_batch (SearchThreadData *data)
 	G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," \
 	G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," \
 	G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
+	G_FILE_ATTRIBUTE_TIME_MODIFIED "," \
 	G_FILE_ATTRIBUTE_ID_FILE
 
 static void
@@ -213,7 +213,7 @@ visit_directory (GFile *dir, SearchThreadData *data)
 	GFile *child;
 	const char *mime_type, *display_name;
 	char *lower_name, *normalized;
-	gboolean hit;
+	gboolean found;
 	int i;
 	GList *l;
 	const char *id;
@@ -246,22 +246,22 @@ visit_directory (GFile *dir, SearchThreadData *data)
 		lower_name = g_utf8_strdown (normalized, -1);
 		g_free (normalized);
 		
-		hit = TRUE;
+		found = TRUE;
 		for (i = 0; data->words[i] != NULL; i++) {
 			if (strstr (lower_name, data->words[i]) == NULL) {
-				hit = FALSE;
+				found = FALSE;
 				break;
 			}
 		}
 		g_free (lower_name);
 		
-		if (hit && data->mime_types) {
+		if (found && data->mime_types) {
 			mime_type = g_file_info_get_content_type (info);
-			hit = FALSE;
+			found = FALSE;
 			
 			for (l = data->mime_types; mime_type != NULL && l != NULL; l = l->next) {
 				if (g_content_type_equals (mime_type, l->data)) {
-					hit = TRUE;
+					found = TRUE;
 					break;
 				}
 			}
@@ -269,8 +269,22 @@ visit_directory (GFile *dir, SearchThreadData *data)
 		
 		child = g_file_get_child (dir, g_file_info_get_name (info));
 		
-		if (hit) {
-			data->uri_hits = g_list_prepend (data->uri_hits, g_file_get_uri (child));
+		if (found) {
+			NautilusSearchHit *hit;
+			GTimeVal tv;
+			GDateTime *dt;
+			char *uri;
+
+			uri = g_file_get_uri (child);
+			hit = nautilus_search_hit_new (uri);
+			g_free (uri);
+			nautilus_search_hit_set_fts_rank (hit, 10.0);
+			g_file_info_get_modification_time (info, &tv);
+			dt = g_date_time_new_from_timeval_local (&tv);
+			nautilus_search_hit_set_modification_time (hit, dt);
+			g_date_time_unref (dt);
+
+			data->hits = g_list_prepend (data->hits, hit);
 		}
 		
 		data->n_processed_files++;
diff --git a/libnautilus-private/nautilus-search-engine-tracker.c b/libnautilus-private/nautilus-search-engine-tracker.c
index fc9c5ec..9591985 100644
--- a/libnautilus-private/nautilus-search-engine-tracker.c
+++ b/libnautilus-private/nautilus-search-engine-tracker.c
@@ -22,6 +22,7 @@
  */
 
 #include <config.h>
+#include "nautilus-search-hit.h"
 #include "nautilus-search-provider.h"
 #include "nautilus-search-engine-tracker.h"
 #include <string.h>
@@ -85,6 +86,12 @@ cursor_callback (GObject      *object,
 	NautilusSearchEngineTracker *tracker;
 	GError *error = NULL;
 	TrackerSparqlCursor *cursor;
+	NautilusSearchHit *hit;
+	const char *uri;
+	const char *mtime_str;
+	const char *atime_str;
+	GTimeVal tv;
+	gdouble rank;
 	GList *hits;
 	gboolean success;
 
@@ -108,9 +115,34 @@ cursor_callback (GObject      *object,
 	}
 
 	/* We iterate result by result, not n at a time. */
-	hits = g_list_append (NULL, (gchar*) tracker_sparql_cursor_get_string (cursor, 0, NULL));
+	uri = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+	rank = tracker_sparql_cursor_get_double (cursor, 1);
+	mtime_str = tracker_sparql_cursor_get_string (cursor, 2, NULL);
+	atime_str = tracker_sparql_cursor_get_string (cursor, 3, NULL);
+
+	hit = nautilus_search_hit_new (uri);
+	nautilus_search_hit_set_fts_rank (hit, rank);
+	if (g_time_val_from_iso8601 (mtime_str, &tv)) {
+		GDateTime *dt;
+		dt = g_date_time_new_from_timeval_local (&tv);
+		nautilus_search_hit_set_modification_time (hit, dt);
+		g_date_time_unref (dt);
+	} else {
+		g_warning ("unable to parse mtime: %s", mtime_str);
+	}
+	if (g_time_val_from_iso8601 (atime_str, &tv)) {
+		GDateTime *dt;
+		dt = g_date_time_new_from_timeval_local (&tv);
+		nautilus_search_hit_set_access_time (hit, dt);
+		g_date_time_unref (dt);
+	} else {
+		g_warning ("unable to parse atime: %s", atime_str);
+	}
+
+	hits = g_list_append (NULL, hit);
 	nautilus_search_provider_hits_added (NAUTILUS_SEARCH_PROVIDER (tracker), hits);
 	g_list_free (hits);
+	g_object_unref (hit);
 
 	/* Get next */
 	cursor_next (tracker, cursor);
@@ -178,7 +210,7 @@ nautilus_search_engine_tracker_start (NautilusSearchProvider *provider)
 
 	mime_count = g_list_length (mimetypes);
 
-	sparql = g_string_new ("SELECT DISTINCT nie:url(?urn) "
+	sparql = g_string_new ("SELECT DISTINCT nie:url(?urn) fts:rank(?urn) tracker:coalesce(nfo:fileLastModified(?urn), nie:contentLastModified(?urn)) AS ?mtime tracker:coalesce(nfo:fileLastAccessed(?urn), nie:contentAccessed(?urn)) AS ?atime "
 			       "WHERE {"
 			       "  ?urn a nfo:FileDataObject ;"
 			       "  tracker:available true ; ");
@@ -212,7 +244,7 @@ nautilus_search_engine_tracker_start (NautilusSearchProvider *provider)
 		g_string_append (sparql, ")");
 	}
 
-	g_string_append (sparql, ")}");
+	g_string_append (sparql, ")} ORDER BY DESC (fts:rank(?urn))");
 
 	tracker->details->cancellable = g_cancellable_new ();
 	tracker->details->query_pending = TRUE;
diff --git a/libnautilus-private/nautilus-search-engine.c b/libnautilus-private/nautilus-search-engine.c
index 946a863..d5d90d2 100644
--- a/libnautilus-private/nautilus-search-engine.c
+++ b/libnautilus-private/nautilus-search-engine.c
@@ -96,11 +96,14 @@ search_provider_hits_added (NautilusSearchProvider *provider,
 	GList *l;
 
 	for (l = hits; l != NULL; l = l->next) {
-		char *uri = l->data;
+		NautilusSearchHit *hit = l->data;
 		int count;
+		const char *uri;
+
+		uri = nautilus_search_hit_get_uri (hit);
 		count = GPOINTER_TO_INT (g_hash_table_lookup (engine->details->uris, uri));
 		if (count == 0)
-			added = g_list_prepend (added, uri);
+			added = g_list_prepend (added, hit);
 		g_hash_table_replace (engine->details->uris, g_strdup (uri), GINT_TO_POINTER (count++));
 	}
 	if (added != NULL) {
@@ -118,12 +121,15 @@ search_provider_hits_subtracted (NautilusSearchProvider *provider,
 	GList *l;
 
 	for (l = hits; l != NULL; l = l->next) {
-		char *uri = l->data;
+		NautilusSearchHit *hit = l->data;
 		int count;
+		const char *uri;
+
+		uri = nautilus_search_hit_get_uri (hit);
 		count = GPOINTER_TO_INT (g_hash_table_lookup (engine->details->uris, uri));
 		g_assert (count > 0);
 		if (count == 1) {
-			removed = g_list_prepend (removed, uri);
+			removed = g_list_prepend (removed, hit);
 			g_hash_table_remove (engine->details->uris, uri);
 		} else {
 			g_hash_table_replace (engine->details->uris, g_strdup (uri), GINT_TO_POINTER (count--));
diff --git a/libnautilus-private/nautilus-search-hit.c b/libnautilus-private/nautilus-search-hit.c
new file mode 100644
index 0000000..7e7d8f1
--- /dev/null
+++ b/libnautilus-private/nautilus-search-hit.c
@@ -0,0 +1,327 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Nautilus is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Nautilus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; see the file COPYING.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "nautilus-search-hit.h"
+#include "nautilus-query.h"
+
+struct NautilusSearchHitDetails
+{
+	char      *uri;
+
+	GDateTime *modification_time;
+	GDateTime *access_time;
+	gdouble    fts_rank;
+
+	gdouble    relevance;
+};
+
+enum {
+	PROP_URI = 1,
+	PROP_RELEVANCE,
+	PROP_MODIFICATION_TIME,
+	PROP_ACCESS_TIME,
+	PROP_FTS_RANK,
+	NUM_PROPERTIES
+};
+
+G_DEFINE_TYPE (NautilusSearchHit, nautilus_search_hit, G_TYPE_OBJECT)
+
+void
+nautilus_search_hit_compute_scores (NautilusSearchHit *hit,
+				    NautilusQuery     *query)
+{
+	GDateTime *now;
+	char *query_uri;
+	char *query_path;
+	int i;
+	GTimeSpan m_diff = G_MAXINT64;
+	GTimeSpan a_diff = G_MAXINT64;
+	GTimeSpan t_diff = G_MAXINT64;
+	gdouble recent_bonus = 0.0;
+	gdouble proximity_bonus = 0.0;
+	gdouble match_bonus = 0.0;
+
+	query_uri = nautilus_query_get_location (query);
+	query_path = g_filename_from_uri (query_uri, NULL, NULL);
+	g_free (query_uri);
+	if (query_path != NULL) {
+		char *hit_path;
+		char *hit_parent;
+		guint dir_count;
+
+		hit_path = g_filename_from_uri (hit->details->uri, NULL, NULL);
+		hit_parent = g_path_get_dirname (hit_path);
+		g_free (hit_path);
+
+		dir_count = 0;
+		for (i = strlen (query_path); hit_parent[i] != '\0'; i++) {
+			if (G_IS_DIR_SEPARATOR (hit_parent[i]))
+				dir_count++;
+		}
+		g_free (hit_parent);
+
+		if (dir_count < 10) {
+			proximity_bonus = 100.0 - 10 * dir_count;
+		} else {
+			proximity_bonus = 0.0;
+		}
+	}
+	g_free (query_path);
+
+	now = g_date_time_new_now_local ();
+	if (hit->details->modification_time != NULL)
+		m_diff = g_date_time_difference (now, hit->details->modification_time);
+	if (hit->details->access_time != NULL)
+		a_diff = g_date_time_difference (now, hit->details->access_time);
+	m_diff /= G_TIME_SPAN_DAY;
+	a_diff /= G_TIME_SPAN_DAY;
+	t_diff = MIN (m_diff, a_diff);
+	if (t_diff > 90) {
+		recent_bonus = 0.0;
+	} else if (t_diff > 30) {
+		recent_bonus = 10.0;
+	} else if (t_diff > 14) {
+		recent_bonus = 30.0;
+	} else if (t_diff > 7) {
+		recent_bonus = 50.0;
+	} else if (t_diff > 1) {
+		recent_bonus = 70.0;
+	} else {
+		recent_bonus = 100.0;
+	}
+
+	if (hit->details->fts_rank > 0) {
+		match_bonus = 10.0 * hit->details->fts_rank;
+	} else {
+		match_bonus = 0.0;
+	}
+
+	hit->details->relevance = recent_bonus + proximity_bonus + match_bonus;
+
+	g_date_time_unref (now);
+}
+
+const char *
+nautilus_search_hit_get_uri (NautilusSearchHit *hit)
+{
+	return hit->details->uri;
+}
+
+gdouble
+nautilus_search_hit_get_relevance (NautilusSearchHit *hit)
+{
+	return hit->details->relevance;
+}
+
+static void
+nautilus_search_hit_set_uri (NautilusSearchHit *hit,
+			     const char        *uri)
+{
+	g_free (hit->details->uri);
+	hit->details->uri = g_strdup (uri);
+}
+
+void
+nautilus_search_hit_set_fts_rank (NautilusSearchHit *hit,
+				  gdouble            rank)
+{
+	hit->details->fts_rank = rank;
+}
+
+void
+nautilus_search_hit_set_modification_time (NautilusSearchHit *hit,
+					   GDateTime         *date)
+{
+	if (hit->details->modification_time != NULL)
+		g_date_time_unref (hit->details->modification_time);
+	if (date != NULL)
+		hit->details->modification_time = g_date_time_ref (date);
+	else
+		hit->details->modification_time = NULL;
+}
+
+void
+nautilus_search_hit_set_access_time (NautilusSearchHit *hit,
+				     GDateTime         *date)
+{
+	if (hit->details->access_time != NULL)
+		g_date_time_unref (hit->details->access_time);
+	if (date != NULL)
+		hit->details->access_time = g_date_time_ref (date);
+	else
+		hit->details->access_time = NULL;
+}
+
+static void
+nautilus_search_hit_set_property (GObject *object,
+				  guint arg_id,
+				  const GValue *value,
+				  GParamSpec *pspec)
+{
+	NautilusSearchHit *hit;
+
+	hit = NAUTILUS_SEARCH_HIT (object);
+
+	switch (arg_id) {
+	case PROP_RELEVANCE:
+		hit->details->relevance = g_value_get_double (value);
+	case PROP_FTS_RANK:
+		nautilus_search_hit_set_fts_rank (hit, g_value_get_double (value));
+		break;
+	case PROP_URI:
+		nautilus_search_hit_set_uri (hit, g_value_get_string (value));
+		break;
+	case PROP_MODIFICATION_TIME:
+		nautilus_search_hit_set_modification_time (hit, g_value_get_boxed (value));
+		break;
+	case PROP_ACCESS_TIME:
+		nautilus_search_hit_set_access_time (hit, g_value_get_boxed (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, pspec);
+		break;
+	}
+}
+
+static void
+nautilus_search_hit_get_property (GObject *object,
+				  guint arg_id,
+				  GValue *value,
+				  GParamSpec *pspec)
+{
+	NautilusSearchHit *hit;
+
+	hit = NAUTILUS_SEARCH_HIT (object);
+
+	switch (arg_id) {
+	case PROP_RELEVANCE:
+		g_value_set_double (value, hit->details->relevance);
+		break;
+	case PROP_FTS_RANK:
+		g_value_set_double (value, hit->details->fts_rank);
+		break;
+	case PROP_URI:
+		g_value_set_string (value, hit->details->uri);
+		break;
+	case PROP_MODIFICATION_TIME:
+		g_value_set_boxed (value, hit->details->modification_time);
+		break;
+	case PROP_ACCESS_TIME:
+		g_value_set_boxed (value, hit->details->access_time);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, pspec);
+		break;
+	}
+}
+
+static void
+nautilus_search_hit_finalize (GObject *object)
+{
+	NautilusSearchHit *hit = NAUTILUS_SEARCH_HIT (object);
+
+	g_free (hit->details->uri);
+
+	if (hit->details->access_time != NULL) {
+		g_date_time_unref (hit->details->access_time);
+	}
+	if (hit->details->modification_time != NULL) {
+		g_date_time_unref (hit->details->modification_time);
+	}
+
+	G_OBJECT_CLASS (nautilus_search_hit_parent_class)->finalize (object);
+}
+
+static void
+nautilus_search_hit_class_init (NautilusSearchHitClass *class)
+{
+	GObjectClass *object_class;
+
+	object_class = (GObjectClass *) class;
+
+	object_class->finalize = nautilus_search_hit_finalize;
+	object_class->get_property = nautilus_search_hit_get_property;
+	object_class->set_property = nautilus_search_hit_set_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_URI,
+					 g_param_spec_string ("uri",
+							      "URI",
+							      "URI",
+							      NULL,
+							      G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE));
+	g_object_class_install_property (object_class,
+					 PROP_MODIFICATION_TIME,
+					 g_param_spec_boxed ("modification-time",
+							     "Modification time",
+							     "Modification time",
+							     G_TYPE_DATE_TIME,
+							     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_ACCESS_TIME,
+					 g_param_spec_boxed ("access-time",
+							     "acess time",
+							     "access time",
+							     G_TYPE_DATE_TIME,
+							     G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+	g_object_class_install_property (object_class,
+					 PROP_RELEVANCE,
+					 g_param_spec_double ("relevance",
+							      NULL,
+							      NULL,
+							      -G_MAXDOUBLE, G_MAXDOUBLE,
+							      0,
+							      G_PARAM_READWRITE));
+	g_object_class_install_property (object_class,
+					 PROP_FTS_RANK,
+					 g_param_spec_double ("fts-rank",
+							      NULL,
+							      NULL,
+							      -G_MAXDOUBLE, G_MAXDOUBLE,
+							      0,
+							      G_PARAM_READWRITE));
+
+	g_type_class_add_private (class, sizeof (NautilusSearchHitDetails));
+}
+
+static void
+nautilus_search_hit_init (NautilusSearchHit *hit)
+{
+	hit->details = G_TYPE_INSTANCE_GET_PRIVATE (hit,
+						    NAUTILUS_TYPE_SEARCH_HIT,
+						    NautilusSearchHitDetails);
+}
+
+NautilusSearchHit *
+nautilus_search_hit_new (const char *uri)
+{
+	NautilusSearchHit *hit;
+
+	hit = g_object_new (NAUTILUS_TYPE_SEARCH_HIT,
+			    "uri", uri,
+			    NULL);
+
+	return hit;
+}
diff --git a/libnautilus-private/nautilus-search-hit.h b/libnautilus-private/nautilus-search-hit.h
new file mode 100644
index 0000000..005990a
--- /dev/null
+++ b/libnautilus-private/nautilus-search-hit.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Nautilus is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Nautilus is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; see the file COPYING.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef NAUTILUS_SEARCH_HIT_H
+#define NAUTILUS_SEARCH_HIT_H
+
+#include <glib-object.h>
+#include "nautilus-query.h"
+
+#define NAUTILUS_TYPE_SEARCH_HIT		(nautilus_search_hit_get_type ())
+#define NAUTILUS_SEARCH_HIT(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_SEARCH_HIT, NautilusSearchHit))
+#define NAUTILUS_SEARCH_HIT_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_SEARCH_HIT, NautilusSearchHitClass))
+#define NAUTILUS_IS_SEARCH_HIT(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_SEARCH_HIT))
+#define NAUTILUS_IS_SEARCH_HIT_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_SEARCH_HIT))
+#define NAUTILUS_SEARCH_HIT_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_SEARCH_HIT, NautilusSearchHitClass))
+
+typedef struct NautilusSearchHitDetails NautilusSearchHitDetails;
+
+typedef struct NautilusSearchHit {
+	GObject parent;
+	NautilusSearchHitDetails *details;
+} NautilusSearchHit;
+
+typedef struct {
+	GObjectClass parent_class;
+} NautilusSearchHitClass;
+
+GType               nautilus_search_hit_get_type      (void);
+
+NautilusSearchHit * nautilus_search_hit_new                   (const char        *uri);
+
+void                nautilus_search_hit_set_fts_rank          (NautilusSearchHit *hit,
+							       gdouble            fts_rank);
+void                nautilus_search_hit_set_modification_time (NautilusSearchHit *hit,
+							       GDateTime         *date);
+void                nautilus_search_hit_set_access_time       (NautilusSearchHit *hit,
+							       GDateTime         *date);
+
+void                nautilus_search_hit_compute_scores        (NautilusSearchHit *hit,
+							       NautilusQuery     *query);
+
+const char *        nautilus_search_hit_get_uri               (NautilusSearchHit *hit);
+gdouble             nautilus_search_hit_get_relevance         (NautilusSearchHit *hit);
+
+#endif /* NAUTILUS_SEARCH_HIT_H */
diff --git a/libnautilus-private/nautilus-search-provider.h b/libnautilus-private/nautilus-search-provider.h
index 07f2bff..abd37a9 100644
--- a/libnautilus-private/nautilus-search-provider.h
+++ b/libnautilus-private/nautilus-search-provider.h
@@ -21,6 +21,7 @@
 
 #include <glib-object.h>
 #include <libnautilus-private/nautilus-query.h>
+#include <libnautilus-private/nautilus-search-hit.h>
 
 G_BEGIN_DECLS
 



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