Skip to content

feat(enrichment): loose MusicBrainz search fallback (find aliased/romanized artists) - #771

Merged
byrongamatos merged 3 commits into
mainfrom
feat/mb-loose-search-fallback
Jul 4, 2026
Merged

feat(enrichment): loose MusicBrainz search fallback (find aliased/romanized artists)#771
byrongamatos merged 3 commits into
mainfrom
feat/mb-loose-search-fallback

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Problem

The MB text search is a strict field-phrase query: recording:"<title>" AND artist:"<artist>". A field phrase only matches MusicBrainz's primary artist/title — it never searches aliases. So a recording stored under a non-Latin primary name (大橋純子) whose romanized form ("Junko Ohashi") is only an alias returns zero results, even though MB has the record. Large parts of a community library (romanized J-pop / city-pop charts) were effectively unsearchable — searches just came up empty.

Fix — strict-then-loose fallback

  • build_recording_query(..., loose=True) drops the field scoping and phrases for plain AND-ed term groups ((telephone number) AND (junko ohashi)), which searches the whole document including aliases.
  • _mb_search_recordings runs the strict query first (unchanged, high precision); only when it returns nothing does it retry once with the loose query. Mainstream matches are untouched, and the extra throttled request is spent only on a miss. Candidates are re-scored by rank_candidates, so recall rises without lowering match quality (auto-accept still requires the per-field floors).

Benefits both the background Refresh-Metadata pass and the manual Fix-match search box.

Verified live

search (all 0 under the strict query) after
Junko Ohashi / Telephone Number 3 — incl. 大橋純子 — Telephone Number
Anri / Windy Summer 4 — incl. Windy Summer (remaster) ✓
AC/DC / Highway to Hell 4 @ score 1.0 — strict still hits, no loose retry (no regression)

Tests

  • build_recording_query loose form (drops field phrases; missing-artist case).
  • _mb_search_recordings retries loose on an empty strict result, and does not retry when strict hits (no wasted request).

Follow-up (separate)

Alias-aware scoring — compare the reference against a candidate's romanized aliases — so these can auto-confirm, not just appear as manual candidates (today the artist-script mismatch keeps the auto score low).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • MusicBrainz recording searches now automatically retry with a broader “loose” match when the initial strict search finds no candidates.
    • Query matching supports a flexible mode for wider title/artist alias detection.
  • Bug Fixes

    • Improves recording lookup accuracy, including scenarios where strict query formatting would otherwise prevent results.
  • Tests

    • Added unit and server-level tests covering strict-to-loose retry behavior and verifying live-only exclusion rules.

…ists)

The MB text search used a strict field-phrase query
(`recording:"<title>" AND artist:"<artist>"`). A field phrase only matches
MusicBrainz's *primary* artist/title — it never searches ALIASES — so a
recording stored under a non-Latin primary name (大橋純子) whose romanized
form ("Junko Ohashi") is only an alias returns ZERO results, even though MB
has it. Whole swaths of a community library (e.g. romanized J-pop / city-pop
charts) were unsearchable.

- `build_recording_query(..., loose=True)` drops the field scoping + phrases
  for plain AND-ed term groups (`(telephone number) AND (junko ohashi)`),
  which searches the whole document incl. aliases.
- `_mb_search_recordings` runs the strict query first (unchanged, high
  precision) and only on an EMPTY result retries once with the loose query —
  so mainstream matches are untouched and the extra throttled request is spent
  only on a miss. Results are re-scored by rank_candidates, so recall goes up
  without lowering match quality (auto-accept still needs the per-field floors).

Verified live: "Junko Ohashi / Telephone Number" and "Anri / Windy Summer"
(both 0 under the strict query) now surface the real records; "AC/DC /
Highway to Hell" still hits strict at score 1.0 with no loose retry.

Follow-up (separate): alias-aware SCORING so these can auto-confirm, not just
appear as manual candidates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d26c85b8-8051-4540-b0b1-c6a0842dd616

📥 Commits

Reviewing files that changed from the base of the PR and between 5108504 and fb354f9.

📒 Files selected for processing (2)
  • lib/mb_match.py
  • tests/test_mb_match.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/mb_match.py

📝 Walkthrough

Walkthrough

Adds a loose keyword-only parameter to build_recording_query, producing unscoped parenthesized token queries instead of strict field-scoped phrases. _mb_search_recordings now retries with this loose query when the strict search returns no candidates. Corresponding unit and integration tests were added.

Changes

Loose Query Fallback

Layer / File(s) Summary
Query builder loose mode
lib/mb_match.py, tests/test_mb_match.py
build_recording_query accepts loose: bool = False; when true, returns an AND of parenthesized denoised token groups for title/artist instead of field-scoped quoted phrases, with tests covering live handling and artist-present or artist-empty cases.
Search retry with loose fallback
server.py, tests/test_mb_enrichment.py
_mb_search_recordings retries once with a loose query when the initial strict query returns no candidates, returning the final candidate list; tests verify retry-on-miss and no-retry-on-hit behavior.

Estimated code review effort: 2 (Simple) | ~12 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant _mb_search_recordings
  participant build_recording_query
  participant MusicBrainzAPI

  Caller->>_mb_search_recordings: search(artist, title)
  _mb_search_recordings->>build_recording_query: build_recording_query(artist, title)
  build_recording_query-->>_mb_search_recordings: strict query
  _mb_search_recordings->>MusicBrainzAPI: GET /recording (strict query)
  MusicBrainzAPI-->>_mb_search_recordings: results
  alt strict results empty
    _mb_search_recordings->>build_recording_query: build_recording_query(artist, title, loose=True)
    build_recording_query-->>_mb_search_recordings: loose query
    _mb_search_recordings->>MusicBrainzAPI: GET /recording (loose query)
    MusicBrainzAPI-->>_mb_search_recordings: results
  end
  _mb_search_recordings-->>Caller: candidates
Loading

Possibly related PRs

  • got-feedBack/feedBack#758: Also changes lib/mb_match.py query construction and live-marker handling in build_recording_query.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding a loose MusicBrainz search fallback for aliased or romanized artists.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mb-loose-search-fallback

Comment @coderabbitai help to get the list of available commands.

…fallback

# Conflicts:
#	lib/mb_match.py
#	server.py
@byrongamatos

Copy link
Copy Markdown
Contributor

Review + fix pass (Claude Code). The loose-fallback logic is correct: denoise() strips every Lucene special (/ + - ! ( ) [ ] :) before the term-group query, so the unescaped (telephone number) AND (junko ohashi) is injection-safe while CJK primaries survive; _mb_search_recordings only retries loose on an empty strict result and skips the retry when the loose query equals the strict one (no wasted request).

Fixed: this branch predated #758 (now merged to main), so it modified the old build_recording_query/_mb_search_recordings. Merged current main in and resolved the conflicts by integrating the loose fallback onto #758's versions — keeping its -secondarytype:Live filter (live-chart-gated) + limit=12, and updating the stale strict-query test to expect the live filter. 55 mb tests green; now conflict-free vs main. (Codex second-opinion still rate-limited — manual + test-backed.)

The loose fallback dropped the strict path's -secondarytype:Live filter, so a
studio chart whose strict query missed could fall back to — and, since
score_candidate doesn't penalize live takes, auto-confirm — a live-only
recording. Apply the same live gate to the loose query (skipped only when the
source title is itself a live take).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos
byrongamatos merged commit 18c4e22 into main Jul 4, 2026
3 of 4 checks passed
byrongamatos added a commit that referenced this pull request Jul 4, 2026
…y artists (#772)

* feat(enrichment): loose MusicBrainz search fallback (find aliased artists)

The MB text search used a strict field-phrase query
(`recording:"<title>" AND artist:"<artist>"`). A field phrase only matches
MusicBrainz's *primary* artist/title — it never searches ALIASES — so a
recording stored under a non-Latin primary name (大橋純子) whose romanized
form ("Junko Ohashi") is only an alias returns ZERO results, even though MB
has it. Whole swaths of a community library (e.g. romanized J-pop / city-pop
charts) were unsearchable.

- `build_recording_query(..., loose=True)` drops the field scoping + phrases
  for plain AND-ed term groups (`(telephone number) AND (junko ohashi)`),
  which searches the whole document incl. aliases.
- `_mb_search_recordings` runs the strict query first (unchanged, high
  precision) and only on an EMPTY result retries once with the loose query —
  so mainstream matches are untouched and the extra throttled request is spent
  only on a miss. Results are re-scored by rank_candidates, so recall goes up
  without lowering match quality (auto-accept still needs the per-field floors).

Verified live: "Junko Ohashi / Telephone Number" and "Anri / Windy Summer"
(both 0 under the strict query) now surface the real records; "AC/DC /
Highway to Hell" still hits strict at score 1.0 with no loose retry.

Follow-up (separate): alias-aware SCORING so these can auto-confirm, not just
appear as manual candidates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(enrichment): alias-aware scoring (auto-confirm non-Latin-primary artists)

Builds on the loose-search fallback: that surfaces a recording stored under a
Japanese primary name (大橋純子) via its romanized alias, but the SCORER still
compared the reference ("Junko Ohashi") against the primary only → artist
similarity 0 → below the auto floor, so it could only ever be a manual
candidate, never an auto-fill.

- mb_match: `cand_artist_sim` takes the best similarity over the candidate's
  primary name AND its `artist_aliases`; score_candidate + classify use it.
- server: `_mb_artist_aliases(id)` fetches an artist's aliases (one throttled
  lookup, process-cached — a one-artist discography costs ONE request) and
  `_alias_enrich` attaches them ONLY to promising near-misses (title agrees,
  primary artist doesn't) so a normal pass spends zero extra requests. Wired
  into both the auto-matcher (_enrich_one) and the manual search proxy.

Verified live: "Junko Ohashi / Telephone Number" → 大橋純子 candidate goes from
score 0.5 (loose-only) to 1.0 (auto-confirmable), ranked #1; "AC/DC / Highway
to Hell" unchanged at 1.0 with no alias lookup.

Stacks on #771 (feat/mb-loose-search-fallback).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(enrichment): keep live exclusion in the loose search fallback

The loose fallback dropped the strict path's -secondarytype:Live filter, so a
studio chart whose strict query missed could fall back to — and, since
score_candidate doesn't penalize live takes, auto-confirm — a live-only
recording. Apply the same live gate to the loose query (skipped only when the
source title is itself a live take).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: byrongamatos <xasiklas@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants