Skip to content

Commit bb1f530

Browse files
committed
fix: using zrange instead of zunion for zunionbyscore
zunion performs really badly with large sets. SD-1555
1 parent b0fe99d commit bb1f530

File tree

1 file changed

+42
-34
lines changed

1 file changed

+42
-34
lines changed

src/commands/sorted_sets.rs

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ pub fn zunionbyscore(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
1919
keys.push(key);
2020
}
2121

22-
keys.push("AGGREGATE");
23-
keys.push("MAX");
24-
keys.push("WITHSCORES");
25-
2622
let min = mutable_args.next_f64()?;
2723
let max = mutable_args.next_f64()?;
2824

@@ -34,35 +30,47 @@ pub fn zunionbyscore(ctx: &Context, args: Vec<RedisString>) -> RedisResult {
3430
let offset = mutable_args.next_u64()?;
3531
let count = mutable_args.next_u64()?;
3632

37-
let all_keys = ctx.call("zunion", &keys)?;
38-
match all_keys {
39-
RedisValue::Array (results) => {
40-
let response = results
41-
.into_iter()
42-
.tuples::<(_, _)>()
43-
.filter(|tuple| {
44-
match &tuple.1 {
45-
RedisValue::SimpleString(score) => {
46-
if score == "-inf" || score == "+inf" {
47-
return true
48-
}
49-
50-
let score_f: f64 = score.parse().unwrap_or(0.0);
33+
let response = keys
34+
.into_iter()
35+
.map(|key| {
36+
let min_str = min.to_string();
37+
let max_str = max.to_string();
5138

52-
return score_f > min && score_f < max;
53-
}
54-
_ => false
55-
}
56-
})
57-
.map(|tuple| tuple.0)
58-
.skip(offset as usize)
59-
.take(count as usize)
60-
.collect::<Vec<_>>();
61-
62-
return Ok(response.into());
63-
},
64-
_ => {
65-
return Err(RedisError::WrongType);
66-
}
67-
}
39+
let results = ctx.call("zrange", &[&key, &min_str, &max_str, "BYSCORE", "WITHSCORES"]).ok()?;
40+
match results {
41+
RedisValue::Array (values) => {
42+
let keys = values
43+
.into_iter()
44+
.tuples::<(_, _)>()
45+
.collect::<Vec<(_, _)>>();
46+
47+
return Some(keys);
48+
49+
},
50+
_ => {
51+
return None;
52+
}
53+
}
54+
})
55+
.flatten()
56+
.flatten()
57+
.map(|tuple| {
58+
match &tuple.1 {
59+
RedisValue::SimpleString(score) => {
60+
let score_f: f64 = score.parse().unwrap_or(0.0);
61+
62+
return (tuple.0, score_f);
63+
}
64+
_ => (tuple.0, 0.0)
65+
}
66+
})
67+
.filter(|tuple| tuple.1 >= min && tuple.1 <= max)
68+
.sorted_by(|a, b| a.1.partial_cmp(&b.1).unwrap())
69+
.dedup()
70+
.map(|tuple| tuple.0)
71+
.skip(offset as usize)
72+
.take(count as usize)
73+
.collect::<Vec<_>>();
74+
75+
return Ok(response.into());
6876
}

0 commit comments

Comments
 (0)