Skip to content

Commit c7a9e4b

Browse files
committed
Fix MongoDB index creation API compatibility across Spring Data MongoDB versions
Use reflection to handle API differences between Spring Data MongoDB versions: - Spring Data MongoDB 4.2.x - 4.4.x: only ensureIndex() is available - Spring Data MongoDB 4.5.x+: createIndex() is the new API, ensureIndex is deprecated This fix ensures compatibility with both Spring Boot 3.4.x (MongoDB 4.4.x) and Spring Boot 3.5.x (MongoDB 4.5.x+), which are both officially supported versions. Fixes #4884 Signed-off-by: shaojie <741047428@qq.com>
1 parent 10bc0a7 commit c7a9e4b

File tree

1 file changed

+73
-13
lines changed
  • auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-mongodb/src/main/java/org/springframework/ai/model/chat/memory/repository/mongo/autoconfigure

1 file changed

+73
-13
lines changed

auto-configurations/models/chat/memory/repository/spring-ai-autoconfigure-model-chat-memory-repository-mongodb/src/main/java/org/springframework/ai/model/chat/memory/repository/mongo/autoconfigure/MongoChatMemoryIndexCreatorAutoConfiguration.java

Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.ai.model.chat.memory.repository.mongo.autoconfigure;
1818

19+
import java.lang.reflect.Method;
20+
1921
import org.slf4j.Logger;
2022
import org.slf4j.LoggerFactory;
2123

@@ -27,11 +29,13 @@
2729
import org.springframework.data.domain.Sort;
2830
import org.springframework.data.mongodb.core.MongoTemplate;
2931
import org.springframework.data.mongodb.core.index.Index;
32+
import org.springframework.data.mongodb.core.index.IndexDefinition;
33+
import org.springframework.data.mongodb.core.index.IndexOperations;
3034

3135
/**
3236
* Class responsible for creating proper MongoDB indices for the ChatMemory. Creates a
33-
* main index on the conversationId and timestamp fields, and a TTL index on the timestamp
34-
* field if the TTL is set in properties.
37+
* main index on the conversationId and timestamp fields, and a TTL index on the
38+
* timestamp field if the TTL is set in properties.
3539
*
3640
* @author Łukasz Jernaś
3741
* @see MongoChatMemoryProperties
@@ -41,42 +45,98 @@
4145
@ConditionalOnProperty(value = "spring.ai.chat.memory.repository.mongo.create-indices", havingValue = "true")
4246
public class MongoChatMemoryIndexCreatorAutoConfiguration {
4347

44-
private static final Logger logger = LoggerFactory.getLogger(MongoChatMemoryIndexCreatorAutoConfiguration.class);
48+
private static final Logger logger = LoggerFactory
49+
.getLogger(MongoChatMemoryIndexCreatorAutoConfiguration.class);
4550

4651
private final MongoTemplate mongoTemplate;
4752

4853
private final MongoChatMemoryProperties mongoChatMemoryProperties;
4954

50-
public MongoChatMemoryIndexCreatorAutoConfiguration(MongoTemplate mongoTemplate,
51-
MongoChatMemoryProperties mongoChatMemoryProperties) {
55+
public MongoChatMemoryIndexCreatorAutoConfiguration(final MongoTemplate mongoTemplate,
56+
final MongoChatMemoryProperties mongoChatMemoryProperties) {
5257
this.mongoTemplate = mongoTemplate;
5358
this.mongoChatMemoryProperties = mongoChatMemoryProperties;
5459
}
5560

61+
/**
62+
* Initializes MongoDB indices after application context refresh.
63+
*/
5664
@EventListener(ContextRefreshedEvent.class)
5765
public void initIndicesAfterStartup() {
5866
logger.info("Creating MongoDB indices for ChatMemory");
5967
// Create a main index
60-
this.mongoTemplate.indexOps(Conversation.class)
61-
.createIndex(new Index().on("conversationId", Sort.Direction.ASC).on("timestamp", Sort.Direction.DESC));
62-
68+
createMainIndex();
6369
createOrUpdateTtlIndex();
6470
}
6571

72+
private void createMainIndex() {
73+
var indexOps = this.mongoTemplate.indexOps(Conversation.class);
74+
var index = new Index().on("conversationId", Sort.Direction.ASC)
75+
.on("timestamp", Sort.Direction.DESC);
76+
77+
// Use reflection to handle API differences across Spring Data MongoDB versions
78+
createIndexSafely(indexOps, index);
79+
}
80+
6681
private void createOrUpdateTtlIndex() {
6782
if (!this.mongoChatMemoryProperties.getTtl().isZero()) {
83+
var indexOps = this.mongoTemplate.indexOps(Conversation.class);
6884
// Check for existing TTL index
69-
this.mongoTemplate.indexOps(Conversation.class).getIndexInfo().forEach(idx -> {
85+
indexOps.getIndexInfo().forEach(idx -> {
7086
if (idx.getExpireAfter().isPresent()
71-
&& !idx.getExpireAfter().get().equals(this.mongoChatMemoryProperties.getTtl())) {
87+
&& !idx.getExpireAfter().get()
88+
.equals(this.mongoChatMemoryProperties.getTtl())) {
7289
logger.warn("Dropping existing TTL index, because TTL is different");
73-
this.mongoTemplate.indexOps(Conversation.class).dropIndex(idx.getName());
90+
indexOps.dropIndex(idx.getName());
7491
}
7592
});
76-
this.mongoTemplate.indexOps(Conversation.class)
77-
.createIndex(new Index().on("timestamp", Sort.Direction.ASC)
93+
// Use reflection to handle API differences across Spring Data MongoDB
94+
// versions
95+
createIndexSafely(indexOps, new Index().on("timestamp", Sort.Direction.ASC)
7896
.expire(this.mongoChatMemoryProperties.getTtl()));
7997
}
8098
}
8199

100+
/**
101+
* Creates an index using reflection to handle API changes across different Spring
102+
* Data MongoDB versions:
103+
* <ul>
104+
* <li>Spring Data MongoDB 4.2.x - 4.4.x: only {@code ensureIndex(IndexDefinition)}
105+
* is available.</li>
106+
* <li>Spring Data MongoDB 4.5.x+: {@code createIndex(IndexDefinition)} is the new
107+
* API, {@code ensureIndex} is deprecated.</li>
108+
* </ul>
109+
* @param indexOps the IndexOperations instance
110+
* @param index the index definition
111+
* @throws IllegalStateException if neither method is available or invocation fails
112+
*/
113+
private void createIndexSafely(final IndexOperations indexOps, final IndexDefinition index) {
114+
try {
115+
// Try new API (Spring Data MongoDB 4.5.x+)
116+
Method method = IndexOperations.class.getMethod("createIndex", IndexDefinition.class);
117+
method.invoke(indexOps, index);
118+
logger.debug("Created index using createIndex() method");
119+
}
120+
catch (NoSuchMethodException createIndexNotFound) {
121+
// Fall back to old API (Spring Data MongoDB 4.2.x - 4.4.x)
122+
try {
123+
Method method = IndexOperations.class.getMethod("ensureIndex", IndexDefinition.class);
124+
method.invoke(indexOps, index);
125+
logger.debug("Created index using ensureIndex() method");
126+
}
127+
catch (NoSuchMethodException ensureIndexNotFound) {
128+
throw new IllegalStateException(
129+
"Neither createIndex() nor ensureIndex() method found on IndexOperations. "
130+
+ "This may indicate an unsupported Spring Data MongoDB version.",
131+
ensureIndexNotFound);
132+
}
133+
catch (ReflectiveOperationException ex) {
134+
throw new IllegalStateException("Failed to invoke ensureIndex() method", ex);
135+
}
136+
}
137+
catch (ReflectiveOperationException ex) {
138+
throw new IllegalStateException("Failed to invoke createIndex() method", ex);
139+
}
140+
}
141+
82142
}

0 commit comments

Comments
 (0)