Skip to content

Allow tag-only search (relax empty-q guard in /api/search and doSearch) #750

@pandas-studio

Description

@pandas-studio

What

Follow-up to #672 / PR #749. After PR #749, _searchByTag no longer pollutes search-input, so a tag pill click on a fresh session leaves search-input empty. doSearch() (packages/memtomem/src/memtomem/web/static/app.js:1675) currently early-returns on empty q:

async function doSearch() {
  const q = qs('search-input').value.trim();
  if (!q) return;  // ← blocks tag-only path
  const tf = qs('tag-filter').value.trim();
  ...
}

…and the backend (packages/memtomem/src/memtomem/web/routes/search.py) declares q as required:

async def search(
    q: str = Query(...),          # ← min length enforced; empty q → 422
    tag_filter: str | None = Query(None),
    ...
):

So the click currently acts as a "filter prep" — Search tab opens with tag-filter populated, but no results render until the user types a query.

Why

The intuitive UX (per #672 discussion) is that clicking a tag pill = "show me all memos with this tag". Today that requires an extra keystroke. Lifting the empty-q guard on both layers makes the click work as expected without any further _searchByTag change — PR #749 already calls doSearch() unconditionally, so this fix is purely additive.

Suggested change

  1. Frontend (app.js:1673-1722): replace if (!q) return; with if (!q && !tf) return; so the search runs whenever either axis has a value.
  2. Backend (packages/memtomem/src/memtomem/web/routes/search.py): make q optional (q: str | None = Query(None)) and validate that at least one of q / tag_filter is present (return 400 otherwise — keeps the "no-op search" guard at the API layer).
  3. pipeline.search (packages/memtomem/src/memtomem/search/pipeline.py): verify it tolerates query=None / query="". The pipeline order is fixed (per CLAUDE.md); the BM25/dense stages should short-circuit cleanly when there's no query and the tag/source filter stage takes over as the primary selector. Add a regression test for tag_filter-only retrieval.
  4. CHANGELOG: [Unreleased] / Changed — "Tag pill click now runs a tag-only search on a fresh session (was a no-op filter prep after _searchByTag pollutes search-input with tag text #672)."

Out of scope

  • Multi-tag selection on tag-filter (separate UX question — issue _searchByTag pollutes search-input with tag text #672 noted the field is currently single-value text).
  • Sort order for tag-only results (BM25 won't rank without a query — likely fall back to recency or importance boost; needs a small UX call).

Test plan

  • Backend: pytest packages/memtomem/tests/test_user_workflows.py::TestSearch::test_tag_filter_search extended to cover q=None.
  • Frontend (manual): Tags tab → click pill on fresh session → Search tab activates and results render immediately, scoped to the tag.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions