Using java and redis to create stackoverflow kind of application

Redis is an open source (BSD licensed), in-memory data structure store, used as a database and cache. It supports data structures such as strings, hashes, lists, sets etc. Jedis is the most commonly used Java client for communicating with redis server

Let’s try to build StackOverFlow.com kind of application in Java using redis with the help of Jedis client. Basically, this app can have multiple questions which can be upvoted, each question can have multiple answers which can also be upvoted

1. Create Jedis client by connecting to the redis server

Jedis jedis = new Jedis("localhost");

2. For each question, we need to generate a unique id which can keep incrementing itself.We will maintain these unique ids under ‘unique_ids‘ key and ‘question‘ field

private Long createQuestionId(){
		return jedis.hincrBy("unique_ids", "question", 1);
	}

3. Similarly, we will maintain unique ids for each answer also

private Long createAnswerId(){
		return jedis.hincrBy("unique_ids", "answer", 1);
	}

4. Next we will create a question. This method will first invoke createQuestionId to generate a unique id for this question and use hset to set propeties of this question like votes, question string.
We will also maintain list of question ids of the questions saved till now. This list will be saved under key “questions

private Long saveQuestion(String question, int votes){
		Long id = createQuestionId();
		jedis.hset("question:" + id, "question", question);
		jedis.hset("question:" + id, "votes", votes+"");
 
                jedis.lpush("questions", id + "");
 
		return id;
 
	}

5. Next, we will save answer. First, we will generate unique id for this answer and then set answer, person who answered, votes and the answer.
We will also maintain a set of answer ids for each question id.

private Long saveAnswer(Long question_id, String answer, String answerer, int votes){
		Long id = createAnswerId();
		jedis.hset("answer:" + id, "question_id", question_id+"");
		jedis.hset("answer:" + id, "answer", answer);
		jedis.hset("answer:" + id, "answerer", answerer);
		jedis.hset("answer:" + id, "votes", votes+"");
 
                jedis.sadd("question_answers:" + question_id + "", id + "");
 
		return id;
	}

6. Now we will write functions which will increment number of votes for questions and answers

private Long upvoteQuestion(Long question_id){
 
		return jedis.hincrBy("question:" + question_id, "votes", 1);
	}
 
	private Long upvoteAnswer(Long answer_id){
		return jedis.hincrBy("answer:" + answer_id, "votes", 1);
	}

7. This method will return the question for the given question_id

private List getQuestion(String question_id,String... parameters){
		List question = jedis.hmget("question:" + question_id,parameters);
		return question;		
	}

8. Now we need to fetch answers for a given question id. First, we will fetch all answer ids for the given question id from the question_answers set we are maintaining. Now for each answer_id we will fetch the answer details like answer text, votes

private List<List> getAnswers(Long question_id){
		List<List> answers = new ArrayList<List>();
		Set answer_ids = jedis.smembers("question_answers:" + question_id + "");
		for(String answer_id : answer_ids){
			answers.add(getAnswer(answer_id));
		}
		return answers;
 
	}
 
	private List getAnswer(String answer_id){
		List answer = jedis.hmget("answer:" + answer_id,"answer","votes"); 
		return answer;
 
	}

9. On the homepage, the website might like to show the latest questions. We will fetch question ids from the “questions” list which we were maintaining in step 4

private List<List> getQuestions(int range){
		List<List> questions = new ArrayList<List>();
		List question_ids = jedis.lrange("questions", 0, range);
		for(String question_id : question_ids){
			questions.add(getQuestion(question_id,"question"));
		}
		return questions;		
	}

10. It will be more logical to show top voted questions. So we need to sort questions by their votes count. Here we are sorting the questions by an external value, in our case by the field “votes“. As in this case we are sorting by a hash field we used the following pattern “question:*->votes“. With this pattern we are saying to look for the key “question:*” where this “*” will be replaced by the value from the list. As it is a hash we need to inform the field, we do this using the pattern “->votes“.

private List<List> getQuestionsByVotes(){
		List question_ids = jedis.sort("questions", new SortingParams().by("question:*->votes").desc());
		List<List> questions = new ArrayList<List>();
		for(String question_id : question_ids){
			questions.add(getQuestion(question_id,"question"));
		}
		return questions;		
	}

Here is the final code:

package Cache;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
 
import redis.clients.jedis.Jedis;
import redis.clients.jedis.SortingParams;
 
public class StackOverflow {
 
	private Jedis jedis;
 
	public StackOverflow(){
		this.jedis = new Jedis("localhost");
	}
 
	public static void main(String[] args) {
		StackOverflow so = new StackOverflow();
		Long question_id = so.saveQuestion("Who is best batsman of the world?", 0);
		Long question_id2 = so.saveQuestion("Who is best bowler of the world?", 0);
		Long question_id3 = so.saveQuestion("Who is best fielder of the world?", 10);
 
		Long answer_id1 = so.saveAnswer(question_id, "Virat kohli without any doubt", "Uday Ogra", 0);
		Long answer_id2 = so.saveAnswer(question_id, "I think ABD Villiers", "Shikha Sharma", 0);
		so.upvoteQuestion(question_id);
		so.upvoteQuestion(question_id);
		so.upvoteQuestion(question_id);
		so.upvoteAnswer(answer_id1);
		so.upvoteAnswer(answer_id1);
 
		System.out.println("First question with number of votes : " + so.getQuestion(question_id + "","question","votes"));
		System.out.println("Answers for first question : " + so.getAnswers(question_id));
 
		System.out.println("First 2 questions for homepage : " + so.getQuestions(1));
 
		System.out.println("Sorted questions for homepage : " + so.getQuestionsByVotes());
 
 
	}
 
	private Long createQuestionId(){
		return jedis.hincrBy("unique_ids", "question", 1);
	}
 
	private Long createAnswerId(){
		return jedis.hincrBy("unique_ids", "answer", 1);
	}
 
	private Long saveQuestion(String question, int votes){
		Long id = createQuestionId();
		jedis.hset("question:" + id, "question", question);
		jedis.hset("question:" + id, "votes", votes+"");
 
		jedis.lpush("questions", id + "");
 
		return id;
 
	}
 
	private List getQuestion(String question_id,String... parameters){
		List question = jedis.hmget("question:" + question_id,parameters);
		return question;		
	}
 
	private List<List> getQuestions(int range){
		List<List> questions = new ArrayList<List>();
		List question_ids = jedis.lrange("questions", 0, range);
		for(String question_id : question_ids){
			questions.add(getQuestion(question_id,"question"));
		}
		return questions;		
	}
 
	private List<List> getQuestionsByVotes(){
		List question_ids = jedis.sort("questions", new SortingParams().by("question:*->votes").desc());
		List<List> questions = new ArrayList<List>();
		for(String question_id : question_ids){
			questions.add(getQuestion(question_id,"question"));
		}
		return questions;		
	}
 
	private Long saveAnswer(Long question_id, String answer, String answerer, int votes){
		Long id = createAnswerId();
		jedis.hset("answer:" + id, "question_id", question_id+"");
		jedis.hset("answer:" + id, "answer", answer);
		jedis.hset("answer:" + id, "answerer", answerer);
		jedis.hset("answer:" + id, "votes", votes+"");
 
		jedis.sadd("question_answers:" + question_id + "", id + "");
 
		return id;
	}
 
	private List<List> getAnswers(Long question_id){
		List<List> answers = new ArrayList<List>();
		Set answer_ids = jedis.smembers("question_answers:" + question_id + "");
		for(String answer_id : answer_ids){
			answers.add(getAnswer(answer_id));
		}
		return answers;
 
	}
 
	private List getAnswer(String answer_id){
		List answer = jedis.hmget("answer:" + answer_id,"answer","votes"); 
		return answer;
 
	}
 
	private Long upvoteQuestion(Long question_id){
 
		return jedis.hincrBy("question:" + question_id, "votes", 1);
	}
 
	private Long upvoteAnswer(Long answer_id){
		return jedis.hincrBy("answer:" + answer_id, "votes", 1);
	}
 
}

The output of above code would be:

First question with number of votes : [Who is best batsman of the world?, 3]
Answers for first question : [[Virat kohli without any doubt, 2], [I think ABD Villiers, 0]]
First 2 questions for homepage : [[Who is best fielder of the world?], [Who is best bowler of the world?]]
Sorted questions for homepage :[Who is best bowler of the world?], [Who is best batsman of the world?], [Who is best fielder of the world?]

Mar Java Mit Java

Uday Ogra

Connect with me at http://facebook.com/tendulkarogra and lets have some healthy discussion :)

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *