feat(search): chip input and chip-based matching for advanced search - #1336
feat(search): chip input and chip-based matching for advanced search#1336ilyesbrh wants to merge 2 commits into
Conversation
Greptile SummaryThis PR adds newline-serialized chips to the global advanced-search UI and updates backend candidate selection and ranking to use OR-across-chip matching.
Confidence Score: 4/5The PR needs a fix before merging because M3U results can be ordered incorrectly when chips match different channel metadata fields. M3U scoring computes chip counts independently for the channel name, TVG name, and group title, so the final score cannot represent the total chips satisfied across those searchable fields. Files Needing Attention: apps/electron-backend/src/app/database/operations/content.operations.ts Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
U[Global search chip input] --> S[Newline-delimited query]
S --> P[Parse committed chips]
P --> X[Xtream per-chip candidate queries]
P --> M[M3U OR payload prefilter]
X --> R[Count matching chips]
M --> F[Score M3U channel fields]
F --> R
R --> O[Sort and paginate global results]
Prompt To Fix All With AI### Issue 1
apps/electron-backend/src/app/database/operations/content.operations.ts:630-636
**Field-local chip counts break ranking**
When an M3U channel satisfies chips across different searchable fields, such as `France` in `channel.name` and `1968` in `channel.group.title`, each field is scored independently and `Math.min` retains only one field's chip count. The channel is therefore ranked as a one-chip match and can appear below a result that satisfies the same number of chips within one field.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(search): chip input for the advance..." | Re-trigger Greptile |
| const scores = [ | ||
| scoreSearchTextMatch(channel.name, searchTerm), | ||
| scoreSearchTextMatch(channel.tvg.name, searchTerm), | ||
| scoreSearchTextMatch(channel.group.title, searchTerm), | ||
| scoreGlobalSearchChips(channel.name, chips), | ||
| scoreGlobalSearchChips(channel.tvg.name, chips), | ||
| scoreGlobalSearchChips(channel.group.title, chips), | ||
| ].filter((score): score is number => score !== null); | ||
|
|
||
| return scores.length > 0 ? Math.min(...scores) : null; |
There was a problem hiding this comment.
Field-local chip counts break ranking
When an M3U channel satisfies chips across different searchable fields, such as France in channel.name and 1968 in channel.group.title, each field is scored independently and Math.min retains only one field's chip count. The channel is therefore ranked as a one-chip match and can appear below a result that satisfies the same number of chips within one field.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/electron-backend/src/app/database/operations/content.operations.ts
Line: 630-636
Comment:
**Field-local chip counts break ranking**
When an M3U channel satisfies chips across different searchable fields, such as `France` in `channel.name` and `1968` in `channel.group.title`, each field is scored independently and `Math.min` retains only one field's chip count. The channel is therefore ranked as a one-chip match and can appear below a result that satisfies the same number of chips within one field.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Advanced (global) search matches on committed search chips instead of the whole query. Each chip is a joined unit (all its words required, via the existing tuned matcher, so compound-word and diacritic handling is preserved); a result matches if any chip matches, ranked by how many chips it satisfies. A plain one-chip query keeps the exact prior behavior. M3U channels score their chips across the searchable fields (name, TVG name, group title) as one set, so a channel satisfying different chips in different fields is ranked by the total chips matched, not per field. Chips travel over the existing string query as newline-delimited units, so the facade, ?q= param and IPC contract stay string-typed. Per-chip candidate selection reuses the prefix/FTS/scan strategies, unioned per chip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
099c475 to
24dfeec
Compare
|
Thanks @greptile-apps — the M3U ranking issue is valid and fixed.
Added a regression test in |
|
The fix looks correct. The implementation properly handles the multi-field case:
One minor note: when a chip matches multiple fields, you take Tip: You can customize Greptile's behavior for this repo with |
The global ("advanced") search bar becomes a chip input: type a phrase and
press Enter to commit it as a visible, removable chip. Each chip is a joined
search unit and results match any chip (ranked by chips matched). Short chips
like "FR" are allowed; draft text does not search until committed. Plain
portal/playlist search bars are unchanged.
Chips serialize to the existing string query as newline-delimited units, so the
facade, ?q= param and IPC contract stay string-typed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
24dfeec to
b3250ea
Compare
What changed
The global Advanced search bar becomes a chip input, and matching is now
chip-based:
Short chips like
FRare allowed; draft text does not search until committed.A result matches if it satisfies any chip, ranked so results matching the
most chips come first (single-chip matches still appear).
?q=links andthe tuned matcher (compound words
#1161, diacritics) are unaffected.Scope is intentionally the global Advanced search bar only — portal and playlist
search bars keep their plain text input. Chips serialize as newline-delimited
units over the existing string query, so the facade,
?q=param and IPCcontract stay string-typed (no new bridge methods).
Known limitation (called out for reviewers): within a chip, a short word (≤2
chars) anchors to the title start via the existing matcher — e.g.
[fr bein]matches
FR beIN 1but notBeIN Sports FR. Broadening short words to matchmid-title would require reworking candidate SQL and is left as a follow-up.
Why
Users want to search with several independent terms (e.g.
FR+beIN+1968) and see the closest matches first, instead of one query forcing everyword to appear in a single title.
Release note
.changes/(search-token-partial-match.md)Checks
content-search.util.spec.ts(chip parsing + chip scoring)content.operations.spec.ts(multi-chip OR ranking end-to-end)workspace-shell-header.component.spec.ts(chip add/remove/dedupe/render)pnpm run lint(electron-backend, workspace-shell-feature) and theaffected
pnpm nx testtargets pass