Skip to content

Commit 0534aa0

Browse files
committed
wip
1 parent 8404179 commit 0534aa0

File tree

20 files changed

+478
-1006
lines changed

20 files changed

+478
-1006
lines changed

crates/store/re_chunk_store/src/query.rs

+307
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use std::{
55

66
use itertools::Itertools;
77
use re_chunk::{Chunk, LatestAtQuery, RangeQuery};
8+
use re_log_types::ResolvedTimeRange;
89
use re_log_types::{EntityPath, TimeInt, Timeline};
10+
use re_types_core::SizeBytes as _;
911
use re_types_core::{ComponentName, ComponentNameSet};
1012

1113
use crate::{store::ChunkIdSetPerTime, ChunkStore};
@@ -61,6 +63,47 @@ impl ChunkStore {
6163
}
6264
}
6365

66+
/// Retrieve all the [`ComponentName`]s that have been written to for a given [`EntityPath`].
67+
///
68+
/// Static components are always included in the results.
69+
///
70+
/// Returns `None` if the entity has never had any data logged to it.
71+
pub fn all_components_on_all_timelines(
72+
&self,
73+
entity_path: &EntityPath,
74+
) -> Option<ComponentNameSet> {
75+
re_tracing::profile_function!();
76+
77+
self.query_id.fetch_add(1, Ordering::Relaxed);
78+
79+
let static_components: Option<ComponentNameSet> = self
80+
.static_chunk_ids_per_entity
81+
.get(entity_path)
82+
.map(|static_chunks_per_component| {
83+
static_chunks_per_component.keys().copied().collect()
84+
});
85+
86+
let temporal_components: Option<ComponentNameSet> = self
87+
.temporal_chunk_ids_per_entity_per_component
88+
.get(entity_path)
89+
.map(|temporal_chunk_ids_per_timeline| {
90+
temporal_chunk_ids_per_timeline
91+
.iter()
92+
.flat_map(|(_, temporal_chunk_ids_per_component)| {
93+
temporal_chunk_ids_per_component.keys().copied()
94+
})
95+
.collect()
96+
});
97+
98+
match (static_components, temporal_components) {
99+
(None, None) => None,
100+
(None, comps @ Some(_)) | (comps @ Some(_), None) => comps,
101+
(Some(static_comps), Some(temporal_comps)) => {
102+
Some(static_comps.into_iter().chain(temporal_comps).collect())
103+
}
104+
}
105+
}
106+
64107
/// Check whether a given entity has a specific [`ComponentName`] either on the specified
65108
/// timeline, or in its static data.
66109
#[inline]
@@ -75,6 +118,56 @@ impl ChunkStore {
75118
.map_or(false, |components| components.contains(component_name))
76119
}
77120

121+
pub fn entity_has_component_on_any_timeline(
122+
&self,
123+
entity_path: &EntityPath,
124+
component_name: &ComponentName,
125+
) -> bool {
126+
re_tracing::profile_function!();
127+
128+
if self
129+
.static_chunk_ids_per_entity
130+
.get(entity_path)
131+
.and_then(|static_chunks_per_component| static_chunks_per_component.get(component_name))
132+
.and_then(|id| self.chunks_per_chunk_id.get(id))
133+
.is_some()
134+
{
135+
return true;
136+
}
137+
138+
for temporal_chunk_ids_per_component in self
139+
.temporal_chunk_ids_per_entity_per_component
140+
.get(entity_path)
141+
.iter()
142+
.flat_map(|temporal_chunk_ids_per_timeline| temporal_chunk_ids_per_timeline.values())
143+
{
144+
if temporal_chunk_ids_per_component
145+
.get(component_name)
146+
.is_some()
147+
{
148+
return true;
149+
}
150+
}
151+
152+
false
153+
}
154+
155+
/// Returns true if the given entity has a specific component with static data.
156+
#[inline]
157+
pub fn entity_has_static_component(
158+
&self,
159+
entity_path: &EntityPath,
160+
component_name: &ComponentName,
161+
) -> bool {
162+
re_tracing::profile_function!();
163+
164+
let Some(static_components) = self.static_chunk_ids_per_entity.get(entity_path) else {
165+
return false;
166+
};
167+
168+
static_components.contains_key(component_name)
169+
}
170+
78171
/// Find the earliest time at which something was logged for a given entity on the specified
79172
/// timeline.
80173
///
@@ -413,3 +506,217 @@ impl ChunkStore {
413506
.collect()
414507
}
415508
}
509+
510+
// Queries returning `usize` and `bool`
511+
impl ChunkStore {
512+
/// Returns the number of events logged for a given component on the given entity path across all timelines.
513+
pub fn num_events_on_timeline_for_component(
514+
&self,
515+
timeline: &Timeline,
516+
entity_path: &EntityPath,
517+
component_name: ComponentName,
518+
) -> usize {
519+
re_tracing::profile_function!();
520+
521+
self.query_id.fetch_add(1, Ordering::Relaxed);
522+
523+
self.num_events_for_static_component(entity_path, component_name)
524+
+ self.num_events_on_timeline_for_temporal_component(
525+
timeline,
526+
entity_path,
527+
component_name,
528+
)
529+
}
530+
531+
pub fn time_range_for_entity(
532+
&self,
533+
timeline: &Timeline,
534+
entity_path: &EntityPath,
535+
) -> Option<ResolvedTimeRange> {
536+
re_tracing::profile_function!();
537+
538+
self.query_id.fetch_add(1, Ordering::Relaxed);
539+
540+
let temporal_chunk_ids_per_timeline =
541+
self.temporal_chunk_ids_per_entity.get(entity_path)?;
542+
let chunk_id_sets = temporal_chunk_ids_per_timeline.get(timeline)?;
543+
544+
let start = chunk_id_sets.per_start_time.keys().min()?;
545+
let end = chunk_id_sets.per_end_time.keys().max()?;
546+
547+
Some(ResolvedTimeRange::new(*start, *end))
548+
}
549+
550+
pub fn num_events_on_timeline_for_all_components(
551+
&self,
552+
timeline: &Timeline,
553+
entity_path: &EntityPath,
554+
) -> usize {
555+
re_tracing::profile_function!();
556+
557+
self.query_id.fetch_add(1, Ordering::Relaxed);
558+
559+
let mut total_events = 0;
560+
561+
if let Some(static_chunks_per_component) = self.static_chunk_ids_per_entity.get(entity_path)
562+
{
563+
for chunk in static_chunks_per_component
564+
.values()
565+
.filter_map(|id| self.chunks_per_chunk_id.get(id))
566+
{
567+
total_events += chunk.num_events_cumulative();
568+
}
569+
}
570+
571+
if let Some(chunk_ids) = self
572+
.temporal_chunk_ids_per_entity
573+
.get(entity_path)
574+
.and_then(|temporal_chunks_events_per_timeline| {
575+
temporal_chunks_events_per_timeline.get(timeline)
576+
})
577+
{
578+
for chunk in chunk_ids
579+
.per_start_time
580+
.values()
581+
.flat_map(|ids| ids.iter().filter_map(|id| self.chunks_per_chunk_id.get(id)))
582+
{
583+
total_events += chunk.num_events_cumulative();
584+
}
585+
}
586+
587+
total_events
588+
}
589+
590+
pub fn entity_has_data_on_timeline(
591+
&self,
592+
timeline: &Timeline,
593+
entity_path: &EntityPath,
594+
) -> bool {
595+
re_tracing::profile_function!();
596+
597+
self.query_id.fetch_add(1, Ordering::Relaxed);
598+
599+
if self.static_chunk_ids_per_entity.get(entity_path).is_some() {
600+
// Static data exists on all timelines
601+
return true;
602+
}
603+
604+
if let Some(temporal_chunk_ids_per_timeline) =
605+
self.temporal_chunk_ids_per_entity.get(entity_path)
606+
{
607+
if temporal_chunk_ids_per_timeline.contains_key(timeline) {
608+
return true;
609+
}
610+
}
611+
612+
false
613+
}
614+
615+
pub fn num_events_for_static_component(
616+
&self,
617+
entity_path: &EntityPath,
618+
component_name: ComponentName,
619+
) -> usize {
620+
re_tracing::profile_function!();
621+
622+
self.query_id.fetch_add(1, Ordering::Relaxed);
623+
624+
if let Some(static_chunk) = self
625+
.static_chunk_ids_per_entity
626+
.get(entity_path)
627+
.and_then(|static_chunks_per_component| {
628+
static_chunks_per_component.get(&component_name)
629+
})
630+
.and_then(|chunk_id| self.chunks_per_chunk_id.get(chunk_id))
631+
{
632+
// Static data overrides temporal data
633+
static_chunk
634+
.num_events_for_component(component_name)
635+
.unwrap_or(0)
636+
} else {
637+
0
638+
}
639+
}
640+
641+
/// Returns the number of events logged for a given component on the given entity path across all timelines.
642+
///
643+
/// This ignores static data.
644+
pub fn num_events_on_timeline_for_temporal_component(
645+
&self,
646+
timeline: &Timeline,
647+
entity_path: &EntityPath,
648+
component_name: ComponentName,
649+
) -> usize {
650+
re_tracing::profile_function!();
651+
652+
self.query_id.fetch_add(1, Ordering::Relaxed);
653+
654+
let Some(temporal_chunk_ids_per_timeline) = self
655+
.temporal_chunk_ids_per_entity_per_component
656+
.get(entity_path)
657+
else {
658+
return 0; // no events logged for the entity path
659+
};
660+
661+
let Some(temporal_chunk_ids_per_component) = temporal_chunk_ids_per_timeline.get(timeline)
662+
else {
663+
return 0; // no events logged on this timeline
664+
};
665+
666+
let Some(chunk_ids) = temporal_chunk_ids_per_component.get(&component_name) else {
667+
return 0; // no events logged for the component on this timeline
668+
};
669+
670+
let mut num_events = 0;
671+
for chunk in chunk_ids
672+
.per_start_time
673+
.values()
674+
.flat_map(|ids| ids.iter().filter_map(|id| self.chunks_per_chunk_id.get(id)))
675+
{
676+
num_events += chunk.num_events_for_component(component_name).unwrap_or(0);
677+
}
678+
679+
num_events
680+
}
681+
682+
pub fn size_of_entity_on_timeline(
683+
&self,
684+
timeline: &Timeline,
685+
entity_path: &EntityPath,
686+
) -> usize {
687+
re_tracing::profile_function!();
688+
689+
self.query_id.fetch_add(1, Ordering::Relaxed);
690+
691+
let mut total_size = 0;
692+
693+
if let Some(static_chunks_per_component) = self.static_chunk_ids_per_entity.get(entity_path)
694+
{
695+
for chunk in static_chunks_per_component
696+
.values()
697+
.filter_map(|id| self.chunks_per_chunk_id.get(id))
698+
{
699+
total_size += Chunk::total_size_bytes(chunk) as usize;
700+
}
701+
}
702+
703+
if let Some(chunk_id_sets) = self
704+
.temporal_chunk_ids_per_entity
705+
.get(entity_path)
706+
.and_then(|temporal_chunk_ids_per_timeline| {
707+
temporal_chunk_ids_per_timeline.get(timeline)
708+
})
709+
{
710+
for chunk in chunk_id_sets
711+
.per_start_time
712+
.values()
713+
.flat_map(|v| v.iter())
714+
.filter_map(|id| self.chunks_per_chunk_id.get(id))
715+
{
716+
total_size += Chunk::total_size_bytes(chunk) as usize;
717+
}
718+
}
719+
720+
total_size
721+
}
722+
}

0 commit comments

Comments
 (0)