Skip to content

Commit 7c182a2

Browse files
committed
fix: fix prefix lookup in basickv and add corresponding test
1 parent a19fa4f commit 7c182a2

File tree

2 files changed

+91
-33
lines changed

2 files changed

+91
-33
lines changed

src/btree/BasicKV.cpp

+22-32
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "leanstore/utils/Misc.hpp"
1212

1313
#include <format>
14+
#include <string>
1415

1516
using namespace std;
1617
using namespace leanstore::storage;
@@ -178,42 +179,31 @@ OpCode BasicKV::Insert(Slice key, Slice val) {
178179
return OpCode::kOK;
179180
}
180181

181-
OpCode BasicKV::PrefixLookup(Slice key, PrefixLookupCallback callback) {
182-
while (true) {
183-
JUMPMU_TRY() {
184-
GuardedBufferFrame<BTreeNode> guardedLeaf;
185-
FindLeafCanJump(key, guardedLeaf);
186-
187-
bool isEqual = false;
188-
int16_t cur = guardedLeaf->LowerBound<false>(key, &isEqual);
189-
if (isEqual) {
190-
callback(key, guardedLeaf->Value(cur));
191-
guardedLeaf.JumpIfModifiedByOthers();
192-
JUMPMU_RETURN OpCode::kOK;
193-
}
194-
195-
if (cur < guardedLeaf->mNumSlots) {
196-
auto fullKeySize = guardedLeaf->GetFullKeyLen(cur);
197-
auto fullKeyBuf = utils::JumpScopedArray<uint8_t>(fullKeySize);
198-
guardedLeaf->CopyFullKey(cur, fullKeyBuf->get());
199-
guardedLeaf.JumpIfModifiedByOthers();
200-
201-
callback(Slice(fullKeyBuf->get(), fullKeySize), guardedLeaf->Value(cur));
202-
guardedLeaf.JumpIfModifiedByOthers();
203-
204-
JUMPMU_RETURN OpCode::kOK;
182+
OpCode BasicKV::PrefixLookup(Slice prefixKey, PrefixLookupCallback callback) {
183+
JUMPMU_TRY() {
184+
auto iter = GetIterator();
185+
if (iter.SeekToFirstGreaterEqual(prefixKey); !iter.Valid()) {
186+
JUMPMU_RETURN OpCode::kNotFound;
187+
}
188+
bool foundPrefixKey = false;
189+
for (; iter.Valid(); iter.Next()) {
190+
iter.AssembleKey();
191+
auto key = iter.Key();
192+
auto value = iter.Val();
193+
auto key_string = key.ToString();
194+
if (key_string.substr(0, prefixKey.ToString().size()) != prefixKey.ToString()) {
195+
break;
205196
}
206-
207-
OpCode ret = ScanAsc(key, [&](Slice scannedKey, Slice scannedVal) {
208-
callback(scannedKey, scannedVal);
209-
return false;
210-
});
211-
JUMPMU_RETURN ret;
197+
callback(key, value);
198+
foundPrefixKey = true;
212199
}
213-
JUMPMU_CATCH() {
200+
if (!foundPrefixKey) {
201+
JUMPMU_RETURN OpCode::kNotFound;
214202
}
203+
JUMPMU_RETURN OpCode::kOK;
204+
}
205+
JUMPMU_CATCH() {
215206
}
216-
217207
UNREACHABLE();
218208
return OpCode::kOther;
219209
}

tests/btree/BasicKVTest.cpp

+69-1
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ TEST_F(BasicKVTest, SameKeyInsertRemoveMultiTimes) {
299299
btree = res.value();
300300
});
301301

302-
// insert 100 key-values to the btree
302+
// insert 1000 key-values to the btree
303303
size_t numKVs(1000);
304304
std::vector<std::pair<std::string, std::string>> kvToTest;
305305
for (size_t i = 0; i < numKVs; ++i) {
@@ -342,4 +342,72 @@ TEST_F(BasicKVTest, SameKeyInsertRemoveMultiTimes) {
342342
mStore->ExecSync(0, [&]() { EXPECT_EQ(btree->CountEntries(), numKVs); });
343343
}
344344

345+
TEST_F(BasicKVTest, PrefixLookup) {
346+
storage::btree::BasicKV* btree;
347+
mStore->ExecSync(0, [&]() {
348+
auto res = mStore->CreateBasicKV(genBtreeName("_tree1"));
349+
ASSERT_TRUE(res);
350+
ASSERT_NE(res.value(), nullptr);
351+
btree = res.value();
352+
});
353+
// callback function
354+
std::vector<std::tuple<std::string, std::string>> copiedKeyValue;
355+
auto copyKeyAndValueOut = [&](Slice key, Slice val) {
356+
copiedKeyValue.emplace_back(key.ToString(), val.ToString());
357+
};
358+
359+
{
360+
// not found valid prefix key
361+
std::string prefixString("key_");
362+
auto prefixKey = Slice(prefixString);
363+
EXPECT_EQ(btree->PrefixLookup(prefixKey, copyKeyAndValueOut), OpCode::kNotFound);
364+
}
365+
366+
{
367+
// insert key and value
368+
size_t numKVs(10);
369+
std::vector<std::pair<std::string, std::string>> kvToTest;
370+
for (size_t i = 0; i < numKVs; ++i) {
371+
std::string key("key_" + std::string(10, 'x') + std::to_string(i));
372+
std::string val("val_" + std::string(100, 'x') + std::to_string(i));
373+
mStore->ExecSync(0, [&]() { EXPECT_EQ(btree->Insert(key, val), OpCode::kOK); });
374+
kvToTest.emplace_back(std::move(key), std::move(val));
375+
}
376+
377+
// prefix lookup the full set
378+
auto prefixString("key_" + std::string(10, 'x'));
379+
auto prefixKey = Slice(prefixString);
380+
EXPECT_EQ(btree->PrefixLookup(prefixKey, copyKeyAndValueOut), OpCode::kOK);
381+
EXPECT_EQ(copiedKeyValue.size(), kvToTest.size());
382+
for (size_t i = 0; i < copiedKeyValue.size(); i++) {
383+
const auto& [key, expectedVal] = kvToTest[i];
384+
const auto& [copiedKey, copiedValue] = copiedKeyValue[i];
385+
EXPECT_EQ(copiedKey, key);
386+
EXPECT_EQ(copiedValue, expectedVal);
387+
}
388+
389+
// insert special key for prefix lookup
390+
std::vector<std::pair<std::string, std::string>> kvToTest2;
391+
for (size_t i = 0; i < numKVs; ++i) {
392+
std::string key("prefix_key_" + std::string(10, 'x') + std::to_string(i));
393+
std::string val("prefix_value_" + std::string(100, 'x') + std::to_string(i));
394+
mStore->ExecSync(0, [&]() { EXPECT_EQ(btree->Insert(key, val), OpCode::kOK); });
395+
kvToTest2.emplace_back(std::move(key), std::move(val));
396+
}
397+
398+
// prefix lookup the partial set
399+
copiedKeyValue.clear();
400+
auto prefixString2("prefix_key_" + std::string(10, 'x'));
401+
auto prefixKey2 = Slice(prefixString2);
402+
EXPECT_EQ(btree->PrefixLookup(prefixKey2, copyKeyAndValueOut), OpCode::kOK);
403+
EXPECT_EQ(copiedKeyValue.size(), kvToTest2.size());
404+
for (size_t i = 0; i < copiedKeyValue.size(); i++) {
405+
const auto& [key, expectedVal] = kvToTest2[i];
406+
const auto& [copiedKey, copiedValue] = copiedKeyValue[i];
407+
EXPECT_EQ(copiedKey, key);
408+
EXPECT_EQ(copiedValue, expectedVal);
409+
}
410+
}
411+
}
412+
345413
} // namespace leanstore::test

0 commit comments

Comments
 (0)