4. Feed流
127.0.0.1:6379> ZREVRANGEBYSCORE z1 1000 0 WITHSCORES LIMIT 0 3
根据分数查询z1 最大当前时间 最小时间 显示分数 从0开始查3条
127.0.0.1:6379> ZREVRANGEBYSCORE z1 5 0 WITHSCORES LIMIT 1 3
上次查询最小的是 5 所以最大从5 开始最小值不管 然后 LIMIE 后面找下一个 不包括5 所以为1 查询3条
根据Zset查询 因为第一次查询没有上一次的最小值 所以为当前时间戳 然后
LIMIT
开始给0滚动查询 每一次查询上一次查询的最小值(分数)开始 防止Feed新消息导致消息混乱
但是当查询到的元素有score
一样的情况 任然会混乱 所以LIMIT
跳过的应当为上一次查询的最小值重复的个数
Controller
@GetMapping("of/follow")
public Result getFeedBlog(
@RequestParam("lastId") Long max, // 上一次查询的最小值的时间戳
@RequestParam(value = "offset", defaultValue = "0") Integer offset// 偏移量 第一次没有 默认0
) {
return followService.queryFeedById(max, offset);
}
Service
public Result queryFeedById(Long max, Integer offset) {
// 获取当前用户id
Long userId = UserHolder.getUser().getId();
if (userId == null) {
return Result.fail("请登录!");
}
String key = BlogConst.FAN_INBOX + userId;
// 查询收件箱
Set<ZSetOperations.TypedTuple<String>> blogIdByScores = stringRedisTemplate.opsForZSet()
.reverseRangeByScoreWithScores(key, 0, max, offset, 3);
if (blogIdByScores == null || blogIdByScores.isEmpty()) {
return Result.ok();
}
// 解析数据
List<Long> ids = new ArrayList<>(blogIdByScores.size());
long minTime = 0; // 这个Set底层是TreeSet
int os = 1; // 初始化为1
for (ZSetOperations.TypedTuple<String> blogIdByScore : blogIdByScores) {
String idStr = blogIdByScore.getValue();
long time = blogIdByScore.getScore().longValue(); // 5 4 4 2 2 不置为1的话offset为 4 就错了
if (time == minTime) {
os++;
} else {
minTime = time;
os = 1;
}
ids.add(Long.valueOf(idStr));
}
// 根据id查询blog
List<Blog> blogs = query()
.in("id",ids)
.last("ORDER BY FIELD( id"+StrUtil.join(",",ids)+")")
.list();
blogs.forEach(blog -> {
queryBlogUser(blog); // 查询有关用户
isBlogLiked(blog); // 查询是否被点赞
});
// 封装返回
ScoreResult<Blog> result = new ScoreResult<>(blogs,minTime,offset);
return Result.ok(result);
}
4 附近商铺
4.1 GEO数据类型
$ 1
GEOADD g1 116.378248 39.865275 bjn 116.42803 39.903738 bj 116.322287 39.893729 bjx
$ 2
127.0.0.1:6379> GEODIST g1 bjx bj
"9091.5648"
127.0.0.1:6379> GEODIST g1 bjx bj KM
"9.0916"
$ 3
127.0.0.1:6379> GEOSEARCH g1 FROMLONLAT 116.397904 39.909005 BYRADIUS 10 km WITHDIST
1) 1) "bj"
2) "2.6361"
2) 1) "bjn"
2) "5.1452"
3) 1) "bjx"
2) "6.6723"
GEOSEARCH key [FROMMEMBER member] [FROMLONLAT longitude latitude] [BYRADIUS radius M|KM|FT|MI] [BYBOX width height M|KM|FT|MI] [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]
summary: Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle.
since: 6.2.0
4.2 java
@Test
void shopImportGeo() {
Map<Long, List<Shop>> listMap = shopService.list().stream().collect(Collectors.groupingBy(Shop::getTypeId));
for (Map.Entry<Long, List<Shop>> entry : listMap.entrySet()) {
Long typeId = entry.getKey();
List<Shop> shopList = entry.getValue();
List<RedisGeoCommands.GeoLocation<String>> loctions = shopList
.stream()
.map(shop ->
new RedisGeoCommands.GeoLocation<>(shop.getId().toString(), new Point(shop.getX(), shop.getY())))
.collect(Collectors.toList());
redisTemplate.opsForGeo().add(CacheConst.SHOP_GEO_PREFIX + typeId, loctions);
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</exclusion>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.1.8.RELEASE</version>
</dependency>
@Override
public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {
// 1.判断是否需要根据坐标查询
if (x == null || y == null) {
// 不需要坐标查询,按数据库查询
Page<Shop> page = query()
.eq("type_id", typeId)
.page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));
// 返回数据
return Result.ok(page.getRecords());
}
// 2.计算分页参数
int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;
int end = current * SystemConstants.DEFAULT_PAGE_SIZE;
// 3.查询redis、按照距离排序、分页。结果:shopId、distance
String key = SHOP_GEO_KEY + typeId;
GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo() // GEOSEARCH key BYLONLAT x y BYRADIUS 10 WITHDISTANCE
.search(
key,
GeoReference.fromCoordinate(x, y),
new Distance(5000),
// RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs() e
RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end)
);
// 4.解析出id
if (results == null) {
return Result.ok(Collections.emptyList());
}
List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();
if (list.size() <= from) {
// 没有下一页了,结束
return Result.ok(Collections.emptyList());
}
// 4.1.截取 from ~ end的部分
List<Long> ids = new ArrayList<>(list.size());
Map<String, Distance> distanceMap = new HashMap<>(list.size());
list.stream().skip(from).forEach(result -> {
// 4.2.获取店铺id
String shopIdStr = result.getContent().getName();
ids.add(Long.valueOf(shopIdStr));
// 4.3.获取距离
Distance distance = result.getDistance();
distanceMap.put(shopIdStr, distance);
});
// 5.根据id查询Shop
String idStr = StrUtil.join(",", ids);
List<Shop> shops = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();
for (Shop shop : shops) {
shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());
}
// 6.返回
return Result.ok(shops);
}
5 BitMap
11110000
BITFIELD b1 GET u2 0
# BITFIELD key GET u(无符号)2(获取几位) 0(从0开始)
# 获取到 11 转换10进制就是3
5.1 Java
5.2 统计签到
从当前时间判断之前的时间里是否为0 为0表示断签
运算符 https://cloud.tencent.com/developer/article/1338265
Long userId = user.getId();
LocalDate now = LocalDate.now();
String key = CacheConst.getCurrentUserSign(now, userId);
// 返回签到记录 为十进制数字
List<Long> result = redisTemplate.opsForValue().bitField(
key,
BitFieldSubCommands
.create()
.get(BitFieldSubCommands.BitFieldType.unsigned(now.getDayOfMonth()))
.valueAt(0)
);
if (result == null || result.isEmpty()) {
return Result.ok(0);
}
// 循环遍历
Long num = result.get(0);
int count = 0;
if (num == null || num == 0) {
return Result.ok(0);
}
// 与1做与运算 得到数字最后一个Bit位
while (true) {
if ((num & 1) == 0) {
// 为0 直接结束
break;
}else {
// 不为0 计数加一
count++;
}
// 数字右移动
num >>>= 1;
}
// 判断bit位是否为0 不为0 加1
return Result.ok(count);
& 按位与运算 将两个数字的二进制位做对比 都为1 才是一
110111 55
111000 56
=
110000 48
55&56=48
\n
1100011 99
10100 20
[不足左边补0]
=
0000000 0
99&20=0
| 按位或 有一边为1就为1
1010 10
001 1
=
1011 11
5 101
9 1000
=
13 1101