能否为Bokeh的Select/MultiSelect组件添加搜索过滤功能?
Absolutely—this is a super common pain point when working with large datasets, and Bokeh gives you all the tools to build this seamlessly. Let's break down how to implement search functionality for both Select and MultiSelect components:
For Single Select Component
The core idea is pairing your Select with a TextInput search box, then using a CustomJS callback to filter options in real-time based on user input. We'll also store the full list of original options so we can revert back when the search box is cleared.
Here's a complete working example:
from bokeh.models import Select, TextInput, CustomJS from bokeh.layouts import column from bokeh.plotting import show, figure import pandas as pd # Simulate your 500+ column dataset data = pd.DataFrame({f"Column_{i}": range(10) for i in range(500)}) all_columns = list(data.columns) # Create search input and select component search_input = TextInput(placeholder="Search columns...", title="Filter:") select = Select(options=all_columns, value=all_columns[0], title="Select Column:") # CustomJS callback to filter options callback = CustomJS(args=dict(select=select, search_input=search_input, original_options=all_columns), code=""" const search_term = search_input.value.toLowerCase(); // Filter options to match search term (case-insensitive) const filtered = original_options.filter(opt => opt.toLowerCase().includes(search_term)); // Update select options—avoid empty list if no matches select.options = filtered.length > 0 ? filtered : original_options; // Reset selected value if it's no longer in filtered list if (!filtered.includes(select.value)) { select.value = filtered[0] || original_options[0]; } """) # Link search input changes to the callback search_input.js_on_change("value", callback) # Optional: Add a plot to demonstrate selection works p = figure() p.line(x=range(10), y=data[select.value]) # Update plot when selection changes select.js_on_change("value", CustomJS(args=dict(p=p, data=data), code=""" p.renderers[0].data_source.data.y = data[cb_obj.value]; """)) # Show the final layout show(column(search_input, select, p))
For MultiSelect Component
The implementation is nearly identical—we just adjust the callback to handle the multi-select's selected values properly. Here's how to adapt it:
from bokeh.models import MultiSelect, TextInput, CustomJS from bokeh.layouts import column from bokeh.plotting import show import pandas as pd data = pd.DataFrame({f"Column_{i}": range(10) for i in range(500)}) all_columns = list(data.columns) search_input = TextInput(placeholder="Search columns...", title="Filter:") multi_select = MultiSelect(options=all_columns, value=[all_columns[0], all_columns[1]], title="Select Columns:") callback = CustomJS(args=dict(multi_select=multi_select, search_input=search_input, original_options=all_columns), code=""" const search_term = search_input.value.toLowerCase(); const filtered = original_options.filter(opt => opt.toLowerCase().includes(search_term)); multi_select.options = filtered.length > 0 ? filtered : original_options; // Keep only selected values that are still in the filtered list const updated_selected = multi_select.value.filter(val => filtered.includes(val)); // Fallback to first two filtered options if no selections remain multi_select.value = updated_selected.length > 0 ? updated_selected : filtered.slice(0, 2); """) search_input.js_on_change("value", callback) show(column(search_input, multi_select))
Extra Tips
- Case Sensitivity: Remove the
toLowerCase()calls if you want case-sensitive search matches. - Bokeh Server: If you're using Bokeh Server, you can replace
CustomJSwith a Python callback instead—great if you need to integrate with other backend logic. The filtering logic stays the same, just written in Python. - Empty State: The code ensures the component never has an empty option list, but you can tweak this if you prefer to show nothing when there are no matches.
内容的提问来源于stack exchange,提问作者SivaM

