1/*
2 This file is part of the KDE Baloo project.
3 SPDX-FileCopyrightText: 2015 Vishesh Handa <me@vhanda.in>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
8#include "postingdb.h"
9#include "enginedebug.h"
10#include "orpostingiterator.h"
11#include "postingcodec.h"
12#include "vectorpostingiterator.h"
13
14using namespace Baloo;
15
16PostingDB::PostingDB(MDB_dbi dbi, MDB_txn* txn)
17 : m_txn(txn)
18 , m_dbi(dbi)
19{
20 Q_ASSERT(txn != nullptr);
21 Q_ASSERT(dbi != 0);
22}
23
24PostingDB::~PostingDB()
25{
26}
27
28MDB_dbi PostingDB::create(MDB_txn* txn)
29{
30 MDB_dbi dbi = 0;
31 int rc = mdb_dbi_open(txn, name: "postingdb", MDB_CREATE, dbi: &dbi);
32 if (rc) {
33 qCWarning(ENGINE) << "PostingDB::create" << mdb_strerror(err: rc);
34 return 0;
35 }
36
37 return dbi;
38}
39
40MDB_dbi PostingDB::open(MDB_txn* txn)
41{
42 MDB_dbi dbi = 0;
43 int rc = mdb_dbi_open(txn, name: "postingdb", flags: 0, dbi: &dbi);
44 if (rc) {
45 qCWarning(ENGINE) << "PostingDB::open" << mdb_strerror(err: rc);
46 return 0;
47 }
48
49 return dbi;
50}
51
52void PostingDB::put(const QByteArray& term, const PostingList& list)
53{
54 Q_ASSERT(!term.isEmpty());
55 Q_ASSERT(!list.isEmpty());
56
57 MDB_val key;
58 key.mv_size = term.size();
59 key.mv_data = static_cast<void*>(const_cast<char*>(term.constData()));
60
61 QByteArray arr = PostingCodec::encode(list);
62
63 MDB_val val;
64 val.mv_size = arr.size();
65 val.mv_data = static_cast<void*>(arr.data());
66
67 int rc = mdb_put(txn: m_txn, dbi: m_dbi, key: &key, data: &val, flags: 0);
68 if (rc) {
69 qCWarning(ENGINE) << "PostingDB::put" << mdb_strerror(err: rc);
70 }
71}
72
73PostingList PostingDB::get(const QByteArray& term)
74{
75 Q_ASSERT(!term.isEmpty());
76
77 MDB_val key;
78 key.mv_size = term.size();
79 key.mv_data = static_cast<void*>(const_cast<char*>(term.constData()));
80
81 MDB_val val{.mv_size: 0, .mv_data: nullptr};
82 int rc = mdb_get(txn: m_txn, dbi: m_dbi, key: &key, data: &val);
83 if (rc) {
84 if (rc != MDB_NOTFOUND) {
85 qCDebug(ENGINE) << "PostingDB::get" << term << mdb_strerror(err: rc);
86 }
87 return PostingList();
88 }
89
90 QByteArray arr = QByteArray::fromRawData(data: static_cast<char*>(val.mv_data), size: val.mv_size);
91
92 return PostingCodec::decode(arr);
93}
94
95void PostingDB::del(const QByteArray& term)
96{
97 Q_ASSERT(!term.isEmpty());
98
99 MDB_val key;
100 key.mv_size = term.size();
101 key.mv_data = static_cast<void*>(const_cast<char*>(term.constData()));
102
103 int rc = mdb_del(txn: m_txn, dbi: m_dbi, key: &key, data: nullptr);
104 if (rc != 0 && rc != MDB_NOTFOUND) {
105 qCDebug(ENGINE) << "PostingDB::del" << term << mdb_strerror(err: rc);
106 }
107}
108
109QVector< QByteArray > PostingDB::fetchTermsStartingWith(const QByteArray& term)
110{
111 MDB_val key;
112 key.mv_size = term.size();
113 key.mv_data = static_cast<void*>(const_cast<char*>(term.constData()));
114
115 MDB_cursor* cursor;
116 int rc = mdb_cursor_open(txn: m_txn, dbi: m_dbi, cursor: &cursor);
117 if (rc) {
118 qCWarning(ENGINE) << "PostingDB::fetchTermsStartingWith" << mdb_strerror(err: rc);
119 return {};
120 }
121
122 QVector<QByteArray> terms;
123 rc = mdb_cursor_get(cursor, key: &key, data: nullptr, op: MDB_SET_RANGE);
124 while (rc == 0) {
125 const QByteArray arr(static_cast<char*>(key.mv_data), key.mv_size);
126 if (!arr.startsWith(bv: term)) {
127 break;
128 }
129 terms << arr;
130 rc = mdb_cursor_get(cursor, key: &key, data: nullptr, op: MDB_NEXT);
131 }
132 if (rc != MDB_NOTFOUND) {
133 qCDebug(ENGINE) << "PostingDB::fetchTermsStartingWith" << mdb_strerror(err: rc);
134 }
135
136 mdb_cursor_close(cursor);
137 return terms;
138}
139
140PostingIterator* PostingDB::iter(const QByteArray& term)
141{
142 MDB_val key;
143 key.mv_size = term.size();
144 key.mv_data = static_cast<void*>(const_cast<char*>(term.constData()));
145
146 MDB_val val;
147 int rc = mdb_get(txn: m_txn, dbi: m_dbi, key: &key, data: &val);
148 if (rc) {
149 qCDebug(ENGINE) << "PostingDB::iter" << term << mdb_strerror(err: rc);
150 return nullptr;
151 }
152
153 auto values = QByteArray::fromRawData(data: static_cast<char *>(val.mv_data), size: val.mv_size);
154 return new VectorPostingIterator(PostingCodec().decode(arr: values));
155}
156
157template <typename Validator>
158PostingIterator* PostingDB::iter(const QByteArray& prefix, Validator validate)
159{
160 Q_ASSERT(!prefix.isEmpty());
161
162 MDB_val key;
163 key.mv_size = prefix.size();
164 key.mv_data = static_cast<void*>(const_cast<char*>(prefix.constData()));
165
166 MDB_cursor* cursor;
167 int rc = mdb_cursor_open(txn: m_txn, dbi: m_dbi, cursor: &cursor);
168
169 if (rc) {
170 qCWarning(ENGINE) << "PostingDB::regexpIter" << mdb_strerror(err: rc);
171 return nullptr;
172 }
173
174 QVector<PostingIterator*> termIterators;
175
176 MDB_val val;
177 rc = mdb_cursor_get(cursor, key: &key, data: &val, op: MDB_SET_RANGE);
178 while (rc == 0) {
179 const QByteArray arr(static_cast<char*>(key.mv_data), key.mv_size);
180 if (!arr.startsWith(bv: prefix)) {
181 break;
182 }
183 if (validate(arr)) {
184 auto values = QByteArray::fromRawData(data: static_cast<char *>(val.mv_data), size: val.mv_size);
185 termIterators << new VectorPostingIterator(PostingCodec().decode(arr: values));
186 }
187 rc = mdb_cursor_get(cursor, key: &key, data: &val, op: MDB_NEXT);
188 }
189
190 if (rc != 0 && rc != MDB_NOTFOUND) {
191 qCWarning(ENGINE) << "PostingDB::regexpIter" << mdb_strerror(err: rc);
192 }
193
194 mdb_cursor_close(cursor);
195 if (termIterators.isEmpty()) {
196 return nullptr;
197 }
198 return new OrPostingIterator(termIterators);
199}
200
201PostingIterator* PostingDB::prefixIter(const QByteArray& prefix)
202{
203 auto validate = [] (const QByteArray& arr) {
204 Q_UNUSED(arr);
205 return true;
206 };
207 return iter(prefix, validate);
208}
209
210PostingIterator* PostingDB::regexpIter(const QRegularExpression& regexp, const QByteArray& prefix)
211{
212 int prefixLen = prefix.length();
213 auto validate = [&regexp, prefixLen] (const QByteArray& arr) {
214 QString term = QString::fromUtf8(ba: arr.mid(index: prefixLen));
215 return regexp.match(subject: term).hasMatch();
216 };
217
218 return iter(prefix, validate);
219}
220
221PostingIterator* PostingDB::compIter(const QByteArray& prefix, qlonglong comVal, PostingDB::Comparator com)
222{
223 int prefixLen = prefix.length();
224 auto validate = [prefixLen, comVal, com] (const QByteArray& arr) {
225 bool ok = false;
226 auto val = QByteArray::fromRawData(data: arr.constData() + prefixLen, size: arr.length() - prefixLen).toLongLong(ok: &ok);
227 return ok && ((com == LessEqual && val <= comVal) || (com == GreaterEqual && val >= comVal));
228 };
229 return iter(prefix, validate);
230}
231
232PostingIterator* PostingDB::compIter(const QByteArray& prefix, double comVal, PostingDB::Comparator com)
233{
234 int prefixLen = prefix.length();
235 auto validate = [prefixLen, comVal, com] (const QByteArray& arr) {
236 bool ok = false;
237 auto val = QByteArray::fromRawData(data: arr.constData() + prefixLen, size: arr.length() - prefixLen).toDouble(ok: &ok);
238 return ok && ((com == LessEqual && val <= comVal) ||
239 (com == GreaterEqual && val >= comVal));
240 };
241 return iter(prefix, validate);
242}
243
244PostingIterator* PostingDB::compIter(const QByteArray& prefix, const QByteArray& comVal, PostingDB::Comparator com)
245{
246 int prefixLen = prefix.length();
247 auto validate = [prefixLen, comVal, com] (const QByteArray& arr) {
248 auto val = QByteArray::fromRawData(data: arr.constData() + prefixLen, size: arr.length() - prefixLen);
249 return ((com == LessEqual && val <= comVal) ||
250 (com == GreaterEqual && val >= comVal));
251 };
252 return iter(prefix, validate);
253}
254
255QMap<QByteArray, PostingList> PostingDB::toTestMap() const
256{
257 MDB_cursor* cursor;
258 mdb_cursor_open(txn: m_txn, dbi: m_dbi, cursor: &cursor);
259
260 MDB_val key = {.mv_size: 0, .mv_data: nullptr};
261 MDB_val val;
262
263 QMap<QByteArray, PostingList> map;
264 while (1) {
265 int rc = mdb_cursor_get(cursor, key: &key, data: &val, op: MDB_NEXT);
266 if (rc == MDB_NOTFOUND) {
267 break;
268 }
269 if (rc) {
270 qCDebug(ENGINE) << "PostingDB::toTestMap" << mdb_strerror(err: rc);
271 break;
272 }
273
274 const QByteArray ba(static_cast<char*>(key.mv_data), key.mv_size);
275 const PostingList plist = PostingCodec::decode(arr: QByteArray(static_cast<char*>(val.mv_data), val.mv_size));
276 map.insert(key: ba, value: plist);
277 }
278
279 mdb_cursor_close(cursor);
280 return map;
281}
282

source code of baloo/src/engine/postingdb.cpp