Search code examples
spring-bootredisredisearchredis-om-spring

How do I search for a nested @Indexed key in Redis redis-om-spring?


I am trying to search for a nested @Indexed field using redis-om-spring

For some reason a find returns expected 1 entry for level 1, but returns unexpected 0 entries for level 2.

I am not sure if I can only search one level deep or if I am making a mistake.

@SpringBootApplication
@Configuration
@EnableRedisDocumentRepositories
@Slf4j
public class Application {
  
  @Autowired
  ProductRepository productRepository;

  @Bean
  CommandLineRunner loadTestData() {
    return args -> {
      productRepository.deleteAll();

      productRepository.save(new Product(new MyKey(new MyId("A_level2Key1"),"A_level1Key1"),"FirstColour"));
      productRepository.save(new Product(new MyKey(new MyId("B_level2Key1"),"B_level1Key1"),"SecondColour"));


      var byMyKeyLevel1Key = productRepository.findByMyKeyLevel1Key("A_level1Key1");
      System.out.println(byMyKeyLevel1Key.size());//returns expected 1 entry for level 1

      var byMyKeyMyIdLevel2Key = productRepository.findByMyKeyMyIdLevel2Key("A_level2Key1");
      System.out.println(byMyKeyMyIdLevel2Key.size());//returns unexpected 0 entries for level 2
    };
  }

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

}

Product

import com.redis.om.spring.annotations.Document;
import com.redis.om.spring.annotations.Indexed;
import com.redis.om.spring.annotations.Searchable;
@Slf4j
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document
public class Product {
    @Indexed
    @Id
    private MyKey myKey;

    @Searchable
    @NonNull
    private String colourDesc;
}

ProductRepository

public interface ProductRepository extends RedisDocumentRepository<Product, String> {
    List<Product> findByMyKeyLevel1Key(String level1Key);

    List<Product> findByMyKeyMyIdLevel2Key(String level2Key);
}

MyKey

import com.redis.om.spring.annotations.Indexed;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyKey {
    @Indexed
    MyId myId;
    @Indexed
    String level1Key;
}

Data in Redis:

Redis Data

FT.INFO "com.redis.om.documents.domain.ProductIdx"

From the logs, it generates searches:

FT.SEARCH "com.redis.om.documents.domain.ProductIdx" @myKey_level1Key:{A_level1Key1}

FT.SEARCH "com.redis.om.documents.domain.ProductIdx" @myKey_myId_level2Key:{A_level2Key1}

Index

I start Redis with:

docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

Solution

  • The issue is likely in the naming of the repository methods. I wrote your example as a test in the project repo, this will be using release 0.7.0 - but it should work with 0.6.x-SNAPSHOTs:

    The entity:

    @Data @AllArgsConstructor @NoArgsConstructor @Document public class Product {
      @Indexed @Id private MyKey myKey;
    
      @Searchable @NonNull private String colourDesc;
    }
    

    The first-level key:

    @Data @AllArgsConstructor @NoArgsConstructor public class MyKey {
      @Indexed MyId myId;
      @Indexed String level1Key;
    
      @Override public String toString() {
        return myId.toString();
      }
    }
    

    The second-level key:

    @Data @AllArgsConstructor @NoArgsConstructor public class MyId {
      @Indexed
      private String level2Key;
    
      @Override public String toString() {
        return level2Key;
      }
    }
    

    The repo, here's where things probably went wrong for you...

    public interface ProductRepository extends RedisDocumentRepository<Product, MyKey> {
      List<Product> findByMyKey_Level1Key(String level1Key);
    
      List<Product> findByMyKey_MyId_Level2Key(String level2Key);
    }
    

    and here's a snippet of the test class I used:

      @Autowired ProductRepository productRepository;
      @BeforeEach
      void setup() {
        productRepository.deleteAll();
    
        productRepository.save(new Product(new MyKey(new MyId("A_level2Key1"),"A_level1Key1"),"FirstColour"));
        productRepository.save(new Product(new MyKey(new MyId("B_level2Key1"),"B_level1Key1"),"SecondColour"));
      }
    
      @Test
      void testFindNestedKeyValues() {
        var byMyKeyLevel1Key = productRepository.findByMyKey_Level1Key("A_level1Key1");
        assertThat(byMyKeyLevel1Key).map(Product::getMyKey).map(MyKey::getLevel1Key).containsExactly("A_level1Key1");
    
        var byMyKeyMyIdLevel2Key = productRepository.findByMyKey_MyId_Level2Key("A_level2Key1");
        assertThat(byMyKeyMyIdLevel2Key).map(Product::getMyKey).map(MyKey::getMyId).map(MyId::getLevel2Key).containsExactly("A_level2Key1");
      }
    

    Hope that helps! Thanks for trying Redis OM Spring!