Skip to content

Commit cc0c7e5

Browse files
authored
fix(search): Ensure RemoveDoc before SORT STORE overwrite to keep indices consi stent (#5887)
fix(search): Ensure RemoveDoc before SORT STORE overwrite to keep indices consistent
1 parent 4bb6fa2 commit cc0c7e5

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
lines changed

src/server/generic_family.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,13 @@ OpResult<uint32_t> OpStore(const OpArgs& op_args, std::string_view key, Iterator
14771477
IteratorEnd&& end_it) {
14781478
uint32_t len = 0;
14791479

1480+
// If we are about to overwrite an existing indexed document (HASH/JSON),
1481+
// remove it from search indices first to avoid duplicate entries.
1482+
auto existing = op_args.GetDbSlice().FindReadOnly(op_args.db_cntx, key).it;
1483+
if (IsValid(existing)) {
1484+
RemoveKeyFromIndexesIfNeeded(key, op_args.db_cntx, existing->second, op_args.shard);
1485+
}
1486+
14801487
QList* ql_v2 = CompactObj::AllocateMR<QList>();
14811488
QList::Where where = QList::TAIL;
14821489
for (auto it = start_it; it != end_it; ++it) {

src/server/search/search_family_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2728,6 +2728,30 @@ TEST_F(SearchFamilyTest, SetDoesNotUpdateIndexesBug) {
27282728
EXPECT_THAT(resp, AreDocIds("k1"));
27292729
}
27302730

2731+
TEST_F(SearchFamilyTest, SortStoreDoesNotUpdateIndexesBug) {
2732+
// Create an index over HASH
2733+
auto resp = Run({"FT.CREATE", "index", "ON", "HASH", "SCHEMA", "field", "TEXT"});
2734+
EXPECT_THAT(resp, "OK");
2735+
2736+
// Index a HASH document under k1
2737+
resp = Run({"HSET", "k1", "field", "value"});
2738+
EXPECT_THAT(resp, IntArg(1));
2739+
2740+
// Prepare a source list to sort and store into k1 (overwriting k1 to LIST)
2741+
EXPECT_THAT(Run({"RPUSH", "lst", "b", "a"}), IntArg(2));
2742+
// SORT lst STORE k1 -> changes type of k1 from HASH to LIST
2743+
Run({"SORT", "lst", "ALPHA", "STORE", "k1"});
2744+
2745+
// Rename away and recreate k1 as HASH again
2746+
EXPECT_EQ(Run({"RENAME", "k1", "anotherkey"}), "OK");
2747+
EXPECT_THAT(Run({"HSET", "k1", "field", "value"}), IntArg(1));
2748+
2749+
// If SORT/STORE failed to remove k1 from indexes, the re-index here should crash.
2750+
// Successful run should contain only the new k1 document in the index.
2751+
resp = Run({"FT.SEARCH", "index", "*"});
2752+
EXPECT_THAT(resp, AreDocIds("k1"));
2753+
}
2754+
27312755
TEST_F(SearchFamilyTest, BlockSizeOptionFtCreate) {
27322756
// Create an index with a block size option
27332757
auto resp = Run({"FT.CREATE", "index", "ON", "HASH", "SCHEMA", "number1", "NUMERIC", "BLOCKSIZE",

0 commit comments

Comments
 (0)