2

Looking at the link - how to have relations many to many in redis. I tried to model this using Spring Data Redis HashRedis class. This example model....

# Here are my categories
> hmset category:1 name cinema  ... more fields ...
> hmset category:2 name music   ... more fields ...
> hmset category:3 name sports  ... more fields ...
> hmset category:4 name nature  ... more fields ...

# Here are my users
> hmset user:1 name Jack   ... more fields ...
> hmset user:2 name John   ... more fields ...
> hmset user:3 name Julia  ... more fields ...

# Let's establish the many-to-many relationship
# Jack likes cinema and sports
# John likes music and nature
# Julia likes cinema, music and nature

# For each category, we keep a set of reference on the users
> sadd category:1:users 1 3
> sadd category:2:users 2 3
> sadd category:3:users 1
> sadd category:4:users 2 3

# For each user, we keep a set of reference on the categories
> sadd user:1:categories 1 3
> sadd user:2:categories 2 4
> sadd user:3:categories 1 2 4

Once we have this data structure, it is easy to query it using the set algebra:

Categories of Julia

> smembers user:3:categories
1) "1"
2) "2"
3) "4"

# Users interested by music
> smembers category:2:users
1) "2"
2) "3"

# Users interested by both music and cinema
> sinter category:1:users category:2:users
1) "3"

User.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@RedisHash("users")
public class User {
    @Id
    private String userId;
    private String firstName;
    private String emailId;
    private List<Category> categories;
}

Category.java

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@RedisHash("category")
public class Category {
    @Id
    private String categoryId;
    private String name;
    private String type;

    private List<User> users;
}

RedisExampleBootApplication.java

@SpringBootApplication
public class RedisExampleBootApplication implements CommandLineRunner{
    @Autowired CategoryRepository categoryRepository;
    @Autowired UserRepository userRepository;

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

    @Override
    public void run(String... args) throws Exception {  

        User jack = User.builder().firstName("Jack").emailId("jack@gmail.com").build();
        User john = User.builder().firstName("John").emailId("john@gmail.com").build();
        User julia  = User.builder().firstName("Julia ").emailId("julia @gmail.com").build();

        Category cinema = Category.builder().name("Cinema").type("Entertainment").users(Arrays.asList(jack, julia)).build();
        Category sports = Category.builder().name("Sports").type("Play Game").users(Arrays.asList(jack)).build();

        Category music = Category.builder().name("Music").type("Sounds").users(Arrays.asList(john, julia)).build();
        Category nature = Category.builder().name("Nature").type("Wild Life").users(Arrays.asList(john, julia)).build();

        categoryRepository.save(cinema);
        categoryRepository.save(sports);
        categoryRepository.save(music);
        categoryRepository.save(nature);



        Category myCinema = Category.builder().name("Cinema").name("Entertainment").build();
        Category mySport = Category.builder().name("Sports").name("Play Game").build();
        Category myMusic = Category.builder().name("Music").name("Sound").build();
        Category myNature = Category.builder().name("Nature").name("Wild Life").build();


        User myJack = User.builder().firstName("Jack").emailId("jack@gmail.com").categories(Arrays.asList(myCinema, mySport)).build();
        User myJohn = User.builder().firstName("John").emailId("john@gmail.com").categories(Arrays.asList(myMusic, myNature)).build();
        User myJulia  = User.builder().firstName("Julia ").emailId("julia @gmail.com").categories(Arrays.asList(myCinema,myMusic, myNature)).build();

        userRepository.save(myJack);
        userRepository.save(myJohn);
        userRepository.save(myJulia);
    }
}

Here is the result of Data Modeling

127.0.0.1:6379> KEYS *
1) "category:0bcba339-9a3e-46e3-b33c-877f8d15595f"
2) "category:4d2b8d10-83de-41fa-8a33-93f30a6f9ffc"
3) "category:f756f18c-53e4-4a20-9a42-ad08b537f380"
4) "category"
5) "users:644d0adc-f0cf-4ba1-8d6b-d6f47145e5e7"
6) "users:8aa5c578-4e05-4ae9-8f80-d10b789e0877"
7) "users:825ab11b-803a-4e4f-a72d-e6b42b98007c"
8) "category:54a56102-5836-41bf-86a8-c82b3f12d3cf"
9) "users"
127.0.0.1:6379>

I've not used @Indexed anywhere, because currently I am not using any findBy method, but thats not the issue. I just need confirmation on how to model the data? Or POJO modelling is correct ?

HGETALL users:8aa5c578-4e05-4ae9-8f80-d10b789e0877

userId 8aa5c578-4e05-4ae9-8f80-d10b789e0877
_class com.example.model.User
firstName Jack
emailId jack@gmail.com
categories.[1].name Play Game
categories.[0].name Entertainment
Jeff Cook
  • 7,956
  • 36
  • 115
  • 186

2 Answers2

0

I was able to solved this. You just maintain relationship into separate @RedishHash class and give PK (There is not PK in Redis, but meant to say unique key) and use PK from Category and PK from User and annotated them with @Indexed, so that you can do custom search using Repository pattern.

In this way, you're just maintaining the list of Categories for single User and also maintain Users for Single category. Just used single mapping here.

Like when saving data for Category, save user for that Category. For example - Assuming User-1 interested in Category-1, User-2 interested in Category-1 and 2 and User-3 interested in

Category-3
Category-1 save User-1
Category-1 save User-2

Category-2 save User-2
Category-2 save User-3

Done !! This works nicely. This is another approach simply using CRUDRepository patterns and without RedisTemplate.

PAA
  • 1
  • 46
  • 174
  • 282
0

I am posting the another answer on the same link. As I've not received any comments from any of the expert yet. I will be so helpful if anyone does so.

I kept the model class as simple as that and just created a key to save the relationship between them. I need guidance from the Redis expert folks how to go ahead with the same.

This is very simple and I supposed what commands are doing, we're also doing the same.

// Category Details
Category c1 = Category.builder().id("c1").name("Cinema").build();
Category c2 = Category.builder().id("c2").name("Sports").build();
Category c3 = Category.builder().id("c3").name("Music").build();
Category c4 = Category.builder().id("c4").name("Nature").build();

redisTemplate.opsForSet().add("category:1", new ObjectMapper().writeValueAsString(c1));
redisTemplate.opsForSet().add("category:2", new ObjectMapper().writeValueAsString(c2));
redisTemplate.opsForSet().add("category:3", new ObjectMapper().writeValueAsString(c3));
redisTemplate.opsForSet().add("category:4", new ObjectMapper().writeValueAsString(c4));

// User 
User u1 = User.builder().id("u1").firstName("Chris").emailId("chris.rogers@gmail.com").build(); //1
User u2 = User.builder().id("u2").firstName("John").emailId("john.doe@gmail.com").build();  //2
User u3 = User.builder().id("u3").firstName("Julia").emailId("julia.cox@gmail.com").build(); //3

redisTemplate.opsForSet().add("user:1", new ObjectMapper().writeValueAsString(u1));
redisTemplate.opsForSet().add("user:2", new ObjectMapper().writeValueAsString(u2));
redisTemplate.opsForSet().add("user:3", new ObjectMapper().writeValueAsString(u3));

redisTemplate.opsForSet().add("category:1:users", "1","3");
redisTemplate.opsForSet().add("category:2:users", "2","3");
redisTemplate.opsForSet().add("category:3:users", "1");
redisTemplate.opsForSet().add("category:4:users", "2","3");

redisTemplate.opsForSet().add("user:1:categories", "1","3");
redisTemplate.opsForSet().add("user:2:categories", "2", "4" );
redisTemplate.opsForSet().add("user:3:categories", "1", "2", "4");

enter image description here

Note: You can manage keys dynamically using UUID.

Jeff Cook
  • 7,956
  • 36
  • 115
  • 186
PAA
  • 1
  • 46
  • 174
  • 282