Style cleanup in sorting

This commit is contained in:
Adrian Sampson 2014-09-12 20:56:33 -07:00
parent d572bde13b
commit bb4082fbfc
3 changed files with 43 additions and 50 deletions

View file

@ -18,6 +18,10 @@ import re
from operator import attrgetter
from beets import util
from datetime import datetime, timedelta
from collections import namedtuple
SortedQuery = namedtuple('SortedQuery', 'query', 'sort')
class Query(object):
@ -500,19 +504,21 @@ class DateQuery(FieldQuery):
return clause, subvals
# Sorting.
class Sort(object):
"""An abstract class representing a sort operation for a query into the
item database.
"""An abstract class representing a sort operation for a query into
the item database.
"""
def select_clause(self):
""" Generates a select sql fragment if the sort operation requires one,
an empty string otherwise.
"""Generate a SELECT fragment (possibly an empty string) for the
sort.
"""
return ""
def union_clause(self):
""" Generates a union sql fragment if the sort operation requires one,
an empty string otherwise.
"""Generate a join SQL fragment (possibly an empty string).
"""
return ""
@ -523,47 +529,32 @@ class Sort(object):
return None
def sort(self, items):
"""Return a key function that can be used with the list.sort() method.
Meant to be used with slow sort, it must be implemented even for sort
that can be done with sql, as they might be used in conjunction with
slow sort.
"""Sort the list of objects and return a list.
"""
return sorted(items, key=lambda x: x)
def is_slow(self):
"""Indicate whether this query is *slow*, meaning that it cannot
be executed in SQL and must be executed in Python.
"""
return False
class MultipleSort(Sort):
"""Sort class that combines several sort criteria.
This implementation tries to implement as many sort operation in sql,
falling back to python sort only when necessary.
"""Sort that encapsulates multiple sub-sorts.
"""
def __init__(self):
self.sorts = []
def add_criteria(self, sort):
def add_sort(self, sort):
self.sorts.append(sort)
def _sql_sorts(self):
""" Returns the list of sort for which sql can be used
"""
# with several Sort, we can use SQL sorting only if there is only
# SQL-capable Sort or if the list ends with SQl-capable Sort.
sql_sorts = []
for sort in reversed(self.sorts):
if not sort.order_clause() is None:
sql_sorts.append(sort)
else:
break
sql_sorts.reverse()
return sql_sorts
def select_clause(self):
sql_sorts = self._sql_sorts()
if self.is_slow():
return ""
select_strings = []
for sort in sql_sorts:
for sort in self.sorts:
select = sort.select_clause()
if select:
select_strings.append(select)
@ -572,18 +563,20 @@ class MultipleSort(Sort):
return select_string
def union_clause(self):
sql_sorts = self._sql_sorts()
if self.is_slow():
return ""
union_strings = []
for sort in sql_sorts:
for sort in self.sorts:
union = sort.union_clause()
union_strings.append(union)
return "".join(union_strings)
def order_clause(self):
sql_sorts = self._sql_sorts()
if self.is_slow():
return None
order_strings = []
for sort in sql_sorts:
for sort in self.sorts:
order = sort.order_clause()
order_strings.append(order)
@ -621,12 +614,12 @@ class FlexFieldSort(Sort):
self.is_ascending = is_ascending
def select_clause(self):
""" Return a select sql fragment.
"""Return a SELECT fragment.
"""
return "sort_flexattr{0!s}.value as flex_{0!s} ".format(self.field)
def union_clause(self):
""" Returns an union sql fragment.
"""Return a JOIN fragment.
"""
union = ("LEFT JOIN {flextable} as sort_flexattr{index!s} "
"ON {table}.id = sort_flexattr{index!s}.entity_id "
@ -637,7 +630,7 @@ class FlexFieldSort(Sort):
return union
def order_clause(self):
""" Returns an order sql fragment.
"""Return an ORDER BY fragment.
"""
order = "ASC" if self.is_ascending else "DESC"
return "flex_{0} {1} ".format(self.field, order)

View file

@ -149,5 +149,5 @@ def sort_from_strings(model_cls, sort_parts):
return None
sort = query.MultipleSort()
for part in sort_parts:
sort.add_criteria(construct_sort_part(model_cls, part))
sort.add_sort(construct_sort_part(model_cls, part))
return sort

View file

@ -113,8 +113,8 @@ class SortFixedFieldTest(DummyDataTestCase):
s1 = dbcore.query.FixedFieldSort("album", True)
s2 = dbcore.query.FixedFieldSort("year", True)
sort = dbcore.query.MultipleSort()
sort.add_criteria(s1)
sort.add_criteria(s2)
sort.add_sort(s1)
sort.add_sort(s2)
results = self.lib.items(q, sort)
self.assertLessEqual(results[0]['album'], results[1]['album'])
self.assertLessEqual(results[1]['album'], results[2]['album'])
@ -160,8 +160,8 @@ class SortFlexFieldTest(DummyDataTestCase):
s1 = dbcore.query.FlexFieldSort(beets.library.Item, "flex2", False)
s2 = dbcore.query.FlexFieldSort(beets.library.Item, "flex1", True)
sort = dbcore.query.MultipleSort()
sort.add_criteria(s1)
sort.add_criteria(s2)
sort.add_sort(s1)
sort.add_sort(s2)
results = self.lib.items(q, sort)
self.assertGreaterEqual(results[0]['flex2'], results[1]['flex2'])
self.assertGreaterEqual(results[1]['flex2'], results[2]['flex2'])
@ -205,8 +205,8 @@ class SortAlbumFixedFieldTest(DummyDataTestCase):
s1 = dbcore.query.FixedFieldSort("genre", True)
s2 = dbcore.query.FixedFieldSort("album", True)
sort = dbcore.query.MultipleSort()
sort.add_criteria(s1)
sort.add_criteria(s2)
sort.add_sort(s1)
sort.add_sort(s2)
results = self.lib.albums(q, sort)
self.assertLessEqual(results[0]['genre'], results[1]['genre'])
self.assertLessEqual(results[1]['genre'], results[2]['genre'])
@ -250,8 +250,8 @@ class SortAlbumFlexdFieldTest(DummyDataTestCase):
s1 = dbcore.query.FlexFieldSort(beets.library.Album, "flex2", True)
s2 = dbcore.query.FlexFieldSort(beets.library.Album, "flex1", True)
sort = dbcore.query.MultipleSort()
sort.add_criteria(s1)
sort.add_criteria(s2)
sort.add_sort(s1)
sort.add_sort(s2)
results = self.lib.albums(q, sort)
self.assertLessEqual(results[0]['flex2'], results[1]['flex2'])
self.assertLessEqual(results[1]['flex2'], results[2]['flex2'])
@ -299,8 +299,8 @@ class SortCombinedFieldTest(DummyDataTestCase):
s1 = dbcore.query.ComputedFieldSort(beets.library.Album, "path", True)
s2 = dbcore.query.FixedFieldSort("year", True)
sort = dbcore.query.MultipleSort()
sort.add_criteria(s1)
sort.add_criteria(s2)
sort.add_sort(s1)
sort.add_sort(s2)
results = self.lib.albums(q, sort)
self.assertLessEqual(results[0]['path'], results[1]['path'])
self.assertLessEqual(results[1]['path'], results[2]['path'])
@ -314,8 +314,8 @@ class SortCombinedFieldTest(DummyDataTestCase):
s1 = dbcore.query.FixedFieldSort("year", True)
s2 = dbcore.query.ComputedFieldSort(beets.library.Album, "path", True)
sort = dbcore.query.MultipleSort()
sort.add_criteria(s1)
sort.add_criteria(s2)
sort.add_sort(s1)
sort.add_sort(s2)
results = self.lib.albums(q, sort)
self.assertLessEqual(results[0]['year'], results[1]['year'])
self.assertLessEqual(results[1]['year'], results[2]['year'])