Skip to content

Add DataFusionTableWidget::column_visibility and use it to improve visibility defaults for the partition table #9936

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions crates/store/re_sorbet/src/column_descriptor_ref.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use re_log_types::EntityPath;

use crate::{
BatchType, ColumnDescriptor, ComponentColumnDescriptor, IndexColumnDescriptor,
RowIdColumnDescriptor,
Expand Down Expand Up @@ -31,6 +33,13 @@ impl ColumnDescriptorRef<'_> {
Self::Component(descr) => descr.component_name.short_name(),
}
}

pub fn entity_path(&self) -> Option<&EntityPath> {
match self {
Self::RowId(_) | Self::Time(_) => None,
Self::Component(descr) => Some(&descr.entity_path),
}
}
}

impl<'a> From<&'a ColumnDescriptor> for ColumnDescriptorRef<'a> {
Expand Down
9 changes: 7 additions & 2 deletions crates/store/re_sorbet/src/sorbet_columns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ use nohash_hasher::IntSet;
use re_log_types::{EntityPath, TimelineName};

use crate::{
ColumnDescriptor, ColumnKind, ComponentColumnDescriptor, ComponentColumnSelector,
IndexColumnDescriptor, RowIdColumnDescriptor, SorbetError, TimeColumnSelector,
ColumnDescriptor, ColumnDescriptorRef, ColumnKind, ComponentColumnDescriptor,
ComponentColumnSelector, IndexColumnDescriptor, RowIdColumnDescriptor, SorbetError,
TimeColumnSelector,
};

#[derive(thiserror::Error, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -72,6 +73,10 @@ impl SorbetColumnDescriptors {
.collect()
}

pub fn iter_ref(&self) -> impl Iterator<Item = ColumnDescriptorRef<'_>> {
self.columns.iter().map(ColumnDescriptorRef::from)
}

pub fn row_id_columns(&self) -> impl Iterator<Item = &RowIdColumnDescriptor> {
self.columns.iter().filter_map(|descr| {
if let ColumnDescriptor::RowId(descr) = descr {
Expand Down
56 changes: 38 additions & 18 deletions crates/viewer/re_dataframe_ui/src/datafusion_table_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ impl<'a> Columns<'a> {
}

impl Columns<'_> {
fn descriptors(&self) -> impl Iterator<Item = &ColumnDescriptorRef<'_>> {
self.inner.values().map(|(_, desc)| desc)
}

fn index_from_id(&self, id: Option<egui::Id>) -> Option<usize> {
id.and_then(|id| self.inner.get(&id).map(|(index, _)| *index))
}
Expand All @@ -57,7 +53,9 @@ impl Columns<'_> {
}
}

type ColumnRenamerFn<'a> = Option<Box<dyn Fn(&ColumnDescriptorRef<'_>) -> String + 'a>>;
type ColumnNameFn<'a> = Option<Box<dyn Fn(&ColumnDescriptorRef<'_>) -> String + 'a>>;

type ColumnVisibilityFn<'a> = Option<Box<dyn Fn(&ColumnDescriptorRef<'_>) -> bool + 'a>>;

pub struct DataFusionTableWidget<'a> {
session_ctx: Arc<SessionContext>,
Expand All @@ -73,7 +71,10 @@ pub struct DataFusionTableWidget<'a> {
/// Closure used to determine the display name of the column.
///
/// Defaults to using [`ColumnDescriptorRef::name`].
column_renamer: ColumnRenamerFn<'a>,
column_name_fn: ColumnNameFn<'a>,

/// Closure used to determine the default visibility of the column
default_column_visibility_fn: ColumnVisibilityFn<'a>,

/// The blueprint used the first time the table is queried.
initial_blueprint: TableBlueprint,
Expand All @@ -99,7 +100,8 @@ impl<'a> DataFusionTableWidget<'a> {

title: None,
title_button: None,
column_renamer: None,
column_name_fn: None,
default_column_visibility_fn: None,
initial_blueprint: Default::default(),
}
}
Expand All @@ -116,11 +118,22 @@ impl<'a> DataFusionTableWidget<'a> {
self
}

pub fn column_renamer(
pub fn column_name(
mut self,
renamer: impl Fn(&ColumnDescriptorRef<'_>) -> String + 'a,
column_name_fn: impl Fn(&ColumnDescriptorRef<'_>) -> String + 'a,
) -> Self {
self.column_renamer = Some(Box::new(renamer));
self.column_name_fn = Some(Box::new(column_name_fn));

self
}

// TODO(ab): this should best be expressed as part of the `TableBlueprint`, but we need better
// column selector first.
pub fn default_column_visibility(
mut self,
column_visibility_fn: impl Fn(&ColumnDescriptorRef<'_>) -> bool + 'a,
) -> Self {
self.default_column_visibility_fn = Some(Box::new(column_visibility_fn));

self
}
Expand Down Expand Up @@ -153,7 +166,8 @@ impl<'a> DataFusionTableWidget<'a> {
table_ref,
title,
title_button,
column_renamer,
column_name_fn,
default_column_visibility_fn,
initial_blueprint,
} = self;

Expand Down Expand Up @@ -256,14 +270,20 @@ impl<'a> DataFusionTableWidget<'a> {
let mut table_config = TableConfig::get_with_columns(
ui.ctx(),
id,
columns.descriptors().map(|c| {
let name = if let Some(renamer) = &column_renamer {
renamer(c)
sorbet_schema.columns.iter_ref().map(|c| {
let name = if let Some(column_name_fn) = &column_name_fn {
column_name_fn(&c)
} else {
c.name(BatchType::Dataframe)
};

ColumnConfig::new(Id::new(c), name)
let visible = if let Some(column_visibility_fn) = &default_column_visibility_fn {
column_visibility_fn(&c)
} else {
true
};

ColumnConfig::new_with_visible(Id::new(c), name, visible)
}),
);

Expand All @@ -280,7 +300,7 @@ impl<'a> DataFusionTableWidget<'a> {
fields,
display_record_batches: &display_record_batches,
columns: &columns,
column_renamer: &column_renamer,
column_name_fn: &column_name_fn,
blueprint: table_state.blueprint(),
new_blueprint: &mut new_blueprint,
table_config,
Expand Down Expand Up @@ -348,7 +368,7 @@ struct DataFusionTableDelegate<'a> {
fields: &'a Fields,
display_record_batches: &'a Vec<DisplayRecordBatch>,
columns: &'a Columns<'a>,
column_renamer: &'a ColumnRenamerFn<'a>,
column_name_fn: &'a ColumnNameFn<'a>,
blueprint: &'a TableBlueprint,
new_blueprint: &'a mut TableBlueprint,
table_config: TableConfig,
Expand All @@ -362,7 +382,7 @@ impl egui_table::TableDelegate for DataFusionTableDelegate<'_> {

if let Some((index, desc)) = self.columns.index_and_descriptor_from_id(id) {
let column_name = self.fields[index].name();
let name = if let Some(renamer) = self.column_renamer {
let name = if let Some(renamer) = self.column_name_fn {
renamer(desc)
} else {
desc.name(BatchType::Dataframe)
Expand Down
4 changes: 4 additions & 0 deletions crates/viewer/re_dataframe_ui/src/table_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ impl ColumnConfig {
visible: true,
}
}

pub fn new_with_visible(id: Id, name: String, visible: bool) -> Self {
Self { id, name, visible }
}
}

// TODO(lucasmerlin): It would be nice to have this in egui_table, so egui_table could do the work
Expand Down
26 changes: 22 additions & 4 deletions crates/viewer/re_redap_browser/src/servers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use std::sync::mpsc::{Receiver, Sender};

use egui::{Frame, Margin, RichText};

use re_log_types::EntryId;
use re_protos::manifest_registry::v1alpha1::DATASET_MANIFEST_ID_FIELD_NAME;
use re_log_types::{EntityPathPart, EntryId};
use re_protos::manifest_registry::v1alpha1::{
DATASET_MANIFEST_ID_FIELD_NAME, DATASET_MANIFEST_REGISTRATION_TIME_FIELD_NAME,
};
use re_ui::list_item::ItemActionButton;
use re_ui::{UiExt as _, icons, list_item};
use re_viewer_context::{
Expand Down Expand Up @@ -115,6 +117,8 @@ impl Server {
ui: &mut egui::Ui,
dataset: &Dataset,
) {
const RECORDING_LINK_FIELD_NAME: &str = "recording link";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this hard-coded string? Where does it come from?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a column that is live injected using datafusion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is it live-injected? Who gives it this name? What other piece of code needs to be updated in tandem with this piece of code?

Copy link
Member Author

@abey79 abey79 May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the location of this const suggests, this is all local to this method. generate_partition_links is what triggers that column generation (it takes a column name as input), and default_column_visibility defines, well, what the columns are visible by default (and it wants that generated column to be visible by default).


re_dataframe_ui::DataFusionTableWidget::new(
self.tables_session_ctx.ctx.clone(),
dataset.name(),
Expand All @@ -125,16 +129,30 @@ impl Server {
.send(Command::RefreshCollection(self.origin.clone()))
.ok();
}))
.column_renamer(|desc| {
.column_name(|desc| {
//TODO(ab): with this strategy, we do not display relevant entity path if any.
let name = desc.short_name();

name.strip_prefix("rerun_")
.unwrap_or(name)
.replace('_', " ")
})
.default_column_visibility(|desc| {
if desc.entity_path().is_some_and(|entity_path| {
entity_path.starts_with(&std::iter::once(EntityPathPart::properties()).collect())
}) {
true
} else {
matches!(
desc.short_name(),
RECORDING_LINK_FIELD_NAME
| DATASET_MANIFEST_ID_FIELD_NAME
| DATASET_MANIFEST_REGISTRATION_TIME_FIELD_NAME
)
Comment on lines +147 to +151
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Matching on string here is extremely brittle and is already causing problems with #9983

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that's a strange comment to make tbh. This code uses short_name, which #9983 removed, hence the compilation error. What is brittle is our lack of standard around column names and how they relate to descriptors—see #9840. I've pushed a commit on #9983 which replaces short_name by display_name and tested that it still works as intended.

Copy link
Member

@emilk emilk May 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But does it make sense for this code to break when we change how columns are displayed (e.g. when we change the implementation of fn display_name)? Wouldn't it be better to explicitly match against e.g. ColumnDescriptor::archetype_field_name ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These columns don't have much metadata—basically just a raw record batch built ad hoc on the server. Specifically, they don't have an archetype. Again, we need a much strong system to relate record batch/dataframe column name with column descriptors, which must account for arbitrary tables sent by the server and/or users.

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is Name in the above screenshot? Is that ComponentName? If so, can we match against that instead of display_name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For component column, it corresponds to component_name (this view is a mirror of the fields of ComponentColumnDescriptor). That field is currently only exposed indirectly in ColumnDescriptorRef's methods. Doing it properly amounts to resolving the desc <-> column name problem.

}
})
.generate_partition_links(
"recording link",
RECORDING_LINK_FIELD_NAME,
DATASET_MANIFEST_ID_FIELD_NAME,
self.origin.clone(),
dataset.id(),
Expand Down
2 changes: 1 addition & 1 deletion crates/viewer/re_viewer/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ fn table_ui(
) {
re_dataframe_ui::DataFusionTableWidget::new(store.session_context(), TableStore::TABLE_NAME)
.title(table_id.as_str())
.column_renamer(|desc| match desc {
.column_name(|desc| match desc {
ColumnDescriptorRef::RowId(_) | ColumnDescriptorRef::Time(_) => {
desc.short_name().to_owned()
}
Expand Down