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 | |
14 | using namespace Baloo; |
15 | |
16 | PostingDB::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 | |
24 | PostingDB::~PostingDB() |
25 | { |
26 | } |
27 | |
28 | MDB_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 | |
40 | MDB_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 | |
52 | void 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 | |
73 | PostingList 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 | |
95 | void 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 | |
109 | QVector< 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 | |
140 | PostingIterator* 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 | |
157 | template <typename Validator> |
158 | PostingIterator* 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 | |
201 | PostingIterator* 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 | |
210 | PostingIterator* PostingDB::regexpIter(const QRegularExpression& regexp, const QByteArray& prefix) |
211 | { |
212 | int prefixLen = prefix.length(); |
213 | auto validate = [®exp, 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 | |
221 | PostingIterator* 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 | |
232 | PostingIterator* 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 | |
244 | PostingIterator* 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 | |
255 | QMap<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 | |