2 This file is part of notmuch.
4 Notmuch is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation, either version 3 of the License, or (at your
7 option) any later version.
9 Notmuch is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 You should have received a copy of the GNU General Public License
15 along with notmuch. If not, see <http://www.gnu.org/licenses/>.
17 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
20 from ctypes import c_char_p, c_uint
21 from notmuch.globals import (
32 from notmuch.thread import Threads
33 from notmuch.message import Messages
37 """Represents a search query on an opened :class:`Database`.
39 A query selects and filters a subset of messages from the notmuch
40 database we derive from.
42 :class:`Query` provides an instance attribute :attr:`sort`, which
43 contains the sort order (if specified via :meth:`set_sort`) or
46 Any function in this class may throw an :exc:`NotInitializedError`
47 in case the underlying query object was not set up correctly.
49 .. note:: Do remember that as soon as we tear down this object,
50 all underlying derived objects such as threads,
51 messages, tags etc will be freed by the underlying library
52 as well. Accessing these objects will lead to segfaults and
53 other unexpected behavior. See above for more details.
56 SORT = Enum(['OLDEST_FIRST', 'NEWEST_FIRST', 'MESSAGE_ID', 'UNSORTED'])
57 """Constants: Sort order in which to return results"""
59 """notmuch_query_create"""
60 _create = nmlib.notmuch_query_create
61 _create.argtypes = [NotmuchDatabaseP, c_char_p]
62 _create.restype = NotmuchQueryP
64 """notmuch_query_search_threads"""
65 _search_threads = nmlib.notmuch_query_search_threads
66 _search_threads.argtypes = [NotmuchQueryP]
67 _search_threads.restype = NotmuchThreadsP
69 """notmuch_query_search_messages"""
70 _search_messages = nmlib.notmuch_query_search_messages
71 _search_messages.argtypes = [NotmuchQueryP]
72 _search_messages.restype = NotmuchMessagesP
74 """notmuch_query_count_messages"""
75 _count_messages = nmlib.notmuch_query_count_messages
76 _count_messages.argtypes = [NotmuchQueryP]
77 _count_messages.restype = c_uint
79 def __init__(self, db, querystr):
81 :param db: An open database which we derive the Query from.
82 :type db: :class:`Database`
83 :param querystr: The query string for the message.
84 :type querystr: utf-8 encoded str or unicode
89 self.create(db, querystr)
91 def _assert_query_is_initialized(self):
92 """Raises :exc:`NotInitializedError` if self._query is `None`"""
93 if self._query is None:
94 raise NotInitializedError()
96 def create(self, db, querystr):
97 """Creates a new query derived from a Database
99 This function is utilized by __init__() and usually does not need to
102 :param db: Database to create the query from.
103 :type db: :class:`Database`
104 :param querystr: The query string
105 :type querystr: utf-8 encoded str or unicode
108 :exc:`NullPointerError` if the query creation failed
109 (e.g. too little memory).
110 :exc:`NotInitializedError` if the underlying db was not
113 db._assert_db_is_initialized()
114 # create reference to parent db to keep it alive
116 # create query, return None if too little mem available
117 query_p = Query._create(db.db_p, _str(querystr))
119 raise NullPointerError
120 self._query = query_p
122 _set_sort = nmlib.notmuch_query_set_sort
123 _set_sort.argtypes = [NotmuchQueryP, c_uint]
124 _set_sort.argtypes = None
126 def set_sort(self, sort):
127 """Set the sort order future results will be delivered in
129 :param sort: Sort order (see :attr:`Query.SORT`)
131 self._assert_query_is_initialized()
133 self._set_sort(self._query, sort)
135 def search_threads(self):
136 """Execute a query for threads
138 Execute a query for threads, returning a :class:`Threads` iterator.
139 The returned threads are owned by the query and as such, will only be
140 valid until the Query is deleted.
142 The method sets :attr:`Message.FLAG`\.MATCH for those messages that
143 match the query. The method :meth:`Message.get_flag` allows us
144 to get the value of this flag.
146 :returns: :class:`Threads`
147 :exception: :exc:`NullPointerError` if search_threads failed
149 self._assert_query_is_initialized()
150 threads_p = Query._search_threads(self._query)
153 raise NullPointerError
154 return Threads(threads_p, self)
156 def search_messages(self):
157 """Filter messages according to the query and return
158 :class:`Messages` in the defined sort order
160 :returns: :class:`Messages`
161 :exception: :exc:`NullPointerError` if search_messages failed
163 self._assert_query_is_initialized()
164 msgs_p = Query._search_messages(self._query)
167 raise NullPointerError
168 return Messages(msgs_p, self)
170 def count_messages(self):
171 """Estimate the number of messages matching the query
173 This function performs a search and returns Xapian's best
174 guess as to the number of matching messages. It is much faster
175 than performing :meth:`search_messages` and counting the
176 result with `len()` (although it always returned the same
177 result in my tests). Technically, it wraps the underlying
178 *notmuch_query_count_messages* function.
180 :returns: :class:`Messages`
182 self._assert_query_is_initialized()
183 return Query._count_messages(self._query)
185 _destroy = nmlib.notmuch_query_destroy
186 _destroy.argtypes = [NotmuchQueryP]
187 _destroy.restype = None
190 """Close and free the Query"""
191 if self._query is not None:
192 self._destroy(self._query)