Redis related Java implementation of article voting site


2. Redis related implementation of shopping website (Java)

Requirements:

1. To build an article voting site, articles need to get at least 200 votes in a day in order to be shown at the top of the list of articles that day.

2. However, in order to avoid the posts that are published for a long time remaining at the top of the list due to the accumulated number of votes, we need to have a scoring mechanism that continuously decreases points as time goes by.

3, so specific ratings calculation method is: will get the support of the articles multiplied by a constant 432 (by the number of seconds to 86400 divided by the number of articles to show a day a day’s worth of 200 draw votes), then add the release time of the article, it is concluded that the result is that the article score.

Redis design

(1) For each article in the website, a hash is needed to store the title of the article, the URL pointing to the article, the user who publishes the article, the publication time of the article, the number of votes obtained by the article and other information.

In order to facilitate the website to display articles according to the order of publication and the score of articles, we need two ordered collections to store articles: (2) Ordered collection, member is the article ID, and the score value is the publication time of articles.

(3) Ordered set, the members are article IDS, and the score is the score of the article.

(4) In order to prevent users from voting for the same article more than once, a list of users who have voted should be recorded for each article. Use collections to store user ids that have been voted. Since collections cannot store more than one of the same elements, the same user does not vote for the same article more than once.

(5) Articles support group function, so users can only see articles related to a specific topic, such as “Python” or “Redis” articles, etc. At this time, we need a collection to record group articles. For example, programming groups

To save memory, users will not be able to vote on an article a week after it expires, its ratings will be fixed, and the collection of lists of users who voted for the article will be deleted.

Code design

1. When a user wants to publish an article,

(1) Execute INCR with a counter counter to create a new article ID.

(2) Use SADD to add the article publisher ID to the collection that records the list of articles voted by users, and use EXPIRE command to set an expiration time for the collection. Redis will automatically delete the collection one week after the articles EXPIRE.

(3) Use HMSET command to store the relevant information of articles, and execute two ZADD commands to add the initial score and release time of articles to two corresponding ordered sets respectively.

public class Chapter01 {
  private static final int ONE_WEEK_IN_SECONDS = 7 * 86400;          Users will not be able to vote on an article one week after it expires.
  private static final int VOTE_SCORE = 432;                        // Calculate the scoring time multiplied by the number of votes in support by dividing the number of seconds in a day by (86400) the number of votes in support (200) needed for the article to display a day
  private static final int ARTICLES_PER_PAGE = 25;

  1. To create a new post id, run INCY on a counter (count). * The program needs to use SADD to add the ID of the article publisher to the list of users that have voted for the article, * and use the EXPIRE command to set an expiration time for the collection. Redis will automatically delete the collection one week after the publication of the article expires. *3. Then the program will use HMSET command to store the relevant information of the article, and execute two ZADD, add the initial score and release time of the article to two corresponding ordered sets respectively. * /
public String postArticle(Jedis conn, String user, String title, String link) {
    // create a new article ID
    String articleId = String.valueOf(conn.incr("article:"));     ValueOf (int I) : Converts int I to a String

    String voted = "voted:" + articleId;
    // add to the list of registered users.
    conn.sadd(voted, user);
    //3. Set the expiration time to one week
    conn.expire(voted, ONE_WEEK_IN_SECONDS);

    long now = System.currentTimeMillis() / 1000;
    String article = "article:" + articleId;
    // create a HashMap.
    HashMap<String,String> articleData = new HashMap<String,String>();
    articleData.put("title", title);
    articleData.put("link", link);
    articleData.put("user", user);
    articleData.put("now", String.valueOf(now));
    articleData.put("oppose"."0");
    articleData.put("votes"."1");
    // Store the article information in a hash.
    //HMSET key field value [field value ...]
    // Set multiple field-value pairs to hash key at the same time.
    // This command overwrites existing fields in the hash table.
    conn.hmset(article, articleData);
    //6. Add articles to the ordered collection with a more graded order
    //ZADD key score member [[score member] [score member] ...]
    // Add one or more member elements and their score values to the ordered set key
    conn.zadd("score:", now + VOTE_SCORE, article);
    // add articles to the ordered collection with a more published chronological order.
    conn.zadd("time:", now, article);

    returnarticleId; }}Copy the code

2. When users try to vote on an article,

(1) Use ZSCORE command to check the ordered set of recording the release time of the article (Redis design 2), and judge whether the release time of the article is not more than a week.

(2) If the article is still in the voting time range, then use SADD to add the user to the collection that records the list of users who voted for the article (Redis design 4).

(3) If the previous step is successful, it means that the user voted for this article for the first time, so use the ZINCRBY command to increase the score of the article by 432(ZINCRBY command is used to increment the score of the members of the ordered set);

And use the HINCRBY command to update the number of article votes recorded in the hash

* 1, when the user tries to vote on an article, the program will use the ZSCORE command to check the ordered set of recorded text release time, and determine whether the article was published more than a week ago. * 2. If the article is still within the voteable time range, the program will use the SADD command to try to add the user to the collection of the list of voted users that record the article. * 3. If the vote is successfully executed, it means that the user voted for this article for the first time, and the program will use ZINCYBY command to increase the score of the article by 432(ZINCYBY command is used to increment the score of the members of the ordered set). * And update the number of article votes in the hash record with the HINCRBY command (the HINCRBY command is used to increment the value stored in the hash) */
public void articleVote(Jedis conn, String user, String article) {
    //1. Calculate the deadline for voting articles.
    long cutoff = (System.currentTimeMillis() / 1000) - ONE_WEEK_IN_SECONDS;
    //2, check whether you can still vote on the article, (although you can also use hashes to get the article published time, but the ordered collection returns the article published time as a floating point number, can not be converted directly)
    if (conn.zscore("time:", article) < cutoff){
        return;
    }

    // select articleId from articleId.
    //nt indexOf(int ch,int fromIndex) is the first position where the character ch appears after the fromIndex bit. If I don't find it, I'm going to return -1
    Substring (Int32) Retrieves substrings from this instance. The substring starts at the specified character position.
    String articleId = article.substring(article.indexOf(':') + 1);
    //4, check whether the user voted for this article for the first time, if it is the first time, then increase the number of votes and ratings of this article.
    if (conn.sadd("voted:" + articleId, user) == 1) {                       // Add one or more member elements to the collection key. Members already in the collection are ignored.
        // Add increment to score of member of ordered set key.
        Increment = score; // Increment = score;
        ZINCRBY key Increment Member ZINCRBY key Increment Member ZINCRBY key Increment Member ZINCRBY key Increment Member ZINCRBY key Increment Member ZINCRBY key Increment Member
        //ZINCRBY salary 2000
        conn.zincrby("score:", VOTE_SCORE, article);

        // add increment increment to field; // Add increment to field.
        // The increment can also be negative, which is equivalent to subtracting a given field.
        //HINCRBY counter page_view 200
        conn.hincrBy(article, "votes".1L); }}/** * Vote against */
public void articleOppose(Jedis conn, String user, String article) {

    long cutoff = (System.currentTimeMillis() / 1000) - ONE_WEEK_IN_SECONDS;

    // Cutoff articles published before cannot be voted on
    if (conn.zscore("time:", article) < cutoff){
        return;
    }


    String articleId = article.substring(article.indexOf(':') + 1);

    // Check to see if user voted for this article
    If sadd returns 0, the set already contains data
    // If 1 is returned, the data is not yet available
    if (conn.sadd("oppose:" + articleId, user) == 1) {
        conn.zincrby("score:", -VOTE_SCORE, article);
        conn.hincrBy(article, "votes", -1L); }}Copy the code

3. We have implemented the function of voting articles and publishing articles. Now we need to consider how to take out the highest rated articles and how to take out the latest published articles

(1) We need to use the ZREVRANGE command to fetch multiple article ids. (Since ordered collections arrange elements according to the members’ score from smallest to largest, use ZREVRANGE to extract the article ids in order of highest to lowest score)

(2) Execute the HGETALL command once for each article ID to retrieve the detailed information of the article.

This method can be used to retrieve both the highest rated articles and the most recently published articles.


public List<Map<String,String>> getArticles(Jedis conn, int page) {
    // Call the overloaded method below
    return getArticles(conn, page, "score:");
}
/** * Retrieve the highest rated article and the latest published article * 1. The program needs to first use ZREVRANGE to fetch multiple article ids, and then execute HGETALL command once for each article ID to fetch the details of the article. * This method can be used to fetch the highest score of the article, and can be used to fetch the latest published article. * A note of caution: * Because ordered collections sort the elements from smallest to largest by the value of the members, it is correct to use the ZREVRANGE command to extract the article IDS in the order of the largest to smallest points * */

public List<Map<String,String>> getArticles(Jedis conn, int page, String order) {
    //1, set the start index and end index of the article.
    int start = (page - 1) * ARTICLES_PER_PAGE;
    int end = start + ARTICLES_PER_PAGE - 1;
     // select * from *;
    Set<String> ids = conn.zrevrange(order, start, end);
    List<Map<String,String>> articles = new ArrayList<Map<String,String>>();
    for (String id : ids){
        // get the details of the article according to the article ID
        Map<String,String> articleData = conn.hgetAll(id);
        articleData.put("id", id);
        // add to the ArrayList container
        articles.add(articleData);
    }

    return articles;
}

Copy the code

4. Group articles so that users can only see articles on related topics that interest them.

The group function mainly has two parts: one is responsible for recording which group articles belong to, and the other is responsible for taking out articles in the group.

To keep track of what articles are saved for each group, you need to create a collection for each group and record all the article ids that belong to the same group into that collection.

Redis can perform operations not only on multiple collections, but in some cases, between collections and ordered collections
public void addGroups(Jedis conn, String articleId, String[] toAdd) {
    //1, build the key name to store the article information
    String article = "article:" + articleId;
    for (String group : toAdd) {
        //2. Add the post to the group it belongs to
        conn.sadd("group:"+ group, article); }}Copy the code

Since we also need to sort and paginate group articles by rating or publication time, we need to store all articles in the same group in an ordered collection by rating or publication time. However, we already have the ordered set of all articles according to the score and release time. We do not need to save the relevant ordered set in each group any more. We can get the ordered set of scores and release time of all articles in each group by taking out the intersection of the group article set and related ordered set.

Redis’s ZINTERSTORE command can take multiple sets and ordered sets as input, find all members that exist in both sets and ordered sets, and combine the scores of those members in several different ways (branches of all set members are treated as 1).

For article voting sites, you can use the ZINTERSTORE command to select the highest score of the same member as the score of the intersection member: depending on the sorting option used, these scores can be either the article’s rating or when the article was published.

The following diagram shows the process of executing the ZINTERSTORE command:

A new ordered set score:programming is obtained by computing the intersection of set groups:programming and ordered set Score:, which contains all members existing in both set groups:programming and ordered set Score:. Because all member scores of the set groups:programming are treated as 1, and all member scores of the ordered set Score: are greater than 1, the points selected by this intersection calculation are the maximum points of the same member. The scores of the members of the ordered set score are determined by the scores of the members of the ordered set score.

So, our operations are as follows:

(1) Execute the ZINTERSTORE command through the ordered set of group articles and scores or the ordered set of release time, and get the ordered set of related group articles.

(2) If there are a lot of group articles, it will take a lot of time to execute ZINTERSTORE. In order to minimize the workload of Redis, we cache the ordered collection queried to minimize the execution times of ZINTERSTORE commands.

To keep an orderly collection of the latest group articles available after continuous updates, we only cache the results for 60 seconds.

(3) Use the getArticles function from the previous step to page and get group articles.

public List<Map<String,String>> getGroupArticles(Jedis conn, String group, int page) {
    // Call the overloaded method below
    return getGroupArticles(conn, group, page, "score:");
}

/ * * * * in order to be able to take out the inside of the group article according to the score to sorting and paging group articles, web site needs to be in the same group of all articles according to grading storage into an ordered set of * application need to use ZINTERSTORE command to select the same members as members of the intersection of the branch the largest value: Depending on the ranking options used, this can be a rating or when the article was published. * /
public List<Map<String,String>> getGroupArticles(Jedis conn, String group, int page, String order) {
     Create a key for each order in each group.
    String key = order + group;
    //2, check whether there is a cached sort result, if not, sort.
    if(! conn.exists(key)) {//3. Sort group articles by rating or publication date
        ZParams params = new ZParams().aggregate(ZParams.Aggregate.MAX);
        conn.zinterstore(key, params, "group:" + group, order);
        // Make Redis automatically delete the ordered collection after 60 seconds
        conn.expire(key, 60);
    }
    //4. Call getArticles() to paginate and retrieve article data
    return getArticles(conn, page, key);
}
Copy the code

The above is a redis implementation of an article voting site.

The test code is as follows:

public static final void main(String[] args) {
    new Chapter01().run();
}

public void run(a) {
    // Initialize the redis connection
    Jedis conn = new Jedis("localhost");
    conn.select(15);

    //2
    String articleId = postArticle(
        conn, "guoxiaoxu"."A title"."http://www.google.com");
    System.out.println("I posted an article with the id: :" + articleId);
    System.out.println("The hash format for the saved article is as follows:");
    Map<String,String> articleData = conn.hgetAll("article:" + articleId);
    for (Map.Entry<String,String> entry : articleData.entrySet()){
        System.out.println("" + entry.getKey() + ":" + entry.getValue());
    }

    System.out.println();
    //2, test article voting process
    articleVote(conn, "other_user"."article:" + articleId);
    String votes = conn.hget("article:" + articleId, "votes");
    System.out.println("We vote for this article, the current number of votes for this article." + votes);
    assert Integer.parseInt(votes) > 1;
    //3, test the voting process for articles
    articleOppose(conn, "other_user"."article:" + articleId);
    String oppose = conn.hget("article:" + articleId, "votes");
    System.out.println("We vote against this article, the current number of votes against this article." + oppose);
    assert Integer.parseInt(oppose) > 1;

    System.out.println("The current top-scoring article is:");
    List<Map<String,String>> articles = getArticles(conn, 1);
    printArticles(articles);
    assert articles.size() >= 1;

    addGroups(conn, articleId, new String[]{"new-group"});
    System.out.println("We're pushing posts to new groups, and other posts include:");
    articles = getGroupArticles(conn, "new-group".1);
    printArticles(articles);
    assert articles.size() >= 1;
}
Copy the code

reference

1. Redis related implementation of article voting site (Python)

Redis combat related code, currently have Java, JS, Node, Python

2.Redis command reference

The code address

Github.com/guoxiaoxu/r…

instructions

If you have the patience to read this far, allow me to clarify:

  • 1. The topic structure of this article refers to the redis related implementation of the article voting website (Python).

  • 2, leaving repeated notes is for their own contrast, and strive to make themselves different

  • 3. Through a day of analysis and learning. The more I felt there was so much to learn. Rather than simply memorizing a few commands

  • Thank you all, thank SegmentFault, let you witness the process of my transformation.