Add basename in separate column so we can orderby it.

For a while I've wanted to be able to sort search results by the file's
basename. This is especially important for the cli. SQLite doesn't have
an easy way to split the filepath column by the slash, so the only
choice is to store basename as a separate column. I put it off for a
while but I think it's the right move. However we must not forget to
update it every time we update filepath, which is a bummer.
This commit is contained in:
voussoir 2020-12-30 15:28:52 -08:00
parent f9dc720ff8
commit 6522bda09c
6 changed files with 75 additions and 6 deletions

View file

@ -41,7 +41,7 @@ ffmpeg = _load_ffmpeg()
# Database ######################################################################################### # Database #########################################################################################
DATABASE_VERSION = 15 DATABASE_VERSION = 16
DB_VERSION_PRAGMA = f''' DB_VERSION_PRAGMA = f'''
PRAGMA user_version = {DATABASE_VERSION}; PRAGMA user_version = {DATABASE_VERSION};
''' '''
@ -91,8 +91,9 @@ CREATE TABLE IF NOT EXISTS photos(
id TEXT PRIMARY KEY NOT NULL, id TEXT PRIMARY KEY NOT NULL,
filepath TEXT COLLATE NOCASE, filepath TEXT COLLATE NOCASE,
dev_ino TEXT, dev_ino TEXT,
basename TEXT COLLATE NOCASE,
override_filename TEXT COLLATE NOCASE, override_filename TEXT COLLATE NOCASE,
extension TEXT, extension TEXT COLLATE NOCASE,
width INT, width INT,
height INT, height INT,
ratio REAL, ratio REAL,
@ -195,6 +196,7 @@ SQL_INDEX = sqlhelpers.reverse_table_column_map(SQL_COLUMNS)
ALLOWED_ORDERBY_COLUMNS = { ALLOWED_ORDERBY_COLUMNS = {
'area', 'area',
'basename',
'bitrate', 'bitrate',
'bytes', 'bytes',
'created', 'created',

View file

@ -1097,6 +1097,7 @@ class Photo(ObjectBase):
data = { data = {
'id': self.id, 'id': self.id,
'filepath': new_filepath.absolute_path, 'filepath': new_filepath.absolute_path,
'basename': new_filepath.basename,
} }
self.photodb.sql_update(table='photos', pairs=data, where_key='id') self.photodb.sql_update(table='photos', pairs=data, where_key='id')
self.real_path = new_filepath self.real_path = new_filepath
@ -1180,9 +1181,11 @@ class Photo(ObjectBase):
spinal.copy_file(old_path, new_path) spinal.copy_file(old_path, new_path)
data = { data = {
'filepath': (old_path.absolute_path, new_path.absolute_path), 'id': self.id,
'filepath': new_path.absolute_path,
'basename': new_path.basename,
} }
self.photodb.sql_update(table='photos', pairs=data, where_key='filepath') self.photodb.sql_update(table='photos', pairs=data, where_key='id')
self.real_path = new_path self.real_path = new_path
if new_path.normcase == old_path.normcase: if new_path.normcase == old_path.normcase:

View file

@ -507,6 +507,7 @@ class PDBPhotoMixin:
data = { data = {
'id': photo_id, 'id': photo_id,
'filepath': filepath.absolute_path, 'filepath': filepath.absolute_path,
'basename': filepath.basename,
'override_filename': None, 'override_filename': None,
'extension': filepath.extension.no_dot, 'extension': filepath.extension.no_dot,
'created': helpers.now(), 'created': helpers.now(),

View file

@ -338,6 +338,9 @@ def normalize_orderby(orderby, warning_bag=None):
elif column == 'area': elif column == 'area':
column = '(width * height)' column = '(width * height)'
elif column == 'basename':
column = 'COALESCE(override_filename, basename)'
elif column == 'bitrate': elif column == 'bitrate':
column = '((bytes / 128) / duration)' column = '((bytes / 128) / duration)'

View file

@ -192,7 +192,6 @@ def purge_empty_albums_argparse(args):
def search_argparse(args): def search_argparse(args):
photos = search_by_argparse(args, yield_photos=True) photos = search_by_argparse(args, yield_photos=True)
photos = sorted(photos, key=lambda p: p.real_path)
for photo in photos: for photo in photos:
print(photo.real_path.absolute_path) print(photo.real_path.absolute_path)
@ -357,7 +356,7 @@ def main(argv):
p_search.add_argument('--tag_expression', '--tag-expression', dest='tag_expression', default=None) p_search.add_argument('--tag_expression', '--tag-expression', dest='tag_expression', default=None)
p_search.add_argument('--limit', dest='limit', default=None) p_search.add_argument('--limit', dest='limit', default=None)
p_search.add_argument('--offset', dest='offset', default=None) p_search.add_argument('--offset', dest='offset', default=None)
p_search.add_argument('--orderby', dest='orderby', default=None) p_search.add_argument('--orderby', dest='orderby', default='basename-ASC')
# p_search.add_argument('--yield_albums', '--yield-albums', dest='yield_albums', default=None) # p_search.add_argument('--yield_albums', '--yield-albums', dest='yield_albums', default=None)
p_search.set_defaults(func=search_argparse) p_search.set_defaults(func=search_argparse)

View file

@ -1,4 +1,5 @@
import argparse import argparse
import os
import sys import sys
from voussoirkit import sqlhelpers from voussoirkit import sqlhelpers
@ -454,6 +455,66 @@ def upgrade_14_to_15(photodb):
dev_ino = f'{dev},{ino}' dev_ino = f'{dev},{ino}'
photodb.sql_execute('UPDATE photos SET dev_ino = ? WHERE id == ?', [dev_ino, photo.id]) photodb.sql_execute('UPDATE photos SET dev_ino = ? WHERE id == ?', [dev_ino, photo.id])
def upgrade_15_to_16(photodb):
'''
Added the basename column to photos. Added collate nocase to extension.
'''
with Regenerator(photodb, except_tables='photos'):
photodb.sql_executescript('''
PRAGMA foreign_keys = OFF;
BEGIN;
ALTER TABLE photos RENAME TO photos_old;
CREATE TABLE photos(
id TEXT PRIMARY KEY NOT NULL,
filepath TEXT COLLATE NOCASE,
dev_ino TEXT,
basename TEXT COLLATE NOCASE,
override_filename TEXT COLLATE NOCASE,
extension TEXT COLLATE NOCASE,
width INT,
height INT,
ratio REAL,
area INT,
duration INT,
bytes INT,
created INT,
thumbnail TEXT,
tagged_at INT,
author_id TEXT,
searchhidden INT,
FOREIGN KEY(author_id) REFERENCES users(id)
);
INSERT INTO photos SELECT
id,
filepath,
dev_ino,
NULL,
override_filename,
extension,
width,
height,
ratio,
area,
duration,
bytes,
created,
thumbnail,
tagged_at,
author_id,
searchhidden
FROM photos_old;
DROP TABLE photos_old;
''')
for (id, filepath) in photodb.sql_select('SELECT id, filepath FROM photos'):
basename = os.path.basename(filepath)
photodb.sql_execute('UPDATE photos SET basename = ? WHERE id == ?', [basename, id])
def upgrade_all(data_directory): def upgrade_all(data_directory):
''' '''
Given the directory containing a phototagger database, apply all of the Given the directory containing a phototagger database, apply all of the