1 /* query.cc - Support for searching a notmuch database
3 * Copyright © 2009 Carl Worth
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see http://www.gnu.org/licenses/ .
18 * Author: Carl Worth <cworth@cworth.org>
21 #include "notmuch-private.h"
22 #include "database-private.h"
24 #include <glib.h> /* GHashTable, GPtrArray */
28 struct _notmuch_query {
29 notmuch_database_t *notmuch;
30 const char *query_string;
34 struct _notmuch_messages {
35 notmuch_database_t *notmuch;
36 Xapian::MSetIterator iterator;
37 Xapian::MSetIterator iterator_end;
40 struct _notmuch_threads {
41 notmuch_query_t *query;
43 notmuch_messages_t *messages;
45 /* This thread ID is our iterator state. */
46 const char *thread_id;
50 notmuch_query_create (notmuch_database_t *notmuch,
51 const char *query_string)
53 notmuch_query_t *query;
56 fprintf (stderr, "Query string is:\n%s\n", query_string);
59 query = talloc (NULL, notmuch_query_t);
60 if (unlikely (query == NULL))
63 query->notmuch = notmuch;
65 query->query_string = talloc_strdup (query, query_string);
67 query->sort = NOTMUCH_SORT_NEWEST_FIRST;
73 notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort)
79 notmuch_query_search_messages (notmuch_query_t *query)
81 notmuch_database_t *notmuch = query->notmuch;
82 const char *query_string = query->query_string;
83 notmuch_message_list_t *message_list;
84 Xapian::MSetIterator i;
86 message_list = _notmuch_message_list_create (query);
87 if (unlikely (message_list == NULL))
91 Xapian::Enquire enquire (*notmuch->xapian_db);
92 Xapian::Query mail_query (talloc_asprintf (query, "%s%s",
93 _find_prefix ("type"),
95 Xapian::Query string_query, final_query;
97 unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN |
98 Xapian::QueryParser::FLAG_PHRASE |
99 Xapian::QueryParser::FLAG_LOVEHATE |
100 Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE |
101 Xapian::QueryParser::FLAG_WILDCARD |
102 Xapian::QueryParser::FLAG_PURE_NOT);
104 if (strcmp (query_string, "") == 0) {
105 final_query = mail_query;
107 string_query = notmuch->query_parser->
108 parse_query (query_string, flags);
109 final_query = Xapian::Query (Xapian::Query::OP_AND,
110 mail_query, string_query);
113 switch (query->sort) {
114 case NOTMUCH_SORT_OLDEST_FIRST:
115 enquire.set_sort_by_value (NOTMUCH_VALUE_TIMESTAMP, FALSE);
117 case NOTMUCH_SORT_NEWEST_FIRST:
118 enquire.set_sort_by_value (NOTMUCH_VALUE_TIMESTAMP, TRUE);
120 case NOTMUCH_SORT_MESSAGE_ID:
121 enquire.set_sort_by_value (NOTMUCH_VALUE_MESSAGE_ID, FALSE);
126 fprintf (stderr, "Final query is:\n%s\n", final_query.get_description().c_str());
129 enquire.set_query (final_query);
131 mset = enquire.get_mset (0, notmuch->xapian_db->get_doccount ());
133 for (i = mset.begin (); i != mset.end (); i++) {
134 notmuch_message_t *message;
135 notmuch_private_status_t status;
137 message = _notmuch_message_create (message_list, notmuch,
141 if (status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND)
142 INTERNAL_ERROR ("A message iterator contains a "
143 "non-existent document ID.\n");
147 _notmuch_message_list_add_message (message_list, message);
150 } catch (const Xapian::Error &error) {
151 fprintf (stderr, "A Xapian exception occurred performing query: %s\n",
152 error.get_msg().c_str());
153 fprintf (stderr, "Query string was: %s\n", query->query_string);
154 notmuch->exception_reported = TRUE;
157 return _notmuch_messages_create (message_list);
160 /* Glib objects force use to use a talloc destructor as well, (but not
161 * nearly as ugly as the for messages due to C++ objects). At
162 * this point, I'd really like to have some talloc-friendly
163 * equivalents for the few pieces of glib that I'm using. */
165 _notmuch_threads_destructor (notmuch_threads_t *threads)
167 g_hash_table_unref (threads->threads);
173 notmuch_query_search_threads (notmuch_query_t *query)
175 notmuch_threads_t *threads;
177 threads = talloc (query, notmuch_threads_t);
181 threads->query = query;
182 threads->threads = g_hash_table_new_full (g_str_hash, g_str_equal,
185 threads->messages = notmuch_query_search_messages (query);
187 threads->thread_id = NULL;
189 talloc_set_destructor (threads, _notmuch_threads_destructor);
195 notmuch_query_destroy (notmuch_query_t *query)
201 notmuch_threads_has_more (notmuch_threads_t *threads)
203 notmuch_message_t *message;
205 if (threads->thread_id)
208 while (notmuch_messages_has_more (threads->messages))
210 message = notmuch_messages_get (threads->messages);
212 threads->thread_id = notmuch_message_get_thread_id (message);
214 if (! g_hash_table_lookup_extended (threads->threads,
218 g_hash_table_insert (threads->threads,
219 xstrdup (threads->thread_id), NULL);
220 notmuch_messages_advance (threads->messages);
224 notmuch_messages_advance (threads->messages);
227 threads->thread_id = NULL;
232 notmuch_threads_get (notmuch_threads_t *threads)
234 if (! notmuch_threads_has_more (threads))
237 return _notmuch_thread_create (threads->query,
238 threads->query->notmuch,
240 threads->query->query_string);
244 notmuch_threads_advance (notmuch_threads_t *threads)
246 threads->thread_id = NULL;
250 notmuch_threads_destroy (notmuch_threads_t *threads)
252 talloc_free (threads);
256 notmuch_query_count_messages (notmuch_query_t *query)
258 notmuch_database_t *notmuch = query->notmuch;
259 const char *query_string = query->query_string;
260 Xapian::doccount count;
263 Xapian::Enquire enquire (*notmuch->xapian_db);
264 Xapian::Query mail_query (talloc_asprintf (query, "%s%s",
265 _find_prefix ("type"),
267 Xapian::Query string_query, final_query;
269 unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN |
270 Xapian::QueryParser::FLAG_PHRASE |
271 Xapian::QueryParser::FLAG_LOVEHATE |
272 Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE |
273 Xapian::QueryParser::FLAG_WILDCARD |
274 Xapian::QueryParser::FLAG_PURE_NOT);
276 if (strcmp (query_string, "") == 0) {
277 final_query = mail_query;
279 string_query = notmuch->query_parser->
280 parse_query (query_string, flags);
281 final_query = Xapian::Query (Xapian::Query::OP_AND,
282 mail_query, string_query);
285 enquire.set_weighting_scheme(Xapian::BoolWeight());
286 enquire.set_docid_order(Xapian::Enquire::ASCENDING);
289 fprintf (stderr, "Final query is:\n%s\n", final_query.get_description().c_str());
292 enquire.set_query (final_query);
294 mset = enquire.get_mset (0, notmuch->xapian_db->get_doccount ());
296 count = mset.get_matches_estimated();
298 } catch (const Xapian::Error &error) {
299 fprintf (stderr, "A Xapian exception occurred: %s\n",
300 error.get_msg().c_str());
301 fprintf (stderr, "Query string was: %s\n", query->query_string);