Refactor queryparse#6663
Open
snejus wants to merge 17 commits into
Open
Conversation
This removes ambiguity with Library._fetch
Move sort case-sensitivity config lookup into `FieldSort` as a cached class property instead of threading a `case_insensitive` flag through query parsing helpers. This centralizes sort behavior at the sort implementation boundary and simplifies parse/orchestration call paths while preserving configurable case-sensitive ordering.
|
Thank you for the PR! The changelog has not been updated, so here is a friendly reminder to check if you need to add an entry. |
|
Contributor
There was a problem hiding this comment.
Pull request overview
PR move query parsing from many helper path into one model entrypoint (LibModel.parse_query), then build query+sort via ModelQuery / QueryTerm / SortTerm. PR also update core + plugins + tests to use new API.
Changes:
- Replace scattered
parse_query_*+dbcore.parse_sorted_queryusage withItem/Album.parse_query(...)returningModelQuery(query, sort). - Move field-aware query construction into
FieldQuery.from_model(...)and flatten query composition viaQuery.__or__+AndQuery/OrQueryflattening. - Restructure tests: new
test/dbcore/test_queryparse.pyand shared model fixtures inbeets/test/fixtures.py.
grug note: big change, many place for bug hide. grug want fix few correctness holes before merge.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| test/test_library.py | Remove legacy parse-query unittest cases (moved/updated elsewhere). |
| test/test_dbcore.py | Update fixtures usage + switch tests from _fetch to get_results; remove old queryparse tests. |
| test/plugins/test_smartplaylist.py | Update smartplaylist tests to use Item/Album.parse_query and new sort expectations. |
| test/dbcore/test_sort.py | Update sort tests for new parsing + config-driven case sensitivity; clear cached classproperty cache. |
| test/dbcore/test_queryparse.py | New pytest coverage for QueryTerm, ModelQuery, and sort parsing behavior. |
| beetsplug/smartplaylist.py | Switch playlist query parsing to model_cls.parse_query; adjust typing. |
| beetsplug/playlist.py | Update typing alias for item query registration (QueryByField). |
| beetsplug/play.py | Use Album.default_sort instead of library helper. |
| beetsplug/lyrics.py | Use Item.parse_query for auto-ignore query parsing. |
| beetsplug/ihate.py | Use model_cls.parse_query(...).query for match checks. |
| beetsplug/convert.py | Use Item.parse_query(...).query for no_convert matching. |
| beetsplug/bpd/init.py | Switch to FieldQuery.from_model / any_writable_media_field_query new arg order. |
| beetsplug/aura.py | Switch field queries to FieldQuery.from_model. |
| beetsplug/advancedrewrite.py | Replace manual shlex + helper parsing with Item.parse_query. |
| beets/test/fixtures.py | New shared fixtures for dbcore query/sort/model tests. |
| beets/plugins.py | Update query-prefix typing and comprehension-based merge of plugin query prefixes. |
| beets/library/queries.py | Deprecate old query parsing helpers and forward to model parsing. |
| beets/library/models.py | Add LibModel.parse_query + default_sort; migrate helper query construction to FieldQuery.from_model. |
| beets/library/library.py | Make library fetching accept string/parts/query; route to model_cls.parse_query and Database.get_results. |
| beets/dbcore/types.py | Tighten Type.query typing to type[FieldQuery]. |
| beets/dbcore/sort.py | Rename order_clause -> clause; move case-sensitivity ownership into FieldSort. |
| beets/dbcore/queryparse.py | New parsing pipeline (ModelQuery, QueryTerm, SortTerm) built on shlex token stream. |
| beets/dbcore/query.py | Add FieldQuery.from_model + pattern normalization hooks; flatten AndQuery/OrQuery composition. |
| beets/dbcore/db.py | Rename internal fetch API to get_results; switch to Sort.clause(). |
| beets/dbcore/init.py | Stop re-exporting old queryparse helper functions. |
2d4b40e to
01e2835
Compare
- Introduce SortTerm to centralize sort-part validation and direction parsing. - Move model-specific sort resolution into SortTerm.get_sort, including smart artist field mapping. - Simplify sort_from_strings to build sorts from parsed terms.
- Replace parse_query_part with a QueryTerm parser that captures negate, field, prefix, and pattern in one pass. - Resolve query class selection through QueryTerm so prefix and field-based dispatch are handled consistently. - Move implicit path query normalization into dbcore parsing, simplifying library-level query parsing. - Tighten plugin query typing and update dbcore tests for path behavior and related fixture assertions.
- Replace default sort helpers with LibModel.default_sort as a shared, config-driven sort helper for Item and Album. - Route Library._fetch through get_results with parsed query sort, explicit sort, or model default_sort fallback.
- Add ModelQuery in dbcore.queryparse and expose LibModel.parse_query as the single parsing entrypoint for string and sequence query inputs. - Route library fetch, path format matching, and plugin query parsing through the model-level API, and deprecate beets.library.parse_query_string and parse_query_parts. - Update tests to cover invalid query parsing via ModelQuery and align smartplaylist sort assertions with parsed sort behavior.
- Move field alias handling, shared-table qualification, and path pattern normalization into FieldQuery.from_model with a query-specific normalize_pattern hook. - Update query parsing and plugin call sites to construct queries through query_cls.from_model and remove redundant model-level field query helpers. This keeps model entrypoints focused on orchestration and colocates transformation behavior with the query types that own it.
- Move per-term query building into QueryTerm.get_query and compose field queries with operator-based OR logic. - Add Query.__or__ plus flattening behavior for AndQuery and OrQuery to avoid nested collection queries. - Update dbcore parsing expectations so single-field terms assert SubstringQuery directly.
- Move query and sort construction into `ModelQuery` classmethods and parse tokenized input once. - Switch call sites to consume `parse_query(...).query` and `parse_query(...).sort` instead of tuple unpacking. - Remove legacy `dbcore` re-exports and obsolete helper functions in `queryparse`. - Update query/sort tests to match the new parsing behavior and simplified query object shapes.
01e2835 to
d9869c5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Query Parsing Architecture Consolidation
What this PR does
This PR reshapes query parsing around a single model-level entrypoint and moves parsing/building responsibilities closer to the objects that own them.
At a high level, the change:
LibModel.parse_query(...).ModelQuery,QueryTerm, andSortTerm.FieldQuery.from_model(...).Why this matters
Before:
unittestpatterns.After:
Item.parse_query(...)/Album.parse_query(...).queryandsortfrom the same token stream.Architecture changes
1. Single parsing entrypoint:
LibModel.parse_query(...)The biggest architectural change is that model classes now own parsing through
LibModel.parse_query(...), which returns aModelQueryobject:This replaces older helper-style flows such as:
parse_query_string(...)parse_query_parts(...)query_from_strings(...)sort_from_strings(...)parse_sorted_query(...)Result
Reviewers can now think about query parsing as:
instead of multiple helper paths.
2. Parsing pipeline is now explicit
The new parsing flow is layered:
This makes responsibilities much clearer:
QueryTerm: parse one search term like'year:1999'or'/tmp'SortTerm: parse one sort directive like'artist+'ModelQuery: orchestrate the full query string3. Query construction moved closer to query classes
A second major design improvement is that field-aware query creation now lives in query classes themselves via
FieldQuery.from_model(...).That method now handles concerns that used to be distributed elsewhere:
normalize_pattern(...)Result
Instead of models and parsers coordinating low-level query setup, the query class now owns how it should be built for a model.
That is a cleaner boundary:
4. Query composition is simpler and flatter
Query.__or__was added, and bothAndQueryandOrQuerynow flatten when combined.This matters because the parser now builds final query trees through operator composition rather than repeatedly wrapping collection queries.
High-level impact
The resulting query objects are simpler:
TrueQuery,NumericQuery, etc.AndQuery/OrQuerytreesThis is why several tests changed from asserting on nested
subqueries[0]shapes to asserting directly on the final query type.5. Sort handling is centralized and model-driven
Sort parsing now follows the same pattern as query parsing:
SortTermparses and validates sort tokensLibModel.default_sortAlso, sort case sensitivity is now owned by
FieldSortrather than threaded through parsing helpers.Result
Sorting behavior is:
Library and plugin integration impact
This PR updates multiple call sites to use the new model API consistently.
Before
or
After
This affects core library flow and plugin consumers such as:
advancedrewriteconvertihatelyricssmartplaylistaurabpdWhy this is good
All callers now depend on the same contract:
.queryand.sortThat reduces API surface and keeps parsing behavior consistent across the application.
Data flow after this PR
And inside parsing:
Behavioral changes reviewers should notice
This is mostly a refactor, but there are a few meaningful parser-contract changes:
1. Simpler query object shapes
Empty and single-term parses now often return direct queries like
TrueQueryorNumericQueryinstead of wrapper collection queries.2. Parsing happens once
ModelQuery.parse(...)tokenizes once and derives both query and sort from the same token stream.3. Implicit path queries are normalized in parser/core flow
Path-like input such as
'/tmp'becomes apathquery earlier and more consistently.4. Sort suffix precedence is clearer
Strings ending in
'+'or'-'are treated as sort directives when valid.5. Public helper surface is reduced
Legacy
dbcoreparsing re-exports/helpers are removed in favor of the model API.Test architecture changes
The test portion of this PR is not just cleanup; it aligns the suite with the new architecture.
What changed
test/dbcore/test_queryparse.pybeets.test.fixturesunittest.TestCasestyle tests were rewritten into grouped pytest classes@pytest.mark.parametrize(...)Why this matters
The tests now mirror the actual parser design:
TestQueryTermParsingTestQueryFromPartsTestSortFromPartsTestParseSortedQueryTestModelQueryErrorsThat makes the parser contract easier to review and safer to extend.
Reviewer guide
If reviewing for architecture, the most important files are:
beets/dbcore/queryparse.pybeets/dbcore/query.pybeets/library/models.pybeets/library/library.pybeets/test/fixtures.pytest/dbcore/test_queryparse.pySuggested review order
beets/dbcore/queryparse.pybeets/dbcore/query.pybeets/library/models.pyNet effect
This PR is a query parsing consolidation.
It moves the system from a helper-driven design to a model-centered parsing API with clearer ownership boundaries:
The overall impact is a smaller public surface, clearer layering, and a test suite that better documents the intended behavior.