8 #include "baloodebug.h"
9 #include "searchstore.h"
14 #include "transaction.h"
15 #include "enginequery.h"
16 #include "termgenerator.h"
17 #include "andpostingiterator.h"
18 #include "orpostingiterator.h"
22 #include <KFileMetaData/PropertyInfo>
23 #include <KFileMetaData/TypeInfo>
24 #include <KFileMetaData/Types>
37 if (com == Term::Equal) {
45 if (com == Term::LessEqual) {
48 if (com == Term::Less) {
49 return {0, timet - 1};
51 if (com == Term::GreaterEqual) {
52 return {timet, std::numeric_limits<quint32>::max()};
54 if (com == Term::Greater) {
55 return {timet + 1, std::numeric_limits<quint32>::max()};
58 Q_ASSERT_X(0, __func__,
"mtime query must contain a valid comparator");
62 struct InternalProperty {
63 const char* propertyName;
67 constexpr std::array<InternalProperty, 7> internalProperties {{
77 std::pair<QByteArray, QVariant::Type> propertyInfo(
const QByteArray& property)
79 auto it = std::find_if(std::begin(internalProperties), std::end(internalProperties),
80 [&property] (
const InternalProperty& entry) {
return property == entry.propertyName; });
81 if (it != std::end(internalProperties)) {
82 return { (*it).prefix, (*it).valueType };
85 if (pi.
property() == KFileMetaData::Property::Empty) {
88 int propPrefix =
static_cast<int>(pi.
property());
106 if (arr.
size() > 25) {
107 queries << EngineQuery(arr.
left(25), EngineQuery::StartsWith);
109 queries << EngineQuery(arr);
114 return EngineQuery();
115 }
else if (queries.
size() == 1) {
116 return queries.
first();
118 return EngineQuery(queries);
122 EngineQuery constructContainsQuery(
const QByteArray& prefix,
const QString& value)
124 auto query = constructEqualsQuery(prefix, value);
125 if (
query.op() == EngineQuery::Equal) {
126 if (
query.term().size() >= 3) {
127 query.setOp(EngineQuery::StartsWith);
133 EngineQuery constructTypeQuery(
const QString& value)
137 KFileMetaData::TypeInfo ti = KFileMetaData::TypeInfo::fromName(value);
138 if (ti == KFileMetaData::Type::Empty) {
139 qCDebug(BALOO) <<
"Type" << value <<
"does not exist";
140 return EngineQuery();
142 int num =
static_cast<int>(ti.type());
148 SearchStore::SearchStore()
151 m_db = globalDatabaseInstance();
152 if (!m_db->open(Database::ReadOnlyDatabase)) {
157 SearchStore::~SearchStore()
162 ResultList SearchStore::exec(
const Term& term, uint offset,
int limit,
bool sortResults)
164 if (!m_db || !m_db->isOpen()) {
168 Transaction tr(m_db, Transaction::ReadOnly);
169 std::unique_ptr<PostingIterator> it(constructQuery(&tr, term));
177 quint64
id = it->docId();
178 quint32 mtime = tr.documentTimeInfo(
id).mTime;
179 resultIds << std::pair<quint64, quint32>{id, mtime};
185 if (offset >=
static_cast<uint
>(resultIds.
size())) {
189 auto compFunc = [](
const std::pair<quint64, quint32>& lhs,
190 const std::pair<quint64, quint32>& rhs) {
191 return lhs.second > rhs.second;
194 std::sort(resultIds.
begin(), resultIds.
end(), compFunc);
196 limit = resultIds.
size();
200 const uint
end = qMin(
static_cast<uint
>(resultIds.
size()), offset +
static_cast<uint
>(limit));
201 results.reserve(end - offset);
202 for (uint i = offset; i <
end; i++) {
203 const quint64
id = resultIds[i].
first;
204 Result res{tr.documentUrl(
id),
id};
206 results.emplace_back(res);
213 uint ulimit = limit < 0 ? UINT_MAX : limit;
215 while (offset && it->next()) {
219 while (ulimit && it->next()) {
220 const quint64
id = it->docId();
222 Result res{tr.documentUrl(
id),
id};
223 Q_ASSERT(!res.filePath.isEmpty());
225 results.emplace_back(res);
234 PostingIterator* SearchStore::constructQuery(Transaction* tr,
const Term& term)
238 if (term.operation() == Term::And || term.operation() == Term::Or) {
243 for (
const Term& t : subTerms) {
244 auto iterator = constructQuery(tr, t);
248 }
else if (term.operation() == Term::And) {
255 }
else if (vec.
size() == 1) {
259 if (term.operation() == Term::And) {
260 return new AndPostingIterator(vec);
262 return new OrPostingIterator(vec);
266 if (term.value().isNull()) {
269 Q_ASSERT(term.value().isValid());
270 Q_ASSERT(term.comparator() != Term::Auto);
271 Q_ASSERT(term.comparator() == Term::Contains ? term.value().type() ==
QVariant::String :
true);
273 const QVariant value = term.value();
276 if (property ==
"type" || property ==
"kind") {
277 EngineQuery q = constructTypeQuery(value.
toString());
278 return tr->postingIterator(q);
280 else if (property ==
"includefolder") {
290 quint64
id = tr->documentId(folder);
292 qCDebug(BALOO) <<
"Folder" << value.
toString() <<
"not indexed";
296 return tr->docUrlIter(
id);
298 else if (property ==
"modified" || property ==
"mtime") {
302 Q_ASSERT(ba.
size() >= 4);
311 month = month >= 0 && month <= 12 ? month : 0;
312 day = day >= 0 && day <= 31 ? day : 0;
314 QDate startDate(year, month ? month : 1, day ? day : 1);
315 QDate endDate(startDate);
318 endDate.setDate(endDate.year(), 12, 31);
319 }
else if (day == 0) {
320 endDate.setDate(endDate.year(), endDate.month(), endDate.daysInMonth());
323 return tr->mTimeRangeIter(startDate.startOfDay().toSecsSinceEpoch(), endDate.endOfDay().toSecsSinceEpoch());
328 if ((timerange.first == 0) && (timerange.second == 0)) {
331 return tr->mTimeRangeIter(timerange.first, timerange.second);
334 Q_ASSERT_X(0,
"SearchStore::constructQuery",
"modified property must contain date/datetime values");
337 }
else if (property ==
"tag") {
338 if (term.comparator() == Term::Equal) {
340 EngineQuery q = EngineQuery(prefix + value.
toByteArray());
341 return tr->postingIterator(q);
342 }
else if (term.comparator() == Term::Contains) {
344 EngineQuery q = constructEqualsQuery(prefix, value.
toString());
345 return tr->postingIterator(q);
350 }
else if (property ==
"") {
351 Term cterm(QStringLiteral(
"content"), term.value(), term.comparator());
352 Term fterm(QStringLiteral(
"filename"), term.value(), term.comparator());
353 return constructQuery(tr, Term{cterm, Term::Operation::Or, fterm});
359 std::tie(prefix, valueType) = propertyInfo(property);
365 auto com = term.comparator();
369 if (com == Term::Contains) {
370 EngineQuery q = constructContainsQuery(prefix, value.
toString());
371 return tr->postingIterator(q);
374 if (com == Term::Equal) {
375 EngineQuery q = constructEqualsQuery(prefix, value.
toString());
376 return tr->postingIterator(q);
379 PostingDB::Comparator pcom;
380 if (com == Term::Greater || com == Term::GreaterEqual) {
381 pcom = PostingDB::GreaterEqual;
382 }
else if (com == Term::Less || com == Term::LessEqual) {
383 pcom = PostingDB::LessEqual;
391 if (term.comparator() == Term::Greater) {
393 }
else if (term.comparator() == Term::Less) {
397 return tr->postingCompIterator(prefix, intVal, pcom);
401 return tr->postingCompIterator(prefix, dVal, pcom);
406 return tr->postingCompIterator(prefix, ba, pcom);
409 qCDebug(BALOO) <<
"Comparison must be with an integer";