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>> =
87 rest.iter().map(|inp| inp.iter().map(|(m, s)| (m.as_slice(), *s)).collect()).collect();
88 let w0 = weight_of(weights, 0);
89 let mut out = Vec::new();
90 'member: for (m, s) in first {
91 let mut score = s * w0;
92 for (j, map) in maps.iter().enumerate() {
93 match map.get(m.as_slice()) {
94 Some(&sj) => score = agg(score, sj * weight_of(weights, j + 1), mode),
95 None => continue 'member,
96 }
97 }
98 out.push((m.clone(), score));
99 }
100 out
101}
102
103pub fn zdiff(inputs: &[Vec<(Vec<u8>, f64)>]) -> Vec<(Vec<u8>, f64)> {
107 let Some((first, rest)) = inputs.split_first() else {
108 return Vec::new();
109 };
110 let excluded: ScratchSet<&[u8]> =
111 rest.iter().flat_map(|inp| inp.iter().map(|(m, _)| m.as_slice())).collect();
112 first.iter().filter(|(m, _)| !excluded.contains(m.as_slice())).cloned().collect()
113}
114
115pub fn zintercard(inputs: &[Vec<(Vec<u8>, f64)>], limit: usize) -> usize {
118 let Some((first, rest)) = inputs.split_first() else {
119 return 0;
120 };
121 let maps: Vec<ScratchSet<&[u8]>> =
122 rest.iter().map(|inp| inp.iter().map(|(m, _)| m.as_slice()).collect()).collect();
123 let mut n = 0;
124 'member: for (m, _) in first {
125 for map in &maps {
126 if !map.contains(m.as_slice()) {
127 continue 'member;
128 }
129 }
130 n += 1;
131 if limit != 0 && n >= limit {
132 return n;
133 }
134 }
135 n
136}
137
138impl Store {
139 pub fn zset_or_set_members(&mut self, key: &[u8]) -> Result<Vec<(Vec<u8>, f64)>, StoreError> {
143 use crate::Value;
144 match self.live_entry(key) {
145 None => Ok(Vec::new()),
146 Some(e) => match &e.value {
147 Value::ZSet(z) => Ok(z.by_member.iter().map(|(m, s)| (m.to_vec(), *s)).collect()),
148 Value::SegZSet(z) => Ok(z.ordered().map(|(m, s)| (m.to_vec(), s)).collect()),
149 Value::SmallZSetInline(z) => Ok(z.iter().map(|(m, s)| (m.to_vec(), s)).collect()),
150 Value::Set(s) => Ok(s.iter().map(|m| (m.to_vec(), 1.0)).collect()),
151 Value::SegSet(s) => Ok(s.keys().map(|m| (m.to_vec(), 1.0)).collect()),
152 Value::SmallSetInline(s) => Ok(s.iter().map(|m| (m.to_vec(), 1.0)).collect()),
153 _ => Err(StoreError::WrongType),
154 },
155 }
156 }
157
158 pub fn zstore_result(&mut self, dst: &[u8], pairs: &[(Vec<u8>, f64)]) -> usize {
164 self.del(&[dst]);
165 if pairs.is_empty() {
166 return 0;
167 }
168 let scored: Vec<(f64, &[u8])> = pairs.iter().map(|(m, s)| (*s, m.as_slice())).collect();
169 let _ = self.zadd(dst, &scored);
170 pairs.len()
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 fn zs(pairs: &[(&str, f64)]) -> Vec<(Vec<u8>, f64)> {
179 pairs.iter().map(|(m, s)| (m.as_bytes().to_vec(), *s)).collect()
180 }
181
182 #[test]
183 fn union_weights_and_aggregates() {
184 let a = zs(&[("x", 1.0), ("y", 2.0)]);
185 let b = zs(&[("y", 3.0), ("z", 4.0)]);
186 let mut u = zunion(&[a.clone(), b.clone()], None, ZAggregate::Sum);
187 u.sort_by(|x, y| x.0.cmp(&y.0));
188 assert_eq!(u, zs(&[("x", 1.0), ("y", 5.0), ("z", 4.0)]));
189 let mut u = zunion(&[a, b], Some(&[2.0, 3.0]), ZAggregate::Min);
191 u.sort_by(|x, y| x.0.cmp(&y.0));
192 assert_eq!(u, zs(&[("x", 2.0), ("y", 4.0), ("z", 12.0)]));
193 }
194
195 #[test]
196 fn inter_semantics() {
197 let a = zs(&[("x", 1.0), ("y", 2.0)]);
198 let b = zs(&[("y", 3.0), ("z", 4.0)]);
199 assert_eq!(zinter(&[a.clone(), b.clone()], None, ZAggregate::Sum), zs(&[("y", 5.0)]));
200 assert_eq!(zinter(&[a, b], Some(&[10.0, 1.0]), ZAggregate::Max), zs(&[("y", 20.0)]));
201 assert!(zinter(&[], None, ZAggregate::Sum).is_empty());
202 }
203
204 #[test]
205 fn diff_and_intercard() {
206 let a = zs(&[("x", 1.0), ("y", 2.0), ("z", 3.0)]);
207 let b = zs(&[("y", 9.0)]);
208 let mut d = zdiff(&[a.clone(), b.clone()]);
209 d.sort_by(|x, y| x.0.cmp(&y.0));
210 assert_eq!(d, zs(&[("x", 1.0), ("z", 3.0)]));
211 assert_eq!(zintercard(&[a.clone(), b.clone()], 0), 1);
212 let c = zs(&[("x", 0.0), ("y", 0.0), ("z", 0.0)]);
213 assert_eq!(zintercard(&[a.clone(), c.clone()], 0), 3);
214 assert_eq!(zintercard(&[a, c], 2), 2); }
216
217 #[test]
218 fn store_materialization_and_source_extraction() {
219 let mut s = Store::new();
220 s.zadd(b"z", &[(1.0, b"a".as_slice()), (2.0, b"b".as_slice())]).unwrap();
221 s.sadd(b"s", &[b"a".as_slice(), b"c".as_slice()]).unwrap();
222 let mut zm = s.zset_or_set_members(b"z").unwrap();
223 zm.sort_by(|x, y| x.0.cmp(&y.0));
224 assert_eq!(zm, zs(&[("a", 1.0), ("b", 2.0)]));
225 let mut sm = s.zset_or_set_members(b"s").unwrap();
226 sm.sort_by(|x, y| x.0.cmp(&y.0));
227 assert_eq!(sm, zs(&[("a", 1.0), ("c", 1.0)]));
228 assert!(s.zset_or_set_members(b"missing").unwrap().is_empty());
229 s.set(b"str", b"v".to_vec(), None, false, false);
230 assert!(s.zset_or_set_members(b"str").is_err());
231
232 s.set(b"dst", b"old".to_vec(), None, false, false);
234 assert_eq!(s.zstore_result(b"dst", &zs(&[("m", 7.0)])), 1);
235 assert_eq!(s.zscore(b"dst", b"m").unwrap(), Some(7.0));
236 assert_eq!(s.zstore_result(b"dst", &[]), 0);
237 assert_eq!(s.zcard(b"dst").unwrap(), 0);
238 assert_eq!(s.exists(&[b"dst".as_slice()]), 0);
239 }
240}