Add support for folder-based searching.

A new "folder:" prefix in the query string can now be used to match
the directories in which mail files are stored.

The addition of this feature causes the recently added
search-by-folder tests to now pass.
This commit is contained in:
Carl Worth 2011-01-15 14:09:04 -08:00
parent 600f3761dc
commit 99cfa27030
3 changed files with 113 additions and 22 deletions

View file

@ -89,8 +89,9 @@ typedef struct {
*
* In addition, terms from the content of the message are added with
* "from", "to", "attachment", and "subject" prefixes for use by the
* user in searching. But the database doesn't really care itself
* about any of these.
* user in searching. Similarly, terms from the path of the mail
* message are added with a "folder" prefix. But the database doesn't
* really care itself about any of these.
*
* The data portion of a mail document is empty.
*
@ -204,7 +205,8 @@ static prefix_t PROBABILISTIC_PREFIX[]= {
{ "from", "XFROM" },
{ "to", "XTO" },
{ "attachment", "XATTACHMENT" },
{ "subject", "XSUBJECT"}
{ "subject", "XSUBJECT"},
{ "folder", "XFOLDER"}
};
int
@ -1716,11 +1718,20 @@ notmuch_database_remove_message (notmuch_database_t *notmuch,
for ( ; i != end; i++) {
Xapian::TermIterator j;
notmuch_message_t *message;
notmuch_private_status_t private_status;
message = _notmuch_message_create (local, notmuch,
*i, &private_status);
if (message == NULL)
return COERCE_STATUS (private_status,
"Inconsistent document ID in datbase.");
_notmuch_message_remove_filename (message, filename);
_notmuch_message_sync (message);
/* Take care to find document after sync'ing filename removal. */
document = find_document_for_doc_id (notmuch, *i);
document.remove_term (term);
j = document.termlist_begin ();
j.skip_to (prefix);
@ -1731,7 +1742,6 @@ notmuch_database_remove_message (notmuch_database_t *notmuch,
db->delete_document (document.get_docid ());
status = NOTMUCH_STATUS_SUCCESS;
} else {
db->replace_document (document.get_docid (), document);
status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
}
}

View file

@ -434,17 +434,24 @@ notmuch_status_t
_notmuch_message_add_filename (notmuch_message_t *message,
const char *filename)
{
const char *relative, *directory;
notmuch_status_t status;
void *local = talloc_new (message);
char *direntry;
if (filename == NULL)
INTERNAL_ERROR ("Message filename cannot be NULL.");
if (message->filename_list) {
_notmuch_filename_list_destroy (message->filename_list);
message->filename_list = NULL;
}
if (filename == NULL)
INTERNAL_ERROR ("Message filename cannot be NULL.");
relative = _notmuch_database_relative_path (message->notmuch, filename);
status = _notmuch_database_split_path (local, relative, &directory, NULL);
if (status)
return status;
status = _notmuch_database_filename_to_direntry (local,
message->notmuch,
@ -452,43 +459,105 @@ _notmuch_message_add_filename (notmuch_message_t *message,
if (status)
return status;
/* New file-direntry allows navigating to this message with
* notmuch_directory_get_child_files() . */
_notmuch_message_add_term (message, "file-direntry", direntry);
/* New terms allow user to search with folder: specification. */
_notmuch_message_gen_terms (message, "folder", directory);
talloc_free (local);
return NOTMUCH_STATUS_SUCCESS;
}
/* Change a particular filename for 'message' from 'old_filename' to
* 'new_filename'
/* Remove a particular 'filename' from 'message'.
*
* This change will not be reflected in the database until the next
* call to _notmuch_message_sync.
*/
static notmuch_status_t
_notmuch_message_rename (notmuch_message_t *message,
const char *old_filename,
const char *new_filename)
*
* Note: This function does not remove a document from the database,
* even if the specified filename is the only filename for this
* message. For that functionality, see
* _notmuch_database_remove_message. */
notmuch_status_t
_notmuch_message_remove_filename (notmuch_message_t *message,
const char *filename)
{
const char *direntry_prefix = _find_prefix ("file-direntry");
int direntry_prefix_len = strlen (direntry_prefix);
const char *folder_prefix = _find_prefix ("folder");
int folder_prefix_len = strlen (folder_prefix);
void *local = talloc_new (message);
char *direntry;
notmuch_private_status_t private_status;
notmuch_status_t status;
Xapian::TermIterator i, last;
status = _notmuch_message_add_filename (message, new_filename);
if (status)
return status;
if (message->filename_list) {
_notmuch_filename_list_destroy (message->filename_list);
message->filename_list = NULL;
}
status = _notmuch_database_filename_to_direntry (local, message->notmuch,
old_filename, &direntry);
filename, &direntry);
if (status)
return status;
/* Unlink this file from its parent directory. */
private_status = _notmuch_message_remove_term (message,
"file-direntry", direntry);
status = COERCE_STATUS (private_status,
"Unexpected error from _notmuch_message_remove_term");
/* Re-synchronize "folder:" terms for this message. This requires
* first removing all "folder:" terms, then adding back terms for
* all remaining filenames of the message. */
while (1) {
i = message->doc.termlist_begin ();
i.skip_to (folder_prefix);
/* Terminate loop when no terms remain with desired prefix. */
if (i == message->doc.termlist_end () ||
strncmp ((*i).c_str (), folder_prefix, folder_prefix_len))
{
break;
}
try {
message->doc.remove_term ((*i));
} catch (const Xapian::InvalidArgumentError) {
/* Ignore failure to remove non-existent term. */
}
}
i = message->doc.termlist_begin ();
i.skip_to (direntry_prefix);
for (; i != message->doc.termlist_end (); i++) {
unsigned int directory_id;
const char *direntry, *directory;
char *colon;
/* Terminate loop at first term without desired prefix. */
if (strncmp ((*i).c_str (), direntry_prefix, direntry_prefix_len))
break;
direntry = (*i).c_str ();
direntry += direntry_prefix_len;
directory_id = strtol (direntry, &colon, 10);
if (colon == NULL || *colon != ':')
INTERNAL_ERROR ("malformed direntry");
directory = _notmuch_database_get_directory_path (local,
message->notmuch,
directory_id);
if (strlen (directory))
_notmuch_message_gen_terms (message, "folder", directory);
}
talloc_free (local);
return status;
@ -1154,8 +1223,16 @@ notmuch_message_tags_to_maildir_flags (notmuch_message_t *message)
if (err)
continue;
new_status = _notmuch_message_rename (message,
filename, filename_new);
new_status = _notmuch_message_remove_filename (message,
filename);
/* Hold on to only the first error. */
if (! status && new_status) {
status = new_status;
continue;
}
new_status = _notmuch_message_add_filename (message,
filename_new);
/* Hold on to only the first error. */
if (! status && new_status) {
status = new_status;

View file

@ -269,6 +269,10 @@ notmuch_status_t
_notmuch_message_add_filename (notmuch_message_t *message,
const char *filename);
notmuch_status_t
_notmuch_message_remove_filename (notmuch_message_t *message,
const char *filename);
notmuch_status_t
_notmuch_message_rename (notmuch_message_t *message,
const char *new_filename);