From 724b06a77d5c2f9a28d512210235078a0c972cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Nejus?= Date: Sat, 8 Apr 2023 05:46:56 +0100 Subject: [PATCH 1/2] Define SingletonQuery to perform filtering through SQL This slightly speeds up the queries and there's a nice side-effect where `singleton:1` and `singleton:0` now work fine! This is ultimately building towards replacing as many python-only queries with SQL equivalents. --- beets/library.py | 10 ++++++++++ docs/changelog.rst | 2 ++ test/test_query.py | 10 ++++++++++ 3 files changed, 22 insertions(+) diff --git a/beets/library.py b/beets/library.py index 9d5219b18..7fb96d400 100644 --- a/beets/library.py +++ b/beets/library.py @@ -44,6 +44,14 @@ log = logging.getLogger('beets') # Library-specific query types. +class SingletonQuery(dbcore.FieldQuery): + def __new__(cls, field, value, *args, **kwargs): + query = dbcore.query.NoneQuery('album_id') + if util.str2bool(value): + return query + return dbcore.query.NotQuery(query) + + class PathQuery(dbcore.FieldQuery): """A query that matches all items under a given path. @@ -569,6 +577,8 @@ class Item(LibModel): _sorts = {'artist': SmartArtistSort} + _queries = {'singleton': SingletonQuery} + _format_config_key = 'format_item' # Cached album object. Read-only. diff --git a/docs/changelog.rst b/docs/changelog.rst index 39cd8fa2d..01f160f8e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -11,6 +11,8 @@ for Python 3.6). New features: +* :ref:`list-cmd` `singleton:true` queries have been made faster +* :ref:`list-cmd` `singleton:1` and `singleton:0` can now alternatively be used in queries, same as `comp` * --from-logfile now parses log files using a UTF-8 encoding in `beets/beets/ui/commands.py`. :bug:`4693` * Added additional error handling for `spotify` plugin. diff --git a/test/test_query.py b/test/test_query.py index 3d7b56781..5458676db 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -290,11 +290,21 @@ class GetTest(DummyDataTestCase): results = self.lib.items(q) self.assert_items_matched(results, ['beets 4 eva']) + def test_singleton_1(self): + q = 'singleton:1' + results = self.lib.items(q) + self.assert_items_matched(results, ['beets 4 eva']) + def test_singleton_false(self): q = 'singleton:false' results = self.lib.items(q) self.assert_items_matched(results, ['foo bar', 'baz qux']) + def test_singleton_0(self): + q = 'singleton:0' + results = self.lib.items(q) + self.assert_items_matched(results, ['foo bar', 'baz qux']) + def test_compilation_true(self): q = 'comp:true' results = self.lib.items(q) From 699e12bd881c1e52d4b15b3d8e7d2f7468a5ceca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0ar=C5=ABnas=20Nejus?= Date: Sun, 9 Apr 2023 18:18:34 +0100 Subject: [PATCH 2/2] Add explanatory docstring --- beets/library.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/beets/library.py b/beets/library.py index 7fb96d400..b52d5f873 100644 --- a/beets/library.py +++ b/beets/library.py @@ -45,6 +45,15 @@ log = logging.getLogger('beets') # Library-specific query types. class SingletonQuery(dbcore.FieldQuery): + """This query is responsible for the 'singleton' lookup. + + It is based on the FieldQuery and constructs a SQL clause + 'album_id is NULL' which yields the same result as the previous filter + in Python but is more performant since it's done in SQL. + + Using util.str2bool ensures that lookups like singleton:true, singleton:1 + and singleton:false, singleton:0 are handled consistently. + """ def __new__(cls, field, value, *args, **kwargs): query = dbcore.query.NoneQuery('album_id') if util.str2bool(value):