当前位置:网站首页>Use redis' sorted set to make weekly hot Reviews
Use redis' sorted set to make weekly hot Reviews
2022-06-25 01:15:00 【Java confidant_】
Click on the official account , Practical technical articles Know in time
General idea
Thought analysis
Do weekly hot discussion , Cache should be used to do , If you check the library directly , It puts pressure on the database . If you do it with a cache , use Redis It's more appropriate to cache .

# utilize Redsi add to Data command
# day:1 Refer to 1 When no. post:1 The first article added 10 comments .
# Back 6 post:2 refer to stay 1 The second part of No. 2 added 6 comments
zadd day:1 10 post:1 6 post:2
zadd day:2 10 post:1 6 post:2
zadd day:3 10 post:1 6 post:2
....
zadd day:8 10 post:1 6 post:2
# That's it 7 A record of days
The above command can help us record 7 Number of all comments in days . But it hasn't helped us figure out who is the most commented . see Redis Of sorted set An ordered set has a command that can help us achieve this function .
This command can help us achieve union , We just have to take 7 You can find out by combining the comments of days .

# Redis command
# It means union, put this 7 Days of Put it in a new collection The new set is week:rank In this way, there will be our... In this new collection
#7 Days of records
union week:rank 7 day:1...day:8
Redis Command practice to see
Local command line test

127.0.0.1:6379> ping
PONG
127.0.0.1:6379> zadd day:1 10 post:1
(integer) 1
127.0.0.1:6379> zadd day:2 10 post:1
(integer) 1
127.0.0.1:6379> zadd day:3 10 post:1
(integer) 1
127.0.0.1:6379> zadd day:1 5 post:2
(integer) 1
127.0.0.1:6379> zadd day:2 5 post:2
(integer) 1
127.0.0.1:6379> zadd day:3 5 post:2
(integer) 1
127.0.0.1:6379> keys *
1) "day:1"
2) "day:2"
3) "day:3"
View the leaderboard command of the day ZRevrange
127.0.0.1:6379> zrevrange day:1 0 -1 withscores
1) "post:1"
2) "10"
3) "post:2"
4) "5"
127.0.0.1:6379>
Weekly review leaderboard records . Because I only have three days , So I just wrote it 3 Days of
127.0.0.1:6379> zrevrange week:rank 0 -1 withscores
1) "post:1"
2) "30"
3) "post:2"
4) "15"
127.0.0.1:6379>
The above record is correct . The above commands can help us simply realize our idea , Here's how to do it in code . There's a small one on it bug Is that when day:1
This day may appear, but it can't be over directly . May increase one by one , At this time, you should use the auto increment command to solve this problem .
# When +1 When -1 It's when you When you add a comment, add 1 When you delete, subtract 1
ZINCRBY day:1 10 post:1
Code to implement
Current front end style , In this case, we need to start this function at the beginning of the project


@Component
// Realization Start class , also Contextual servlect
public class ContextStartup implements ApplicationRunner, ServletContextAware {
// Inject categoryService
@Autowired
IMCategoryService categoryService;
ServletContext servletContext;
// Inject post The service class
@Autowired
IMPostService postService;
@Override
public void run(ApplicationArguments args) throws Exception {
// Call the method of full query
List<MCategory> list = categoryService.list(new QueryWrapper<MCategory>().eq("status", 0));
servletContext.setAttribute("List", list);
// Call the method of weekly hot review
postService.initweek();
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
Service serviceimpl class
General idea
obtain 7 Articles published within days
Initialize the total reading of the article
Cache the basic information of the article (id, title , comments , author ID )
In this way, we can avoid searching the database . You can use our cache directly .
Do Union
I need a Redis Tool class of , I found it on the Internet , I didn't write it . There's a lot of stuff on the Internet . Just use it directly
package com.example.springbootblog.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* Specify cache expiration time
*
* @param key key
* @param time Time ( second )
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* according to key Get expiration time
*
* @param key key Not for null
* @return Time ( second ) return 0 Stands for permanent validity
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* Judge key Whether there is
*
* @param key key
* @return true There is false non-existent
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* Delete cache
*
* @param key You can pass a value Or more
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* Normal cache fetch
*
* @param key key
* @return value
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* Normal cache put in
*
* @param key key
* @param value value
* @return true success false Failure
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* The normal cache is put in and set the time
*
* @param key key
* @param value value
* @param time Time ( second ) time Be greater than 0 If time Less than or equal to 0 Will be set indefinitely
* @return true success false Failure
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* Increasing
*
* @param key key
* @param delta How much more should I add ( Greater than 0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException(" The increment factor must be greater than 0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* Decline
*
* @param key key
* @param delta To cut it down a few ( Less than 0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException(" The decline factor must be greater than 0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
*
* @param key key Not for null
* @param item term Not for null
* @return value
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* obtain hashKey All the corresponding key values
*
* @param key key
* @return Corresponding multiple key values
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @param key key
* @param map Corresponding to multiple key values
* @return true success false Failure
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet And set the time
*
* @param key key
* @param map Corresponding to multiple key values
* @param time Time ( second )
* @return true success false Failure
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* To a piece of hash Put data in the table , If it doesn't exist, it will create
*
* @param key key
* @param item term
* @param value value
* @return true success false Failure
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* To a piece of hash Put data in the table , If it doesn't exist, it will create
*
* @param key key
* @param item term
* @param value value
* @param time Time ( second ) Be careful : If there is already hash Watch has time , This will replace the original time
* @return true success false Failure
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* Delete hash The values in the table
*
* @param key key Not for null
* @param item term Can make multiple Not for null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* Judge hash Whether there is a value of this item in the table
*
* @param key key Not for null
* @param item term Not for null
* @return true There is false non-existent
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash Increasing If it doesn't exist , It creates a And return the added value to
*
* @param key key
* @param item term
* @param by How much more should I add ( Greater than 0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash Decline
*
* @param key key
* @param item term
* @param by Remember less ( Less than 0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
//============================set=============================
/**
* according to key obtain Set All the values in
*
* @param key key
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* according to value From a set Query in , Whether there is
*
* @param key key
* @param value value
* @return true There is false non-existent
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* Put data into set cache
*
* @param key key
* @param values value It can be more than one
* @return The number of successes
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* take set Data is put into the cache
*
* @param key key
* @param time Time ( second )
* @param values value It can be more than one
* @return The number of successes
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* obtain set The length of the cache
*
* @param key key
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* The removal value is value Of
*
* @param key key
* @param values value It can be more than one
* @return Number of removed
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* obtain list The contents of the cache
*
* @param key key
* @param start Start
* @param end end 0 To -1 For all values
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* obtain list The length of the cache
*
* @param key key
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* Through the index obtain list The value in
*
* @param key key
* @param index Indexes index>=0 when , 0 Header ,1 The second element , By analogy ;index<0 when ,-1, Tail ,-2 The next to last element , By analogy
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* take list Put into cache
*
* @param key key
* @param value value
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* take list Put into cache
*
* @param key key
* @param value value
* @param time Time ( second )
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* take list Put into cache
*
* @param key key
* @param value value
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* take list Put into cache
*
* @param key key
* @param value value
* @param time Time ( second )
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* Modify according to the index list A piece of data in
*
* @param key key
* @param index Indexes
* @param value value
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* remove N The values are value
*
* @param key key
* @param count How many removed
* @param value value
* @return Number of removed
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//================ Ordered set sort set===================
/**
* Orderly set Additive elements
*
* @param key
* @param value
* @param score
* @return
*/
public boolean zSet(String key, Object value, double score) {
return redisTemplate.opsForZSet().add(key, value, score);
}
public long batchZSet(String key, Set<ZSetOperations.TypedTuple> typles) {
return redisTemplate.opsForZSet().add(key, typles);
}
public void zIncrementScore(String key, Object value, long delta) {
redisTemplate.opsForZSet().incrementScore(key, value, delta);
}
public void zUnionAndStore(String key, Collection otherKeys, String destKey) {
redisTemplate.opsForZSet().unionAndStore(key, otherKeys, destKey);
}
/**
* obtain zset Number
* @param key
* @param value
* @return
*/
public long getZsetScore(String key, Object value) {
Double score = redisTemplate.opsForZSet().score(key, value);
if(score==null){
return 0;
}else{
return score.longValue();
}
}
/**
* Get ordered set key Member of the member Ranking .
* The members of the ordered set press score The value is decreasing ( From big to small ) Sort .
* @param key
* @param start
* @param end
* @return
*/
public Set<ZSetOperations.TypedTuple> getZSetRank(String key, long start, long end) {
return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
}
}
Code of implementation class
// The method of weekly hot review
@Override
public void initweek() {
// obtain 7 Day's article
List<MPost> posts = this.list(new QueryWrapper<MPost>().ge("created", DateUtil.lastWeek())
.select("id", "title", "user_id", "comment_count", "view_count", "created")
);// Get 7 Days ago and according to these queries , You don't need to query all
// Initialize the general comments of the article
for (MPost post : posts) {
// Set up key
String key = "day:rank:" + DateUtil.format(post.getCreated(), DatePattern.PURE_DATE_FORMAT);
// Number of comments cached
redisUtil.zSet(key, post.getId(), post.getCommentCount());
// Set auto expiration 7 Days overdue
long between = DateUtil.between(new Date(), post.getCreated(), DateUnit.DAY);
long expireTime = (7 - between) * 24 * 60 * 60; // It works Time
redisUtil.expire(key, expireTime);
// Cache some basic information of the article
this.hashCachePost(post, expireTime);
}
// Do Union
this.zunionAndStore();
}
/**
* The number of comments per week is combined
**/
private void zunionAndStore() {
String destkey = "day:rank:" + DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT);
// Set new after union key
String newkey = "week:rank";
ArrayList<String> otherKeys = new ArrayList<>();
// Calculation 7 Days of
for (int i = -6; i < 0; i++) {
String temp = "day:rank:" + DateUtil.format(DateUtil.offsetDay(new Date(), i), DatePattern.PURE_DATE_FORMAT);
otherKeys.add(temp);
}
redisUtil.zUnionAndStore(destkey, otherKeys, newkey);
}
/**
* Article author
**/
private void hashCachePost(MPost post, long expireTime) {
// Set up key
String key = "rank:post:" + post.getId();
// Judge whether there is
boolean hasKey = redisUtil.hasKey(key);
if (!hasKey) {
// Just put it in the cache
redisUtil.hset(key, "post:id", post.getId(), expireTime);
redisUtil.hset(key, "post:title", post.getTitle(), expireTime);
redisUtil.hset(key, "post:commentCount", post.getCommentCount(), expireTime);
}
}
}
In this way, we can convert our command line into code form . Then we can store the data of our database in the cache first .
effect
127.0.0.1:6379> keys *
1) "rank:post:4"
2) "week:rank"
3) "day:rank:20210724"
4) "rank:post:3"
5) "rank:post:2"
6) "day:rank:20210726"
# Look at the data after our union id by 3 There are 1 comments .
127.0.0.1:6379> zrevrange week:rank 0 -1 withscores
1) "3"
2) "1"
3) "2"
4) "1"
5) "4"
6) "0"
127.0.0.1:6379>
In the database id by 3 There are 1 comments


There is really only one comment
The front end is displayed
The idea here is relatively simple , Just take our data out of the cache . With freemarker You can customize the label . I customized the label .
Hosttemplate
/**
* Hot discussion this week
*/
@Component
public class HotsTemplate extends TemplateDirective {
@Autowired
RedisUtil redisUtil;
@Override
public String getName() {
return "hots";
}
@Override
public void execute(DirectiveHandler handler) throws Exception {
// Set up key
String key ="week:rank";
Set<ZSetOperations.TypedTuple> zSetRank = redisUtil.getZSetRank(key, 0, 6);
ArrayList<Map> maps = new ArrayList<>();
// facilitate
for (ZSetOperations.TypedTuple typedTuple : zSetRank) {
// establish Map
HashMap<String, Object> map = new HashMap<>();
Object post_id = typedTuple.getValue();
String PostHashKey = "rank:post:" +post_id;
map.put("id",post_id);
map.put("title",redisUtil.hget(PostHashKey,"post:title"));
map.put("commentCount",typedTuple.getScore());
maps.add(map);
}
handler.put(RESULTS,maps).render();
}
}
stay FreemarkerConfig
We can use our custom tags by injecting the notes we wrote
@Configuration
public class FreemarkerConfig {
@Autowired
private freemarker.template.Configuration configuration;
@Autowired
PostsTemplate postsTemplate;
@Autowired
HotsTemplate hotsTemplate;
@PostConstruct
public void setUp() {
configuration.setSharedVariable("timeAgo", new TimeAgoMethod());
configuration.setSharedVariable("posts", postsTemplate);
configuration.setSharedVariable("hosts", hotsTemplate);
}
}
Get the front page

effect

summary
When doing this function . Not comprehensive enough . Although it's finished , But it should be to get 7 Comments within days . I got 7 Day's article . Though it is a bug But I don't want to fix . be it so . Just use it now . It's the same thing . There will be time for problems. Let's change it . Tired , Ruthless code machines record code life .....
source :blog.csdn.net/m0_46937429/article/
details/119172118
recommend
Technical involution group , Learn together !!
PS: Because the official account platform changed the push rules. , If you don't want to miss the content , Remember to click after reading “ Looking at ”, Add one “ Star standard ”, In this way, each new article push will appear in your subscription list for the first time . spot “ Looking at ” Support us !
边栏推荐
- 对技术的乐观,正让戴尔取得比想象中更多的成就
- Programmer: did you spend all your savings to buy a house in Shenzhen? Or return to Changsha to live a "surplus" life?
- Scala template method pattern
- 4 ans d'expérience de travail, 5 modes de communication Multi - thread ne peuvent pas être décrits, vous osez croire?
- Bi-sql like
- Text editor of QT project practice ---------- episode 11
- 高考之后,必然会出现以下四种情况:
- 【无标题】
- 卷积与转置卷积
- Bi-sql top
猜你喜欢
I 刷题 I — 复制带随机指针的链表
图书馆管理系统代码源码(php+css+js+mysql) 完整的代码源码
Scala IO reads by lexical units and numbers
Using macro code to generate handwriting automatically in word or WPS
51 single chip microcomputer multi computer communication
2022r1 quick opening pressure vessel operation test questions and answers
Syntax highlighting of rich text
15.线程同步的几种方法
Preliminary understanding of qtoolbutton
Bi SQL alias
随机推荐
生成订单30分钟未支付,则自动取消,该怎么实现?
Text border format and text block of rich text
Bi skill - judge 0 and null
汇编语言(4)函数传参
Assembly language (4) function transfer parameters
How much commission does CICC wealth securities open an account? Is stock account opening and trading safe and reliable?
Which securities company should I choose to open an account online? Is it safe to open an account online?
Start service 11111
Distinguish between i++ and ++i seconds
Preliminary understanding of qtoolbutton
Examination questions and mock examination for safety management personnel of hazardous chemical business units in 2022
Heavyweight: the domestic ide was released, developed by Alibaba, and is completely open source! (high performance + high customization)
Leetcode 1248. Statistics of "graceful subarray" (harm, suddenly found that it can only enumerate violently)
Bi-sql select into
Tencent has completed the comprehensive cloud launch to build the largest cloud native practice in China
重磅:国产IDE发布,由阿里研发,完全开源!(高性能+高定制性)
adb shell sendevent
ImageView shows network pictures
图书馆管理系统代码源码(php+css+js+mysql) 完整的代码源码
生态护航 云服务商挥起“英特尔大旗”