Prototype support for named (pseudo-field) queries

As discussed here:
https://github.com/beetbox/beets/pull/3145#pullrequestreview-204523870

This would replace the need for #3149.
This commit is contained in:
Adrian Sampson 2019-02-17 13:41:05 -05:00
parent 3633c1e27f
commit d8e167637e
3 changed files with 43 additions and 18 deletions

View file

@ -143,6 +143,11 @@ class Model(object):
are subclasses of `Sort`.
"""
_queries = {}
"""Named queries that use a field-like `name:value` syntax but which
do not relate to any specific field.
"""
_always_dirty = False
"""By default, fields only become "dirty" when their value actually
changes. Enabling this flag marks fields as dirty even when the new

View file

@ -119,12 +119,13 @@ def construct_query_part(model_cls, prefixes, query_part):
if not query_part:
return query.TrueQuery()
# Use `model_cls` to build up a map from field names to `Query`
# classes.
# Use `model_cls` to build up a map from field (or query) names to
# `Query` classes.
query_classes = {}
for k, t in itertools.chain(model_cls._fields.items(),
model_cls._types.items()):
query_classes[k] = t.query
query_classes.update(model_cls._queries) # Non-field queries.
# Parse the string.
key, pattern, query_class, negate = \
@ -137,26 +138,27 @@ def construct_query_part(model_cls, prefixes, query_part):
# The query type matches a specific field, but none was
# specified. So we use a version of the query that matches
# any field.
q = query.AnyFieldQuery(pattern, model_cls._search_fields,
query_class)
if negate:
return query.NotQuery(q)
else:
return q
out_query = query.AnyFieldQuery(pattern, model_cls._search_fields,
query_class)
else:
# Non-field query type.
if negate:
return query.NotQuery(query_class(pattern))
else:
return query_class(pattern)
out_query = query_class(pattern)
# Otherwise, this must be a `FieldQuery`. Use the field name to
# construct the query object.
key = key.lower()
q = query_class(key.lower(), pattern, key in model_cls._fields)
# Field queries get constructed according to the name of the field
# they are querying.
elif issubclass(query_class, query.FieldQuery):
key = key.lower()
out_query = query_class(key.lower(), pattern, key in model_cls._fields)
# Non-field (named) query.
else:
out_query = query_class(pattern)
# Apply negation.
if negate:
return query.NotQuery(q)
return q
return query.NotQuery(out_query)
else:
return out_query
def query_from_strings(query_cls, model_cls, prefixes, query_parts):

View file

@ -36,6 +36,17 @@ class TestSort(dbcore.query.FieldSort):
pass
class TestQuery(dbcore.query.Query):
def __init__(self, pattern):
self.pattern = pattern
def clause(self):
return None, ()
def match(self):
return True
class TestModel1(dbcore.Model):
_table = 'test'
_flex_table = 'testflex'
@ -49,6 +60,9 @@ class TestModel1(dbcore.Model):
_sorts = {
'some_sort': TestSort,
}
_queries = {
'some_query': TestQuery,
}
@classmethod
def _getters(cls):
@ -519,6 +533,10 @@ class QueryFromStringsTest(unittest.TestCase):
q = self.qfs([''])
self.assertIsInstance(q.subqueries[0], dbcore.query.TrueQuery)
def test_parse_named_query(self):
q = self.qfs(['some_query:foo'])
self.assertIsInstance(q.subqueries[0], TestQuery)
class SortFromStringsTest(unittest.TestCase):
def sfs(self, strings):