Query Nested Objects in Redis using Spring Data

拈花ヽ惹草 提交于 2020-01-04 05:43:08

问题


I have a java object as below that i store in a redis store.

@RedisHash("UserProfile")
public class UserProfile implements Serializable {
    @Id String id;
    @Reference PersonalInfo personalInfo = new PersonalInfo();
    @Reference BusinessInfo businessInfo = new BusinessInfo();
    ...
}

Now, the PersonalInfo object is structured as:

public class PersonalInfo {
    private String firstName;
    private String lastName;
    @Indexed private String userId;
    private ContactInfo contactInfo = new ContactInfo();
}

Note that userId in the PersonalInfo object is indexed.

My goal is to query the UserProfile objects in redis by their userIds, using spring data.

The CrudRepository interface will not be able to directly do a findByUserId(), unless i have userId in the UserProfile object. I dont want to have the userId directly within the UserProfile object, if there is some better way to write a query to find users by userid.

Any suggestion on how i can go about doing this?

Update: I added the method as below to the repository:

public interface UserProfileRepository extends CrudRepository<UserProfile, String> {
    List<UserProfile> findByPersonalInfo_UserId(String userId);
}

But on calling this method in code, i get the below error. Not sure if there is an issue with the redis/spring libraries i am using. I am using

  • springboot-starter-parent version: 1.5.2.RELEASE
  • spring-data-redis version: 1.8.6.RELEASE

Exception:

java.lang.NoSuchMethodError: org.springframework.data.keyvalue.core.query.KeyValueQuery.getCriteria()Ljava/lang/Object;
    at org.springframework.data.redis.repository.query.RedisQueryCreator.complete(RedisQueryCreator.java:101) ~[spring-data-redis-1.8.6.RELEASE.jar:na]
    at org.springframework.data.redis.repository.query.RedisQueryCreator.complete(RedisQueryCreator.java:41) ~[spring-data-redis-1.8.6.RELEASE.jar:na]
    at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
    at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
    at org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.createQuery(KeyValuePartTreeQuery.java:184) ~[spring-data-keyvalue-1.2.1.RELEASE.jar:na]
    at org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.prepareQuery(KeyValuePartTreeQuery.java:128) ~[spring-data-keyvalue-1.2.1.RELEASE.jar:na]
    at org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.execute(KeyValuePartTreeQuery.java:87) ~[spring-data-keyvalue-1.2.1.RELEASE.jar:na]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:483) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57) ~[spring-data-commons-1.13.1.RELEASE.jar:na]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at com.sun.proxy.$Proxy66.findByPersonalInfo_UserId(Unknown Source) ~[na:na]

Update 2: I was able to fix the above exception by changing the springboot version to 1.5.6.RELEASE.

However, now the issue i am facing is, when i query by userid, it doesnt give me the UserProfiles, even though there is a match.

For example, redis-cli shows:

KEYS *
UserProfile:d0876c1f-5684-4ee4-acbb-7a92b7fa25ae
UserProfile:3591e476-29d7-4c3c-a7e5-6272231f96e0
UserProfile:3591e476-29d7-4c3c-a7e5-6272231f96e0:idx
UserProfile:d0876c1f-5684-4ee4-acbb-7a92b7fa25ae:idx
UserProfile:51814a77-bf40-4912-b700-cfa50d1c4b25
UserProfile:personalInfo.userId:adrian.tremblay
UserProfile:66ba8276-1bb0-47a0-a54d-4c9d99b8bf80
UserProfile:66ba8276-1bb0-47a0-a54d-4c9d99b8bf80:idx
UserProfile:personalInfo.userId:anthony.turner
UserProfile:personalInfo.userId:ashleigh.hayes
UserProfile:a81356b0-27ef-4a34-92a3-629be5114f0e
UserProfile

When i do a findAll, findById, it gives me results back. When I do a findByPersonalInfo_UserId(), and pass values like: anthony.turner or ashleigh.hayes, nothing shows up. Am i giving the userId in some incorrect format? I tried a few different things, but didnt work.

Update 3: I also tried removing the @Reference in the UserProfile object (so that the redis objects get flattened out, instead of pointing to references). But this didn't help either.


回答1:


Try to name your method without any underscore like:

List<UserProfile> findByPersonalInfoUserId(String userId);

Had similar case and it worked for me. Also don't need @Reference annotation.

Using spring-boot-starter-parent 2.0.3.RELEASE




回答2:


I faced the same situation,

Basically if you have a reference then Redis internally stores the Id for the reference, which cannot be directly queried as it need to fetch the whole Object to match, yep its the drawback for Redis(or any Hashbased/Non-relational database).

You may ask why not possible, technically it can be done but it will be processor and memory intensive operation which will ultimately hit performance, and Redis is known for its performance.

@Reference should only be used only and only when you wish to share a single object (here PersonalInfo) among multiple Objects (here UserInfo).

Possible solutions if you want to use @Reference (assuming UserProfile's 'id' not equal to PersonalInfo's userId ):

My personal solution at this answer's end :

  1. (Not Recommended - slow performance) - first findAllUserProfile() then go through each PersonalInfo present in UserProfile and match desired field (here userId)

  2. (Recommended) - Restructuring your schema, ask me why, because if you wish to keep references for more than just reading (i mean for querying too) then non-relational database is not one for you in this situation. Restructure to make it fit in non-relational structure if possible.

Note: solution by @Aurimas above is good and suits your situation best, if you won't put reference then it will create a new object which will be stored in UserProfile object but any changes in PersonalInfo will be specific to this UserProfile and won't reflect in other UserProfile objects. But it's advantage is it will be query-able :-)

So as per you situation I will recommend to ditch the @Reference for PersonalInfo as logically it should not be shared (i.e Personal ;-) ), and go ahead with @Aurimas solution.



来源:https://stackoverflow.com/questions/45419196/query-nested-objects-in-redis-using-spring-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!