Make NotQuery subclass Query, update tests

* Modify NotQuery so it subclasses Query instead of MutableCollectionQuery.
* Update instances where NotQuery objects are created on tests and queryparse,
as NotQuery expects a single Query as a parameter to the constructor instead of
a list of Queries.
This commit is contained in:
Diego Moreda 2015-11-18 14:54:20 +01:00
parent f2c8e9ff07
commit dd8b80e320
3 changed files with 49 additions and 40 deletions

View file

@ -447,14 +447,14 @@ class OrQuery(MutableCollectionQuery):
return any([q.match(item) for q in self.subqueries])
class NotQuery(MutableCollectionQuery):
class NotQuery(Query):
"""A query that matches the negation of its `subquery`, as a shorcut for
performing `not(subquery)` without using regular expressions.
performing `not(subquery)` without using regular expressions."""
def __init__(self, subquery):
self.subquery = subquery
TODO: revise class hierarchy, probably limiting to one subquery
"""
def clause(self):
clause, subvals = self.clause_with_joiner('TODO')
clause, subvals = self.subquery.clause()
if clause:
return 'not ({0})'.format(clause), subvals
else:
@ -462,7 +462,17 @@ class NotQuery(MutableCollectionQuery):
return clause, subvals
def match(self, item):
return not all([q.match(item) for q in self.subqueries])
return not self.subquery.match(item)
def __repr__(self):
return "{0.__class__.__name__}({0.subquery})".format(self)
def __eq__(self, other):
return super(NotQuery, self).__eq__(other) and \
self.subquery == other.subquery
def __hash__(self):
return hash(('not', hash(self.subquery)))
class TrueQuery(Query):

View file

@ -110,22 +110,21 @@ def construct_query_part(model_cls, prefixes, query_part):
q = query.AnyFieldQuery(pattern, model_cls._search_fields,
query_class)
if negate:
return query.NotQuery([q])
return query.NotQuery(q)
else:
return q
else:
# Other query type.
if negate:
return query.NotQuery([query_class(pattern)])
return query.NotQuery(query_class(pattern))
else:
return query_class(pattern)
else:
if negate:
return query.NotQuery([query_class(key.lower(), pattern,
key in model_cls._fields)])
key = key.lower()
return query_class(key.lower(), pattern, key in model_cls._fields)
q = query_class(key.lower(), pattern, key in model_cls._fields)
if negate:
return query.NotQuery(q)
return q
def query_from_strings(query_cls, model_cls, prefixes, query_parts):

View file

@ -726,56 +726,56 @@ class NotQueryMatchTest(_common.TestCase):
def test_regex_match_positive(self):
q = dbcore.query.RegexpQuery('album', '^the album$')
self.assertTrue(q.match(self.item))
self.assertFalse(dbcore.query.NotQuery((q,)).match(self.item))
self.assertFalse(dbcore.query.NotQuery(q).match(self.item))
def test_regex_match_negative(self):
q = dbcore.query.RegexpQuery('album', '^album$')
self.assertFalse(q.match(self.item))
self.assertTrue(dbcore.query.NotQuery((q,)).match(self.item))
self.assertTrue(dbcore.query.NotQuery(q).match(self.item))
def test_regex_match_non_string_value(self):
q = dbcore.query.RegexpQuery('disc', '^6$')
self.assertTrue(q.match(self.item))
self.assertFalse(dbcore.query.NotQuery((q,)).match(self.item))
self.assertFalse(dbcore.query.NotQuery(q).match(self.item))
def test_substring_match_positive(self):
q = dbcore.query.SubstringQuery('album', 'album')
self.assertTrue(q.match(self.item))
self.assertFalse(dbcore.query.NotQuery((q,)).match(self.item))
self.assertFalse(dbcore.query.NotQuery(q).match(self.item))
def test_substring_match_negative(self):
q = dbcore.query.SubstringQuery('album', 'ablum')
self.assertFalse(q.match(self.item))
self.assertTrue(dbcore.query.NotQuery((q,)).match(self.item))
self.assertTrue(dbcore.query.NotQuery(q).match(self.item))
def test_substring_match_non_string_value(self):
q = dbcore.query.SubstringQuery('disc', '6')
self.assertTrue(q.match(self.item))
self.assertFalse(dbcore.query.NotQuery((q,)).match(self.item))
self.assertFalse(dbcore.query.NotQuery(q).match(self.item))
def test_year_match_positive(self):
q = dbcore.query.NumericQuery('year', '1')
self.assertTrue(q.match(self.item))
self.assertFalse(dbcore.query.NotQuery((q,)).match(self.item))
self.assertFalse(dbcore.query.NotQuery(q).match(self.item))
def test_year_match_negative(self):
q = dbcore.query.NumericQuery('year', '10')
self.assertFalse(q.match(self.item))
self.assertTrue(dbcore.query.NotQuery((q,)).match(self.item))
self.assertTrue(dbcore.query.NotQuery(q).match(self.item))
def test_bitrate_range_positive(self):
q = dbcore.query.NumericQuery('bitrate', '100000..200000')
self.assertTrue(q.match(self.item))
self.assertFalse(dbcore.query.NotQuery((q,)).match(self.item))
self.assertFalse(dbcore.query.NotQuery(q).match(self.item))
def test_bitrate_range_negative(self):
q = dbcore.query.NumericQuery('bitrate', '200000..300000')
self.assertFalse(q.match(self.item))
self.assertTrue(dbcore.query.NotQuery((q,)).match(self.item))
self.assertTrue(dbcore.query.NotQuery(q).match(self.item))
def test_open_range(self):
q = dbcore.query.NumericQuery('bitrate', '100000..')
dbcore.query.NotQuery((q,))
dbcore.query.NotQuery(q)
class NotQueryTest(DummyDataTestCase):
@ -790,7 +790,7 @@ class NotQueryTest(DummyDataTestCase):
- q AND not(q) == 0
- not(not(q)) == q
"""
not_q = dbcore.query.NotQuery([q])
not_q = dbcore.query.NotQuery(q)
# assert using OrQuery, AndQuery
q_or = dbcore.query.OrQuery([q, not_q])
q_and = dbcore.query.AndQuery([q, not_q])
@ -805,7 +805,7 @@ class NotQueryTest(DummyDataTestCase):
self.assertEqual(q_results.intersection(not_q_results), set())
# round trip
not_not_q = dbcore.query.NotQuery([not_q])
not_not_q = dbcore.query.NotQuery(not_q)
self.assertEqual([i.title for i in self.lib.items(q)],
[i.title for i in self.lib.items(not_not_q)])
@ -813,50 +813,50 @@ class NotQueryTest(DummyDataTestCase):
# not(a and b) <-> not(a) or not(b)
q = dbcore.query.AndQuery([dbcore.query.BooleanQuery('comp', True),
dbcore.query.NumericQuery('year', '2002')])
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, ['foo bar', 'beets 4 eva'])
self.assertNegationProperties(q)
def test_type_anyfield(self):
q = dbcore.query.AnyFieldQuery('foo', ['title', 'artist', 'album'],
dbcore.query.SubstringQuery)
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, ['baz qux'])
self.assertNegationProperties(q)
def test_type_boolean(self):
q = dbcore.query.BooleanQuery('comp', True)
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, ['beets 4 eva'])
self.assertNegationProperties(q)
def test_type_date(self):
q = dbcore.query.DateQuery('mtime', '0.0')
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, [])
self.assertNegationProperties(q)
def test_type_false(self):
q = dbcore.query.FalseQuery()
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched_all(not_results)
self.assertNegationProperties(q)
def test_type_match(self):
q = dbcore.query.MatchQuery('year', '2003')
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, ['foo bar', 'baz qux'])
self.assertNegationProperties(q)
def test_type_none(self):
q = dbcore.query.NoneQuery('rg_track_gain')
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, [])
self.assertNegationProperties(q)
def test_type_numeric(self):
q = dbcore.query.NumericQuery('year', '2001..2002')
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, ['beets 4 eva'])
self.assertNegationProperties(q)
@ -864,25 +864,25 @@ class NotQueryTest(DummyDataTestCase):
# not(a or b) <-> not(a) and not(b)
q = dbcore.query.OrQuery([dbcore.query.BooleanQuery('comp', True),
dbcore.query.NumericQuery('year', '2002')])
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, ['beets 4 eva'])
self.assertNegationProperties(q)
def test_type_regexp(self):
q = dbcore.query.RegexpQuery('artist', '^t')
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, ['foo bar'])
self.assertNegationProperties(q)
def test_type_substring(self):
q = dbcore.query.SubstringQuery('album', 'ba')
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, ['beets 4 eva'])
self.assertNegationProperties(q)
def test_type_true(self):
q = dbcore.query.TrueQuery()
not_results = self.lib.items(dbcore.query.NotQuery([q]))
not_results = self.lib.items(dbcore.query.NotQuery(q))
self.assert_items_matched(not_results, [])
self.assertNegationProperties(q)
@ -903,8 +903,8 @@ class NotQueryTest(DummyDataTestCase):
(dbcore.query.SubstringQuery, ['title', 'x'])]
for klass, args in classes:
q_fast = dbcore.query.NotQuery([klass(*(args + [True]))])
q_slow = dbcore.query.NotQuery([klass(*(args + [False]))])
q_fast = dbcore.query.NotQuery(klass(*(args + [True])))
q_slow = dbcore.query.NotQuery(klass(*(args + [False])))
try:
self.assertEqual([i.title for i in self.lib.items(q_fast)],