From 8cd67271583647a8c0e982a1ef6e294dc9b04bc8 Mon Sep 17 00:00:00 2001 From: Guy Royse Date: Thu, 25 Jun 2026 16:50:59 -0400 Subject: [PATCH] fix(index): attach to existing index when overwrite is false SearchIndex.create(overwrite, drop) only dropped-then-recreated when overwrite was true; otherwise it fell through to an unconditional FT.CREATE and threw SEARCH_INDEX_EXISTS on an existing index. There was no "create-if-absent / attach" branch. This made MessageHistory single-use per index: its constructor calls create(false), so resuming a conversation or opening a second session in the same index threw mid-construction. SemanticMessageHistory was affected the same way through its default overwrite=false constructors. Match Python's create(overwrite=False): when the index already exists and overwrite is false, attach to it and return instead of recreating. The fix lives in the shared SearchIndex.create, so MessageHistory and SemanticMessageHistory are both repaired at once. Add SearchIndexTest coverage for the attach (overwrite=false) and recreate (overwrite=true) paths, each asserting existing data is preserved. Co-Authored-By: Claude Opus 4.8 --- .../java/com/redis/vl/index/SearchIndex.java | 17 ++++++++-- .../com/redis/vl/index/SearchIndexTest.java | 34 +++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/redis/vl/index/SearchIndex.java b/core/src/main/java/com/redis/vl/index/SearchIndex.java index d9594b3..3366a16 100644 --- a/core/src/main/java/com/redis/vl/index/SearchIndex.java +++ b/core/src/main/java/com/redis/vl/index/SearchIndex.java @@ -947,13 +947,24 @@ public void create(boolean overwrite) { } /** - * Create the index with overwrite and drop options + * Create the index with overwrite and drop options. * - * @param overwrite Whether to overwrite an existing index + *

If the index already exists and {@code overwrite} is false, this attaches to the existing + * index and returns without modifying it. If {@code overwrite} is true, the existing index is + * dropped (optionally with its data) and recreated. + * + * @param overwrite Whether to overwrite an existing index; when false, an existing index is left + * intact and attached to * @param drop Whether to drop existing data when overwriting */ public void create(boolean overwrite, boolean drop) { - if (overwrite && exists()) { + if (exists()) { + if (!overwrite) { + // Index already exists and overwrite is false; attach to the existing index instead + // of recreating it (matches Python's create(overwrite=False) early return). + log.info("Index {} already exists, not overwriting", getName()); + return; + } delete(drop); } diff --git a/core/src/test/java/com/redis/vl/index/SearchIndexTest.java b/core/src/test/java/com/redis/vl/index/SearchIndexTest.java index 67db6f6..f5a360c 100644 --- a/core/src/test/java/com/redis/vl/index/SearchIndexTest.java +++ b/core/src/test/java/com/redis/vl/index/SearchIndexTest.java @@ -114,6 +114,40 @@ void shouldRecreateIndex() { assertThat(searchIndex.exists()).isTrue(); } + @Test + @DisplayName("Should attach to an existing index without dropping data when not overwriting") + void shouldAttachWhenOverwriteFalseAndIndexExists() { + // Given an existing index with a document + searchIndex.create(); + Map document = new HashMap<>(); + document.put("title", "Redis in Action"); + searchIndex.addDocument("doc:1", document); + + // When + searchIndex.create(false); + + // Then - the index is attached and the existing data is preserved + assertThat(searchIndex.exists()).isTrue(); + assertThat(searchIndex.getDocumentCount()).isEqualTo(1); + } + + @Test + @DisplayName("Should recreate an index without dropping data when overwriting") + void shouldRecreateWhenOverwriteTrueAndIndexExists() { + // Given an existing index with a document + searchIndex.create(); + Map document = new HashMap<>(); + document.put("title", "Redis in Action"); + searchIndex.addDocument("doc:1", document); + + // When + searchIndex.create(true); + + // Then - the index is recreated + assertThat(searchIndex.exists()).isTrue(); + assertThat(searchIndex.getDocumentCount()).isEqualTo(1); + } + @Test @DisplayName("Should add document to index") void shouldAddDocumentToIndex() {