This is the fifth day of my participation in the August More text Challenge. For details, see: August More Text Challenge
@TOC
Thinking about
Thought analysis
Do weekly hot talk, should use the cache to do, if directly check the database, will cause pressure on the database. If you’re doing it with a cache, Redis is a better way to do it.
Add data using Redsi
# Day :1 refers to 10 comments added to the first post at Post :1 on the 1st.
6 post:2 refers to the 6 comments added to the second post on # 1
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
# This completes 7 days of recording
Copy the code
The above command will help us keep track of the total number of comments for 7 days. But it hasn’t helped us figure out who gets the most reviews. Just look at Redis’ sorted set. There’s a command that does this for you.
This command can help us achieve union, we just need to make a union of 7 days of comments to find,.
# Redis command
We're going to put the 7 days into a new set called week: Rank and then we're going to have ours in the new set
#7 days of recordunion week:rank 7 day:1... day:8Copy the code
Put the Redis command into practice
Local command line tests
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"
Copy the code
View the leaderboard command ZRevrange for the day
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 >Copy the code
A weekly review list. Because I only have three days, so I only wrote three days
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 >Copy the code
There is no error in the record above. The above command can help us to implement our idea easily. Let’s do this in code. There is also a small bug on it when the day:1 May appear but it is not possible to pass it directly. It is possible to increase one by one, and you should use the increment command to solve this problem.
# When +1 when -1 is when you add a comment you add 1 and when you delete it you subtract 1
ZINCRBY day:1 10 post:1
Copy the code
Code to implement
The current style of the front end, so that we need to start this feature at the beginning of the project
@Component
// Implement the boot class, as well as the context servlect
public class ContextStartup implements ApplicationRunner.ServletContextAware {
/ / into the categoryService
@Autowired
IMCategoryService categoryService;
ServletContext servletContext;
// Inject the post service class
@Autowired
IMPostService postService;
@Override
public void run(ApplicationArguments args) throws Exception {
// Call the full lookup method
List<MCategory> list = categoryService.list(new QueryWrapper<MCategory>().eq("status".0));
servletContext.setAttribute("List", list);
// Call the weekly heat review method
postService.initweek();
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext; }}Copy the code
This is the general idea of the ServiceImpl class
2, initialize the total number of articles read // Cache the basic information of the article (ID, title, number of comments, author ID) // so as to avoid library lookup. We can just use our cache. 3, // do unionCopy the code
Here we need a Redis utility class, which I found on the Internet, not written by me. Lots of them on the Internet. Just use it
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;
/** * specifies the cache expiration time **@paramThe key key *@paramTime Time (seconds) x@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; }}/** * Get expiration time based on key **@paramThe key cannot be null *@returnTime (seconds) returns 0 for permanent */
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/** * Determine whether the key has **@paramThe key key *@returnTrue Exists false Does not exist */
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false; }}/** * Delete cache **@paramThe key can pass one 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=============================
/** * Get ** from normal cache@paramThe key key *@returnValue * /
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/** * Normal cache put **@paramThe key key *@paramThe value value *@returnTrue Succeeded false Failed */
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false; }}/** * Normal cache put and set time **@paramThe key key *@paramThe value value *@paramTime Time (seconds) Time must be greater than 0. If time is less than or equal to 0, set indefinite *@returnTrue Succeeded false Failed */
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; }}/** * Increments **@paramThe key key *@paramHow much more delta (greater than 0) *@return* /
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("The increment factor must be greater than zero.");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/** **@paramThe key key *@paramDelta to be reduced by (less than 0) *@return* /
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("The decreasing factor must be greater than zero.");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* HashGet
*
* @paramThe key cannot be null *@paramItem Items cannot be null *@returnValue * /
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/** * Get all key values for a hashKey **@paramThe key key *@returnMultiple corresponding keys */
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
*
* @paramThe key key *@paramMap corresponds to multiple keys *@returnTrue Succeeded false Failed */
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 time **@paramThe key key *@paramMap corresponds to multiple keys *@paramTime Time (seconds) x@returnTrue Succeeded false Failed */
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; }}/** * puts data into a hash table, creating a hash table if it does not exist@paramThe key key *@paramItem item *@paramThe value value *@returnTrue Succeeded false Failed */
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; }}/** * puts data into a hash table, creating a hash table if it does not exist@paramThe key key *@paramItem item *@paramThe value value *@paramTime Time (seconds) Note: If an existing hash table has a time, the original time * will be replaced@returnTrue Succeeded false Failed */
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 the hash table value **@paramThe key cannot be null *@paramItem can make multiple items not null */
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/** * Determines whether the hash table has a value for this entry **@paramThe key cannot be null *@paramItem Items cannot be null *@returnTrue Exists false Does not exist */
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/** * If the hash increment does not exist, one is created and the new value is returned@paramThe key key *@paramItem item *@paramHow much do I have to increase by *@return* /
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/** * Hash decrement **@paramThe key key *@paramItem item *@paramBy is going to be reduced by (less than 0) *@return* /
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
//============================set=============================
/** * get all values in the Set based on key **@paramThe key key *@return* /
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null; }}/** * select ** from set@paramThe key key *@paramThe value value *@returnTrue Exists false Does not exist */
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false; }}/** * Put the data in the set cache **@paramThe key key *@paramValues can be multiple *'s@returnNumber of successes */
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0; }}/** * Put the set data in cache **@paramThe key key *@paramTime Time (seconds) x@paramValues can be multiple *'s@returnNumber 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; }}/** * Get the length of the set cache **@paramThe key key *@return* /
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0; }}/** * Remove ** of value@paramThe key key *@paramValues can be multiple *'s@returnNumber 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=================================
/** * Get the contents of the list cache **@paramThe key key *@paramStart to *@param0 to -1 represents 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; }}/** * Get the length of the list cache **@paramThe key key *@return* /
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0; }}/** * Get the value in the list by index **@paramThe key key *@paramIndex >=0; index>=0; If index<0, -1 is the end of the table, -2 is the penultimate element, and so on *@return* /
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null; }}/** * Put the list in cache **@paramThe key key *@paramThe 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; }}/** * Put the list in cache **@paramThe key key *@paramThe value value *@paramTime Time (seconds) x@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; }}/** * Put the list in cache **@paramThe key key *@paramThe 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; }}/** * Put the list in cache **@paramThe key key *@paramThe value value *@paramTime Time (seconds) x@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 a list of data according to the index **@paramThe key key *@paramThe index index *@paramThe 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 values to value **@paramThe key key *@paramCount How many stars to remove@paramThe value value *@returnNumber 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 = = = = = = = = = = = = = = = = = = =
/** * Add element ** to ordered set@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 the number of zsets *@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{
returnscore.longValue(); }}/** * Obtain the rank of members in the ordered set key. * Where the members of the ordered set are sorted in descending order by the score value. *@param key
* @param start
* @param end
* @return* /
public Set<ZSetOperations.TypedTuple> getZSetRank(String key, long start, long end) {
returnredisTemplate.opsForZSet().reverseRangeWithScores(key, start, end); }}Copy the code
The code that implements the class
// Weekly reviews
@Override
public void initweek(a) {
// Get 7 days of articles
List<MPost> posts = this.list(new QueryWrapper<MPost>().ge("created", DateUtil.lastWeek())
.select("id"."title"."user_id"."comment_count"."view_count"."created"));// Retrieve the query from 7 days ago and according to these queries, not all queries are required
// Initialize the general comment for the article
for (MPost post : posts) {
/ / set the key
String key = "day:rank:" + DateUtil.format(post.getCreated(), DatePattern.PURE_DATE_FORMAT);
// The number of cached comments
redisUtil.zSet(key, post.getId(), post.getCommentCount());
// Set the automatic expiration date to 7 days
long between = DateUtil.between(new Date(), post.getCreated(), DateUnit.DAY);
long expireTime = (7 - between) * 24 * 60 * 60; // The valid time
redisUtil.expire(key, expireTime);
// Cache some basic information about the article
this.hashCachePost(post, expireTime);
}
/ / and set
this.zunionAndStore();
}
/** * Article number of comments per week and set operation **/
private void zunionAndStore(a) {
String destkey = "day:rank:" + DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT);
// Set the new key after the union
String newkey = "week:rank";
ArrayList<String> otherKeys = new ArrayList<>();
// Count 7 days
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 cache **/
private void hashCachePost(MPost post, long expireTime) {
/ / set the key
String key = "rank:post:" + post.getId();
// Determine whether there is an existing or not
boolean hasKey = redisUtil.hasKey(key);
if(! hasKey) {// Store 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); }}}Copy the code
This converts our command line into code form. We can store our database data in the cache first. The 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"
Select * from '3' where id = 3 where id = 3 '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 >Copy the code
There is one comment in the database with id 3 There was really only one comment
The front end is displayed
So the idea here is pretty simple, just get our data out of the cache. Freemarker allows you to customize your tags. I have custom tags. Hosttemplate
/** * This week's hot topic */
@Component
public class HotsTemplate extends TemplateDirective {
@Autowired
RedisUtil redisUtil;
@Override
public String getName(a) {
return "hots";
}
@Override
public void execute(DirectiveHandler handler) throws Exception {
/ / set the key
String key ="week:rank";
Set<ZSetOperations.TypedTuple> zSetRank = redisUtil.getZSetRank(key, 0.6);
ArrayList<Map> maps = new ArrayList<>();
/ / convenience
for (ZSetOperations.TypedTuple typedTuple : zSetRank) {
/ / create a 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(); }}Copy the code
To use our custom tags, inject our notes into FreemarkerConfig
@Configuration
public class FreemarkerConfig {
@Autowired
private freemarker.template.Configuration configuration;
@Autowired
PostsTemplate postsTemplate;
@Autowired
HotsTemplate hotsTemplate;
@PostConstruct
public void setUp(a) {
configuration.setSharedVariable("timeAgo".new TimeAgoMethod());
configuration.setSharedVariable("posts", postsTemplate);
configuration.setSharedVariable("hosts", hotsTemplate); }}Copy the code
Front page access The effect
conclusion
When I’m doing this feature. Not comprehensive. Although finished, but should be to get a review within 7 days. I got 7 days of articles. It’s a bug but I don’t want to fix it. That’s it. It works for now. It’s the same thing. There will be time to change the problem. Tired, ruthless code machine record code in life…