Akonadi Search

emailquery.cpp
1 /*
2  * This file is part of the KDE Akonadi Search Project
3  * SPDX-FileCopyrightText: 2013 Vishesh Handa <[email protected]>
4  *
5  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6  *
7  */
8 
9 #include <xapian.h>
10 
11 #include "../search/email/agepostingsource.h"
12 #include "akonadi_search_pim_debug.h"
13 #include "emailquery.h"
14 #include "resultiterator_p.h"
15 
16 #include <QFile>
17 #include <QRegularExpression>
18 #include <QStandardPaths>
19 
20 using namespace Akonadi::Search::PIM;
21 
22 class Akonadi::Search::PIM::EmailQueryPrivate
23 {
24 public:
25  EmailQueryPrivate();
26 
27  QStringList involves;
28  QStringList to;
29  QStringList cc;
30  QStringList bcc;
31  QString from;
32 
34 
35  char important;
36  char read;
37  char attachment;
38 
39  QString matchString;
40  QString subjectMatchString;
41  QString bodyMatchString;
42 
43  EmailQuery::OpType opType = EmailQuery::OpAnd;
44  int limit = 0;
45  bool splitSearchMatchString = true;
46 };
47 
48 EmailQueryPrivate::EmailQueryPrivate()
49  : important('0')
50  , read('0')
51  , attachment('0')
52 {
53 }
54 
55 EmailQuery::EmailQuery()
56  : Query()
57  , d(new EmailQueryPrivate)
58 {
59 }
60 
61 EmailQuery::~EmailQuery() = default;
62 
63 void EmailQuery::setSplitSearchMatchString(bool split)
64 {
65  d->splitSearchMatchString = split;
66 }
67 
68 void EmailQuery::setSearchType(EmailQuery::OpType op)
69 {
70  d->opType = op;
71 }
72 
73 void EmailQuery::addInvolves(const QString &email)
74 {
75  d->involves << email;
76 }
77 
78 void EmailQuery::setInvolves(const QStringList &involves)
79 {
80  d->involves = involves;
81 }
82 
83 void EmailQuery::addBcc(const QString &bcc)
84 {
85  d->bcc << bcc;
86 }
87 
88 void EmailQuery::setBcc(const QStringList &bcc)
89 {
90  d->bcc = bcc;
91 }
92 
93 void EmailQuery::setCc(const QStringList &cc)
94 {
95  d->cc = cc;
96 }
97 
98 void EmailQuery::setFrom(const QString &from)
99 {
100  d->from = from;
101 }
102 
103 void EmailQuery::addTo(const QString &to)
104 {
105  d->to << to;
106 }
107 
108 void EmailQuery::setTo(const QStringList &to)
109 {
110  d->to = to;
111 }
112 
113 void EmailQuery::addCc(const QString &cc)
114 {
115  d->cc << cc;
116 }
117 
118 void EmailQuery::addFrom(const QString &from)
119 {
120  d->from = from;
121 }
122 
123 void EmailQuery::addCollection(Akonadi::Collection::Id id)
124 {
125  d->collections << id;
126 }
127 
128 void EmailQuery::setCollection(const QList<Akonadi::Collection::Id> &collections)
129 {
130  d->collections = collections;
131 }
132 
133 int EmailQuery::limit() const
134 {
135  return d->limit;
136 }
137 
138 void EmailQuery::setLimit(int limit)
139 {
140  d->limit = limit;
141 }
142 
143 void EmailQuery::matches(const QString &match)
144 {
145  d->matchString = match;
146 }
147 
148 void EmailQuery::subjectMatches(const QString &subjectMatch)
149 {
150  d->subjectMatchString = subjectMatch;
151 }
152 
153 void EmailQuery::bodyMatches(const QString &bodyMatch)
154 {
155  d->bodyMatchString = bodyMatch;
156 }
157 
158 void EmailQuery::setAttachment(bool hasAttachment)
159 {
160  d->attachment = hasAttachment ? 'T' : 'F';
161 }
162 
163 void EmailQuery::setImportant(bool important)
164 {
165  d->important = important ? 'T' : 'F';
166 }
167 
168 void EmailQuery::setRead(bool read)
169 {
170  d->read = read ? 'T' : 'F';
171 }
172 
174 {
175  const QString dir = defaultLocation(QStringLiteral("email"));
176  Xapian::Database db;
177  try {
178  db = Xapian::Database(QFile::encodeName(dir).toStdString());
179  } catch (const Xapian::DatabaseOpeningError &) {
180  qCWarning(AKONADI_SEARCH_PIM_LOG) << "Xapian Database does not exist at " << dir;
181  return {};
182  } catch (const Xapian::DatabaseCorruptError &) {
183  qCWarning(AKONADI_SEARCH_PIM_LOG) << "Xapian Database corrupted";
184  return {};
185  } catch (const Xapian::DatabaseError &e) {
186  qCWarning(AKONADI_SEARCH_PIM_LOG) << "Failed to open Xapian database:" << QString::fromStdString(e.get_description());
187  return {};
188  } catch (...) {
189  qCWarning(AKONADI_SEARCH_PIM_LOG) << "Random exception, but we do not want to crash";
190  return {};
191  }
192 
193  QList<Xapian::Query> m_queries;
194 
195  if (!d->involves.isEmpty()) {
196  Xapian::QueryParser parser;
197  parser.set_database(db);
198  parser.add_prefix("", "F");
199  parser.add_prefix("", "T");
200  parser.add_prefix("", "CC");
201  parser.add_prefix("", "BCC");
202 
203  // vHanda: Do we really need the query parser over here?
204  for (const QString &str : std::as_const(d->involves)) {
205  const QByteArray ba = str.toUtf8();
206  m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
207  }
208  }
209 
210  if (!d->from.isEmpty()) {
211  Xapian::QueryParser parser;
212  parser.set_database(db);
213  parser.add_prefix("", "F");
214  const QByteArray ba = d->from.toUtf8();
215  m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
216  }
217 
218  if (!d->to.isEmpty()) {
219  Xapian::QueryParser parser;
220  parser.set_database(db);
221  parser.add_prefix("", "T");
222 
223  for (const QString &str : std::as_const(d->to)) {
224  const QByteArray ba = str.toUtf8();
225  m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
226  }
227  }
228 
229  if (!d->cc.isEmpty()) {
230  Xapian::QueryParser parser;
231  parser.set_database(db);
232  parser.add_prefix("", "CC");
233 
234  for (const QString &str : std::as_const(d->cc)) {
235  const QByteArray ba = str.toUtf8();
236  m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
237  }
238  }
239 
240  if (!d->bcc.isEmpty()) {
241  Xapian::QueryParser parser;
242  parser.set_database(db);
243  parser.add_prefix("", "BC");
244 
245  for (const QString &str : std::as_const(d->bcc)) {
246  const QByteArray ba = str.toUtf8();
247  m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
248  }
249  }
250 
251  if (!d->subjectMatchString.isEmpty()) {
252  Xapian::QueryParser parser;
253  parser.set_database(db);
254  parser.add_prefix("", "SU");
255  parser.set_default_op(Xapian::Query::OP_AND);
256  const QByteArray ba = d->subjectMatchString.toUtf8();
257  m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
258  }
259 
260  if (!d->collections.isEmpty()) {
261  Xapian::Query query;
262  for (Akonadi::Collection::Id id : std::as_const(d->collections)) {
263  const QString c = QString::number(id);
264  const Xapian::Query q = Xapian::Query('C' + c.toStdString());
265 
266  query = Xapian::Query(Xapian::Query::OP_OR, query, q);
267  }
268 
269  m_queries << query;
270  }
271 
272  if (!d->bodyMatchString.isEmpty()) {
273  Xapian::QueryParser parser;
274  parser.set_database(db);
275  parser.add_prefix("", "BO");
276  parser.set_default_op(Xapian::Query::OP_AND);
277  const QByteArray ba = d->bodyMatchString.toUtf8();
278  m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
279  }
280 
281  if (d->important == 'T') {
282  m_queries << Xapian::Query("BI");
283  } else if (d->important == 'F') {
284  m_queries << Xapian::Query("BNI");
285  }
286 
287  if (d->read == 'T') {
288  m_queries << Xapian::Query("BR");
289  } else if (d->read == 'F') {
290  m_queries << Xapian::Query("BNR");
291  }
292 
293  if (d->attachment == 'T') {
294  m_queries << Xapian::Query("BA");
295  } else if (d->attachment == 'F') {
296  m_queries << Xapian::Query("BNA");
297  }
298 
299  if (!d->matchString.isEmpty()) {
300  Xapian::QueryParser parser;
301  parser.set_database(db);
302  parser.set_default_op(Xapian::Query::OP_AND);
303  if (d->splitSearchMatchString) {
304  const QStringList list = d->matchString.split(QRegularExpression(QStringLiteral("\\s")), Qt::SkipEmptyParts);
305  for (const QString &s : list) {
306  const QByteArray ba = s.toUtf8();
307  m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
308  }
309  } else {
310  const QByteArray ba = d->matchString.toUtf8();
311  m_queries << parser.parse_query(ba.constData(), Xapian::QueryParser::FLAG_PARTIAL);
312  }
313  }
314  Xapian::Query query;
315  switch (d->opType) {
316  case OpAnd:
317  query = Xapian::Query(Xapian::Query::OP_AND, m_queries.begin(), m_queries.end());
318  break;
319  case OpOr:
320  query = Xapian::Query(Xapian::Query::OP_OR, m_queries.begin(), m_queries.end());
321  break;
322  }
323 
324  AgePostingSource ps(0);
325  query = Xapian::Query(Xapian::Query::OP_AND_MAYBE, query, Xapian::Query(&ps));
326 
327  try {
328  Xapian::Enquire enquire(db);
329  enquire.set_query(query);
330 
331  if (d->limit == 0) {
332  // d->limit = 1000000;
333  d->limit = 100000;
334  }
335 
336  Xapian::MSet mset = enquire.get_mset(0, d->limit);
337 
338  ResultIterator iter;
339  iter.d->init(mset);
340  return iter;
341  } catch (const Xapian::Error &e) {
342  qCWarning(AKONADI_SEARCH_PIM_LOG) << QString::fromStdString(e.get_type()) << QString::fromStdString(e.get_description());
343  return {};
344  }
345 }
QString number(int n, int base)
QByteArray encodeName(const QString &fileName)
void setRead(bool read=true)
By default the read status is ignored.
Definition: emailquery.cpp:168
void setImportant(bool important=true)
By default the importance is ignored.
Definition: emailquery.cpp:163
void subjectMatches(const QString &subjectMatch)
Matches the string subjectMatch specifically in the email subject.
Definition: emailquery.cpp:148
QString fromStdString(const std::string &str)
ResultIterator exec() override
Execute the query and return an iterator to fetch the results.
Definition: emailquery.cpp:173
SkipEmptyParts
std::string toStdString() const const
Query base class.
Definition: lib/query.h:23
PIM specific search API.
void setAttachment(bool hasAttachment=true)
By default the attachment status is ignored.
Definition: emailquery.cpp:158
const char * constData() const const
void bodyMatches(const QString &bodyMatch)
Matches the string bodyMatch specifically in the body email.
Definition: emailquery.cpp:153
QList::iterator begin()
QVariant read(const QByteArray &data, int versionOverride=0)
QList::iterator end()
void matches(const QString &match)
Matches the string match anywhere in the entire email body.
Definition: emailquery.cpp:143
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Thu May 19 2022 04:04:48 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.