Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing multiple nested objects in redis

Tags:

json

redis

I would like to store multiple complex json data in redis but am not sure how

THis is my json structure

"users":{

        "user01":{username:"ally", email:"[email protected]"},
         "user02":{username:"user2".....}
        },

 "trucks":{
         "truck01":{reg_no:"azn102", make:"subaru" .......},
         "truck02":{reg_no:"kcaher3".....}
       }

I have checked on THis question which provides a way to store the users but i would like to store users(01, 02) inside users then trucks(01, 02) in trucks so that if i want to retrieve users i can simply do

hmget users

and similar case for trucks

Later i would also want to get user01 in users but am confused on how to store such data in redis

like image 720
Geoff Avatar asked Jun 25 '17 06:06

Geoff


People also ask

Can Redis store nested objects?

Unlike document-oriented databases, Redis is not designed to have nested objects in it. However, sometimes needs to store the complex object, so how to do it if it's not actually supported by Redis? The answer is short: store the preferred object as a string or hash type.

Does Redis support nested hash?

Redis doesn't support nested data structures, and specifically it doesn't support a Hash inside a Hash :) You basically have a choice between two options: either serialize the internal Hash and store it in a Hash field or use another Hash key and just keep a reference to it in a field of the outer Hash.

Can Redis store objects?

Object Storing Procedure. In the Redis, Everything can be stored as only key-value pair format. Key must be unique and storing an object in a string format is not a good practice anyway. Objects are usually stored in a binary array format in the databases.

Can we store JSON in Redis?

RedisJSON stores the data in a binary format which removes the storage overhead from JSON, provides quicker access to elements without de-/re-serialization times. To use RedisJSON you need to install it in your Redis server or enable it in your Redis Enterprise database.


4 Answers

You can use the ReJSON Redis Module and store the objects directly. http://rejson.io/

like image 160
Not_a_Golfer Avatar answered Sep 17 '22 17:09

Not_a_Golfer


Option 1

I think if you are searching for an implementation language independent way, i.e. the same operation and result are expected in different parts of the system written in different programming languages, then @Not_a_Golfer 's recommendation is best suited for your case.

Option 2

However, if you happened to be using Java only and you are not running Redis 4.0 yet, I would recommend you use Redisson as your client side library to make this job a lot easier for yourself.

Disclaimer, I am a member of the Redisson project, my view below is biased. It is also written in a hope that it can reach to others who are in this exact situation.

Redisson is a client side Java library that lets you operate Redis as an In-Memory Data Grid. Multi-dimensional complex objects are naturally supported.

Redisson provides Redis data types with standard Java interfaces, i.e. Redis hash is provided as java.util.Map and java.util.concurrent.ConcurrentMap so in your case the usage would be simply be:

//lets imagine you have a builder that creates users 
User user01 = new User();
user01.setUsername("ally");
user01.setEmail("[email protected]");

User user02 = new User();
user02.setUsername("user2");
user02.setEmail("...");

//You get a Redis hash handler that works as a standard Java map
Map<String, User> users = redisson.getMap("users");
//This is how you put your data in Redis
//Redisson serialize the data into JSON format by default
users.put("user01", user01);
users.put("user02", user02);

//the same works for trucks
Truck truck01 = new Truck();
truck01.setRegNo("azn102");
truck01.setMake("subaru");

Truck truck02 = new Truck();
truck02.setRegNo("kcaher3");
truck02.setMake("...");

//The same as above
Map<String, Truck> trucks = redisson.getMap("trucks");
trucks.put("truck01", truck01);
trucks.put("truck02", truck02);

Getting your data out is just as easy

User user01 = users.get("user01");
Truck truck02 = trucks.get("truck02");

In Redis, what you would get is two hashes, one called users and another called trucks, you would see JSON strings are stored against specified field names in these hash objects.

Now, you may argue this is not really a nested objects, this is just a data serialization.

OK, lets make the example a little more complex to see the difference: What if you want to keep a list of all the users who had driven a particular truck, you may also want to easily find out which truck a user is currently driving.

I would say these are fairly typical business use cases.

That really adds more dimensions and complexity to the data structures. Normally you would have to break them in to different pieces:

  • You would need the Truck and User be mapped to a different hash to avoid data duplication and consistency issue;

  • You would also need to manage a separate list for each truck to store the usage log.

In Redisson, these type of tasks are handled more naturally and without bearing the aforementioned worries in mind.

You would simply do, as you would normally, in Java like this:

  • Annotate your User class and Truck class with @REntity annotation, and choose an identifier generator or specifier your own, this could be the value of a field.

  • Add a List field (usageLog) to Truck class.

  • Add a Truck field (currentlyDriving) to User class.

That's all you need. So the usage is not much more than you would normally do in Java:

//prepare the service and register your class.
RLiveObjectService service = redisson.getLiveObjectService();
service.registerClass(User.class);
service.registerClass(Truck.class);

Truck truck01 = new Truck();
truck01.setRegNo("azn102");
truck01.setMake("subaru");
//stores this record as a Redis hash
service.persist(truck01);

User user02 = new User();
user02.setUsername("user2");
user02.setEmail("...");
//stores this record as a Redis hash
service.persist(user02);

//assuming you have invoked setUsageLog elsewhere.
truck01.getUsageLog().add(user02);
user02.setCurrentlyDriving(truck01);

//under normal circumstance keeping a Redis hash registry is not necessary.
//the service object can be used for this type of look up.
//this is only for demonstration.
Map<String, Truck> trucks = redisson.getMap("trucks");
trucks.put("truck01", truck01);

Map<String, User> users = redisson.getMap("users");
users.put("user02", user02);

So what you get in the end is each record is stored in Redis and a hash, and each truck record keeps an independent list of user records who has used it, user record now has information of the truck he/she is currently driving. All of these things are done using object references instead of duplicating the domain records.

As you can see Redisson provides a solution that ticks all the boxes and takes the headaches away during the process.

For more information relating to how Redisson handles multi-dimensional complex objects in Redis through object hash mapping and object referencing:

  • Introducing Redisson Live Objects (Object Hash Mapping)(written by myself), and
  • A Look at the Java Distributed In-Memory Data Model (Powered by Redis)(written by Nikita Koksharov, founder of Redisson project)
like image 23
Redisson_RuiGu Avatar answered Sep 21 '22 17:09

Redisson_RuiGu


I think better you can use HMSET to save keys.

Like in your case you can create a HMSET with name "users" & "trucks". In that HMSET you can create keys (i.e. fields) like "user01" and "user02". As these keys(like "user01", "user02" etc) are able to store value in it, so you can store your rest (i.e. {username:"ally", email:"[email protected]"}) of JSON object in stringified form.

For example: HMSET users user01 "{\"username\":\"ally\",\"email\":\"[email protected]\"}"

After this you can get your users list by HGETALL (like by using HGETALL users). Similarly, if you need to fetch user details of "user01" then you can use HGET like HGET users user01. After fetching value of "user01" you can parse that value and use according to your requirement.

like image 33
Mayank Jain Avatar answered Sep 21 '22 17:09

Mayank Jain


You can save data in redis like in attached images.In this images i have made a structure for you

for user data

EDIT

The sample code is :

public void saveInRedis(Jedis jedis) throws JSONException{

        JSONObject jsonObject=new JSONObject();
        JSONObject jsonObject1=new JSONObject();
        JSONObject jsonObject2=new JSONObject();
        JSONObject jsonObject3=new JSONObject();


        jsonObject2.put("username", "ally");
        jsonObject2.put("email", "[email protected]");
        jsonObject3.put("username", "xyz");
        jsonObject3.put("email", "[email protected]");
        jsonObject1.put("user01", jsonObject2);
        jsonObject1.put("user02", jsonObject3);
        jsonObject.put("users", jsonObject1);


    // json is -- >  {"users":{"user02":{"email":"[email protected]","username":"xyz"},"user01":{"email":"[email protected]","username":"ally"}}}

        System.out.println("json is ---  >  "+jsonObject);

        JSONObject parse=new JSONObject(jsonObject.toString());
        JSONObject parseJson=parse.getJSONObject("users");
        JSONObject parseJson2=parseJson.getJSONObject("user02");
        JSONObject parseJson3=parseJson.getJSONObject("user01");

        Map<String, String> map=new HashMap<>();
        Map<String, String> map1=new HashMap<>();

        map.put("email", parseJson2.getString("email"));
        map.put("username", parseJson2.getString("username"));
        map1.put("email", parseJson3.getString("email"));
        map1.put("username", parseJson3.getString("username"));
        jedis.hmset("users:user01", map);
        jedis.hmset("users:user02", map1);



    }

you can do hmget and hmgetAll on that keys.

like image 32
Abhijeet Behare Avatar answered Sep 18 '22 17:09

Abhijeet Behare