Spring Data Redis extensions for better search, documents models, and more

Overview


Redis OM

Object Mapping (and more) for Redis!


Redis OM Spring extends Spring Data Redis to take full advantage of the power of Redis.

Project Stage Snapshot Issues License
Project stage Snapshots Percentage of issues still open Average time to resolve an issue License
Table of contents

💡 Why Redis OM?

The Redis OM family of projects aim is to provide high-level abstractions idiomatically implemented for your language/platform of choice. We currently cater to the Node, Python, .Net and Spring communities.

🍀 Redis OM Spring

Redis OM Spring provides powerful repository and custom object-mapping abstractions built on top of the powerful Spring Data Redis (SDR) framework.

This preview release provides all of SDRs capabilities plus:

  • @Document annotation to map Spring Data models to Redis JSON documents
  • Enhances SDR's @RedisHash via @EnableRedisEnhancedRepositories to:
    • uses Redis' native search engine (RediSearch) for secondary indexing
    • uses ULID for @Id annotated fields
  • RedisDocumentRepository with automatic implementation of Repository interfaces for complex querying capabilities using @EnableRedisDocumentRepositories
  • Declarative Search Indices via @Indexable
  • Full-text Search Indices via @Searchable
  • @Bloom annotation to determine very fast, with and with high degree of certainty, whether a value is in a collection.

Note: Redis OM Spring currently works only with Jedis.

🏁 Getting Started

Here is a quick teaser of an application using Redis OM Spring to map a Spring Data model using a RedisJSON document.

🚀 Launch Redis

Redis OM Spring relies on the power of the RediSearch and RedisJSON modules. We have provided a docker compose YAML file for you to quickly get started. To launch the docker compose application, on the command line (or via Docker Desktop), clone this repository and run (from the root folder):

docker compose up

The SpringBoot App

Use the @EnableRedisDocumentRepositories annotation to scan for @Document annotated Spring models, Inject repositories beans implementing RedisDocumentRepository which you can use for CRUD operations and custom queries (all by declaring Spring Data Query Interfaces):

package com.redis.om.documents;

import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.geo.Point;

import com.redis.om.documents.domain.Company;
import com.redis.om.documents.repositories.CompanyRepository;

@SpringBootApplication
@Configuration
@EnableRedisDocumentRepositories(basePackages = "com.redis.om.documents.*")
public class RomsDocumentsApplication {

  @Autowired
  CompanyRepository companyRepo;

  @Bean
  CommandLineRunner loadTestData() {
    return args -> {
      // remove all companies
      companyRepo.deleteAll();

      // Create a couple of `Company` domain entities
      Company redis = Company.of(
        "Redis", "https://redis.com", new Point(-122.066540, 37.377690), 526, 2011 //
      );
      redis.setTags(Set.of("fast", "scalable", "reliable"));

      Company microsoft = Company.of(
        "Microsoft", "https://microsoft.com", new Point(-122.124500, 47.640160), 182268, 1975 //
      );
      microsoft.setTags(Set.of("innovative", "reliable"));

      // save companies to the database
      companyRepo.save(redis);
      companyRepo.save(microsoft);
    };
  }

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

The Mapped Model

Like many other Spring Data projects, an annotation at the class level determines how instances of the class are persisted. Redis OM Spring provides the @Document annotation to persist models as JSON documents using RedisJSON:

package com.redis.om.documents.domain;

import java.util.HashSet;
import java.util.Set;
import org.springframework.data.annotation.Id;
import org.springframework.data.geo.Point;
import com.redis.om.spring.annotations.Document;
import com.redis.om.spring.annotations.Searchable;
import lombok.*;

@Data
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Document
public class Company {
  @Id private String id;
  @Searchable private String name;
  @Indexed private Point location;
  @Indexed private Set<String> tags = new HashSet<String>();
  @Indexed private Integer numberOfEmployees;
  @Indexed private Integer yearFounded;
  private String url;
  private boolean publiclyListed;

  // ...
}

Redis OM Spring, replaces the conventional UUID primary key strategy generation with a ULID (Universally Unique Lexicographically Sortable Identifier) which is faster to generate and easier on the eyes.

The Repository

Redis OM Spring data repository's goal, like other Spring Data repositories, is to significantly reduce the amount of boilerplate code required to implement data access. Simply create a Java interface that extends RedisDocumentRepository that takes the domain class to manage as well as the ID type of the domain class as type arguments. RedisDocumentRepository extends Spring Data's PagingAndSortingRepository.

Declare query methods on the interface. You can both, expose CRUD methods or create declarations for complex queries that Redis OM Spring will fullfil at runtime:

package com.redis.om.documents.repositories;

import java.util.*;

import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.repository.query.Param;

import com.redis.om.documents.domain.Company;
import com.redis.om.spring.annotations.Query;
import com.redis.om.spring.repository.RedisDocumentRepository;

public interface CompanyRepository extends RedisDocumentRepository<Company, String> {
  // find one by property
  Optional<Company> findOneByName(String name);

  // geospatial query
  Iterable<Company> findByLocationNear(Point point, Distance distance);

  // find by tag field, using JRediSearch "native" annotation
  @Query("@tags:{$tags}")
  Iterable<Company> findByTags(@Param("tags") Set<String> tags);

  // find by numeric property
  Iterable<Company> findByNumberOfEmployees(int noe);

  // find by numeric property range
  Iterable<Company> findByNumberOfEmployeesBetween(int noeGT, int noeLT);

  // starting with/ending with
  Iterable<Company> findByNameStartingWith(String prefix);
}

The repository proxy has two ways to derive a store-specific query from the method name:

  • By deriving the query from the method name directly.
  • By using a manually defined query using the @Query or @Aggregation annotations.

💻 Maven configuration

Official Releases

None Yet

Snapshots

  <repositories>
    <repository>
      <id>snapshots-repo</id>
      <url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
    </repository>
  </repositories>

and

<dependency>
  <groupId>com.redis.om</groupId>
  <artifactId>redis-om-spring</artifactId>
  <version>${version}</version>
</dependency>

Ready to learn more? Check out the getting started guide.

📚 Documentation

The Redis OM documentation is available here.

Demos

Basic JSON Mapping and Querying

  • roms-documents:
    • Simple API example of @Document mapping, Spring Repositories and Querying.
    • Run with ./mvnw install -Dmaven.test.skip && ./mvnw spring-boot:run -pl demos/roms-documents
  • rds-hashes:
    • Simple API example of @RedisHash, enhanced secondary indices and querying.
    • Run with ./mvnw install -Dmaven.test.skip && ./mvnw spring-boot:run -pl demos/roms-hashes

⛏️ Troubleshooting

If you run into trouble or have any questions, we're here to help!

First, check the FAQ. If you don't find the answer there, hit us up on the Redis Discord Server.

So How Do You Get RediSearch and RedisJSON?

Some advanced features of Redis OM rely on core features from two source available Redis modules: RediSearch and RedisJSON.

You can run these modules in your self-hosted Redis deployment, or you can use Redis Enterprise, which includes both modules.

To learn more, read our documentation.

❤️ Contributing

We'd love your contributions!

Bug reports are especially helpful at this stage of the project. You can open a bug report on GitHub.

You can also contribute documentation -- or just let us know if something needs more detail. Open an issue on GitHub to get started.

🧑‍🤝‍🧑 Sibling Projects

📝 License

Redis OM uses the BSD 3-Clause license.

Comments
  • NPE when querying an expired (TTL) JSON document

    NPE when querying an expired (TTL) JSON document

    I think this a bug related to the TimeToLive but I may be wrong, when a query looks for an element of a key, in my case a hash compute with some data, that has been erased by the time to live, there is a null pointer exception thrown:

    java.lang.NullPointerException at com.redis.om.spring.repository.query.RediSearchQuery.executeQuery(RediSearchQuery.java:344) at com.redis.om.spring.repository.query.RediSearchQuery.execute(RediSearchQuery.java:267) at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137) at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:159) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) at com.sun.proxy.$Proxy174.findByHash(Unknown Source) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) at com.sun.proxy.$Proxy174.findByHash(Unknown Source)

    Here is the class:

    @document(value = "XXXXXX", timeToLive = 10 * 60) @DaTa public class XXXXXX {

    @Id private String id;

    @Indexed @NonNull private String hash; }

    opened by Aurh1l 17
  • Enable**Repositories annotation does not discover Models if not as part of main app

    Enable**Repositories annotation does not discover Models if not as part of main app

    Hi

    I tried to use search functionality:

    public interface UserRepository 
    		extends RedisDocumentRepository<User, Integer> {
    	
    	Optional<User> findByName(String name);
    }
    

    But when I call findByName method I receive an exception:

    
    redis.clients.jedis.exceptions.JedisDataException: demo.model.UserIdx: no such index
    	at redis.clients.jedis.Protocol.processError(Protocol.java:142) ~[jedis-3.8.0.jar:na]
    	at redis.clients.jedis.Protocol.process(Protocol.java:176) ~[jedis-3.8.0.jar:na]
    	at redis.clients.jedis.Protocol.read(Protocol.java:230) ~[jedis-3.8.0.jar:na]
    

    My document:

    @Getter
    @Setter
    
    @Document("users")
    public class User {
    
    	@Id
    	private int id;
    
    	@Indexed
    	private String name;
    
    	private int age;
    }
    

    And configuration:

    @Configuration
    @EnableRedisDocumentRepositories
    public class RedisConfig {
    
    }
    
    bug 
    opened by sergey-morenets 12
  • Repositories init fail

    Repositories init fail

    I try to use it just like what /demos/roms-documents shows.

    my redis-om version is 0.3.0-SNAPSHOT and I use spring boot 2.6.3

    org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'teacherRepository' defined in com.xxxxxx.domain.repository.school.TeacherRepository defined in @EnableRedisDocumentRepositories declared on DemoAService: Unsatisfied dependency expressed through constructor parameter 1: Ambiguous argument values for parameter of type [com.redis.om.spring.ops.RedisModulesOperations] - did you specify the correct bean references as arguments? at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:787) ~[spring-beans-5.3.14.jar:5.3.14] at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229) ~[spring-beans-5.3.14.jar:5.3.14] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1372) ~[spring-beans-5.3.14.jar:5.3.14] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1222) ~[spring-beans-5.3.14.jar:5.3.14] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck(AbstractAutowireCapableBeanFactory.java:1027) ~[spring-beans-5.3.14.jar:5.3.14] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:907) ~[spring-beans-5.3.14.jar:5.3.14] at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:637) ~[spring-beans-5.3.14.jar:5.3.14] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:583) ~[spring-beans-5.3.14.jar:5.3.14] at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:542) ~[spring-beans-5.3.14.jar:5.3.14] at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:536) ~[spring-beans-5.3.14.jar:5.3.14] at org.springdoc.core.SpringdocBeanFactoryConfigurer.initBeanFactoryPostProcessor(SpringdocBeanFactoryConfigurer.java:87) ~[springdoc-openapi-common-1.6.5.jar:1.6.5] at org.springdoc.core.SpringdocBeanFactoryConfigurer.postProcessBeanFactory(SpringdocBeanFactoryConfigurer.java:78) ~[springdoc-openapi-common-1.6.5.jar:1.6.5] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:325) ~[spring-context-5.3.14.jar:5.3.14] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:198) ~[spring-context-5.3.14.jar:5.3.14] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) ~[spring-context-5.3.14.jar:5.3.14] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) ~[spring-context-5.3.14.jar:5.3.14] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.3.jar:2.6.3] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[spring-boot-2.6.3.jar:2.6.3] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:414) ~[spring-boot-2.6.3.jar:2.6.3] at org.springframework.boot.SpringApplication.run(SpringApplication.java:302) ~[spring-boot-2.6.3.jar:2.6.3] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.6.3.jar:2.6.3] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.6.3.jar:2.6.3] at com.mininglamp.tech.myai.pvt.traffic.DemoAService.main(DemoAService.java:15) ~[classes/:?]

    Disconnected from the target VM, address: '127.0.0.1:64483', transport: 'socket'

    Process finished with exit code 1

    opened by luxia7758 12
  • How to implement IN Clause

    How to implement IN Clause

    Hi, I have a list of employees in my data set. Please let me know how do I write a query to select like

    select employees where employeeId in (1,2,3)

    Appreciate your help

    thanks

    Jay

    opened by shannonantony 10
  • TTL not used

    TTL not used

    Hi,

    I'm the using the TTL on a Redis Document like that @Document(value = "XXXXX", timeToLive = 60 * 60 * 24 * 7), it works for some keys but after a while the new keys have a TTL equals to -1.

    duplicate 
    opened by Aurh1l 9
  • "findBy...Between..()", findBy..GreaterThanEqual() etc. Methods throw NoSuchMethodError

    Redis-om-spring version 0.6.0

    As an extension to the example of Search on Redis Hashes(in this repo), I added a long field 'startUTC' that is indexed. None of the findBy methods work - findByStartUTCBetween() , findByStartUTCGreaterThanEqual(), findByStartUTCGreaterThanEqualAndStartUTCLessThanEqual() work. On using FT.SEARCH on the same index with the due params gives correct result for all of them. The stack trace on searching using OM is as follows:

    java.lang.NoSuchMethodError: 'org.springframework.data.mapping.InstanceCreatorMetadata org.springframework.data.redis.core.mapping.RedisPersistentEntity.getInstanceCreatorMetadata()' at com.redis.om.spring.convert.MappingRedisOMConverter.lambda$doReadInternal$0(MappingRedisOMConverter.java:198) ~[redis-om-spring-0.6.0.jar:na] at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:360) ~[spring-data-commons-2.6.3.jar:2.6.3] at com.redis.om.spring.convert.MappingRedisOMConverter.doReadInternal(MappingRedisOMConverter.java:196) ~[redis-om-spring-0.6.0.jar:na] at com.redis.om.spring.convert.MappingRedisOMConverter.read(MappingRedisOMConverter.java:142) ~[redis-om-spring-0.6.0.jar:na] at com.redis.om.spring.util.ObjectUtils.documentToObject(ObjectUtils.java:236) ~[redis-om-spring-0.6.0.jar:na] at com.redis.om.spring.repository.query.RedisEnhancedQuery.lambda$executeQuery$5(RedisEnhancedQuery.java:353) ~[redis-om-spring-0.6.0.jar:na] at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na] at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) ~[na:na] at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[na:na] at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na] at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) ~[na:na] at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na] at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) ~[na:na] at com.redis.om.spring.repository.query.RedisEnhancedQuery.executeQuery(RedisEnhancedQuery.java:354) ~[redis-om-spring-0.6.0.jar:na] at com.redis.om.spring.repository.query.RedisEnhancedQuery.execute(RedisEnhancedQuery.java:269) ~[redis-om-spring-0.6.0.jar:na] at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137) ~[spring-data-commons-2.6.3.jar:2.6.3] at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121) ~[spring-data-commons-2.6.3.jar:2.6.3] at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:159) ~[spring-data-commons-2.6.3.jar:2.6.3] at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:138) ~[spring-data-commons-2.6.3.jar:2.6.3] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.18.jar:5.3.18] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.18.jar:5.3.18] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.18.jar:5.3.18] at jdk.proxy3/jdk.proxy3.$Proxy85.findByStartUTCGreaterThanEqualAndStartUTCLessThanEqual(Unknown Source) ~[na:na]

    opened by AnujaK 8
  • Adding Redis om spring dependency in spring cloud stream project gives startup error

    Adding Redis om spring dependency in spring cloud stream project gives startup error

    Hello,

    I have a spring cloud stream project with kafka streams binder dependency - no issues at startup. Next I add redis om spring dependency 0.6.0, add a model object annotate it with @Document and annotate main class with @EnableRedisDocumentRepositories. Now I get below error at startup

    Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0 at com.redis.om.spring.RedisModulesConfiguration.getBeanDefinitionsFor(RedisModulesConfiguration.java:392)

    Apologies for any mistake in posting this issue with details - new to this, so please guide me.

    bug 
    opened by praks84 8
  • About updateField() method

    About updateField() method

    I am trying to update field without retrieving the whole document by using updateField().

    User u = User.builder().number("test").password("test01").build();
    User user2 = userRepository.save(u);
    userRepository.updateField(user2, new TextTagField<User, String>(User.class.getDeclaredField("password"), true),  "101010");
    

    When running the first time, the data can be saved and updated. However, if running the same code, trying to save and update a new document again. I got this, the fully-qualified class name is missing.

    "JSON.SET" "null01GGZDH1152DTNHGYP5X4Z23N9" "$.password" "\"101010\""
    bug 
    opened by bwhyman 7
  • worked with springdoc

    worked with springdoc

    provided java bean @Bean public GroupedOpenApi publicApi() { return GroupedOpenApi.builder() .packagesToScan("com.universal") .group("universal-uaa") .pathsToMatch("/**") .build(); }

    then org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'redisSessionTokenRepository' defined in com.universal.services.uaa.opaque.manager.dao.redis.RedisSessionTokenRepository defined in @EnableRedisDocumentRepositories declared on UaaServerApplication: Unsatisfied dependency expressed through constructor parameter 1: Ambiguous argument values for parameter of type [com.redis.om.spring.ops.RedisModulesOperations] - did you specify the correct bean references as arguments? at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:787) ~[spring-beans-5.3.16.jar:5.3.16] at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229) ~[spring-beans-5.3.16.jar:5.3.16] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1372) ~[spring-beans-5.3.16.jar:5.3.16] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1222) ~[spring-beans-5.3.16.jar:5.3.16] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck(AbstractAutowireCapableBeanFactory.java:1027) ~[spring-beans-5.3.16.jar:5.3.16] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:907) ~[spring-beans-5.3.16.jar:5.3.16] at org.springframework.beans.factory.support.AbstractBeanFactory.isTypeMatch(AbstractBeanFactory.java:637) ~[spring-beans-5.3.16.jar:5.3.16] at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:583) ~[spring-beans-5.3.16.jar:5.3.16] at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:542) ~[spring-beans-5.3.16.jar:5.3.16] at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:536) ~[spring-beans-5.3.16.jar:5.3.16] at org.springdoc.core.SpringdocBeanFactoryConfigurer.initBeanFactoryPostProcessor(SpringdocBeanFactoryConfigurer.java:87) ~[springdoc-openapi-common-1.6.6.jar:1.6.6] at org.springdoc.core.SpringdocBeanFactoryConfigurer.postProcessBeanFactory(SpringdocBeanFactoryConfigurer.java:78) ~[springdoc-openapi-common-1.6.6.jar:1.6.6] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:325) ~[spring-context-5.3.16.jar:5.3.16] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:198) ~[spring-context-5.3.16.jar:5.3.16] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) ~[spring-context-5.3.16.jar:5.3.16] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) ~[spring-context-5.3.16.jar:5.3.16] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.4.jar:2.6.4] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740) ~[spring-boot-2.6.4.jar:2.6.4] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415) ~[spring-boot-2.6.4.jar:2.6.4] at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) ~[spring-boot-2.6.4.jar:2.6.4] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.4.jar:2.6.4] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.4.jar:2.6.4] at com.universal.services.uaa.UaaServerApplication.main(UaaServerApplication.java:29) ~[classes/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na] at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.6.4.jar:2.6.4]

    why ?

    opened by GeWiier 7
  • Add limit/offset support to @Query

    Add limit/offset support to @Query

    The current @Query annotation only supports value and returnFields. By default redis search only return 10 results. Could we have support with limit filed in the @Query annotation?

    enhancement question 
    opened by anyili 6
  • com.google.gson.stream.MalformedJsonException for EntityStream

    com.google.gson.stream.MalformedJsonException for EntityStream

    I was following the example to write query using EntityStream. Something like

    List<String> results =  entityStream
                    .of(SomeDocument.class)
                    .filter(SomeDocument$.NAME.eq(name))
                    .limit(1000
                    ).map(SomeDocument$.DESCRIPTION)
                    .collect(Collectors.toList());
            results.stream().forEach(System.out::println);
    

    However, I always got com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 12 path $ Any ideas? Thanks

    opened by anyili 6
  • feature: completes @Aggregation annotation features (resolves gh-79, gh-88)

    feature: completes @Aggregation annotation features (resolves gh-79, gh-88)

    Allow for the creation of repository methods backed by RediSearch Aggregations for JSON and Hashes indices. See

    • src/test/java/com/redis/om/spring/annotations/document/AggregationAnnotationTest.java
    • src/test/java/com/redis/om/spring/annotations/hash/AggregationAnnotationTest.java

    for usage examples.

    Here's a example:

      /**
       * <pre>
       * "FT.AGGREGATE" "com.redis.om.spring.annotations.document.fixtures.GameIdx" "*"
       *   "GROUPBY" "1" "@brand"
       *   "REDUCE" "QUANTILE" "2" "@price" "0.5" "AS" "q50"
       *   "REDUCE" "QUANTILE" "2" "@price" "0.9" "AS" "q90"
       *   "REDUCE" "QUANTILE" "2" "@price" "0.95" "AS" "q95"
       *   "REDUCE" "AVG" "1" "@price"
       *   "REDUCE" "COUNT" "0" "AS" "rowcount"
       *   "SORTBY" "2" "@rowcount" "DESC" "MAX" "1"
       * </pre>
       */
      @Aggregation( //
          groupBy = { //
              @GroupBy( //
                  properties = "@brand", //
                  reduce = { //
                      @Reducer(func = ReducerFunction.QUANTILE, args={"@price", "0.50"}, alias="q50"), //
                      @Reducer(func = ReducerFunction.QUANTILE, args={"@price", "0.90"}, alias="q90"), //
                      @Reducer(func = ReducerFunction.QUANTILE, args={"@price", "0.95"}, alias="q95"), //
                      @Reducer(func = ReducerFunction.AVG, args={"@price"}), //
                      @Reducer(func = ReducerFunction.COUNT, alias = "rowcount") //
                  } //
              ) //
          }, //
          sortBy = { //
              @SortBy(field = "@rowcount", direction = Direction.DESC), //
          },
          sortByMax = 1 //
      ) //
      AggregationResult priceQuantiles();
    
    opened by bsbodden 0
  • findOneByName error

    findOneByName error

    pom

    <dependency>
      <groupId>com.redis.om</groupId>
      <artifactId>redis-om-spring</artifactId>
      <version>0.6.3</version>
    </dependency>
    

    redis

     docker run -d --name redis-stack -p 6376:6379 -p 8001:8001 redis/redis-stack:edge
    
    @Data
    @NoArgsConstructor
    @RequiredArgsConstructor(staticName = "of")
    @AllArgsConstructor(access = AccessLevel.PROTECTED)
    @Document
    public class Company {
        @Id
        private String id;
    
        @NonNull
        @Searchable
        private String name;
    
        @Indexed
        private Set<String> tags = new HashSet<String>();
    
        @NonNull
        private String url;
    
        @NonNull
        @Indexed
        private Point location;
    
        @NonNull
        @Indexed
        private Integer numberOfEmployees;
    
        @NonNull
        @Indexed
        private Integer yearFounded;
    
        private boolean publiclyListed;
    
        // audit fields
    
        @CreatedDate
        private Date createdDate;
    
        @LastModifiedDate
        private Date lastModifiedDate;
    }
    
    public interface CompanyRepository extends RedisDocumentRepository<Company, String> {
      // find one by property
      Optional<Company> findOneByName(String name);
    
      Iterable<Company> findAllByName(String name);
    }
    
      @Test
        void test2() {
            Optional<Company> redis = companyRepo.findOneByName("Redis");
            System.out.println("redis = " + redis);
    
            Iterable<Company> redis1 = companyRepo.findAllByName("Redis");
            System.out.println("redis1 = " + redis1);
        }
    

    error

    redis.clients.jedis.exceptions.JedisDataException: xxx.CompanyIdx: no such index
    
    	at redis.clients.jedis.Protocol.processError(Protocol.java:139)
    	at redis.clients.jedis.Protocol.process(Protocol.java:173)
    	at redis.clients.jedis.Protocol.read(Protocol.java:227)
    
    opened by axinger 0
  • [Bug] RediSearchIndexer.createIndexFor method use hardcoded entity prefix as class name making RedisMappingContext and FallbackKeySpaceResolver unusable.

    [Bug] RediSearchIndexer.createIndexFor method use hardcoded entity prefix as class name making RedisMappingContext and FallbackKeySpaceResolver unusable.

    spring-data-redis allows to define custom RedisMappingContext bean with configurable fallbackKeySpaceResolver; this is useful when the requirement is to prefix/suffix every redis key with some custom value e.g. environment/service name in shared redis instance. This can be configured as below bean definition;

      @Bean
      @Primary
      public RedisMappingContext keyValueMappingContext(
          @Value(value = "${redis.custom.keyspace.prefix}") String keyspacePrefix) {
        RedisMappingContext mappingContext = new RedisMappingContext();
        mappingContext.setFallbackKeySpaceResolver(type -> keyspacePrefix + ":" + type.getSimpleName());
        return mappingContext;
      }
    

    What this bean is supposed to do is every spring-data-redis operation will use fallback keyspace response and prefix the keyspace with some configurable value. As an example if we are trying to store/lookup com.example.Company entity instead of the key being com.example.Company:ID1 it would be <prefix>:Company:ID1.

    This works for standard spring-data-redis however not for redis-om-spring. I did some investigation and to me this looks like a bug with RediSearchIndexer.createIndexFor(class) method. It has hardcoded logic to always use entity classname as prefix in json schema creation. RediSearchIndexer already has an instance of RedisMappingContext and straightforward fix for this would be to add the following lines for entityPrefix logic;

          String entityPrefix = cl.getName() + ":";
          if (mappingContext.hasPersistentEntityFor(cl)) {
            RedisPersistentEntity<?> persistentEntity = mappingContext.getRequiredPersistentEntity(cl);
            entityPrefix = persistentEntity.getKeySpace() != null ? persistentEntity.getKeySpace() + ":" : entityPrefix;
          }
    

    I have created reproducible example in this repository. I am happy to get inputs on this and also contribute the fix if agreed.

    enhancement 
    opened by setu9760 2
  • Extract tests to separate project under the main POM and extract test utilities to a reusable library

    Extract tests to separate project under the main POM and extract test utilities to a reusable library

    The current directory structure:

    redis-om-spring
    \_ redis-om-spring ==> builds JAR redis-om-spring
        \_src/test/java  ==> currently has all tests and test utility classes
        \_src/test/resources
    \_demos
        \_roms-documents
        \_roms-hashes
        \_roms-permits
    

    Proposed directory structure:

    redis-om-spring
    \_ redis-om-spring ==> builds JAR redis-om-spring
    \_ roms-junit           ==> will build test library (redis-om-spring-junit)
    \_ tests                    ==> move the tests here and use (redis-om-spring-junit) to test
        \_src/test/java    
        \_src/test/resources
    \_demos ==> add tests using redis-om-spring junit to all the projects under demos (see issue #37 )
        \_roms-documents
        \_roms-hashes
        \_roms-permits
    
    enhancement 
    opened by bsbodden 0
Releases(v0.6.3)
  • v0.6.3(Nov 16, 2022)

    Changes

    Features

    • feature: Add limit/offset support to @Query (resolves gh-125)

    Fixes

    • fix: remove @Autowired in favor of constructor injection (resolved gh-133)

    • fix: Only autoindex @Id field when not explicitely indexed by the user (resolves gh-135)

    • fix: NPE when querying an expired (TTL) JSON document - check .get($) (resolves gh-131)

    • fix: NPE when querying an expired (TTL) JSON document (resolves gh-131)

    • docs: fix Javadoc for getIds methods

    Contributors

    We'd like to thank all the contributors who worked on this release!

    @bsbodden

    Source code(tar.gz)
    Source code(zip)
  • v0.6.2(Nov 4, 2022)

    What's Changed

    • Removes need to maintain a Redis Set for Primary Keys by @bsbodden in https://github.com/redis/redis-om-spring/pull/118
    • docs: clean up Javadoc warnings by @bsbodden in https://github.com/redis/redis-om-spring/pull/121
    • fix: addresses null key prefix when indices already created (resolves… by @bsbodden in https://github.com/redis/redis-om-spring/pull/123

    Full Changelog: https://github.com/redis/redis-om-spring/compare/v0.6.1...v0.6.2

    Source code(tar.gz)
    Source code(zip)
  • v0.6.1(Oct 13, 2022)

    🔥 Breaking Changes

    • Upgrades to the latest Spring Boot (2.7.4) and Spring Data Redis (2.7.3)

    🚀 New Features

    • feature: Expose Gson builder factory by @bsbodden in https://github.com/redis/redis-om-spring/pull/114
    • feature: return field with labels for tuple returns (resolves gh-110) by @bsbodden in https://github.com/redis/redis-om-spring/pull/115
    • refactor: sonar lint cleanup by @bsbodden in https://github.com/redis/redis-om-spring/pull/116

    Full Changelog: https://github.com/redis/redis-om-spring/compare/v0.6.0...v0.6.1

    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Sep 6, 2022)

    Changes

    🔥 Breaking Changes

    • Upgrades to the latest Spring Boot (2.7.3) and Spring Data Redis (2.7.2) along with test dependencies (#94)

    🚀 New Features

    • Adds convenience generated constants for nested fields in metamodel (#96)
    • Improves Hash repos to match Document repos, fully tests EntityStreams, general clean up (#84)
    • Support @TimeToLive on JSON-mapped objects #66 (#68)
    • Redis JSON ARR* functionality as terminal operations on Entity Streams (#55)
    • Use pipelining to improve performance put/get in adapters (#71)
    • Overwriting saveAll methods of SimpleRedisRepositories to use pipelining (#91)
    • Implementing TTL capability to saveAll overwritten methods (#101)

    🐛 Bug Fixes

    • Honor indexing annotations 'aliases' when querying (resolves gh-97) (#98)
    • Use pipe '|' as default separator for TAG index fields (resovles gh-72) (#90)
    • Prevent character escaping for full-text searches (resolves gh-69) (#73)

    🧰 Maintenance

    • refactor: apply sonar lint recommendations (#95)
    • test: ups test coverage and light refactoring (#89)
    • ci: update wordlist for spellchecker action (#85)
    • Updating the release an CI processes to fully live in GitHub Actions (#75)
    • Integrating spellcheck for CI (#67)
    • Updating release drafter to match current standard (#57)
    • test: add test for @Query annotation with Pagination support (#53)

    Contributors

    We'd like to thank all the contributors who worked on this release!

    @Pwhxbdk, @ally-jarrett, @bsbodden, @chayim, @daveish, @gkorland, @raphaeldelio, @simonprickett and David Fischer

    Source code(tar.gz)
    Source code(zip)
Owner
Redis
Redis
Search API with spelling correction using ngram-index algorithm: implementation using Java Spring-boot and MySQL ngram full text search index

Search API to handle Spelling-Corrections Based on N-gram index algorithm: using MySQL Ngram Full-Text Parser Sample Screen-Recording Screen.Recording

Hardik Singh Behl 5 Dec 4, 2021
This is a Meme repo for fixed & Cleaned source of 'Better'Bungeecord but its not realy better code is trash!

#Fucking cleaned by CryCodes Disclaimer: Based of MD_5's Bungeecord (Fork of "BetterBungee") | I am not the owner of the code This repo is just for fu

Rooks 3 Jan 2, 2022
循序渐进,学习Spring Boot、Spring Boot & Shiro、Spring Batch、Spring Cloud、Spring Cloud Alibaba、Spring Security & Spring Security OAuth2,博客Spring系列源码:https://mrbird.cc

Spring 系列教程 该仓库为个人博客https://mrbird.cc中Spring系列源码,包含Spring Boot、Spring Boot & Shiro、Spring Cloud,Spring Boot & Spring Security & Spring Security OAuth2

mrbird 24.8k Jan 6, 2023
A high availability shopping(ecommerce) system using SpringBoot, Spring Cloud, Eureka Server, Spring Cloud Gateway, resillience4j, Kafka, Redis and MySQL.

High-availability-shopping-system A high availability shopping(ecommerce) system using SpringBoot, Spring Cloud, Eureka Server, Spring Cloud Gateway,

LeiH 1 Oct 26, 2022
Realtime Data Processing and Search Engine Implementation.

Mutad The name Mutad is a reverse spelling of datum. Overview An implementation of a real-time data platform/search engine based on various technology

Shingo OKAWA 14 Aug 4, 2022
Code4Me provides automatic intelligent code completion based on large pre-trained language models

Code4Me Code4Me provides automatic intelligent code completion based on large pre-trained language models. Code4Me predicts statement (line) completio

Code4Me 38 Dec 5, 2022
Spring Boot & MongoDB Login and Registration example with JWT, Spring Security, Spring Data MongoDB

Spring Boot Login and Registration example with MongoDB Build a Spring Boot Auth with HttpOnly Cookie, JWT, Spring Security and Spring Data MongoDB. Y

null 15 Dec 30, 2022
Spring REST service built with Spring initializr and Spring Data.

Spring REST Service Generated with start.spring.io, using Spring Data. Documented using Spring REST Docs. Spring Initializr - Generate new Spring Rest

null 1 Jan 28, 2022
🦄 开源社区系统:基于 SpringBoot + MyBatis + MySQL + Redis + Kafka + Elasticsearch + Spring Security + ... 并提供详细的开发文档和配套教程。包含帖子、评论、私信、系统通知、点赞、关注、搜索、用户设置、数据统计等模块。

Echo — 开源社区系统 项目上线到服务器之后可能会出现各种各样的 BUG,比如 Elasticsearch 服务启动失败导致搜索模块不可用,但是在本地运行是完全没问题的,所以各位小伙伴可以放心下载部署。 ?? 项目简介 Echo 是一套前后端不分离的开源社区系统,基于目前主流 Java Web

小牛肉 434 Jan 7, 2023
Spring-boot application using redis as a caching database

Java Spring-boot application using Redis as a caching database Running Application Entities involved Two main entities are involved MasterHouse (maste

null 18 Aug 9, 2022
Rqueue aka Redis Queue [Task Queue, Message Broker] for Spring framework

Rqueue: Redis Queue, Task Queue, Scheduled Queue for Spring and Spring Boot Rqueue is an asynchronous task executor(worker) built for spring and sprin

Sonu Kumar 221 Jan 5, 2023
SecureDB is an extension for Ai2 Appinventor and its distros which stores the data in the form of key and value just like TinyDB but in a more secure manner.

SecureDB SecureDB is an extension for Ai2 Appinventor and its distros which stores data for your app in a secure format locally on user's device. Expl

Akshat Developer 3 Sep 24, 2022
Additional plug-ins and extensions for Java's ImageIO using native libraries

NightMonkeys A collection of ImageIO plugins, adding support for newer image formats. NightMonkeys uses the newer Foreign Linker API available in JDK

Gauthier 20 Dec 3, 2022
Spring Boot JdbcTemplate example with SQL Server: CRUD Rest API using Spring Data JDBC, Spring Web MVC

Spring Boot JdbcTemplate example with SQL Server: Build CRUD Rest API Build a Spring Boot CRUD Rest API example that uses Spring Data Jdbc to make CRU

null 7 Dec 20, 2022
Spring Boot JWT Authentication example with Spring Security & Spring Data JPA

Spring Boot JWT Authentication example with Spring Security & Spring Data JPA

null 1 Jan 26, 2022
A library for common extensions for Truth.

truth-extensions A library for common extensions for Truth. Usage Repository To depend on a release, you need only depend on the official Maven centra

Mardrömmar 2 Jan 24, 2022
An intelliJ plugin providing a UI layer for git-flow, which in itself is a collection of Git extensions to provide high-level repository operations for Vincent Driessen's branching model.

Git Flow Integration Plus for Intellij An intelliJ plugin providing a UI layer for git-flow, which in itself is a collection of Git extensions to prov

RubinCarter 35 Nov 8, 2022
A template repository for new extensions.

SAP CX Template The sapcxtemplate extension improves ... FEATURE DESCRIPTION COPY-TEXT INTRODUCTION How to activate and use COPY-TEXT ACTIVATION / SET

SAP CX Tools 3 Dec 15, 2022
A distributed lock that supports the use of Redis and Zookeeper, out of the box, fast and easy to use

lock-spring-boot-starter A distributed lock that supports the use of Redis and Zookeeper, out of the box, fast and easy to use 一款基于 Redis 和 Zookeeper

Pear Stack 9 Oct 15, 2022