Range scan on secondary index entries.
(
&self,
tenant_id: u64,
collection: &str,
field: &str,
lower: Option<&[u8]>,
upper: Option<&[u8]>,
limit: usize,
)
| 239 | |
| 240 | /// Range scan on secondary index entries. |
| 241 | pub fn range_scan( |
| 242 | &self, |
| 243 | tenant_id: u64, |
| 244 | collection: &str, |
| 245 | field: &str, |
| 246 | lower: Option<&[u8]>, |
| 247 | upper: Option<&[u8]>, |
| 248 | limit: usize, |
| 249 | ) -> crate::Result<Vec<(String, Vec<u8>)>> { |
| 250 | let prefix = format!("{tenant_id}:{collection}:{field}:"); |
| 251 | |
| 252 | let read_txn = self.db.begin_read().map_err(|e| redb_err("read txn", e))?; |
| 253 | let table = read_txn |
| 254 | .open_table(INDEXES) |
| 255 | .map_err(|e| redb_err("open table", e))?; |
| 256 | |
| 257 | let start = match lower { |
| 258 | Some(l) => format!("{prefix}{}", String::from_utf8_lossy(l)), |
| 259 | None => prefix.clone(), |
| 260 | }; |
| 261 | let end = match upper { |
| 262 | Some(u) => format!("{prefix}{}", String::from_utf8_lossy(u)), |
| 263 | None => { |
| 264 | let mut end = prefix.clone(); |
| 265 | end.push('\u{ffff}'); |
| 266 | end |
| 267 | } |
| 268 | }; |
| 269 | |
| 270 | let mut results = Vec::with_capacity(limit.min(256)); |
| 271 | let range = table |
| 272 | .range(start.as_str()..end.as_str()) |
| 273 | .map_err(|e| redb_err("range", e))?; |
| 274 | |
| 275 | for entry in range { |
| 276 | if results.len() >= limit { |
| 277 | break; |
| 278 | } |
| 279 | let entry = entry.map_err(|e| redb_err("range entry", e))?; |
| 280 | let key = entry.0.value().to_string(); |
| 281 | let value = entry.1.value().to_vec(); |
| 282 | results.push((key, value)); |
| 283 | } |
| 284 | |
| 285 | debug!(collection, field, count = results.len(), "range scan"); |
| 286 | Ok(results) |
| 287 | } |
| 288 | |
| 289 | /// Insert a secondary index entry (tenant-scoped). |
| 290 | /// |