Database: Improve config and SQL queries

This commit is contained in:
Michael Mayer 2020-12-15 20:14:06 +01:00
parent 40966c2add
commit 28880e682d
20 changed files with 107 additions and 61 deletions

View file

@ -73,7 +73,7 @@ func backupAction(ctx *cli.Context) error {
var cmd *exec.Cmd var cmd *exec.Cmd
switch conf.DatabaseDriver() { switch conf.DatabaseDriver() {
case config.MySQL: case config.MySQL, config.MariaDB:
cmd = exec.Command( cmd = exec.Command(
conf.MysqldumpBin(), conf.MysqldumpBin(),
"-h", conf.DatabaseHost(), "-h", conf.DatabaseHost(),

View file

@ -101,7 +101,7 @@ func restoreAction(ctx *cli.Context) error {
var cmd *exec.Cmd var cmd *exec.Cmd
switch conf.DatabaseDriver() { switch conf.DatabaseDriver() {
case config.MySQL: case config.MySQL, config.MariaDB:
cmd = exec.Command( cmd = exec.Command(
conf.MysqlBin(), conf.MysqlBin(),
"-h", conf.DatabaseHost(), "-h", conf.DatabaseHost(),

View file

@ -232,77 +232,93 @@ func (c *Config) UserConfig() ClientConfig {
Server: NewRuntimeInfo(), Server: NewRuntimeInfo(),
} }
c.Db().Table("photos"). c.Db().
Table("photos").
Select("photo_uid, cell_id, photo_lat, photo_lng, taken_at"). Select("photo_uid, cell_id, photo_lat, photo_lng, taken_at").
Where("deleted_at IS NULL AND photo_lat != 0 AND photo_lng != 0"). Where("deleted_at IS NULL AND photo_lat != 0 AND photo_lng != 0").
Order("taken_at DESC"). Order("taken_at DESC").
Limit(1).Offset(0). Limit(1).Offset(0).
Take(&result.Pos) Take(&result.Pos)
c.Db().Table("cameras"). c.Db().
Table("cameras").
Where("camera_slug <> 'zz' AND camera_slug <> ''"). Where("camera_slug <> 'zz' AND camera_slug <> ''").
Select("COUNT(*) AS cameras"). Select("COUNT(*) AS cameras").
Take(&result.Count) Take(&result.Count)
c.Db().Table("lenses"). c.Db().
Table("lenses").
Where("lens_slug <> 'zz' AND lens_slug <> ''"). Where("lens_slug <> 'zz' AND lens_slug <> ''").
Select("COUNT(*) AS lenses"). Select("COUNT(*) AS lenses").
Take(&result.Count) Take(&result.Count)
c.Db().Table("photos"). c.Db().
Table("photos").
Select("SUM(photo_type = 'video' AND photo_quality >= 0 AND photo_private = 0) AS videos, SUM(photo_type IN ('image','raw','live') AND photo_quality < 3 AND photo_quality >= 0 AND photo_private = 0) AS review, SUM(photo_quality = -1) AS hidden, SUM(photo_type IN ('image','raw','live') AND photo_private = 0 AND photo_quality >= 0) AS photos, SUM(photo_favorite = 1 AND photo_private = 0 AND photo_quality >= 0) AS favorites, SUM(photo_private = 1 AND photo_quality >= 0) AS private"). Select("SUM(photo_type = 'video' AND photo_quality >= 0 AND photo_private = 0) AS videos, SUM(photo_type IN ('image','raw','live') AND photo_quality < 3 AND photo_quality >= 0 AND photo_private = 0) AS review, SUM(photo_quality = -1) AS hidden, SUM(photo_type IN ('image','raw','live') AND photo_private = 0 AND photo_quality >= 0) AS photos, SUM(photo_favorite = 1 AND photo_private = 0 AND photo_quality >= 0) AS favorites, SUM(photo_private = 1 AND photo_quality >= 0) AS private").
Where("photos.id NOT IN (SELECT photo_id FROM files WHERE file_primary = 1 AND (file_missing = 1 OR file_error <> ''))"). Where("photos.id NOT IN (SELECT photo_id FROM files WHERE file_primary = 1 AND (file_missing = 1 OR file_error <> ''))").
Where("deleted_at IS NULL"). Where("deleted_at IS NULL").
Take(&result.Count) Take(&result.Count)
c.Db().Table("labels"). c.Db().
Table("labels").
Select("MAX(photo_count) as label_max_photos, COUNT(*) AS labels"). Select("MAX(photo_count) as label_max_photos, COUNT(*) AS labels").
Where("photo_count > 0"). Where("photo_count > 0").
Where("deleted_at IS NULL"). Where("deleted_at IS NULL").
Where("(label_priority >= 0 OR label_favorite = 1)"). Where("(label_priority >= 0 OR label_favorite = 1)").
Take(&result.Count) Take(&result.Count)
c.Db().Table("albums"). c.Db().
Table("albums").
Select("SUM(album_type = ?) AS albums, SUM(album_type = ?) AS moments, SUM(album_type = ?) AS months, SUM(album_type = ?) AS states, SUM(album_type = ?) AS folders", entity.AlbumDefault, entity.AlbumMoment, entity.AlbumMonth, entity.AlbumState, entity.AlbumFolder). Select("SUM(album_type = ?) AS albums, SUM(album_type = ?) AS moments, SUM(album_type = ?) AS months, SUM(album_type = ?) AS states, SUM(album_type = ?) AS folders", entity.AlbumDefault, entity.AlbumMoment, entity.AlbumMonth, entity.AlbumState, entity.AlbumFolder).
Where("deleted_at IS NULL AND (albums.album_type <> 'folder' OR albums.album_path IN (SELECT photos.photo_path FROM photos WHERE photos.deleted_at IS NULL))"). Where("deleted_at IS NULL AND (albums.album_type <> 'folder' OR albums.album_path IN (SELECT photos.photo_path FROM photos WHERE photos.deleted_at IS NULL))").
Take(&result.Count) Take(&result.Count)
c.Db().Table("files"). c.Db().
Table("files").
Select("COUNT(*) AS files"). Select("COUNT(*) AS files").
Where("file_missing = 0"). Where("file_missing = 0").
Where("deleted_at IS NULL"). Where("deleted_at IS NULL").
Take(&result.Count) Take(&result.Count)
c.Db().Table("countries"). c.Db().
Table("countries").
Select("(COUNT(*) - 1) AS countries"). Select("(COUNT(*) - 1) AS countries").
Take(&result.Count) Take(&result.Count)
c.Db().Table("places"). c.Db().
Table("places").
Select("SUM(photo_count > 0) AS places"). Select("SUM(photo_count > 0) AS places").
Where("id != 'zz'"). Where("id != 'zz'").
Take(&result.Count) Take(&result.Count)
c.Db().Order("country_slug"). c.Db().
Order("country_slug").
Find(&result.Countries) Find(&result.Countries)
c.Db().Where("deleted_at IS NULL"). c.Db().
Where("id IN (SELECT photos.camera_id FROM photos WHERE photos.photo_quality >= 0 OR photos.deleted_at IS NULL)").
Where("deleted_at IS NULL").
Limit(10000).Order("camera_slug"). Limit(10000).Order("camera_slug").
Find(&result.Cameras) Find(&result.Cameras)
c.Db().Where("deleted_at IS NULL"). c.Db().
Where("deleted_at IS NULL").
Limit(10000).Order("lens_slug"). Limit(10000).Order("lens_slug").
Find(&result.Lenses) Find(&result.Lenses)
c.Db().Where("deleted_at IS NULL AND album_favorite = 1"). c.Db().
Where("deleted_at IS NULL AND album_favorite = 1").
Limit(20).Order("album_title"). Limit(20).Order("album_title").
Find(&result.Albums) Find(&result.Albums)
c.Db().Table("photos"). c.Db().
Where("photo_year > 0"). Table("photos").
Where("photo_year > 0 AND (photos.photo_quality >= 0 OR photos.deleted_at IS NULL)").
Order("photo_year DESC"). Order("photo_year DESC").
Pluck("DISTINCT photo_year", &result.Years) Pluck("DISTINCT photo_year", &result.Years)
c.Db().Table("categories"). c.Db().
Table("categories").
Select("l.label_uid, l.custom_slug, l.label_name"). Select("l.label_uid, l.custom_slug, l.label_name").
Joins("JOIN labels l ON categories.category_id = l.id"). Joins("JOIN labels l ON categories.category_id = l.id").
Where("l.deleted_at IS NULL"). Where("l.deleted_at IS NULL").
@ -311,7 +327,8 @@ func (c *Config) UserConfig() ClientConfig {
Limit(1000).Offset(0). Limit(1000).Offset(0).
Scan(&result.Categories) Scan(&result.Categories)
c.Db().Table("albums"). c.Db().
Table("albums").
Select("album_category"). Select("album_category").
Where("deleted_at IS NULL AND album_category <> ''"). Where("deleted_at IS NULL AND album_category <> ''").
Group("album_category"). Group("album_category").

View file

@ -27,7 +27,7 @@ var dsnPattern = regexp.MustCompile(
// DatabaseDriver returns the database driver name. // DatabaseDriver returns the database driver name.
func (c *Config) DatabaseDriver() string { func (c *Config) DatabaseDriver() string {
switch strings.ToLower(c.params.DatabaseDriver) { switch strings.ToLower(c.params.DatabaseDriver) {
case MySQL, "mariadb": case MySQL, MariaDB:
c.params.DatabaseDriver = MySQL c.params.DatabaseDriver = MySQL
case SQLite, "sqlite", "sqllite", "test", "file", "": case SQLite, "sqlite", "sqllite", "test", "file", "":
c.params.DatabaseDriver = SQLite c.params.DatabaseDriver = SQLite
@ -48,14 +48,23 @@ func (c *Config) DatabaseDriver() string {
func (c *Config) DatabaseDsn() string { func (c *Config) DatabaseDsn() string {
if c.params.DatabaseDsn == "" { if c.params.DatabaseDsn == "" {
switch c.DatabaseDriver() { switch c.DatabaseDriver() {
case MySQL: case MySQL, MariaDB:
return fmt.Sprintf( return fmt.Sprintf(
"%s:%s@tcp(%s)/%s?charset=utf8mb4,utf8&parseTime=true", "%s:%s@tcp(%s)/%s?charset=utf8mb4,utf8&collation=utf8mb4_unicode_ci&parseTime=true",
c.DatabaseUser(), c.DatabaseUser(),
c.DatabasePassword(), c.DatabasePassword(),
c.DatabaseServer(), c.DatabaseServer(),
c.DatabaseName(), c.DatabaseName(),
) )
case Postgres:
return fmt.Sprintf(
"user=%s password=%s dbname=%s host=%s port=%d sslmode=disable TimeZone=UTC",
c.DatabaseUser(),
c.DatabasePassword(),
c.DatabaseName(),
c.DatabaseHost(),
c.DatabasePort(),
)
case SQLite: case SQLite:
return filepath.Join(c.StoragePath(), "index.db") return filepath.Join(c.StoragePath(), "index.db")
default: default:

View file

@ -15,9 +15,10 @@ import (
// Database drivers (sql dialects). // Database drivers (sql dialects).
const ( const (
MySQL = "mysql" MySQL = "mysql"
SQLite = "sqlite3" MariaDB = "mariadb"
// Postgres = "postgres" // TODO: Requires GORM 2.0 for generic column data types SQLite = "sqlite3"
Postgres = "postgres" // TODO: Requires GORM 2.0 for generic column data types
) )
// Params provides a struct in which application configuration is stored. // Params provides a struct in which application configuration is stored.
@ -32,6 +33,7 @@ type Params struct {
Name string Name string
Version string Version string
Copyright string Copyright string
ConfigFile string
SiteUrl string `yaml:"site-url" flag:"site-url"` SiteUrl string `yaml:"site-url" flag:"site-url"`
SitePreview string `yaml:"site-preview" flag:"site-preview"` SitePreview string `yaml:"site-preview" flag:"site-preview"`
SiteTitle string `yaml:"site-title" flag:"site-title"` SiteTitle string `yaml:"site-title" flag:"site-title"`
@ -53,7 +55,6 @@ type Params struct {
ImportPath string `yaml:"import-path" flag:"import-path"` ImportPath string `yaml:"import-path" flag:"import-path"`
OriginalsPath string `yaml:"originals-path" flag:"originals-path"` OriginalsPath string `yaml:"originals-path" flag:"originals-path"`
OriginalsLimit int64 `yaml:"originals-limit" flag:"originals-limit"` OriginalsLimit int64 `yaml:"originals-limit" flag:"originals-limit"`
ConfigFile string
SettingsPath string `yaml:"settings-path" flag:"settings-path"` SettingsPath string `yaml:"settings-path" flag:"settings-path"`
SettingsHidden bool `yaml:"settings-hidden" flag:"settings-hidden"` SettingsHidden bool `yaml:"settings-hidden" flag:"settings-hidden"`
TempPath string `yaml:"temp-path" flag:"temp-path"` TempPath string `yaml:"temp-path" flag:"temp-path"`

View file

@ -36,10 +36,10 @@ type Account struct {
AccShare bool AccShare bool
AccSync bool AccSync bool
RetryLimit int RetryLimit int
SharePath string `gorm:"type:VARBINARY(255);"` SharePath string `gorm:"type:VARBINARY(500);"`
ShareSize string `gorm:"type:VARBINARY(16);"` ShareSize string `gorm:"type:VARBINARY(16);"`
ShareExpires int ShareExpires int
SyncPath string `gorm:"type:VARBINARY(255);"` SyncPath string `gorm:"type:VARBINARY(500);"`
SyncStatus string `gorm:"type:VARBINARY(16);"` SyncStatus string `gorm:"type:VARBINARY(16);"`
SyncInterval int SyncInterval int
SyncDate sql.NullTime `deepcopier:"skip"` SyncDate sql.NullTime `deepcopier:"skip"`

View file

@ -32,7 +32,7 @@ type Album struct {
CoverUID string `gorm:"type:VARBINARY(42);" json:"CoverUID" yaml:"CoverUID,omitempty"` CoverUID string `gorm:"type:VARBINARY(42);" json:"CoverUID" yaml:"CoverUID,omitempty"`
FolderUID string `gorm:"type:VARBINARY(42);index;" json:"FolderUID" yaml:"FolderUID,omitempty"` FolderUID string `gorm:"type:VARBINARY(42);index;" json:"FolderUID" yaml:"FolderUID,omitempty"`
AlbumSlug string `gorm:"type:VARBINARY(255);index;" json:"Slug" yaml:"Slug"` AlbumSlug string `gorm:"type:VARBINARY(255);index;" json:"Slug" yaml:"Slug"`
AlbumPath string `gorm:"type:VARBINARY(768);index;" json:"Path" yaml:"-"` AlbumPath string `gorm:"type:VARBINARY(500);index;" json:"Path" yaml:"-"`
AlbumType string `gorm:"type:VARBINARY(8);default:'album';" json:"Type" yaml:"Type,omitempty"` AlbumType string `gorm:"type:VARBINARY(8);default:'album';" json:"Type" yaml:"Type,omitempty"`
AlbumTitle string `gorm:"type:VARCHAR(255);" json:"Title" yaml:"Title"` AlbumTitle string `gorm:"type:VARCHAR(255);" json:"Title" yaml:"Title"`
AlbumLocation string `gorm:"type:VARCHAR(255);" json:"Location" yaml:"Location,omitempty"` AlbumLocation string `gorm:"type:VARCHAR(255);" json:"Location" yaml:"Location,omitempty"`

View file

@ -11,7 +11,7 @@ type DuplicatesMap map[string]Duplicate
// Duplicate represents an exact file duplicate. // Duplicate represents an exact file duplicate.
type Duplicate struct { type Duplicate struct {
FileName string `gorm:"type:VARBINARY(768);primary_key;" json:"Name" yaml:"Name"` FileName string `gorm:"type:VARBINARY(755);primary_key;" json:"Name" yaml:"Name"`
FileRoot string `gorm:"type:VARBINARY(16);primary_key;default:'/';" json:"Root" yaml:"Root,omitempty"` FileRoot string `gorm:"type:VARBINARY(16);primary_key;default:'/';" json:"Root" yaml:"Root,omitempty"`
FileHash string `gorm:"type:VARBINARY(128);default:'';index" json:"Hash" yaml:"Hash,omitempty"` FileHash string `gorm:"type:VARBINARY(128);default:'';index" json:"Hash" yaml:"Hash,omitempty"`
FileSize int64 `json:"Size" yaml:"Size,omitempty"` FileSize int64 `json:"Size" yaml:"Size,omitempty"`

View file

@ -24,9 +24,9 @@ type File struct {
PhotoUID string `gorm:"type:VARBINARY(42);index;" json:"PhotoUID" yaml:"PhotoUID"` PhotoUID string `gorm:"type:VARBINARY(42);index;" json:"PhotoUID" yaml:"PhotoUID"`
InstanceID string `gorm:"type:VARBINARY(42);index;" json:"InstanceID,omitempty" yaml:"InstanceID,omitempty"` InstanceID string `gorm:"type:VARBINARY(42);index;" json:"InstanceID,omitempty" yaml:"InstanceID,omitempty"`
FileUID string `gorm:"type:VARBINARY(42);unique_index;" json:"UID" yaml:"UID"` FileUID string `gorm:"type:VARBINARY(42);unique_index;" json:"UID" yaml:"UID"`
FileName string `gorm:"type:VARBINARY(768);unique_index:idx_files_name_root;" json:"Name" yaml:"Name"` FileName string `gorm:"type:VARBINARY(755);unique_index:idx_files_name_root;" json:"Name" yaml:"Name"`
FileRoot string `gorm:"type:VARBINARY(16);default:'/';unique_index:idx_files_name_root;" json:"Root" yaml:"Root,omitempty"` FileRoot string `gorm:"type:VARBINARY(16);default:'/';unique_index:idx_files_name_root;" json:"Root" yaml:"Root,omitempty"`
OriginalName string `gorm:"type:VARBINARY(768);" json:"OriginalName" yaml:"OriginalName,omitempty"` OriginalName string `gorm:"type:VARBINARY(755);" json:"OriginalName" yaml:"OriginalName,omitempty"`
FileHash string `gorm:"type:VARBINARY(128);index" json:"Hash" yaml:"Hash,omitempty"` FileHash string `gorm:"type:VARBINARY(128);index" json:"Hash" yaml:"Hash,omitempty"`
FileSize int64 `json:"Size" yaml:"Size,omitempty"` FileSize int64 `json:"Size" yaml:"Size,omitempty"`
FileCodec string `gorm:"type:VARBINARY(32)" json:"Codec" yaml:"Codec,omitempty"` FileCodec string `gorm:"type:VARBINARY(32)" json:"Codec" yaml:"Codec,omitempty"`
@ -48,7 +48,7 @@ type File struct {
FileLuminance string `gorm:"type:VARBINARY(9);" json:"Luminance" yaml:"Luminance,omitempty"` FileLuminance string `gorm:"type:VARBINARY(9);" json:"Luminance" yaml:"Luminance,omitempty"`
FileDiff uint32 `json:"Diff" yaml:"Diff,omitempty"` FileDiff uint32 `json:"Diff" yaml:"Diff,omitempty"`
FileChroma uint8 `json:"Chroma" yaml:"Chroma,omitempty"` FileChroma uint8 `json:"Chroma" yaml:"Chroma,omitempty"`
FileError string `gorm:"type:varbinary(512)" json:"Error" yaml:"Error,omitempty"` FileError string `gorm:"type:VARBINARY(512)" json:"Error" yaml:"Error,omitempty"`
ModTime int64 `json:"ModTime" yaml:"-"` ModTime int64 `json:"ModTime" yaml:"-"`
CreatedAt time.Time `json:"CreatedAt" yaml:"-"` CreatedAt time.Time `json:"CreatedAt" yaml:"-"`
CreatedIn int64 `json:"CreatedIn" yaml:"-"` CreatedIn int64 `json:"CreatedIn" yaml:"-"`

View file

@ -21,7 +21,7 @@ type Folders []Folder
// Folder represents a file system directory. // Folder represents a file system directory.
type Folder struct { type Folder struct {
Path string `gorm:"type:VARBINARY(255);unique_index:idx_folders_path_root;" json:"Path" yaml:"Path"` Path string `gorm:"type:VARBINARY(500);unique_index:idx_folders_path_root;" json:"Path" yaml:"Path"`
Root string `gorm:"type:VARBINARY(16);default:'';unique_index:idx_folders_path_root;" json:"Root" yaml:"Root,omitempty"` Root string `gorm:"type:VARBINARY(16);default:'';unique_index:idx_folders_path_root;" json:"Root" yaml:"Root,omitempty"`
FolderUID string `gorm:"type:VARBINARY(42);primary_key;" json:"UID,omitempty" yaml:"UID,omitempty"` FolderUID string `gorm:"type:VARBINARY(42);primary_key;" json:"UID,omitempty" yaml:"UID,omitempty"`
FolderType string `gorm:"type:VARBINARY(16);" json:"Type" yaml:"Type,omitempty"` FolderType string `gorm:"type:VARBINARY(16);" json:"Type" yaml:"Type,omitempty"`

View file

@ -19,12 +19,12 @@ type Labels []Label
// Label is used for photo, album and location categorization // Label is used for photo, album and location categorization
type Label struct { type Label struct {
ID uint `gorm:"primary_key" json:"ID" yaml:"-"` ID uint `gorm:"primary_key" json:"ID" yaml:"-"`
LabelUID string `gorm:"type:varbinary(42);unique_index;" json:"UID" yaml:"UID"` LabelUID string `gorm:"type:VARBINARY(42);unique_index;" json:"UID" yaml:"UID"`
LabelSlug string `gorm:"type:varbinary(255);unique_index;" json:"Slug" yaml:"-"` LabelSlug string `gorm:"type:VARBINARY(255);unique_index;" json:"Slug" yaml:"-"`
CustomSlug string `gorm:"type:varbinary(255);index;" json:"CustomSlug" yaml:"-"` CustomSlug string `gorm:"type:VARBINARY(255);index;" json:"CustomSlug" yaml:"-"`
LabelName string `gorm:"type:VARCHAR(255);" json:"Name" yaml:"Name"` LabelName string `gorm:"type:VARCHAR(255);" json:"Name" yaml:"Name"`
LabelPriority int `gorm:"type:VARCHAR(255);" json:"Priority" yaml:"Priority,omitempty"` LabelPriority int `json:"Priority" yaml:"Priority,omitempty"`
LabelFavorite bool `gorm:"type:VARCHAR(255);" json:"Favorite" yaml:"Favorite,omitempty"` LabelFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
LabelDescription string `gorm:"type:TEXT;" json:"Description" yaml:"Description,omitempty"` LabelDescription string `gorm:"type:TEXT;" json:"Description" yaml:"Description,omitempty"`
LabelNotes string `gorm:"type:TEXT;" json:"Notes" yaml:"Notes,omitempty"` LabelNotes string `gorm:"type:TEXT;" json:"Notes" yaml:"Notes,omitempty"`
LabelCategories []*Label `gorm:"many2many:categories;association_jointable_foreignkey:category_id" json:"-" yaml:"-"` LabelCategories []*Label `gorm:"many2many:categories;association_jointable_foreignkey:category_id" json:"-" yaml:"-"`

View file

@ -17,9 +17,9 @@ type Lens struct {
ID uint `gorm:"primary_key" json:"ID" yaml:"ID"` ID uint `gorm:"primary_key" json:"ID" yaml:"ID"`
LensSlug string `gorm:"type:VARBINARY(255);unique_index;" json:"Slug" yaml:"Slug,omitempty"` LensSlug string `gorm:"type:VARBINARY(255);unique_index;" json:"Slug" yaml:"Slug,omitempty"`
LensName string `gorm:"type:VARCHAR(255);" json:"Name" yaml:"Name"` LensName string `gorm:"type:VARCHAR(255);" json:"Name" yaml:"Name"`
LensMake string `json:"Make" yaml:"Make,omitempty"` LensMake string `gorm:"type:VARCHAR(255);" json:"Make" yaml:"Make,omitempty"`
LensModel string `json:"Model" yaml:"Model,omitempty"` LensModel string `gorm:"type:VARCHAR(255);" json:"Model" yaml:"Model,omitempty"`
LensType string `json:"Type" yaml:"Type,omitempty"` LensType string `gorm:"type:VARCHAR(255);" json:"Type" yaml:"Type,omitempty"`
LensDescription string `gorm:"type:TEXT;" json:"Description,omitempty" yaml:"Description,omitempty"` LensDescription string `gorm:"type:TEXT;" json:"Description,omitempty" yaml:"Description,omitempty"`
LensNotes string `gorm:"type:TEXT;" json:"Notes,omitempty" yaml:"Notes,omitempty"` LensNotes string `gorm:"type:TEXT;" json:"Notes,omitempty" yaml:"Notes,omitempty"`
CreatedAt time.Time `json:"-" yaml:"-"` CreatedAt time.Time `json:"-" yaml:"-"`

View file

@ -54,9 +54,9 @@ type Photo struct {
TitleSrc string `gorm:"type:VARBINARY(8);" json:"TitleSrc" yaml:"TitleSrc,omitempty"` TitleSrc string `gorm:"type:VARBINARY(8);" json:"TitleSrc" yaml:"TitleSrc,omitempty"`
PhotoDescription string `gorm:"type:TEXT;" json:"Description" yaml:"Description,omitempty"` PhotoDescription string `gorm:"type:TEXT;" json:"Description" yaml:"Description,omitempty"`
DescriptionSrc string `gorm:"type:VARBINARY(8);" json:"DescriptionSrc" yaml:"DescriptionSrc,omitempty"` DescriptionSrc string `gorm:"type:VARBINARY(8);" json:"DescriptionSrc" yaml:"DescriptionSrc,omitempty"`
PhotoPath string `gorm:"type:VARBINARY(768);index:idx_photos_path_name;" json:"Path" yaml:"-"` PhotoPath string `gorm:"type:VARBINARY(500);index:idx_photos_path_name;" json:"Path" yaml:"-"`
PhotoName string `gorm:"type:VARBINARY(255);index:idx_photos_path_name;" json:"Name" yaml:"-"` PhotoName string `gorm:"type:VARBINARY(255);index:idx_photos_path_name;" json:"Name" yaml:"-"`
OriginalName string `gorm:"type:VARBINARY(768);" json:"OriginalName" yaml:"OriginalName,omitempty"` OriginalName string `gorm:"type:VARBINARY(755);" json:"OriginalName" yaml:"OriginalName,omitempty"`
PhotoFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"` PhotoFavorite bool `json:"Favorite" yaml:"Favorite,omitempty"`
PhotoSingle bool `json:"Single" yaml:"Single,omitempty"` PhotoSingle bool `json:"Single" yaml:"Single,omitempty"`
PhotoPrivate bool `json:"Private" yaml:"Private,omitempty"` PhotoPrivate bool `json:"Private" yaml:"Private,omitempty"`

View file

@ -13,7 +13,7 @@ var placeMutex = sync.Mutex{}
// Place used to associate photos to places // Place used to associate photos to places
type Place struct { type Place struct {
ID string `gorm:"type:VARBINARY(42);primary_key;auto_increment:false;" json:"PlaceID" yaml:"PlaceID"` ID string `gorm:"type:VARBINARY(42);primary_key;auto_increment:false;" json:"PlaceID" yaml:"PlaceID"`
PlaceLabel string `gorm:"type:VARBINARY(768);unique_index;" json:"Label" yaml:"Label"` PlaceLabel string `gorm:"type:VARBINARY(755);unique_index;" json:"Label" yaml:"Label"`
PlaceCity string `gorm:"type:VARCHAR(255);" json:"City" yaml:"City,omitempty"` PlaceCity string `gorm:"type:VARCHAR(255);" json:"City" yaml:"City,omitempty"`
PlaceState string `gorm:"type:VARCHAR(255);" json:"State" yaml:"State,omitempty"` PlaceState string `gorm:"type:VARCHAR(255);" json:"State" yaml:"State,omitempty"`
PlaceCountry string `gorm:"type:VARBINARY(2);" json:"Country" yaml:"Country,omitempty"` PlaceCountry string `gorm:"type:VARBINARY(2);" json:"Country" yaml:"Country,omitempty"`

View file

@ -57,7 +57,7 @@ type User struct {
RoleFamily bool `json:"RoleFamily" yaml:"RoleFamily,omitempty"` RoleFamily bool `json:"RoleFamily" yaml:"RoleFamily,omitempty"`
RoleFriend bool `json:"RoleFriend" yaml:"RoleFriend,omitempty"` RoleFriend bool `json:"RoleFriend" yaml:"RoleFriend,omitempty"`
WebDAV bool `gorm:"column:webdav" json:"WebDAV" yaml:"WebDAV,omitempty"` WebDAV bool `gorm:"column:webdav" json:"WebDAV" yaml:"WebDAV,omitempty"`
StoragePath string `gorm:"column:storage_path;type:VARBINARY(255);" json:"StoragePath" yaml:"StoragePath,omitempty"` StoragePath string `gorm:"column:storage_path;type:VARBINARY(500);" json:"StoragePath" yaml:"StoragePath,omitempty"`
CanInvite bool `json:"CanInvite" yaml:"CanInvite,omitempty"` CanInvite bool `json:"CanInvite" yaml:"CanInvite,omitempty"`
InviteToken string `gorm:"type:VARBINARY(32);" json:"-" yaml:"-"` InviteToken string `gorm:"type:VARBINARY(32);" json:"-" yaml:"-"`
InvitedBy string `gorm:"type:VARBINARY(32);" json:"-" yaml:"-"` InvitedBy string `gorm:"type:VARBINARY(32);" json:"-" yaml:"-"`

View file

@ -199,7 +199,7 @@ func (m *Moments) Start() (err error) {
continue continue
} }
if err := form.ParseQueryString(&f); err != nil { if err := form.Unserialize(&f, a.AlbumFilter); err != nil {
log.Errorf("moments: %s", err.Error()) log.Errorf("moments: %s", err.Error())
} else { } else {
w := txt.Words(f.Label) w := txt.Words(f.Label)

View file

@ -127,8 +127,8 @@ func AlbumSearch(f form.AlbumSearch) (results AlbumResults, err error) {
} }
if f.Query != "" { if f.Query != "" {
likeString := "%" + strings.ToLower(f.Query) + "%" likeString := "%" + f.Query + "%"
s = s.Where("LOWER(albums.album_title) LIKE ? OR LOWER(albums.album_location) LIKE ?", likeString, likeString) s = s.Where("albums.album_title LIKE ? OR albums.album_location LIKE ?", likeString, likeString)
} }
if f.Type != "" { if f.Type != "" {

View file

@ -5,6 +5,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/internal/form"
@ -136,12 +138,15 @@ func Geo(f form.GeoSearch) (results GeoResults, err error) {
} }
} }
if f.Name != "" { if strings.Contains(f.Name, OrSep) {
s = s.Where("photos.photo_name LIKE ?", strings.ReplaceAll(f.Name, "*", "%")) s = s.Where("photos.photo_name IN (?)", strings.Split(f.Name, OrSep))
} else if f.Name != "" {
s = s.Where("photos.photo_name LIKE ?", strings.ReplaceAll(fs.StripKnownExt(f.Name), "*", "%"))
} }
// Filter by status. // Filter by status.
if f.Archived { if f.Archived {
s = s.Where("photos.photo_quality > -1")
s = s.Where("photos.deleted_at IS NOT NULL") s = s.Where("photos.deleted_at IS NOT NULL")
} else { } else {
s = s.Where("photos.deleted_at IS NULL") s = s.Where("photos.deleted_at IS NULL")

View file

@ -2,7 +2,6 @@ package query
import ( import (
"fmt" "fmt"
"strings"
"time" "time"
"github.com/gosimple/slug" "github.com/gosimple/slug"
@ -46,12 +45,12 @@ func Labels(f form.LabelSearch) (results []LabelResult, err error) {
var label entity.Label var label entity.Label
slugString := slug.Make(f.Query) slugString := slug.Make(f.Query)
likeString := "%" + strings.ToLower(f.Query) + "%" likeString := "%" + f.Query + "%"
if result := Db().First(&label, "label_slug = ? OR custom_slug = ?", slugString, slugString); result.Error != nil { if result := Db().First(&label, "label_slug = ? OR custom_slug = ?", slugString, slugString); result.Error != nil {
log.Infof("search: label %s not found", txt.Quote(f.Query)) log.Infof("search: label %s not found", txt.Quote(f.Query))
s = s.Where("LOWER(labels.label_name) LIKE ?", likeString) s = s.Where("labels.label_name LIKE ?", likeString)
} else { } else {
labelIds = append(labelIds, label.ID) labelIds = append(labelIds, label.ID)

View file

@ -5,6 +5,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/photoprism/photoprism/pkg/fs"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/internal/form"
@ -144,6 +146,7 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
s = s.Where("photos.photo_quality = -1") s = s.Where("photos.photo_quality = -1")
s = s.Where("photos.deleted_at IS NULL") s = s.Where("photos.deleted_at IS NULL")
} else if f.Archived { } else if f.Archived {
s = s.Where("photos.photo_quality > -1")
s = s.Where("photos.deleted_at IS NOT NULL") s = s.Where("photos.deleted_at IS NOT NULL")
} else { } else {
s = s.Where("photos.deleted_at IS NULL") s = s.Where("photos.deleted_at IS NULL")
@ -235,28 +238,40 @@ func PhotoSearch(f form.PhotoSearch) (results PhotoResults, count int, err error
if strings.HasSuffix(p, "/") { if strings.HasSuffix(p, "/") {
s = s.Where("photos.photo_path = ?", p[:len(p)-1]) s = s.Where("photos.photo_path = ?", p[:len(p)-1])
} else if strings.Contains(p, OrSep) {
s = s.Where("photos.photo_path IN (?)", strings.Split(p, OrSep))
} else { } else {
s = s.Where("photos.photo_path LIKE ?", strings.ReplaceAll(p, "*", "%")) s = s.Where("photos.photo_path LIKE ?", strings.ReplaceAll(p, "*", "%"))
} }
} }
if f.Name != "" { if strings.Contains(f.Name, OrSep) {
s = s.Where("photos.photo_name LIKE ?", strings.ReplaceAll(f.Name, "*", "%")) s = s.Where("photos.photo_name IN (?)", strings.Split(f.Name, OrSep))
} else if f.Name != "" {
s = s.Where("photos.photo_name LIKE ?", strings.ReplaceAll(fs.StripKnownExt(f.Name), "*", "%"))
} }
if f.Filename != "" { if strings.Contains(f.Filename, OrSep) {
s = s.Where("files.file_name IN (?)", strings.Split(f.Filename, OrSep))
} else if f.Filename != "" {
s = s.Where("files.file_name LIKE ?", strings.ReplaceAll(f.Filename, "*", "%")) s = s.Where("files.file_name LIKE ?", strings.ReplaceAll(f.Filename, "*", "%"))
} }
if f.Original != "" { if strings.Contains(f.Original, OrSep) {
s = s.Where("photos.original_name IN (?)", strings.Split(f.Original, OrSep))
} else if f.Original != "" {
s = s.Where("photos.original_name LIKE ?", strings.ReplaceAll(f.Original, "*", "%")) s = s.Where("photos.original_name LIKE ?", strings.ReplaceAll(f.Original, "*", "%"))
} }
if f.Title != "" { if strings.Contains(f.Title, OrSep) {
s = s.Where("LOWER(photos.photo_title) LIKE ?", strings.ReplaceAll(strings.ToLower(f.Title), "*", "%")) s = s.Where("photos.photo_title IN (?)", strings.Split(strings.ToLower(f.Title), OrSep))
} else if f.Title != "" {
s = s.Where("photos.photo_title LIKE ?", strings.ReplaceAll(strings.ToLower(f.Title), "*", "%"))
} }
if f.Hash != "" { if strings.Contains(f.Hash, OrSep) {
s = s.Where("files.file_hash IN (?)", strings.Split(strings.ToLower(f.Hash), OrSep))
} else if f.Hash != "" {
s = s.Where("files.file_hash IN (?)", strings.Split(strings.ToLower(f.Hash), OrSep)) s = s.Where("files.file_hash IN (?)", strings.Split(strings.ToLower(f.Hash), OrSep))
} }