use crate::try_read;
use crate::try_write;
use alloc::{collections::BTreeMap, string::String, string::ToString, sync::Arc, vec::Vec};
#[cfg(feature = "std")]
use std::{collections::HashMap, sync::RwLock};
#[cfg(not(feature = "std"))]
use crate::memory::allocator::Mutex;
#[cfg(not(feature = "std"))]
use crate::memory::allocator::Mutex as RwLock;
pub struct TimeSeriesIndex {
time_index: RwLock<BTreeMap<u64, Vec<usize>>>,
#[cfg(feature = "std")]
tag_index: RwLock<HashMap<String, HashMap<String, Vec<usize>>>>,
#[cfg(not(feature = "std"))]
tag_index: RwLock<BTreeMap<String, BTreeMap<String, Vec<usize>>>>,
}
impl TimeSeriesIndex {
pub fn new() -> Arc<Self> {
Arc::new(Self {
time_index: RwLock::new(BTreeMap::new()),
#[cfg(feature = "std")]
tag_index: RwLock::new(HashMap::new()),
#[cfg(not(feature = "std"))]
tag_index: RwLock::new(BTreeMap::new()),
})
}
pub fn insert(&self, timestamp: u64, record_id: usize) {
#[cfg(feature = "std")]
let mut time_index = try_write!(self.time_index);
#[cfg(not(feature = "std"))]
let mut time_index = try_lock!(self.time_index);
time_index.entry(timestamp).or_default().push(record_id);
}
pub fn insert_tag(&self, tag_name: &str, tag_value: &str, record_id: usize) {
#[cfg(feature = "std")]
let mut tag_index = try_write!(self.tag_index);
#[cfg(not(feature = "std"))]
let mut tag_index = try_lock!(self.tag_index);
tag_index
.entry(tag_name.to_string())
.or_default()
.entry(tag_value.to_string())
.or_default()
.push(record_id);
}
pub fn query_time_range(&self, start_time: u64, end_time: u64) -> Vec<usize> {
#[cfg(feature = "std")]
let time_index = try_read!(self.time_index);
#[cfg(not(feature = "std"))]
let time_index = try_lock!(self.time_index);
let mut result = Vec::new();
for (_, ids) in time_index.range(start_time..=end_time) {
result.extend_from_slice(ids);
}
result
}
#[cfg(feature = "std")]
pub fn filter_by_tags(
&self,
record_ids: &[usize],
tags: &HashMap<String, String>,
) -> Vec<usize> {
if tags.is_empty() {
return record_ids.to_vec();
}
let mut filtered_ids = record_ids.to_vec();
let tag_index = try_read!(self.tag_index);
for (tag_name, tag_value) in tags {
if let Some(tag_values) = tag_index.get(tag_name) {
if let Some(matching_ids) = tag_values.get(tag_value) {
let mut new_filtered: Vec<usize> = Vec::new();
let matching_ids: &Vec<usize> = matching_ids;
for &id in &filtered_ids {
if matching_ids.contains(&id) {
new_filtered.push(id);
}
}
filtered_ids = new_filtered;
if filtered_ids.is_empty() {
break;
}
} else {
return Vec::new();
}
} else {
return Vec::new();
}
}
filtered_ids
}
#[cfg(not(feature = "std"))]
pub fn filter_by_tags(
&self,
record_ids: &[usize],
tags: &BTreeMap<String, String>,
) -> Vec<usize> {
if tags.is_empty() {
return record_ids.to_vec();
}
let tag_index = try_lock!(self.tag_index);
let mut filtered_ids = record_ids.to_vec();
for (tag_name, tag_value) in tags {
if let Some(tag_values) = tag_index.get(tag_name) {
if let Some(matching_ids) = tag_values.get(tag_value) {
let mut new_filtered: Vec<usize> = Vec::new();
let matching_ids: &Vec<usize> = matching_ids;
for &id in &filtered_ids {
if matching_ids.contains(&id) {
new_filtered.push(id);
}
}
filtered_ids = new_filtered;
if filtered_ids.is_empty() {
break;
}
} else {
return Vec::new();
}
} else {
return Vec::new();
}
}
filtered_ids
}
pub fn clear_before(&self, timestamp: u64) {
#[cfg(feature = "std")]
let mut time_index = try_write!(self.time_index);
#[cfg(not(feature = "std"))]
let mut time_index = try_lock!(self.time_index);
let keys_to_remove: Vec<u64> = time_index.range(..timestamp).map(|(k, _)| *k).collect();
for key in keys_to_remove {
time_index.remove(&key);
}
}
pub fn remove(&self, timestamp: u64) {
#[cfg(feature = "std")]
let mut time_index = try_write!(self.time_index);
#[cfg(not(feature = "std"))]
let mut time_index = try_lock!(self.time_index);
time_index.remove(×tamp);
}
}