sewenew / redis-plus-plus

Redis client written in C++
Apache License 2.0
1.65k stars 351 forks source link

I want to store an structure in binary form using redis-plus-plus, How can i do this, I find no method to do this in documentation, I am attaching my code. Please help #64

Closed Abhishekrawat1916 closed 4 years ago

Abhishekrawat1916 commented 4 years ago
#include <sw/redis++/redis++.h>                                                                                                    
#include<iostream>                                                                                                                                                                                                                                                    
using namespace sw::redis;                                                                                                                 
using namespace std;                                                                                                                                                                                                                                                  
struct person                                                                                                                      
{                                                                                                                                  
int age;                                                                                                                           
string name;                                                                                                                       
};                                                                                                                                                                                                                                                                    
int main(){                                                                                                                        
person p1;                                                                                                                         
p1.age=18;                                                                                                                         
p1.name="Abhishek";                                                                                                                                                                                                                                                   
try{                                                                                                                                       
auto redis_cluster = RedisCluster("tcp://127.0.0.1:30001");                                                                        
redis_cluster.set("person",StringView(&p1,size_t(sizeof(person))));                                                                
auto reply=redis_cluster.get("person");                                                                                            
if(reply){                                                                                                                                 
person* p2=*reply;                                                                                                                 
cout<<p2->age<<endl;                                                                                                       
}                                                                                                                          
}                                                                                                                                  
catch(const Error &e){                                                                                                             cout<<"Error"<<endl;                                                                                                               
}                                                                                                                                  
}
sewenew commented 4 years ago

Hi @Abhishekrawat1916,

You should serialize person into a string before setting it to Redis. When getting the string back from Redis, you should deserialize it into person. Normally, you can use JSON or Protobuf to do the serialization work. Take JSON as an example (you need to install nlohmann::json from here):

#include <nlohmann/json.hpp>
#include <sw/redis++/redis++.h>

using namespace std;
using namespace sw::redis;

struct person {
    int age;
    string name;
};

int main() {
    person p1
    p1.age = 18;
    p1.name = "Abhishek";

    // serialize person into a JSON string
    nlohmann::json p;
    p["age"] = p1.age;
    p["name"] = p1.name;
    auto val = p.dump();       // JSON string

    auto redis_cluster = RedisCluster("tcp://127.0.0.1:30001");
    redis_cluster.set("person", val);
    auto reply = redis_cluster.get("person");
    if (reply) {
        // deserialize JSON string to person
        auto val = nlohmann::json::parse(*reply);
        person p2;
        p2.age = val["age"].get<int>();
        p2.name = val["name"].get<string>();
    }
    return 0;
}

However, if your data structure is as simple as this person, you can use Redis HASH to store it.

#include <sw/redis++/redis++.h>
#include <string>

using namespace std;
using namespace sw::redis;

int main() {
    auto redis_cluster = RedisCluster("tcp://127.0.0.1:30001");
    redis_cluster.hmset("person", {std::make_pair("age", "18"), std::make_pair("name", "Abhishek")});

    // Get all attributes of this person
    std::unordered_map<string, string> attributes;
    redis_cluster.hgetall("person", std::inserter(attributes, attributes.begin()));
    for (const auto & ele : attributes) {
        cout << ele.first << "\t" << ele.second << endl;
    }

    // Only get a single field:
    auto age = redis_cluster.hget("person", "age");
    if (age) {
        cout << *age << endl;
    }
    return 0;
}

Also there's a third option that is to use redis-protobuf or RedisJSON module to save these user defined data structure into Redis.

If you still have any problem, feel free to let me know :)

Regards

Abhishekrawat1916 commented 4 years ago

Hi, using JSON library is not suitable for my project. I was trying to store struct in this way but it is giving garabage value. A similar program in c using hiredis works where %b specifier is used. If anyone could help me out, I am providing my code that i am trying.


#include <sw/redis++/redis++.h>
#include<iostream>

using namespace sw::redis;
using namespace std;

struct person
{
int age;
string name;
};

int main(){
person p1;
p1.age=18;
p1.name="Rawat";

try{
Redis redis_cluster = Redis("tcp://127.0.0.1:6379");
char* ch=(char*)&p1;
person* p=(person*)ch;
cout<<p->name<<endl<<p->age<<endl;
auto reply=redis_cluster.set("surname",StringView(ch,sizeof(p1)));
auto reply1=redis_cluster.get("surname");
if(reply1){
// person* p2=(reply);
person* p2=(person*)&reply1;

cout<<p2->age<<endl;
cout<<p2->name<<endl;
//cout<<1<<endl;
}
}
catch(const Error &e){
cout<<"Error"<<endl;
}
}
sewenew commented 4 years ago

Hi @Abhishekrawat1916

You must serialize person object into a string. If you cannot use JSON or Protobuf, you can serialize it to a string and deserialize the string back to person with std::stringstream:

#include <sstream>

person p;
p.age = 18;
p.name = "Rawat";
stringstream output;
output << p.age << p.name;
redis_cluster.set("surname", output.str());

auto reply = redis_cluster.get("surname");
if (reply) {
    stringstream input(*reply);
    person p1;
    input >> p1.age >> p1.name;
    cout << p1.age << endl;
    cout << p1.name << endl;
}

Regards

Abhishekrawat1916 commented 4 years ago

Thanks, this would really help.

VaibhawChandrakar commented 3 years ago

What is the way to store something in plain binary format in redis-plus-plus and just get back the same binary output ?

Basically I have some complex class for which I am using serialization which gives output in plain binary (not a string, which redis set call expects ) and my de serialization uses that binary stream to build back the object.

sewenew commented 3 years ago

What do you mean plain binary format? Do you have a const char * pointer and a length? If you do, you can manually create a StringView object, and pass it as parameter to Redis::set:

const char *data; // pointer to your binary data
size_t len;  // length of your data

redis.set("key", StringView(data, len));
VaibhawChandrakar commented 3 years ago

It worked thanks :)