mirror of
https://github.com/beetbox/beets.git
synced 2025-12-14 12:35:19 +01:00
Refactor query utilities
We now use somewhat more general query constructors in `dbcore`, avoiding the need for somewhat special-purpose `duplicates` methods on the model objects.
This commit is contained in:
parent
ca38486b83
commit
bcc8903036
3 changed files with 34 additions and 26 deletions
|
|
@ -27,7 +27,7 @@ import beets
|
|||
from beets.util import functemplate
|
||||
from beets.util import py3_path
|
||||
from beets.dbcore import types
|
||||
from .query import MatchQuery, NullSort, TrueQuery
|
||||
from .query import MatchQuery, NullSort, TrueQuery, AndQuery
|
||||
from collections.abc import Mapping
|
||||
|
||||
|
||||
|
|
@ -641,14 +641,24 @@ class Model:
|
|||
"""
|
||||
self[key] = self._parse(key, string)
|
||||
|
||||
# Convenient queries.
|
||||
|
||||
@classmethod
|
||||
def construct_match_queries(cls, **info):
|
||||
subqueries = []
|
||||
for key, value in info.items():
|
||||
# Use slow queries for flexible attributes.
|
||||
fast = key in cls._fields
|
||||
subqueries.append(MatchQuery(key, value, fast))
|
||||
return subqueries
|
||||
def field_query(cls, field, pattern, query_cls=MatchQuery):
|
||||
"""Get a `FieldQuery` for this model."""
|
||||
return query_cls(field, pattern, field in cls._fields)
|
||||
|
||||
@classmethod
|
||||
def all_fields_query(cls, pats, query_cls=MatchQuery):
|
||||
"""Get a query that matches many fields with different patterns.
|
||||
|
||||
`pats` should be a mapping from field names to patterns. The
|
||||
resulting query is a conjunction ("and") of per-field queries
|
||||
for all of these field/pattern pairs.
|
||||
"""
|
||||
subqueries = [cls.field_query(k, v, query_cls)
|
||||
for k, v in pats.items()]
|
||||
return AndQuery(subqueries)
|
||||
|
||||
|
||||
# Database controller and supporting interfaces.
|
||||
|
|
|
|||
|
|
@ -675,16 +675,20 @@ class ImportTask(BaseImportTask):
|
|||
# As-is import with no artist. Skip check.
|
||||
return []
|
||||
|
||||
# Create a temporary Album so computed fields are available for
|
||||
# duplicate detection.
|
||||
# Construct a query to find duplicates with this metadata. We
|
||||
# use a temporary Album object to generate any computed fields.
|
||||
tmp_album = library.Album(lib, **info)
|
||||
keys = config['import']['duplicate_keys']['album'].as_str_seq()
|
||||
dup_query = library.Album.all_fields_query({
|
||||
key: tmp_album.get(key)
|
||||
for key in keys
|
||||
})
|
||||
|
||||
# Don't count albums with the same files as duplicates.
|
||||
task_paths = {i.path for i in self.items if i}
|
||||
|
||||
duplicates = []
|
||||
keys = config['import']['duplicate_keys']['album'].as_str_seq()
|
||||
for album in tmp_album.duplicates(*keys):
|
||||
for album in lib.albums(dup_query):
|
||||
# Check whether the album paths are all present in the task
|
||||
# i.e. album is being completely re-imported by the task,
|
||||
# in which case it is not a duplicate (will be replaced).
|
||||
|
|
@ -930,16 +934,20 @@ class SingletonImportTask(ImportTask):
|
|||
"""
|
||||
info = self.chosen_info()
|
||||
|
||||
# Use a temporary Item to provide computed fields.
|
||||
# Query for existing items using the same metadata. We use a
|
||||
# temporary `Item` object to generate any computed fields.
|
||||
tmp_item = library.Item(lib, **info)
|
||||
keys = config['import']['duplicate_keys']['single'].as_str_seq()
|
||||
dup_query = library.Album.all_fields_query({
|
||||
key: tmp_item.get(key)
|
||||
for key in keys
|
||||
})
|
||||
|
||||
found_items = []
|
||||
keys = config['import']['duplicate_keys']['single'].as_str_seq()
|
||||
for other_item in tmp_item.duplicates(*keys):
|
||||
for other_item in lib.items(dup_query):
|
||||
# Existing items not considered duplicates.
|
||||
if other_item.path != self.item.path:
|
||||
found_items.append(other_item)
|
||||
|
||||
return found_items
|
||||
|
||||
duplicate_items = find_duplicates
|
||||
|
|
|
|||
|
|
@ -607,11 +607,6 @@ class Item(LibModel):
|
|||
i.mtime = i.current_mtime() # Initial mtime.
|
||||
return i
|
||||
|
||||
def duplicates(self, *keys):
|
||||
info = {key: self.get(key) for key in keys}
|
||||
subqueries = self.construct_match_queries(**info)
|
||||
return self._db.items(dbcore.AndQuery(subqueries))
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Set the item's value for a standard field or a flexattr."""
|
||||
# Encode unicode paths and read buffers.
|
||||
|
|
@ -1147,11 +1142,6 @@ class Album(LibModel):
|
|||
getters['albumtotal'] = Album._albumtotal
|
||||
return getters
|
||||
|
||||
def duplicates(self, *keys):
|
||||
info = {key: self.get(key) for key in keys}
|
||||
subqueries = self.construct_match_queries(**info)
|
||||
return self._db.albums(dbcore.AndQuery(subqueries))
|
||||
|
||||
def items(self):
|
||||
"""Return an iterable over the items associated with this
|
||||
album.
|
||||
|
|
|
|||
Loading…
Reference in a new issue