Warning, file /pim/akonadi-search/xapian/xapiansearchstore.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * This file is part of the KDE Akonadi Search Project 0003 * SPDX-FileCopyrightText: 2013-2014 Vishesh Handa <me@vhanda.in> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 * 0007 */ 0008 0009 #include "xapiansearchstore.h" 0010 #include "query.h" 0011 #include "xapianqueryparser.h" 0012 0013 #include "akonadi_search_xapian_debug.h" 0014 #include <QList> 0015 0016 #include <algorithm> 0017 0018 using namespace Akonadi::Search; 0019 0020 XapianSearchStore::XapianSearchStore(QObject *parent) 0021 : SearchStore(parent) 0022 , m_mutex() 0023 { 0024 } 0025 0026 XapianSearchStore::~XapianSearchStore() 0027 { 0028 delete m_db; 0029 } 0030 0031 void XapianSearchStore::setDbPath(const QString &path) 0032 { 0033 m_dbPath = path; 0034 0035 delete m_db; 0036 m_db = nullptr; 0037 0038 try { 0039 m_db = new Xapian::Database(m_dbPath.toStdString()); 0040 } catch (const Xapian::DatabaseOpeningError &) { 0041 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Xapian Database does not exist at " << m_dbPath; 0042 } catch (const Xapian::DatabaseCorruptError &) { 0043 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Xapian Database corrupted at " << m_dbPath; 0044 } catch (...) { 0045 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Random exception, but we do not want to crash"; 0046 } 0047 } 0048 0049 QString XapianSearchStore::dbPath() 0050 { 0051 return m_dbPath; 0052 } 0053 0054 Xapian::Query XapianSearchStore::toXapianQuery(Xapian::Query::op op, const QList<Term> &terms) 0055 { 0056 Q_ASSERT_X(op == Xapian::Query::OP_AND || op == Xapian::Query::OP_OR, "XapianSearchStore::toXapianQuery", "The op must be AND / OR"); 0057 0058 QList<Xapian::Query> queries; 0059 queries.reserve(terms.size()); 0060 0061 for (const Term &term : terms) { 0062 const Xapian::Query q = toXapianQuery(term); 0063 queries << q; 0064 } 0065 0066 return {op, queries.begin(), queries.end()}; 0067 } 0068 0069 static Xapian::Query negate(bool shouldNegate, const Xapian::Query &query) 0070 { 0071 if (shouldNegate) { 0072 return Xapian::Query(Xapian::Query::OP_AND_NOT, Xapian::Query::MatchAll, query); 0073 } 0074 return query; 0075 } 0076 0077 Xapian::Query XapianSearchStore::toXapianQuery(const Term &term) 0078 { 0079 if (term.operation() == Term::And) { 0080 return negate(term.isNegated(), toXapianQuery(Xapian::Query::OP_AND, term.subTerms())); 0081 } 0082 if (term.operation() == Term::Or) { 0083 return negate(term.isNegated(), toXapianQuery(Xapian::Query::OP_OR, term.subTerms())); 0084 } 0085 0086 return negate(term.isNegated(), constructQuery(term.property(), term.value(), term.comparator())); 0087 } 0088 0089 Xapian::Query XapianSearchStore::andQuery(const Xapian::Query &a, const Xapian::Query &b) 0090 { 0091 if (a.empty() && !b.empty()) { 0092 return b; 0093 } 0094 if (!a.empty() && b.empty()) { 0095 return a; 0096 } 0097 if (a.empty() && b.empty()) { 0098 return {}; 0099 } else { 0100 return Xapian::Query(Xapian::Query::OP_AND, a, b); 0101 } 0102 } 0103 0104 Xapian::Query XapianSearchStore::constructSearchQuery(const QString &str) 0105 { 0106 XapianQueryParser parser; 0107 parser.setDatabase(m_db); 0108 return parser.parseQuery(str); 0109 } 0110 0111 int XapianSearchStore::exec(const Query &query) 0112 { 0113 if (!m_db) { 0114 return 0; 0115 } 0116 0117 while (true) { 0118 try { 0119 QMutexLocker lock(&m_mutex); 0120 try { 0121 m_db->reopen(); 0122 } catch (Xapian::DatabaseError &e) { 0123 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Failed to reopen database" << dbPath() << ":" << QString::fromStdString(e.get_msg()); 0124 return 0; 0125 } 0126 0127 Xapian::Query xapQ = toXapianQuery(query.term()); 0128 // The term was not properly converted. Lets abort. The properties 0129 // must not exist 0130 if (!query.term().empty() && xapQ.empty()) { 0131 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << query.term() << "could not be processed. Aborting"; 0132 return 0; 0133 } 0134 if (!query.searchString().isEmpty()) { 0135 QString str = query.searchString(); 0136 0137 Xapian::Query q = constructSearchQuery(str); 0138 xapQ = andQuery(xapQ, q); 0139 } 0140 xapQ = andQuery(xapQ, convertTypes(query.types())); 0141 xapQ = andQuery(xapQ, constructFilterQuery(query.yearFilter(), query.monthFilter(), query.dayFilter())); 0142 xapQ = applyCustomOptions(xapQ, query.customOptions()); 0143 xapQ = finalizeQuery(xapQ); 0144 0145 if (xapQ.empty()) { 0146 // Return all the results 0147 xapQ = Xapian::Query(std::string()); 0148 } 0149 Xapian::Enquire enquire(*m_db); 0150 enquire.set_query(xapQ); 0151 0152 if (query.sortingOption() == Query::SortNone) { 0153 enquire.set_weighting_scheme(Xapian::BoolWeight()); 0154 } 0155 0156 Result &res = m_queryMap[m_nextId++]; 0157 res.mset = enquire.get_mset(query.offset(), query.limit()); 0158 res.it = res.mset.begin(); 0159 0160 return m_nextId - 1; 0161 } catch (const Xapian::DatabaseModifiedError &) { 0162 continue; 0163 } catch (const Xapian::Error &) { 0164 return 0; 0165 } 0166 } 0167 0168 return 0; 0169 } 0170 0171 void XapianSearchStore::close(int queryId) 0172 { 0173 QMutexLocker lock(&m_mutex); 0174 m_queryMap.remove(queryId); 0175 } 0176 0177 QByteArray XapianSearchStore::id(int queryId) 0178 { 0179 QMutexLocker lock(&m_mutex); 0180 Q_ASSERT_X(m_queryMap.contains(queryId), "FileSearchStore::id", "Passed a queryId which does not exist"); 0181 0182 const Result res = m_queryMap.value(queryId); 0183 if (!res.lastId) { 0184 return {}; 0185 } 0186 0187 return serialize(idPrefix(), res.lastId); 0188 } 0189 0190 QUrl XapianSearchStore::url(int queryId) 0191 { 0192 QMutexLocker lock(&m_mutex); 0193 Result &res = m_queryMap[queryId]; 0194 0195 if (!res.lastId) { 0196 return {}; 0197 } 0198 0199 if (!res.lastUrl.isEmpty()) { 0200 return res.lastUrl; 0201 } 0202 0203 res.lastUrl = constructUrl(res.lastId); 0204 return res.lastUrl; 0205 } 0206 0207 bool XapianSearchStore::next(int queryId) 0208 { 0209 if (!m_db) { 0210 return false; 0211 } 0212 0213 QMutexLocker lock(&m_mutex); 0214 Result &res = m_queryMap[queryId]; 0215 0216 bool atEnd = (res.it == res.mset.end()); 0217 if (atEnd) { 0218 res.lastId = 0; 0219 res.lastUrl.clear(); 0220 } else { 0221 res.lastId = *res.it; 0222 res.lastUrl.clear(); 0223 ++res.it; 0224 } 0225 0226 return !atEnd; 0227 } 0228 0229 Xapian::Document XapianSearchStore::docForQuery(int queryId) 0230 { 0231 if (!m_db) { 0232 return {}; 0233 } 0234 0235 QMutexLocker lock(&m_mutex); 0236 0237 try { 0238 const Result &res = m_queryMap.value(queryId); 0239 if (!res.lastId) { 0240 return {}; 0241 } 0242 0243 return m_db->get_document(res.lastId); 0244 } catch (const Xapian::DocNotFoundError &) { 0245 return {}; 0246 } catch (const Xapian::DatabaseModifiedError &) { 0247 m_db->reopen(); 0248 return docForQuery(queryId); 0249 } catch (const Xapian::Error &) { 0250 return {}; 0251 } 0252 } 0253 0254 Xapian::Database *XapianSearchStore::xapianDb() 0255 { 0256 return m_db; 0257 } 0258 0259 Xapian::Query XapianSearchStore::constructFilterQuery(int year, int month, int day) 0260 { 0261 Q_UNUSED(year) 0262 Q_UNUSED(month) 0263 Q_UNUSED(day) 0264 return {}; 0265 } 0266 0267 Xapian::Query XapianSearchStore::finalizeQuery(const Xapian::Query &query) 0268 { 0269 return query; 0270 } 0271 0272 Xapian::Query XapianSearchStore::applyCustomOptions(const Xapian::Query &q, const QVariantMap &options) 0273 { 0274 Q_UNUSED(options) 0275 return q; 0276 } 0277 0278 #include "moc_xapiansearchstore.cpp"