Skip to content

Commit ad71af2

Browse files
author
Thomas Scholtes
committed
Add NoneQuery
This makes fast SQL queries for singletons possible
1 parent 89d7c5d commit ad71af2

File tree

2 files changed

+54
-9
lines changed

2 files changed

+54
-9
lines changed

beets/dbcore/query.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,23 @@ def value_match(cls, pattern, value):
8383
return pattern == value
8484

8585

86+
class NoneQuery(FieldQuery):
87+
88+
def __init__(self, field, fast=True):
89+
self.field = field
90+
self.fast = fast
91+
92+
def col_clause(self):
93+
return self.field + " IS NULL", ()
94+
95+
@classmethod
96+
def match(self, item):
97+
try:
98+
return item[self.field] is None
99+
except KeyError:
100+
return True
101+
102+
86103
class StringFieldQuery(FieldQuery):
87104
"""A FieldQuery that converts values to strings before matching
88105
them.

test/test_query.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,26 @@
1616
"""
1717
import _common
1818
from _common import unittest
19-
from helper import TestHelper
19+
import helper
2020

2121
import beets.library
2222
from beets import dbcore
2323
from beets.dbcore import types
24+
from beets.dbcore.query import NoneQuery
2425
from beets.library import Library, Item
2526

2627

28+
class TestHelper(helper.TestHelper):
29+
30+
def assertInResult(self, item, results):
31+
result_ids = map(lambda i: i.id, results)
32+
self.assertIn(item.id, result_ids)
33+
34+
def assertNotInResult(self, item, results):
35+
result_ids = map(lambda i: i.id, results)
36+
self.assertNotIn(item.id, result_ids)
37+
38+
2739
class AnyFieldQueryTest(_common.LibTestCase):
2840
def test_no_restriction(self):
2941
q = dbcore.query.AnyFieldQuery(
@@ -469,14 +481,6 @@ def test_flex_parse_any_string(self):
469481
self.assertInResult(item_false, matched)
470482
self.assertNotInResult(item_true, matched)
471483

472-
def assertInResult(self, item, results):
473-
result_ids = map(lambda i: i.id, results)
474-
self.assertIn(item.id, result_ids)
475-
476-
def assertNotInResult(self, item, results):
477-
result_ids = map(lambda i: i.id, results)
478-
self.assertNotIn(item.id, result_ids)
479-
480484

481485
class DefaultSearchFieldsTest(DummyDataTestCase):
482486
def test_albums_matches_album(self):
@@ -496,6 +500,30 @@ def test_items_does_not_match_year(self):
496500
self.assert_matched(items, [])
497501

498502

503+
class NoneQueryTest(unittest.TestCase, TestHelper):
504+
505+
def setUp(self):
506+
self.lib = Library(':memory:')
507+
508+
def test_match_singletons(self):
509+
singleton = self.add_item()
510+
album_item = self.add_album().items().get()
511+
512+
matched = self.lib.items(NoneQuery('album_id'))
513+
self.assertInResult(singleton, matched)
514+
self.assertNotInResult(album_item, matched)
515+
516+
def test_match_after_set_none(self):
517+
item = self.add_item(rg_track_gain=0)
518+
matched = self.lib.items(NoneQuery('rg_track_gain'))
519+
self.assertNotInResult(item, matched)
520+
521+
item['rg_track_gain'] = None
522+
item.store()
523+
matched = self.lib.items(NoneQuery('rg_track_gain'))
524+
self.assertInResult(item, matched)
525+
526+
499527
def suite():
500528
return unittest.TestLoader().loadTestsFromName(__name__)
501529

0 commit comments

Comments
 (0)