Skip to content

UX: Add search history and saved searches #78

@sfloess

Description

@sfloess

Overview

Remember recent searches, save complex filter combinations for reuse, and provide search templates for common patterns.

Current State

  • ✅ Advanced search filters work
  • ❌ No search history
  • ❌ No saved searches
  • ❌ Must re-enter filters every time

UX Score Impact

Current: UX A (94/100)
With #75 + #76 + this: UX A+ (99/100)

1. Search History

Concept: Automatically remember last 50 searches for quick recall.

Storage Format (JSON):

{
  "history": [
    {
      "timestamp": "2026-05-28T10:30:00Z",
      "repository": "maven-releases",
      "criteria": {
        "regexFilter": ".*SNAPSHOT.*",
        "minSize": 1000000,
        "createdBefore": "2025-01-01T00:00:00Z"
      },
      "resultsCount": 150
    }
  ]
}

Location:

Max Size: 50 searches (configurable)

Auto-Cleanup: Remove entries older than 90 days

GUI Implementation (Swing)

Search History Dropdown:

JComboBox<SearchHistoryEntry> historyCombo = new JComboBox<>();
historyCombo.setRenderer(new SearchHistoryRenderer());

// Populate from history
searchHistory.getRecent(10).forEach(historyCombo::addItem);

// On selection
historyCombo.addActionListener(e -> {
    SearchHistoryEntry entry = (SearchHistoryEntry) historyCombo.getSelectedItem();
    if (entry != null) {
        applySearchCriteria(entry.criteria());
    }
});

Renderer:

class SearchHistoryRenderer extends DefaultListCellRenderer {
    @Override
    public Component getListCellRendererComponent(...) {
        SearchHistoryEntry entry = (SearchHistoryEntry) value;
        setText(entry.describe());  // "maven-releases: *.SNAPSHOT.* (150 results) - 2h ago"
        return this;
    }
}

CLI Implementation

# View history
jnexus search --history
# Output:
# Recent searches:
# 1. maven-releases: .*SNAPSHOT.* (150 results) - 2 hours ago
# 2. npm-public: created before 2025-01-01 (45 results) - 1 day ago
# 3. maven-releases: >10MB (1234 results) - 3 days ago

# Repeat search #2
jnexus search --history-index 2

# Clear history
jnexus search --clear-history

2. Saved Searches

Concept: Name and save complex searches for easy reuse.

Storage Format (JSON):

{
  "savedSearches": [
    {
      "name": "Large SNAPSHOTs",
      "description": "SNAPSHOT JARs over 10 MB",
      "category": "cleanup",
      "repository": "maven-releases",
      "criteria": {
        "regexFilter": ".*SNAPSHOT.*\.jar",
        "minSize": 10485760
      },
      "createdAt": "2026-05-01T10:00:00Z",
      "lastUsed": "2026-05-28T10:30:00Z",
      "useCount": 15
    },
    {
      "name": "Old Dependencies",
      "description": "Dependencies older than 1 year",
      "category": "audit",
      "repository": "maven-releases",
      "criteria": {
        "createdBefore": "relative:-365d"  // Special: relative to now
      },
      "createdAt": "2026-05-01T10:00:00Z",
      "lastUsed": "2026-05-28T09:00:00Z",
      "useCount": 8
    }
  ]
}

Location:

GUI Implementation (Swing)

Saved Searches Menu:

JMenu searchMenu = new JMenu("Search");

JMenuItem saveSearchItem = new JMenuItem("Save Current Search...");
saveSearchItem.addActionListener(e -> saveCurrentSearch());

JMenu savedSearchesMenu = new JMenu("Saved Searches");
// Dynamically populate
savedSearchManager.getAll().forEach(search -> {
    JMenuItem item = new JMenuItem(search.name());
    item.setToolTipText(search.description());
    item.addActionListener(e -> applySearch(search));
    savedSearchesMenu.add(item);
});

JMenuItem manageSearchesItem = new JMenuItem("Manage Saved Searches...");
manageSearchesItem.addActionListener(e -> showManageSearchesDialog());

Save Search Dialog:

┌─ Save Search ──────────────────────────────────┐
│                                                 │
│  Name: [Large SNAPSHOTs                      ]  │
│                                                 │
│  Description:                                   │
│  [SNAPSHOT JARs over 10 MB                   ]  │
│                                                 │
│  Category:                                      │
│  ( ) Cleanup  ( ) Audit  ( ) Analysis           │
│  ( ) Other: [_____________]                     │
│                                                 │
│  Current Filters:                               │
│    Repository: maven-releases                   │
│    Regex: .*SNAPSHOT.*\.jar                     │
│    Min Size: 10 MB                              │
│                                                 │
│  [Cancel]                         [Save]        │
└─────────────────────────────────────────────────┘

Manage Searches Dialog:

┌─ Manage Saved Searches ────────────────────────┐
│                                                 │
│  ┌───────────────────────────────────────────┐ │
│  │ Name              Category   Last Used    │ │
│  ├───────────────────────────────────────────┤ │
│  │ Large SNAPSHOTs   Cleanup    2 hours ago  │ │
│  │ Old Dependencies  Audit      Yesterday    │ │
│  │ Security JARs     Analysis   1 week ago   │ │
│  │ ...                                       │ │
│  └───────────────────────────────────────────┘ │
│                                                 │
│  [Edit]  [Delete]  [Export]  [Import]  [Close] │
└─────────────────────────────────────────────────┘

CLI Implementation

# List saved searches
jnexus search --saved
# Output:
# Saved searches:
# 1. Large SNAPSHOTs (cleanup) - Used 15 times
# 2. Old Dependencies (audit) - Used 8 times

# Run saved search
jnexus search --saved "Large SNAPSHOTs"
# or
jnexus search --saved-index 1

# Save current search
jnexus list maven-releases --regex '.*SNAPSHOT.*' --min-size 10485760 \
  --save-search "Large SNAPSHOTs" \
  --search-description "SNAPSHOT JARs over 10 MB" \
  --search-category cleanup

# Export/import
jnexus search --export-saved saved-searches.json
jnexus search --import-saved saved-searches.json

3. Search Templates

Built-In Templates for Common Patterns:

public enum SearchTemplate {
    LARGE_FILES("Large Files", "Files over 100 MB",
        criteria -> criteria.minSize(100_000_000L)),
    
    SMALL_FILES("Small Files", "Files under 1 MB",
        criteria -> criteria.maxSize(1_000_000L)),
    
    OLD_SNAPSHOTS("Old SNAPSHOTs", "SNAPSHOTs older than 90 days",
        criteria -> criteria
            .regexFilter(".*SNAPSHOT.*")
            .createdBefore(Instant.now().minus(Duration.ofDays(90)))),
    
    RECENT_UPLOADS("Recent Uploads", "Uploaded in last 7 days",
        criteria -> criteria.createdAfter(Instant.now().minus(Duration.ofDays(7)))),
    
    JAR_FILES("JAR Files", "All JAR files",
        criteria -> criteria.fileExtension(".jar")),
    
    WAR_FILES("WAR Files", "All WAR files",
        criteria -> criteria.fileExtension(".war")),
    
    POM_FILES("POM Files", "All POM files",
        criteria -> criteria.fileExtension(".pom")),
    
    DOCKER_IMAGES("Docker Images", "Docker format repositories",
        criteria -> criteria.format("docker")),
    
    NPM_PACKAGES("NPM Packages", "NPM format repositories",
        criteria -> criteria.format("npm"));
    
    private final String name;
    private final String description;
    private final Function<SearchCriteria.Builder, SearchCriteria.Builder> configurator;
    
    public SearchCriteria apply(String repository) {
        SearchCriteria.Builder builder = SearchCriteria.builder().repository(repository);
        return configurator.apply(builder).build();
    }
}

GUI Usage:

Search → Templates → Old SNAPSHOTs
# Auto-fills filters

CLI Usage:

jnexus search maven-releases --template old-snapshots
jnexus search maven-releases --template large-files

4. Search Sharing/Export

Export Search to File:

# Export single search
jnexus search --export-search my-search.json

# Import and run
jnexus search --import-search my-search.json

Share via URL (Future Enhancement):

jnexus://search?repo=maven-releases&regex=.*SNAPSHOT.*&minSize=10485760
# Copy to clipboard, share with team

5. Search Statistics

Track Search Effectiveness:

public record SearchStats(
    int totalSearches,
    int avgResultsPerSearch,
    Map<String, Integer> popularFilters,  // "regex" → 45, "minSize" → 30
    List<String> mostUsedSavedSearches
) {}

View Stats:

jnexus search --stats
# Output:
# Search Statistics:
# - Total searches: 150
# - Average results: 87 components
# - Most used filter: regex (45 times)
# - Most popular saved search: "Large SNAPSHOTs" (15 times)

Configuration

# Search history size (default: 50)
nexus.search.history.max=50

# Search history retention (days, default: 90)
nexus.search.history.retention.days=90

# Enable search statistics (default: true)
nexus.search.stats.enabled=true

Testing

@Test
void testSearchHistoryLimited() {
    SearchHistory history = new SearchHistory(3);  // Max 3
    
    history.add(search1);
    history.add(search2);
    history.add(search3);
    history.add(search4);  // Should evict search1
    
    List<SearchHistoryEntry> recent = history.getRecent(10);
    assertEquals(3, recent.size());
    assertEquals(search4, recent.get(0));  // Most recent first
}

@Test
void testSavedSearchUsageTracking() {
    SavedSearchManager manager = new SavedSearchManager();
    SavedSearch search = new SavedSearch("test", "Test", "category", criteria);
    
    manager.save(search);
    manager.use("test");
    manager.use("test");
    
    SavedSearch loaded = manager.get("test");
    assertEquals(2, loaded.useCount());
    assertNotNull(loaded.lastUsed());
}

Acceptance Criteria

  • SearchHistory class implemented
  • SavedSearchManager class implemented
  • GUI history dropdown added
  • GUI saved searches menu added
  • GUI manage searches dialog added
  • CLI --history flag added
  • CLI --saved flag added
  • Search templates implemented (9+ templates)
  • Export/import functionality
  • Tests for history and saved searches
  • User guide section added

Priority

Low - Quality of life feature, not essential

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions