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>
33QPair<quint32, quint32> calculateTimeRange(
const QDateTime& dt, Term::Comparator com)
37 if (com == Term::Equal) {
39 auto start =
static_cast<quint32
>(dt.
date().startOfDay().toSecsSinceEpoch());
40 auto end =
static_cast<quint32
>(dt.
date().endOfDay().toSecsSinceEpoch());
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");
62struct InternalProperty {
63 const char* propertyName;
67constexpr std::array<InternalProperty, 7> internalProperties{{{
"content",
"",
QMetaType::QString},
75std::pair<QByteArray, QMetaType::Type> propertyInfo(
const QByteArray &property)
77 auto it = std::find_if(std::begin(internalProperties), std::end(internalProperties),
78 [&property] (
const InternalProperty& entry) {
return property == entry.propertyName; });
79 if (it != std::end(internalProperties)) {
80 return { (*it).prefix, (*it).valueType };
83 if (pi.
property() == KFileMetaData::Property::Empty) {
86 int propPrefix =
static_cast<int>(pi.
property());
104 if (arr.
size() > 25) {
105 queries << EngineQuery(arr.
left(25), EngineQuery::StartsWith);
107 queries << EngineQuery(arr);
112 return EngineQuery();
113 }
else if (queries.
size() == 1) {
114 return queries.
first();
116 return EngineQuery(queries);
120EngineQuery constructContainsQuery(
const QByteArray& prefix,
const QString& value)
122 auto query = constructEqualsQuery(prefix, value);
123 if (
query.op() == EngineQuery::Equal) {
125 query.setOp(EngineQuery::StartsWith);
131EngineQuery constructTypeQuery(
const QString& value)
136 if (ti == KFileMetaData::Type::Empty) {
137 qCDebug(BALOO) <<
"Type" << value <<
"does not exist";
138 return EngineQuery();
140 int num =
static_cast<int>(ti.
type());
146SearchStore::SearchStore()
149 m_db = globalDatabaseInstance();
150 if (!m_db->open(Database::ReadOnlyDatabase)) {
155SearchStore::~SearchStore()
160ResultList SearchStore::exec(
const Term& term, uint offset,
int limit,
bool sortResults)
162 if (!m_db || !m_db->isOpen()) {
166 Transaction tr(m_db, Transaction::ReadOnly);
167 std::unique_ptr<PostingIterator> it(constructQuery(&tr, term));
175 quint64
id = it->docId();
176 quint32 mtime = tr.documentTimeInfo(
id).mTime;
177 resultIds << std::pair<quint64, quint32>{id, mtime};
183 if (offset >=
static_cast<uint
>(resultIds.
size())) {
187 auto compFunc = [](
const std::pair<quint64, quint32>& lhs,
188 const std::pair<quint64, quint32>& rhs) {
189 return lhs.second > rhs.second;
192 std::sort(resultIds.
begin(), resultIds.
end(), compFunc);
194 limit = resultIds.
size();
198 const uint
end = qMin(
static_cast<uint
>(resultIds.
size()), offset +
static_cast<uint
>(limit));
199 results.reserve(end - offset);
200 for (uint i = offset; i <
end; i++) {
201 const quint64
id = resultIds[i].
first;
202 Result res{tr.documentUrl(
id),
id};
204 results.emplace_back(res);
211 uint ulimit = limit < 0 ? UINT_MAX : limit;
213 while (offset && it->next()) {
217 while (ulimit && it->next()) {
218 const quint64
id = it->docId();
220 Result res{tr.documentUrl(
id),
id};
221 Q_ASSERT(!res.filePath.isEmpty());
223 results.emplace_back(res);
232PostingIterator* SearchStore::constructQuery(Transaction* tr,
const Term& term)
236 if (term.operation() == Term::And || term.operation() == Term::Or) {
241 for (
const Term& t : subTerms) {
242 auto iterator = constructQuery(tr, t);
246 }
else if (term.operation() == Term::And) {
253 }
else if (vec.
size() == 1) {
257 if (term.operation() == Term::And) {
258 return new AndPostingIterator(vec);
260 return new OrPostingIterator(vec);
264 if (term.value().isNull()) {
267 Q_ASSERT(term.value().isValid());
268 Q_ASSERT(term.comparator() != Term::Auto);
269 Q_ASSERT(term.comparator() == Term::Contains ? term.value().typeId() ==
QMetaType::QString :
true);
271 const QVariant value = term.value();
274 if (property ==
"type" || property ==
"kind") {
275 EngineQuery q = constructTypeQuery(value.
toString());
276 return tr->postingIterator(q);
278 else if (property ==
"includefolder") {
288 quint64
id = tr->documentId(folder);
290 qCDebug(BALOO) <<
"Folder" << value.
toString() <<
"not indexed";
294 return tr->docUrlIter(
id);
296 else if (property ==
"modified" || property ==
"mtime") {
300 Q_ASSERT(ba.
size() >= 4);
309 month = month >= 0 && month <= 12 ? month : 0;
310 day = day >= 0 && day <= 31 ? day : 0;
312 QDate startDate(year, month ? month : 1, day ? day : 1);
313 QDate endDate(startDate);
316 endDate.setDate(endDate.year(), 12, 31);
317 }
else if (day == 0) {
318 endDate.setDate(endDate.year(), endDate.month(), endDate.daysInMonth());
321 return tr->mTimeRangeIter(startDate.startOfDay().toSecsSinceEpoch(), endDate.endOfDay().toSecsSinceEpoch());
324 QPair<quint32, quint32> timerange = calculateTimeRange(dt, term.comparator());
325 if ((timerange.first == 0) && (timerange.second == 0)) {
328 return tr->mTimeRangeIter(timerange.first, timerange.second);
330 Q_ASSERT_X(0,
"SearchStore::constructQuery",
"modified property must contain date/datetime values");
333 }
else if (property ==
"tag") {
334 if (term.comparator() == Term::Equal) {
336 EngineQuery q = EngineQuery(prefix + value.
toByteArray());
337 return tr->postingIterator(q);
338 }
else if (term.comparator() == Term::Contains) {
340 EngineQuery q = constructEqualsQuery(prefix, value.
toString());
341 return tr->postingIterator(q);
346 }
else if (property ==
"") {
347 Term cterm(QStringLiteral(
"content"), term.value(), term.comparator());
348 Term fterm(QStringLiteral(
"filename"), term.value(), term.comparator());
349 return constructQuery(tr, Term{cterm, Term::Operation::Or, fterm});
355 std::tie(prefix, valueType) = propertyInfo(property);
361 auto com = term.comparator();
365 if (com == Term::Contains) {
366 EngineQuery q = constructContainsQuery(prefix, value.
toString());
367 return tr->postingIterator(q);
370 if (com == Term::Equal) {
371 EngineQuery q = constructEqualsQuery(prefix, value.
toString());
372 return tr->postingIterator(q);
375 PostingDB::Comparator pcom;
376 if (com == Term::Greater || com == Term::GreaterEqual) {
377 pcom = PostingDB::GreaterEqual;
378 }
else if (com == Term::Less || com == Term::LessEqual) {
379 pcom = PostingDB::LessEqual;
387 if (term.comparator() == Term::Greater) {
389 }
else if (term.comparator() == Term::Less) {
393 return tr->postingCompIterator(prefix, intVal, pcom);
397 return tr->postingCompIterator(prefix, dVal, pcom);
402 return tr->postingCompIterator(prefix, ba, pcom);
405 qCDebug(BALOO) <<
"Comparison must be with an integer";
The result class is where all the data extracted by the KFileMetaData extractors is saved to.
Q_SCRIPTABLE Q_NOREPLY void start()
Implements storage for docIds without any associated data Instantiated for:
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
const QList< QKeySequence > & end()
bool isEmpty() const const
QByteArray left(qsizetype len) const const
QByteArray mid(qsizetype pos, qsizetype len) const const
QByteArray number(double n, char format, int precision)
qsizetype size() const const
bool startsWith(QByteArrayView bv) const const
int toInt(bool *ok, int base) const const
QByteArray toLower() const const
bool isValid() const const
qint64 toSecsSinceEpoch() const const
QString toString(QStringView format, QCalendar cal) const const
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QByteArray toUtf8() const const
QByteArray toByteArray() const const
QDateTime toDateTime() const const
double toDouble(bool *ok) const const
qlonglong toLongLong(bool *ok) const const
QString toString() const const