Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/content/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* xref:version-requirements.adoc[Version Requirements]
* xref:configuration.adoc[Configuration]
* xref:quickstart.adoc[Quick Start Example]
* xref:migration-guide.adoc[Migration Guide]

.Core Concepts
* xref:data-models.adoc[Redis Data Models]
Expand Down
114 changes: 114 additions & 0 deletions docs/content/modules/ROOT/pages/migration-guide.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
= Migration Guide
:navtitle: Migration Guide

This guide helps you migrate between major versions of Redis OM Spring.

== Upgrading to 1.1.0 (Spring Boot 3.5.x)

Redis OM Spring 1.1.0 introduces support for Spring Boot 3.5.x and includes important dependency upgrades.

=== Dependency Changes

[cols="1,1,1"]
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AsciiDoc table definition is missing the options="header" attribute, which is needed to properly format the first row as a header. This is inconsistent with other tables in the documentation (e.g., version-requirements.adoc line 13).

Change from:

[cols="1,1,1"]

to:

[cols="1,1,1", options="header"]

This will ensure the "Dependency | Previous Version | New Version" row is formatted as a table header.

Suggested change
[cols="1,1,1"]
[cols="1,1,1", options="header"]

Copilot uses AI. Check for mistakes.
|===
|Dependency |Previous Version |New Version

|Spring Boot
|3.4.x
|3.5.8

|Spring Data Redis
|3.4.x
|3.5.6

|Jedis
|5.2.0
|6.0.0
|===

=== Breaking Changes

==== Jedis 6.0.0 Query Escaping

**Impact:** Users writing custom RediSearch queries using Jedis directly.

Jedis 6.0.0 changed how it handles query string escaping for RediSearch queries. Multi-word search terms must now use double quotes (`"`) instead of single quotes (`'`).

**Before (Jedis 5.2.0):**
[source,java]
----
SearchOperations<String> ops = modulesOperations.opsForSearch("myIndex");
SearchResult result = ops.search(new Query("@title:'hello world'"));
----

**After (Jedis 6.0.0):**
[source,java]
----
SearchOperations<String> ops = modulesOperations.opsForSearch("myIndex");
SearchResult result = ops.search(new Query("@title:\"hello world\""));
----

**Who is affected:**

* Users implementing custom repository methods that construct `Query` objects with Jedis
* Users directly using `SearchOperations` with string-based queries containing spaces

**Who is NOT affected:**

* Users only using Redis OM Spring's built-in repository query methods
* Users using Entity Streams API
* Users using `@Query` annotations (these are handled internally by Redis OM Spring)

=== Migration Steps

. Update your `pom.xml` or `build.gradle` to use Redis OM Spring 1.1.0
. If you have custom repository implementations using Jedis `Query` objects:
.. Review all custom query strings
.. Replace single quotes with double quotes around multi-word search terms
.. Test your custom queries thoroughly

=== Example Migration

Here's a complete example of migrating a custom repository method:

.Before (1.0.x)
[source,java]
----
@Override
public Optional<MyEntity> findByTitle(String title) {
SearchOperations<String> ops = modulesOperations.opsForSearch(MyEntity.class.getName() + "Idx");
SearchResult result = ops.search(new Query("@title:'" + title + "'")); // <1>
if (result.getTotalResults() > 0) {
Document doc = result.getDocuments().get(0);
return Optional.of(ObjectUtils.documentToEntity(doc, MyEntity.class, converter));
}
return Optional.empty();
}
----
<1> Single quotes around the title value

.After (1.1.0)
[source,java]
----
@Override
public Optional<MyEntity> findByTitle(String title) {
SearchOperations<String> ops = modulesOperations.opsForSearch(MyEntity.class.getName() + "Idx");
SearchResult result = ops.search(new Query("@title:\"" + title + "\"")); // <1>
if (result.getTotalResults() > 0) {
Document doc = result.getDocuments().get(0);
return Optional.of(ObjectUtils.documentToEntity(doc, MyEntity.class, converter));
}
return Optional.empty();
}
----
<1> Double quotes around the title value

=== Testing Your Migration

After upgrading, ensure you test:

. All custom repository methods that use Jedis `Query` objects
. Any direct usage of `SearchOperations`
. Integration tests with multi-word search terms

The Redis OM Spring test suite includes comprehensive tests for query escaping. Refer to the test examples in the repository for best practices.
22 changes: 11 additions & 11 deletions docs/content/modules/ROOT/pages/version-requirements.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ Redis OM Spring has the following version requirements:

|Spring Boot
|3.3.x
|3.4.x or 3.5.x
|Built with Spring Boot 3.4.5
|3.5.8 or later
|Built with Spring Boot 3.5.8

|Spring Data Redis
|3.4.1
|3.4.5 or later
|3.5.6 or later
|Aligned with Spring Boot version

|Spring Framework
Expand All @@ -33,9 +33,9 @@ Redis OM Spring has the following version requirements:
|Transitive via Spring Boot

|Jedis
|5.2.0
|5.2.0 or later
|Redis Java client
|6.0.0
|6.0.0 or later
|Redis Java client. **Breaking change in 6.0.0** - see xref:migration-guide.adoc[Migration Guide]

|Java
|17
Expand All @@ -58,11 +58,11 @@ Redis OM Spring follows an **N-2 support policy** for Spring Boot versions:

=== Example

As of Redis OM Spring 1.0.0-RC4 (August 2025):
As of Redis OM Spring 1.1.0 (November 2025):

* **Built with**: Spring Boot 3.4.8
* **Built with**: Spring Boot 3.5.8
* **Minimum supported**: Spring Boot 3.3.x (OSS support until June 2025)
* **Recommended**: Spring Boot 3.4.x or 3.5.x
* **Recommended**: Spring Boot 3.5.x

[WARNING]
====
Expand Down Expand Up @@ -146,8 +146,8 @@ When using Redis OM Spring, ensure your dependencies are aligned:
[source,xml]
----
<properties>
<spring-boot.version>3.4.5</spring-boot.version>
<redis-om-spring.version>1.0.0-RC4</redis-om-spring.version>
<spring-boot.version>3.5.8</spring-boot.version>
<redis-om-spring.version>1.1.0</redis-om-spring.version>
</properties>

<dependencies>
Expand Down
8 changes: 4 additions & 4 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
version = 1.0.5
version = 1.1.0

group = com.redis.om
description = Redis OM Spring provides powerful repository and custom object-mapping abstractions built on top of the powerful Spring Data Redis (SDR) framework.

springBootVersion = 3.4.12
springBootVersion = 3.5.8
springDependencyVersion = 1.1.7

testcontainersRedisVersion = 2.2.4
sdrVersion = 3.4.12
jedisVersion = 5.2.0
sdrVersion = 3.5.6
jedisVersion = 6.0.0
cdi = 2.0-PFD
autoServiceVersion = 1.1.1
guavaVersion = 33.4.8-jre
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,18 +202,18 @@ private static FieldType getRedisFieldTypeForMapValue(Class<?> fieldType) {
* <li>Initializes pagination, sorting, and projection capabilities</li>
* </ul>
*
* @param queryMethod the repository method metadata containing signature and return type information
* @param metadata the repository metadata providing domain type and interface details
* @param indexer the RediSearch indexer for managing search indexes and field mappings
* @param evaluationContextProvider Spring Data's context provider for SpEL expression evaluation
* @param keyValueOperations low-level Redis key-value operations template
* @param rmo Redis modules operations providing access to RediSearch, RedisJSON, and
* probabilistic data structures
* @param queryCreator the query creator class for building Redis queries (currently unused but reserved
* for future extensibility)
* @param gsonBuilder pre-configured Gson builder for JSON serialization/deserialization of Redis
* documents
* @param redisOMProperties configuration properties for Redis OM Spring behavior and defaults
* @param queryMethod the repository method metadata containing signature and return type information
* @param metadata the repository metadata providing domain type and interface details
* @param indexer the RediSearch indexer for managing search indexes and field mappings
* @param valueExpressionDelegate Spring Data's delegate for value expression evaluation in query methods
* @param keyValueOperations low-level Redis key-value operations template
* @param rmo Redis modules operations providing access to RediSearch, RedisJSON, and
* probabilistic data structures
* @param queryCreator the query creator class for building Redis queries (currently unused but reserved
* for future extensibility)
* @param gsonBuilder pre-configured Gson builder for JSON serialization/deserialization of Redis
* documents
* @param redisOMProperties configuration properties for Redis OM Spring behavior and defaults
*
*/
@SuppressWarnings(
Expand All @@ -223,7 +223,7 @@ public RediSearchQuery(//
QueryMethod queryMethod, //
RepositoryMetadata metadata, //
RediSearchIndexer indexer, //
QueryMethodEvaluationContextProvider evaluationContextProvider, //
org.springframework.data.repository.query.ValueExpressionDelegate valueExpressionDelegate, //
KeyValueOperations keyValueOperations, //
RedisModulesOperations<?> rmo, //
Class<? extends AbstractQueryCreator<?, ?>> queryCreator, //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,17 +161,17 @@ public class RedisEnhancedQuery implements RepositoryQuery {
* <li>Set up parameter binding for safe query execution</li>
* </ul>
*
* @param queryMethod the Spring Data query method metadata containing method signature,
* return type, and parameter information
* @param metadata the repository metadata providing domain type and interface information
* @param indexer the RediSearch indexer for index name resolution and field mapping
* @param evaluationContextProvider context provider for SpEL expression evaluation
* @param keyValueOperations Spring Data KeyValue operations for basic entity operations
* @param redisOperations low-level Redis operations for direct Redis access
* @param rmo Redis modules operations providing access to RediSearch, RedisJSON, and other
* modules
* @param queryCreator the query creator class for custom query construction (currently unused)
* @param redisOMProperties configuration properties for Redis OM behavior and defaults
* @param queryMethod the Spring Data query method metadata containing method signature,
* return type, and parameter information
* @param metadata the repository metadata providing domain type and interface information
* @param indexer the RediSearch indexer for index name resolution and field mapping
* @param valueExpressionDelegate Spring Data's delegate for value expression evaluation in query methods
* @param keyValueOperations Spring Data KeyValue operations for basic entity operations
* @param redisOperations low-level Redis operations for direct Redis access
* @param rmo Redis modules operations providing access to RediSearch, RedisJSON, and other
* modules
* @param queryCreator the query creator class for custom query construction (currently unused)
* @param redisOMProperties configuration properties for Redis OM behavior and defaults
*
*/
@SuppressWarnings(
Expand All @@ -180,7 +180,7 @@ public class RedisEnhancedQuery implements RepositoryQuery {
public RedisEnhancedQuery(QueryMethod queryMethod, //
RepositoryMetadata metadata, //
RediSearchIndexer indexer, //
QueryMethodEvaluationContextProvider evaluationContextProvider, //
org.springframework.data.repository.query.ValueExpressionDelegate valueExpressionDelegate, //
KeyValueOperations keyValueOperations, //
RedisOperations<?, ?> redisOperations, //
RedisModulesOperations<?> rmo, //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -222,9 +222,9 @@ protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {

@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key, //
QueryMethodEvaluationContextProvider evaluationContextProvider //
ValueExpressionDelegate valueExpressionDelegate //
) {
return Optional.of(new RediSearchQueryLookupStrategy(evaluationContextProvider, //
return Optional.of(new RediSearchQueryLookupStrategy(valueExpressionDelegate, //
this.keyValueOperations, //
this.rmo, //
this.indexer, //
Expand All @@ -237,7 +237,7 @@ protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key
}

private record RediSearchQueryLookupStrategy( //
QueryMethodEvaluationContextProvider evaluationContextProvider, //
ValueExpressionDelegate valueExpressionDelegate, //
KeyValueOperations keyValueOperations, //
RedisModulesOperations<?> rmo, //
RediSearchIndexer indexer, //
Expand All @@ -247,12 +247,12 @@ private record RediSearchQueryLookupStrategy( //
GsonBuilder gsonBuilder) implements QueryLookupStrategy {

/**
* @param evaluationContextProvider
* @param valueExpressionDelegate
* @param keyValueOperations
* @param queryCreator
*/
private RediSearchQueryLookupStrategy {
Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!");
Assert.notNull(valueExpressionDelegate, "ValueExpressionDelegate must not be null!");
Assert.notNull(keyValueOperations, "KeyValueOperations must not be null!");
Assert.notNull(rmo, "RedisModulesOperations must not be null!");
Assert.notNull(indexer, "RediSearchIndexer must not be null!");
Expand Down Expand Up @@ -288,23 +288,23 @@ public RepositoryQuery resolveQuery( //
QueryMethod.class, //
RepositoryMetadata.class, //
RediSearchIndexer.class, //
QueryMethodEvaluationContextProvider.class, //
ValueExpressionDelegate.class, //
KeyValueOperations.class, //
RedisModulesOperations.class, //
Class.class, //
GsonBuilder.class, //
RedisOMProperties.class);

Assert.state(constructor != null, String.format(
"Constructor %s(QueryMethod, RepositoryMetadata, RediSearchIndexer, EvaluationContextProvider, KeyValueOperations, RedisModulesOperations, Class, GsonBuilder, RedisOMSpringProperties) not available!",
"Constructor %s(QueryMethod, RepositoryMetadata, RediSearchIndexer, ValueExpressionDelegate, KeyValueOperations, RedisModulesOperations, Class, GsonBuilder, RedisOMSpringProperties) not available!",
ClassUtils.getShortName(this.repositoryQueryType)));

return BeanUtils.instantiateClass( //
constructor, //
queryMethod, //
metadata, //
indexer, //
evaluationContextProvider, //
valueExpressionDelegate, //
this.keyValueOperations, //
this.rmo, //
this.queryCreator, //
Expand Down
Loading
Loading