Skip to content

Commit 53fd201

Browse files
rdhyeeclaude
andcommitted
Fix three cross-filter bugs
1. Multi-value within single facet: fast path now requires exactly one value in the active facet, not just one active dimension. Multiple selections (e.g., SESAR+GEOME) correctly fall through to on-the-fly queries. 2. Text search participates in cross-filtering: buildCrossFilterWhere now includes ILIKE conditions. sample_facets_v2 regenerated with label, description, place_name columns (63 MB on R2). 3. Clearing filters restores baseline counts: the update cell now resets all facet-count labels to baseline values and removes zero-count dimming when crossFilteredFacets is null. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 68ec7ee commit 53fd201

1 file changed

Lines changed: 33 additions & 6 deletions

File tree

tutorials/isamples_explorer.qmd

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,16 @@ facetsByType = {
397397
function buildCrossFilterWhere(excludeFacet) {
398398
const conditions = [];
399399
400+
// Text search participates in cross-filtering
401+
if (searchInput?.trim()) {
402+
const term = searchInput.trim().replace(/'/g, "''");
403+
conditions.push(`(
404+
label ILIKE '%${term}%'
405+
OR description ILIKE '%${term}%'
406+
OR CAST(place_name AS VARCHAR) ILIKE '%${term}%'
407+
)`);
408+
}
409+
400410
if (excludeFacet !== 'source') {
401411
const sources = Array.from(sourceCheckboxes || []);
402412
if (sources.length > 0) {
@@ -463,10 +473,15 @@ crossFilteredFacets = {
463473
const activeFilterCount = [activeSources, activeMaterials, activeContexts, activeObjectTypes]
464474
.filter(a => a.length > 0).length;
465475
466-
// Try pre-computed cache for single-filter (no text search)
467-
if (activeFilterCount === 1 && !hasSearch) {
476+
// Try pre-computed cache: exactly one facet active, exactly one value, no text search
477+
const singleValueFacet = (
478+
!hasSearch && activeFilterCount === 1 &&
479+
[activeSources, activeMaterials, activeContexts, activeObjectTypes]
480+
.every(a => a.length <= 1)
481+
);
482+
483+
if (singleValueFacet) {
468484
try {
469-
// Build filter conditions for the cache lookup
470485
const conditions = ["filter_source IS NULL", "filter_material IS NULL",
471486
"filter_context IS NULL", "filter_object_type IS NULL"];
472487
if (activeSources.length === 1)
@@ -552,10 +567,23 @@ function getDisplayCounts(facetKey) {
552567

553568
```{ojs}
554569
//| code-fold: true
555-
// Update facet count labels in-place when cross-filtered counts arrive
570+
// Update facet count labels in-place when cross-filtered counts change
556571
// This avoids re-rendering checkboxes (which would reset user selections)
557572
{
558-
if (!crossFilteredFacets) return; // No active filters — keep pre-computed counts
573+
if (!crossFilteredFacets) {
574+
// No active filters — restore baseline counts and remove dimming
575+
for (const facetKey of ['source', 'material', 'context', 'object_type']) {
576+
const baseline = facetsByType[facetKey] || [];
577+
const countMap = new Map(baseline.map(r => [r.value, r.count]));
578+
document.querySelectorAll(`.facet-count[data-facet="${facetKey}"]`).forEach(el => {
579+
const value = el.getAttribute('data-value');
580+
const count = countMap.get(value) ?? 0;
581+
el.textContent = `(${Number(count).toLocaleString()})`;
582+
el.style.opacity = '1';
583+
});
584+
}
585+
return;
586+
}
559587
560588
for (const [facetKey, rows] of Object.entries(crossFilteredFacets)) {
561589
if (!rows) continue;
@@ -565,7 +593,6 @@ function getDisplayCounts(facetKey) {
565593
const value = el.getAttribute('data-value');
566594
const count = countMap.get(value) ?? 0;
567595
el.textContent = `(${Number(count).toLocaleString()})`;
568-
// Dim zero-count items
569596
el.style.opacity = count === 0 ? '0.4' : '1';
570597
});
571598
}

0 commit comments

Comments
 (0)