From ad71af2c3d1d6e266f77594cbd36613174de12f9 Mon Sep 17 00:00:00 2001 From: Thomas Scholtes Date: Wed, 17 Sep 2014 11:23:00 +0200 Subject: [PATCH] Add NoneQuery This makes fast SQL queries for singletons possible --- beets/dbcore/query.py | 17 ++++++++++++++++ test/test_query.py | 46 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py index 4f6c0c6ab..5a6dff054 100644 --- a/beets/dbcore/query.py +++ b/beets/dbcore/query.py @@ -83,6 +83,23 @@ class MatchQuery(FieldQuery): return pattern == value +class NoneQuery(FieldQuery): + + def __init__(self, field, fast=True): + self.field = field + self.fast = fast + + def col_clause(self): + return self.field + " IS NULL", () + + @classmethod + def match(self, item): + try: + return item[self.field] is None + except KeyError: + return True + + class StringFieldQuery(FieldQuery): """A FieldQuery that converts values to strings before matching them. diff --git a/test/test_query.py b/test/test_query.py index 6dd31dcec..e71f7f484 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -16,14 +16,26 @@ """ import _common from _common import unittest -from helper import TestHelper +import helper import beets.library from beets import dbcore from beets.dbcore import types +from beets.dbcore.query import NoneQuery from beets.library import Library, Item +class TestHelper(helper.TestHelper): + + def assertInResult(self, item, results): + result_ids = map(lambda i: i.id, results) + self.assertIn(item.id, result_ids) + + def assertNotInResult(self, item, results): + result_ids = map(lambda i: i.id, results) + self.assertNotIn(item.id, result_ids) + + class AnyFieldQueryTest(_common.LibTestCase): def test_no_restriction(self): q = dbcore.query.AnyFieldQuery( @@ -469,14 +481,6 @@ class BoolQueryTest(unittest.TestCase, TestHelper): self.assertInResult(item_false, matched) self.assertNotInResult(item_true, matched) - def assertInResult(self, item, results): - result_ids = map(lambda i: i.id, results) - self.assertIn(item.id, result_ids) - - def assertNotInResult(self, item, results): - result_ids = map(lambda i: i.id, results) - self.assertNotIn(item.id, result_ids) - class DefaultSearchFieldsTest(DummyDataTestCase): def test_albums_matches_album(self): @@ -496,6 +500,30 @@ class DefaultSearchFieldsTest(DummyDataTestCase): self.assert_matched(items, []) +class NoneQueryTest(unittest.TestCase, TestHelper): + + def setUp(self): + self.lib = Library(':memory:') + + def test_match_singletons(self): + singleton = self.add_item() + album_item = self.add_album().items().get() + + matched = self.lib.items(NoneQuery('album_id')) + self.assertInResult(singleton, matched) + self.assertNotInResult(album_item, matched) + + def test_match_after_set_none(self): + item = self.add_item(rg_track_gain=0) + matched = self.lib.items(NoneQuery('rg_track_gain')) + self.assertNotInResult(item, matched) + + item['rg_track_gain'] = None + item.store() + matched = self.lib.items(NoneQuery('rg_track_gain')) + self.assertInResult(item, matched) + + def suite(): return unittest.TestLoader().loadTestsFromName(__name__)