-
Notifications
You must be signed in to change notification settings - Fork 21
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
feat: experiment filters in UI #424
base: main
Are you sure you want to change the base?
Conversation
combined_resource: Resource< | ||
(String, ExperimentListFilters, PaginationParams, String), | ||
CombinedResource, | ||
>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
combined_resource: Resource< | |
(String, ExperimentListFilters, PaginationParams, String), | |
CombinedResource, | |
>, | |
combined_resource: CombinedResource, |
let dim = combined_resource | ||
.get() | ||
.unwrap_or(CombinedResource { | ||
experiments: PaginatedResponse { | ||
total_items: 0, | ||
total_pages: 0, | ||
data: vec![], | ||
}, | ||
dimensions: vec![], | ||
default_config: vec![], | ||
}) | ||
.dimensions; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is no point of taking a Resource<...>
here as this is not going to reactive since its outside a move closure, better to send in the CombinedResource
.
checked=move || { | ||
filters_buffer_rws | ||
.get() | ||
.status | ||
.unwrap_or_default() | ||
.iter() | ||
.any(|item| *item == ExperimentStatusType::CREATED) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing a move clousre to checked
prop does it really work as intented?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah! Hopefully. Maybe...
|
||
let status_filter_management = | ||
move |checked: bool, filter_status: ExperimentStatusType| { | ||
let vec = filters_buffer_rws.get().status.unwrap_or_default(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vec
name is already used by macro vec!
.
let filters = filters_buffer_rws.get(); | ||
let filters = ExperimentListFilters { | ||
experiment_ids: id, | ||
..filters | ||
}; | ||
filters_buffer_rws.set(filters); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let filters = filters_buffer_rws.get(); | |
let filters = ExperimentListFilters { | |
experiment_ids: id, | |
..filters | |
}; | |
filters_buffer_rws.set(filters); | |
filters_buffer_rws.update(|value| { | |
value.experiments_ids = id; | |
}) |
sql::<Bool>("context::text LIKE ") | ||
.bind::<Text, _>(format!("%{}%", context_search)), | ||
); | ||
for c in context_search.iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Little bit confused by this filter, isn't the context
column a json? How does this exactly work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take an example:
{"and":[{"==":[{"var":"city"},"BLR"]}]}
{"and":[{"==":[{"var":"city"},"MUM"]},{"==":[{"var":"vehicle_type"},"suv"]}]}
Lets say you want to find the second one, the query formed will look like:
SELECT * FROM <schema>.experiments WHERE context::text = '%{"==":[{"var":"city"},"MUM"]}%' AND context = '%{"==":[{"var":"vehicle_type"},"suv"]}%';
FAQs
Q. The type is JSON, but you're using LIKE which needs text. How does this work?
A. The ::text
does the conversion
Q. Why not just do the whole context?
A. Imagine single searches, like
city = BLR
If I searched by the whole context, it would only match records that had this structure,
{"and":[{"==":[{"var":"city"},"BLR"]}]}
and would ignore records like these:
{"and":[{"==":[{"var":"city"},"BLR"]}]}
{"and":[{"==":[{"var":"city"},"BLR"]},{"==":[{"var":"vehicle_type"},"suv"]}]}
{"and":[{"==":[{"var":"city"},"BLR"]},{"==":[{"var":"vehicle_type"},"sedan"]}]}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be a bit compute heavy, so we should think about a conservative LIMIT
clause here.
PS: @knutties looks like we CAN query w/ JSON as well :P
crates/frontend/src/logic.rs
Outdated
@@ -340,12 +341,12 @@ impl Conditions { | |||
None => Condition::try_from_condition_map(&context).map(|v| vec![v])?, | |||
})) | |||
} | |||
pub fn to_context_json(self) -> serde_json::Value { | |||
pub fn to_context_json(&self) -> serde_json::Value { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it's not longer consuming, maybe it's better to call it as_context_json
. Would be more coherent imo.
crates/frontend/src/logic.rs
Outdated
json!({ | ||
"and": self.iter().map(|v| v.to_condition_json()).collect::<Vec<serde_json::Value>>() | ||
}) | ||
} | ||
pub fn to_query_string(self) -> String { | ||
pub fn to_query_string(&self) -> String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for this one.
on:change=move |e| { | ||
let date = format!("{}T00:00:00Z", event_target_value(&e)); | ||
logging::log!("The date selected is: {}", date); | ||
match DateTime::parse_from_rfc3339(&date) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the user can't really type anything & all the input is via the calender-widget, I think we can just unwrap w/o any consequences.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They can still programmatically change the field in inspect, better safe than sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- How is what I'm saying any worse? It'll just panic and nothing will happen. Anyway the user chose to break the UI.
- If we're willing to believe that, then the user can also just run the API call themselves, so any protection here isn't real anyway. So there's no point in bothering about any sort of protection if we are assuming such case.
@@ -46,8 +46,8 @@ pub fn table( | |||
{ | |||
match (currently_sorted, sort_by) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You know, maybe you can keep sort_by as Option<SortBy>
, that way you can avoid keeping currently_sorted
. Also this way it's also less error prone, as you if forget to change currently_sorted
, the UI change will not be performed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just saying that you can collapse the current_sorted
field (in ColumnSortable::Yes
) into sort_by
by changing it's type to Option<SortBy>
. So, if a column is not sorted, instead of making currently_sorted
false
you'd just change sort_by
to None
. Basically in experiment_table_columns
, you'd change it to:
sort_by: if current_sort_on == ExperimentSortOn::LastModifiedAt { Some(current_sort_by) } else { None }
To simplify, currently there is possibility of one changing the sort_by
but forgetting to correctly set the value of current_sorted
, w/ this you eliminate that possibility itself. Quite minute, but something I personally prefer.
Just a suggestion.
|
||
let dim = combined_resource | ||
.get() | ||
.unwrap_or(CombinedResource { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not unwrap_or_default
? CombinedResource
already has Default
derived for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The resource it wraps isn't defined
event.prevent_default(); | ||
let filter = filters_buffer_rws.get(); | ||
filters_rws.set(filter); | ||
close_drawer("experiment_filter_drawer") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This string probably should either an enum or a constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its the element ID, too dynamic for enum. If its wrong it won't work and its pretty obvious to the person who is building and testing the drawer
close_drawer("experiment_filter_drawer") | ||
} | ||
/> | ||
<Button |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be styled as some kind of cancellation button, maybe a cross?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It resets your filters, don't think cancellation is the right word here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try restart
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I meant was, it should convey a drop of filters visually, that's all, restart/reset is good.
crates/frontend/src/types.rs
Outdated
impl Default for StatusTypes { | ||
fn default() -> Self { | ||
Self(vec![ | ||
ExperimentStatusType::CREATED, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For listing out all the variants there is a macro, & if add a new variant for this enum, we will probably miss it this way. Can you use that macro? Here's the link: https://docs.rs/strum/latest/strum/derive.EnumIter.html
|
||
<div class="form-control w-full"> | ||
<label class="label"> | ||
<span class="label-text">Experiment Status</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could have looped over the variants to get this out, in-fact, if some body adds a new variant, then we might forget it. Can you loop over the variants instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The colors change for each variant which isn't captured by the enum, how would I apply that here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I am understanding you correctly, that can just be a match inside the loop:
let color = match status ...
Not sure if that was your concern though.
8702820
to
909cd94
Compare
7022fb4
to
7d1efa3
Compare
7d1efa3
to
551133e
Compare
Problem
It is hard to search for experiments
Solution
Add the ability to filter and manage experiments. Search is done on the backend, so even paginated experiments can be searched for
Screen.Recording.2025-02-19.at.7.06.34.PM.mov