1#[cfg(not(feature = "std"))]
13use crate::nostd_prelude::*;
14use crate::{Store, StoreError};
15
16#[cfg(feature = "std")]
20type ScratchMap<K, V> = std::collections::HashMap<K, V>;
21#[cfg(feature = "std")]
22type ScratchSet<T> = std::collections::HashSet<T>;
23#[cfg(not(feature = "std"))]
24type ScratchMap<K, V> = alloc::collections::BTreeMap<K, V>;
25#[cfg(not(feature = "std"))]
26type ScratchSet<T> = alloc::collections::BTreeSet<T>;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum ZAggregate {
31 Sum,
33 Min,
35 Max,
37}
38
39fn agg(a: f64, b: f64, mode: ZAggregate) -> f64 {
40 match mode {
41 ZAggregate::Sum => a + b,
42 ZAggregate::Min => a.min(b),
43 ZAggregate::Max => a.max(b),
44 }
45}
46
47fn weight_of(weights: Option<&[f64]>, i: usize) -> f64 {
48 weights.map_or(1.0, |w| w.get(i).copied().unwrap_or(1.0))
49}
50
51pub fn zunion(
54 inputs: &[Vec<(Vec<u8>, f64)>],
55 weights: Option<&[f64]>,
56 mode: ZAggregate,
57) -> Vec<(Vec<u8>, f64)> {
58 let mut acc: Vec<(Vec<u8>, f64)> = Vec::new();
59 let mut idx: ScratchMap<Vec<u8>, usize> = ScratchMap::new();
60 for (i, input) in inputs.iter().enumerate() {
61 let w = weight_of(weights, i);
62 for (m, s) in input {
63 let ws = s * w;
64 match idx.get(m) {
65 Some(&slot) => acc[slot].1 = agg(acc[slot].1, ws, mode),
66 None => {
67 idx.insert(m.clone(), acc.len());
68 acc.push((m.clone(), ws));
69 }
70 }
71 }
72 }
73 acc
74}
75
76pub fn zinter(
78 inputs: &[Vec<(Vec<u8>, f64)>],
79 weights: Option<&[f64]>,
80 mode: ZAggregate,
81) -> Vec<(Vec<u8>, f64)> {
82 let Some((first, rest)) = inputs.split_first() else {
83 return Vec::new();
84 };
85 let maps: Vec<ScratchMap<&[u8], f64>> = rest
87 .iter()
88 .map(|inp| inp.iter().map(|(m, s)| (m.as_slice(), *s)).collect())
89 .collect();
90 let w0 = weight_of(weights, 0);
91 let mut out = Vec::new();
92 'member: for (m, s) in first {
93 let mut score = s * w0;
94 for (j, map) in maps.iter().enumerate() {
95 match map.get(m.as_slice()) {
96 Some(&sj) => score = agg(score, sj * weight_of(weights, j + 1), mode),
97 None => continue 'member,
98 }
99 }
100 out.push((m.clone(), score));
101 }
102 out
103}
104
105pub fn zdiff(inputs: &[Vec<(Vec<u8>, f64)>]) -> Vec<(Vec<u8>, f64)> {
109 let Some((first, rest)) = inputs.split_first() else {
110 return Vec::new();
111 };
112 let excluded: ScratchSet<&[u8]> = rest
113 .iter()
114 .flat_map(|inp| inp.iter().map(|(m, _)| m.as_slice()))
115 .collect();
116 first
117 .iter()
118 .filter(|(m, _)| !excluded.contains(m.as_slice()))
119 .cloned()
120 .collect()
121}
122
123pub fn zintercard(inputs: &[Vec<(Vec<u8>, f64)>], limit: usize) -> usize {
126 let Some((first, rest)) = inputs.split_first() else {
127 return 0;
128 };
129 let maps: Vec<ScratchSet<&[u8]>> = rest
130 .iter()
131 .map(|inp| inp.iter().map(|(m, _)| m.as_slice()).collect())
132 .collect();
133 let mut n = 0;
134 'member: for (m, _) in first {
135 for map in &maps {
136 if !map.contains(m.as_slice()) {
137 continue 'member;
138 }
139 }
140 n += 1;
141 if limit != 0 && n >= limit {
142 return n;
143 }
144 }
145 n
146}
147
148impl Store {
149 pub fn zset_or_set_members(&mut self, key: &[u8]) -> Result<Vec<(Vec<u8>, f64)>, StoreError> {
153 use crate::Value;
154 match self.live_entry(key) {
155 None => Ok(Vec::new()),
156 Some(e) => match &e.value {
157 Value::ZSet(z) => Ok(z
158 .by_member
159 .iter()
160 .map(|(m, s)| (m.to_vec(), *s))
161 .collect()),
162 Value::SmallZSetInline(z) => {
163 Ok(z.iter().map(|(m, s)| (m.to_vec(), s)).collect())
164 }
165 Value::Set(s) => Ok(s.iter().map(|m| (m.to_vec(), 1.0)).collect()),
166 Value::SmallSetInline(s) => Ok(s.iter().map(|m| (m.to_vec(), 1.0)).collect()),
167 _ => Err(StoreError::WrongType),
168 },
169 }
170 }
171
172 pub fn zstore_result(&mut self, dst: &[u8], pairs: &[(Vec<u8>, f64)]) -> usize {
178 self.del(&[dst]);
179 if pairs.is_empty() {
180 return 0;
181 }
182 let scored: Vec<(f64, &[u8])> = pairs.iter().map(|(m, s)| (*s, m.as_slice())).collect();
183 let _ = self.zadd(dst, &scored);
184 pairs.len()
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 fn zs(pairs: &[(&str, f64)]) -> Vec<(Vec<u8>, f64)> {
193 pairs.iter().map(|(m, s)| (m.as_bytes().to_vec(), *s)).collect()
194 }
195
196 #[test]
197 fn union_weights_and_aggregates() {
198 let a = zs(&[("x", 1.0), ("y", 2.0)]);
199 let b = zs(&[("y", 3.0), ("z", 4.0)]);
200 let mut u = zunion(&[a.clone(), b.clone()], None, ZAggregate::Sum);
201 u.sort_by(|x, y| x.0.cmp(&y.0));
202 assert_eq!(u, zs(&[("x", 1.0), ("y", 5.0), ("z", 4.0)]));
203 let mut u = zunion(&[a, b], Some(&[2.0, 3.0]), ZAggregate::Min);
205 u.sort_by(|x, y| x.0.cmp(&y.0));
206 assert_eq!(u, zs(&[("x", 2.0), ("y", 4.0), ("z", 12.0)]));
207 }
208
209 #[test]
210 fn inter_semantics() {
211 let a = zs(&[("x", 1.0), ("y", 2.0)]);
212 let b = zs(&[("y", 3.0), ("z", 4.0)]);
213 assert_eq!(zinter(&[a.clone(), b.clone()], None, ZAggregate::Sum), zs(&[("y", 5.0)]));
214 assert_eq!(
215 zinter(&[a, b], Some(&[10.0, 1.0]), ZAggregate::Max),
216 zs(&[("y", 20.0)])
217 );
218 assert!(zinter(&[], None, ZAggregate::Sum).is_empty());
219 }
220
221 #[test]
222 fn diff_and_intercard() {
223 let a = zs(&[("x", 1.0), ("y", 2.0), ("z", 3.0)]);
224 let b = zs(&[("y", 9.0)]);
225 let mut d = zdiff(&[a.clone(), b.clone()]);
226 d.sort_by(|x, y| x.0.cmp(&y.0));
227 assert_eq!(d, zs(&[("x", 1.0), ("z", 3.0)]));
228 assert_eq!(zintercard(&[a.clone(), b.clone()], 0), 1);
229 let c = zs(&[("x", 0.0), ("y", 0.0), ("z", 0.0)]);
230 assert_eq!(zintercard(&[a.clone(), c.clone()], 0), 3);
231 assert_eq!(zintercard(&[a, c], 2), 2); }
233
234 #[test]
235 fn store_materialization_and_source_extraction() {
236 let mut s = Store::new();
237 s.zadd(b"z", &[(1.0, b"a".as_slice()), (2.0, b"b".as_slice())]).unwrap();
238 s.sadd(b"s", &[b"a".as_slice(), b"c".as_slice()]).unwrap();
239 let mut zm = s.zset_or_set_members(b"z").unwrap();
240 zm.sort_by(|x, y| x.0.cmp(&y.0));
241 assert_eq!(zm, zs(&[("a", 1.0), ("b", 2.0)]));
242 let mut sm = s.zset_or_set_members(b"s").unwrap();
243 sm.sort_by(|x, y| x.0.cmp(&y.0));
244 assert_eq!(sm, zs(&[("a", 1.0), ("c", 1.0)]));
245 assert!(s.zset_or_set_members(b"missing").unwrap().is_empty());
246 s.set(b"str", b"v".to_vec(), None, false, false);
247 assert!(s.zset_or_set_members(b"str").is_err());
248
249 s.set(b"dst", b"old".to_vec(), None, false, false);
251 assert_eq!(s.zstore_result(b"dst", &zs(&[("m", 7.0)])), 1);
252 assert_eq!(s.zscore(b"dst", b"m").unwrap(), Some(7.0));
253 assert_eq!(s.zstore_result(b"dst", &[]), 0);
254 assert_eq!(s.zcard(b"dst").unwrap(), 0);
255 assert_eq!(s.exists(&[b"dst".as_slice()]), 0);
256 }
257}