Backend: Code clean-up #225

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-03-28 17:17:41 +01:00
parent a28144f953
commit c98ed8a125
17 changed files with 186 additions and 185 deletions

View file

@ -2,10 +2,10 @@ package form
// AccountSearch represents search form fields for "/api/v1/accounts".
type AccountSearch struct {
Query string `form:"q"`
Count int `form:"count" binding:"required"`
Offset int `form:"offset"`
Order string `form:"order"`
Query string `form:"q"`
Count int `form:"count" binding:"required"`
Offset int `form:"offset"`
Order string `form:"order"`
}
func (f *AccountSearch) GetQuery() string {

View file

@ -5,9 +5,9 @@ import (
"github.com/photoprism/photoprism/internal/form"
)
// FindAccounts returns a list of accounts.
func (s *Query) Accounts(f form.AccountSearch) (result []entity.Account, err error) {
if err := s.db.Where(&entity.Account{}).Limit(f.Count).Offset(f.Offset).Find(&result).Error; err != nil {
// Accounts returns a list of accounts.
func (q *Query) Accounts(f form.AccountSearch) (result []entity.Account, err error) {
if err := q.db.Where(&entity.Account{}).Limit(f.Count).Offset(f.Offset).Find(&result).Error; err != nil {
return result, err
}
@ -15,8 +15,8 @@ func (s *Query) Accounts(f form.AccountSearch) (result []entity.Account, err err
}
// AccountByID finds an account by primary key.
func (s *Query) AccountByID(id uint) (result entity.Account, err error) {
if err := s.db.Where("id = ?", id).First(&result).Error; err != nil {
func (q *Query) AccountByID(id uint) (result entity.Account, err error) {
if err := q.db.Where("id = ?", id).First(&result).Error; err != nil {
return result, err
}

View file

@ -26,8 +26,8 @@ type AlbumResult struct {
}
// AlbumByUUID returns a Album based on the UUID.
func (s *Query) AlbumByUUID(albumUUID string) (album entity.Album, err error) {
if err := s.db.Where("album_uuid = ?", albumUUID).First(&album).Error; err != nil {
func (q *Query) AlbumByUUID(albumUUID string) (album entity.Album, err error) {
if err := q.db.Where("album_uuid = ?", albumUUID).First(&album).Error; err != nil {
return album, err
}
@ -35,10 +35,10 @@ func (s *Query) AlbumByUUID(albumUUID string) (album entity.Album, err error) {
}
// AlbumThumbByUUID returns a album preview file based on the uuid.
func (s *Query) AlbumThumbByUUID(albumUUID string) (file entity.File, err error) {
// s.db.LogMode(true)
func (q *Query) AlbumThumbByUUID(albumUUID string) (file entity.File, err error) {
// q.db.LogMode(true)
if err := s.db.Where("files.file_primary AND files.deleted_at IS NULL").
if err := q.db.Where("files.file_primary AND files.deleted_at IS NULL").
Joins("JOIN albums ON albums.album_uuid = ?", albumUUID).
Joins("JOIN photos_albums pa ON pa.album_uuid = albums.album_uuid AND pa.photo_uuid = files.photo_uuid").
First(&file).Error; err != nil {
@ -49,25 +49,25 @@ func (s *Query) AlbumThumbByUUID(albumUUID string) (file entity.File, err error)
}
// Albums searches albums based on their name.
func (s *Query) Albums(f form.AlbumSearch) (results []AlbumResult, err error) {
func (q *Query) Albums(f form.AlbumSearch) (results []AlbumResult, err error) {
if err := f.ParseQueryString(); err != nil {
return results, err
}
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("albums: %+v", f)))
q := s.db.NewScope(nil).DB()
s := q.db.NewScope(nil).DB()
q = q.Table("albums").
s = s.Table("albums").
Select(`albums.*, COUNT(photos_albums.album_uuid) AS album_count`).
Joins("LEFT JOIN photos_albums ON photos_albums.album_uuid = albums.album_uuid").
Where("albums.deleted_at IS NULL").
Group("albums.id")
if f.ID != "" {
q = q.Where("albums.album_uuid = ?", f.ID)
s = s.Where("albums.album_uuid = ?", f.ID)
if result := q.Scan(&results); result.Error != nil {
if result := s.Scan(&results); result.Error != nil {
return results, result.Error
}
@ -76,27 +76,27 @@ func (s *Query) Albums(f form.AlbumSearch) (results []AlbumResult, err error) {
if f.Query != "" {
likeString := "%" + strings.ToLower(f.Query) + "%"
q = q.Where("LOWER(albums.album_name) LIKE ?", likeString)
s = s.Where("LOWER(albums.album_name) LIKE ?", likeString)
}
if f.Favorites {
q = q.Where("albums.album_favorite = 1")
s = s.Where("albums.album_favorite = 1")
}
switch f.Order {
case "slug":
q = q.Order("albums.album_favorite DESC, album_slug ASC")
s = s.Order("albums.album_favorite DESC, album_slug ASC")
default:
q = q.Order("albums.album_favorite DESC, album_count DESC, albums.created_at DESC")
s = s.Order("albums.album_favorite DESC, album_count DESC, albums.created_at DESC")
}
if f.Count > 0 && f.Count <= 1000 {
q = q.Limit(f.Count).Offset(f.Offset)
s = s.Limit(f.Count).Offset(f.Offset)
} else {
q = q.Limit(100).Offset(0)
s = s.Limit(100).Offset(0)
}
if result := q.Scan(&results); result.Error != nil {
if result := s.Scan(&results); result.Error != nil {
return results, result.Error
}

View file

@ -8,7 +8,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
)
func TestRepo_FindAlbumByUUID(t *testing.T) {
func TestQuery_AlbumByUUID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
@ -26,7 +26,7 @@ func TestRepo_FindAlbumByUUID(t *testing.T) {
})
}
func TestRepo_FindAlbumThumbByUUID(t *testing.T) {
func TestQuery_AlbumThumbByUUID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
@ -44,7 +44,7 @@ func TestRepo_FindAlbumThumbByUUID(t *testing.T) {
})
}
func TestRepo_Albums(t *testing.T) {
func TestQuery_Albums(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())

View file

@ -9,16 +9,16 @@ type CategoryLabel struct {
Title string
}
func (s *Query) CategoryLabels(limit, offset int) (results []CategoryLabel) {
q := s.db.NewScope(nil).DB()
func (q *Query) CategoryLabels(limit, offset int) (results []CategoryLabel) {
s := q.db.NewScope(nil).DB()
q = q.Table("categories").
s = s.Table("categories").
Select("label_name AS name").
Joins("JOIN labels l ON categories.category_id = l.id").
Group("label_name").
Limit(limit).Offset(offset)
if err := q.Scan(&results).Error; err != nil {
if err := s.Scan(&results).Error; err != nil {
log.Errorf("categories: %s", err.Error())
return results
}

View file

@ -7,7 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
)
func TestRepo_CategoryLabels(t *testing.T) {
func TestQuery_CategoryLabels(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())

View file

@ -4,8 +4,8 @@ import "github.com/photoprism/photoprism/internal/entity"
// Files finds files returning maximum results defined by limit
// and finding them from an offest defined by offset.
func (s *Query) Files(limit int, offset int) (files []entity.File, err error) {
if err := s.db.Where(&entity.File{}).Limit(limit).Offset(offset).Find(&files).Error; err != nil {
func (q *Query) Files(limit int, offset int) (files []entity.File, err error) {
if err := q.db.Where(&entity.File{}).Limit(limit).Offset(offset).Find(&files).Error; err != nil {
return files, err
}
@ -13,8 +13,8 @@ func (s *Query) Files(limit int, offset int) (files []entity.File, err error) {
}
// FilesByUUID
func (s *Query) FilesByUUID(u []string, limit int, offset int) (files []entity.File, err error) {
if err := s.db.Where("(photo_uuid IN (?) AND file_primary = 1) OR file_uuid IN (?)", u, u).Preload("Photo").Limit(limit).Offset(offset).Find(&files).Error; err != nil {
func (q *Query) FilesByUUID(u []string, limit int, offset int) (files []entity.File, err error) {
if err := q.db.Where("(photo_uuid IN (?) AND file_primary = 1) OR file_uuid IN (?)", u, u).Preload("Photo").Limit(limit).Offset(offset).Find(&files).Error; err != nil {
return files, err
}
@ -22,8 +22,8 @@ func (s *Query) FilesByUUID(u []string, limit int, offset int) (files []entity.F
}
// FileByPhotoUUID
func (s *Query) FileByPhotoUUID(u string) (file entity.File, err error) {
if err := s.db.Where("photo_uuid = ? AND file_primary = 1", u).Preload("Photo").First(&file).Error; err != nil {
func (q *Query) FileByPhotoUUID(u string) (file entity.File, err error) {
if err := q.db.Where("photo_uuid = ? AND file_primary = 1", u).Preload("Photo").First(&file).Error; err != nil {
return file, err
}
@ -31,8 +31,8 @@ func (s *Query) FileByPhotoUUID(u string) (file entity.File, err error) {
}
// FileByID returns a MediaFile given a certain ID.
func (s *Query) FileByID(id string) (file entity.File, err error) {
if err := s.db.Where("id = ?", id).Preload("Photo").First(&file).Error; err != nil {
func (q *Query) FileByID(id string) (file entity.File, err error) {
if err := q.db.Where("id = ?", id).Preload("Photo").First(&file).Error; err != nil {
return file, err
}
@ -40,8 +40,8 @@ func (s *Query) FileByID(id string) (file entity.File, err error) {
}
// FirstFileByHash finds a file with a given hash string.
func (s *Query) FileByHash(fileHash string) (file entity.File, err error) {
if err := s.db.Where("file_hash = ?", fileHash).Preload("Photo").First(&file).Error; err != nil {
func (q *Query) FileByHash(fileHash string) (file entity.File, err error) {
if err := q.db.Where("file_hash = ?", fileHash).Preload("Photo").First(&file).Error; err != nil {
return file, err
}

View file

@ -7,7 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
)
func TestRepo_FindFiles(t *testing.T) {
func TestQuery_Files(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
@ -20,7 +20,7 @@ func TestRepo_FindFiles(t *testing.T) {
})
}
func TestRepo_FindFilesByUUID(t *testing.T) {
func TestQuery_FilesByUUID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
@ -34,7 +34,7 @@ func TestRepo_FindFilesByUUID(t *testing.T) {
})
}
func TestRepo_FindFileByPhotoUUID(t *testing.T) {
func TestQuery_FileByPhotoUUID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
@ -54,7 +54,7 @@ func TestRepo_FindFileByPhotoUUID(t *testing.T) {
})
}
func TestRepo_FindFileByID(t *testing.T) {
func TestQuery_FileByID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
@ -74,7 +74,7 @@ func TestRepo_FindFileByID(t *testing.T) {
})
}
func TestRepo_FindFileByHash(t *testing.T) {
func TestQuery_FileByHash(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())

View file

@ -25,16 +25,16 @@ type GeoResult struct {
}
// Geo searches for photos based on a Form and returns a PhotoResult slice.
func (s *Query) Geo(f form.GeoSearch) (results []GeoResult, err error) {
func (q *Query) Geo(f form.GeoSearch) (results []GeoResult, err error) {
if err := f.ParseQueryString(); err != nil {
return results, err
}
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("search: %+v", f)))
q := s.db.NewScope(nil).DB()
s := q.db.NewScope(nil).DB()
q = q.Table("photos").
s = s.Table("photos").
Select(`photos.id, photos.photo_uuid, photos.photo_lat, photos.photo_lng, photos.photo_title, photos.taken_at,
files.file_hash, files.file_width, files.file_height`).
Joins(`JOIN files ON files.photo_id = photos.id
@ -44,43 +44,43 @@ func (s *Query) Geo(f form.GeoSearch) (results []GeoResult, err error) {
Group("photos.id, files.id")
if f.Query != "" {
q = q.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
s = s.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
Joins("LEFT JOIN keywords ON photos_keywords.keyword_id = keywords.id").
Where("keywords.keyword LIKE ?", strings.ToLower(f.Query)+"%")
}
if f.S2 != "" {
s2Min, s2Max := s2.Range(f.S2, 7)
q = q.Where("photos.location_id BETWEEN ? AND ?", s2Min, s2Max)
s = s.Where("photos.location_id BETWEEN ? AND ?", s2Min, s2Max)
} else if f.Olc != "" {
s2Min, s2Max := s2.Range(pluscode.S2(f.Olc), 7)
q = q.Where("photos.location_id BETWEEN ? AND ?", s2Min, s2Max)
s = s.Where("photos.location_id BETWEEN ? AND ?", s2Min, s2Max)
} else {
// Inaccurate distance search, but probably 'good enough' for now
if f.Lat > 0 {
latMin := f.Lat - SearchRadius*float64(f.Dist)
latMax := f.Lat + SearchRadius*float64(f.Dist)
q = q.Where("photos.photo_lat BETWEEN ? AND ?", latMin, latMax)
s = s.Where("photos.photo_lat BETWEEN ? AND ?", latMin, latMax)
}
if f.Lng > 0 {
lngMin := f.Lng - SearchRadius*float64(f.Dist)
lngMax := f.Lng + SearchRadius*float64(f.Dist)
q = q.Where("photos.photo_lng BETWEEN ? AND ?", lngMin, lngMax)
s = s.Where("photos.photo_lng BETWEEN ? AND ?", lngMin, lngMax)
}
}
if !f.Before.IsZero() {
q = q.Where("photos.taken_at <= ?", f.Before.Format("2006-01-02"))
s = s.Where("photos.taken_at <= ?", f.Before.Format("2006-01-02"))
}
if !f.After.IsZero() {
q = q.Where("photos.taken_at >= ?", f.After.Format("2006-01-02"))
s = s.Where("photos.taken_at >= ?", f.After.Format("2006-01-02"))
}
q = q.Order("taken_at, photos.photo_uuid")
s = s.Order("taken_at, photos.photo_uuid")
if result := q.Scan(&results); result.Error != nil {
if result := s.Scan(&results); result.Error != nil {
return results, result.Error
}

View file

@ -8,7 +8,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
)
func TestRepo_Geo(t *testing.T) {
func TestQuery_Geo(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())

View file

@ -29,8 +29,8 @@ type LabelResult struct {
}
// LabelBySlug returns a Label based on the slug name.
func (s *Query) LabelBySlug(labelSlug string) (label entity.Label, err error) {
if err := s.db.Where("label_slug = ?", labelSlug).First(&label).Error; err != nil {
func (q *Query) LabelBySlug(labelSlug string) (label entity.Label, err error) {
if err := q.db.Where("label_slug = ?", labelSlug).First(&label).Error; err != nil {
return label, err
}
@ -38,8 +38,8 @@ func (s *Query) LabelBySlug(labelSlug string) (label entity.Label, err error) {
}
// LabelByUUID returns a Label based on the label UUID.
func (s *Query) LabelByUUID(labelUUID string) (label entity.Label, err error) {
if err := s.db.Where("label_uuid = ?", labelUUID).First(&label).Error; err != nil {
func (q *Query) LabelByUUID(labelUUID string) (label entity.Label, err error) {
if err := q.db.Where("label_uuid = ?", labelUUID).First(&label).Error; err != nil {
return label, err
}
@ -47,10 +47,10 @@ func (s *Query) LabelByUUID(labelUUID string) (label entity.Label, err error) {
}
// LabelThumbBySlug returns a label preview file based on the slug name.
func (s *Query) LabelThumbBySlug(labelSlug string) (file entity.File, err error) {
// s.db.LogMode(true)
func (q *Query) LabelThumbBySlug(labelSlug string) (file entity.File, err error) {
// q.db.LogMode(true)
if err := s.db.Where("files.file_primary AND files.deleted_at IS NULL").
if err := q.db.Where("files.file_primary AND files.deleted_at IS NULL").
Joins("JOIN labels ON labels.label_slug = ?", labelSlug).
Joins("JOIN photos_labels ON photos_labels.label_id = labels.id AND photos_labels.photo_id = files.photo_id").
Order("photos_labels.label_uncertainty ASC").
@ -62,9 +62,9 @@ func (s *Query) LabelThumbBySlug(labelSlug string) (file entity.File, err error)
}
// LabelThumbByUUID returns a label preview file based on the label UUID.
func (s *Query) LabelThumbByUUID(labelUUID string) (file entity.File, err error) {
func (q *Query) LabelThumbByUUID(labelUUID string) (file entity.File, err error) {
// Search matching label
err = s.db.Where("files.file_primary AND files.deleted_at IS NULL").
err = q.db.Where("files.file_primary AND files.deleted_at IS NULL").
Joins("JOIN labels ON labels.label_uuid = ?", labelUUID).
Joins("JOIN photos_labels ON photos_labels.label_id = labels.id AND photos_labels.photo_id = files.photo_id").
Order("photos_labels.label_uncertainty ASC").
@ -75,7 +75,7 @@ func (s *Query) LabelThumbByUUID(labelUUID string) (file entity.File, err error)
}
// If failed, search for category instead
err = s.db.Where("files.file_primary AND files.deleted_at IS NULL").
err = q.db.Where("files.file_primary AND files.deleted_at IS NULL").
Joins("JOIN photos_labels ON photos_labels.photo_id = files.photo_id").
Joins("JOIN categories c ON photos_labels.label_id = c.label_id").
Joins("JOIN labels ON c.category_id = labels.id AND labels.label_uuid= ?", labelUUID).
@ -86,26 +86,26 @@ func (s *Query) LabelThumbByUUID(labelUUID string) (file entity.File, err error)
}
// Labels searches labels based on their name.
func (s *Query) Labels(f form.LabelSearch) (results []LabelResult, err error) {
func (q *Query) Labels(f form.LabelSearch) (results []LabelResult, err error) {
if err := f.ParseQueryString(); err != nil {
return results, err
}
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("labels: %+v", f)))
q := s.db.NewScope(nil).DB()
s := q.db.NewScope(nil).DB()
// q.LogMode(true)
// s.LogMode(true)
q = q.Table("labels").
s = s.Table("labels").
Select(`labels.*`).
Where("labels.deleted_at IS NULL").
Group("labels.id")
if f.ID != "" {
q = q.Where("labels.label_uuid = ?", f.ID)
s = s.Where("labels.label_uuid = ?", f.ID)
if result := q.Scan(&results); result.Error != nil {
if result := s.Scan(&results); result.Error != nil {
return results, result.Error
}
@ -120,14 +120,14 @@ func (s *Query) Labels(f form.LabelSearch) (results []LabelResult, err error) {
slugString := slug.Make(f.Query)
likeString := "%" + strings.ToLower(f.Query) + "%"
if result := s.db.First(&label, "label_slug = ?", slugString); result.Error != nil {
if result := q.db.First(&label, "label_slug = ?", slugString); result.Error != nil {
log.Infof("search: label \"%s\" not found", f.Query)
q = q.Where("LOWER(labels.label_name) LIKE ?", likeString)
s = s.Where("LOWER(labels.label_name) LIKE ?", likeString)
} else {
labelIds = append(labelIds, label.ID)
s.db.Where("category_id = ?", label.ID).Find(&categories)
q.db.Where("category_id = ?", label.ID).Find(&categories)
for _, category := range categories {
labelIds = append(labelIds, category.LabelID)
@ -135,32 +135,32 @@ func (s *Query) Labels(f form.LabelSearch) (results []LabelResult, err error) {
log.Infof("search: label \"%s\" includes %d categories", label.LabelName, len(labelIds))
q = q.Where("labels.id IN (?)", labelIds)
s = s.Where("labels.id IN (?)", labelIds)
}
}
if f.Favorites {
q = q.Where("labels.label_favorite = 1")
s = s.Where("labels.label_favorite = 1")
}
if !f.All {
q = q.Where("labels.label_priority >= 0 OR labels.label_favorite = 1")
s = s.Where("labels.label_priority >= 0 OR labels.label_favorite = 1")
}
switch f.Order {
case "slug":
q = q.Order("labels.label_favorite DESC, label_slug ASC")
s = s.Order("labels.label_favorite DESC, label_slug ASC")
default:
q = q.Order("labels.label_favorite DESC, label_slug ASC")
s = s.Order("labels.label_favorite DESC, label_slug ASC")
}
if f.Count > 0 && f.Count <= 1000 {
q = q.Limit(f.Count).Offset(f.Offset)
s = s.Limit(f.Count).Offset(f.Offset)
} else {
q = q.Limit(100).Offset(0)
s = s.Limit(100).Offset(0)
}
if result := q.Scan(&results); result.Error != nil {
if result := s.Scan(&results); result.Error != nil {
return results, result.Error
}

View file

@ -8,94 +8,94 @@ import (
"github.com/photoprism/photoprism/internal/config"
)
func TestRepo_FindLabelBySlug(t *testing.T) {
func TestQuery_LabelBySlug(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
q := New(conf.Db())
t.Run("files found", func(t *testing.T) {
label, err := search.LabelBySlug("flower")
label, err := q.LabelBySlug("flower")
assert.Nil(t, err)
assert.Equal(t, "Flower", label.LabelName)
})
t.Run("no files found", func(t *testing.T) {
label, err := search.LabelBySlug("111")
label, err := q.LabelBySlug("111")
assert.Error(t, err, "record not found")
t.Log(label)
})
}
func TestRepo_FindLabelByUUID(t *testing.T) {
func TestQuery_LabelByUUID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
q := New(conf.Db())
t.Run("files found", func(t *testing.T) {
label, err := search.LabelByUUID("14")
label, err := q.LabelByUUID("14")
assert.Nil(t, err)
assert.Equal(t, "COW", label.LabelName)
})
t.Run("no files found", func(t *testing.T) {
label, err := search.LabelByUUID("111")
label, err := q.LabelByUUID("111")
assert.Error(t, err, "record not found")
t.Log(label)
})
}
func TestRepo_FindLabelThumbBySlug(t *testing.T) {
func TestQuery_LabelThumbBySlug(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
q := New(conf.Db())
t.Run("files found", func(t *testing.T) {
file, err := search.LabelThumbBySlug("flower")
file, err := q.LabelThumbBySlug("flower")
assert.Nil(t, err)
assert.Equal(t, "exampleFileName.jpg", file.FileName)
})
t.Run("no files found", func(t *testing.T) {
file, err := search.LabelThumbBySlug("cow")
file, err := q.LabelThumbBySlug("cow")
assert.Error(t, err, "record not found")
t.Log(file)
})
}
func TestRepo_FindLabelThumbByUUID(t *testing.T) {
func TestQuery_LabelThumbByUUID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
q := New(conf.Db())
t.Run("files found", func(t *testing.T) {
file, err := search.LabelThumbByUUID("13")
file, err := q.LabelThumbByUUID("13")
assert.Nil(t, err)
assert.Equal(t, "exampleFileName.jpg", file.FileName)
})
t.Run("no files found", func(t *testing.T) {
file, err := search.LabelThumbByUUID("14")
file, err := q.LabelThumbByUUID("14")
assert.Error(t, err, "record not found")
t.Log(file)
})
}
func TestRepo_Labels(t *testing.T) {
func TestQuery_Labels(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
q := New(conf.Db())
t.Run("search with query", func(t *testing.T) {
query := form.NewLabelSearch("Query:C Count:1005 Order:slug")
result, err := search.Labels(query)
result, err := q.Labels(query)
assert.Nil(t, err)
assert.Equal(t, 2, len(result))
@ -105,7 +105,7 @@ func TestRepo_Labels(t *testing.T) {
t.Run("search for favorites", func(t *testing.T) {
query := form.NewLabelSearch("Favorites:true")
result, err := search.Labels(query)
result, err := q.Labels(query)
assert.Nil(t, err)
assert.Equal(t, 2, len(result))
@ -115,14 +115,14 @@ func TestRepo_Labels(t *testing.T) {
t.Run("search with empty query", func(t *testing.T) {
query := form.NewLabelSearch("")
result, err := search.Labels(query)
result, err := q.Labels(query)
assert.Nil(t, err)
assert.Equal(t, 3, len(result))
})
t.Run("search with invalid query string", func(t *testing.T) {
query := form.NewLabelSearch("xxx:bla")
result, err := search.Labels(query)
result, err := q.Labels(query)
assert.Error(t, err, "unknown filter")
t.Log(result)
})

View file

@ -8,16 +8,16 @@ type MomentsTimeResult struct {
}
// GetMomentsTime counts photos per month and year
func (s *Query) GetMomentsTime() (results []MomentsTimeResult, err error) {
q := s.db.NewScope(nil).DB()
func (q *Query) GetMomentsTime() (results []MomentsTimeResult, err error) {
s := q.db.NewScope(nil).DB()
q = q.Table("photos").
s = s.Table("photos").
Where("deleted_at IS NULL").
Select("photos.photo_year, photos.photo_month, COUNT(*) AS count").
Group("photos.photo_year, photos.photo_month").
Order("photos.photo_year DESC, photos.photo_month DESC")
if result := q.Scan(&results); result.Error != nil {
if result := s.Scan(&results); result.Error != nil {
return results, result.Error
}

View file

@ -7,7 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
)
func TestRepo_GetMomentsTime(t *testing.T) {
func TestQuery_GetMomentsTime(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())

View file

@ -94,18 +94,18 @@ func (m *PhotoResult) DownloadFileName() string {
}
// Photos searches for photos based on a Form and returns a PhotoResult slice.
func (s *Query) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
func (q *Query) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
if err := f.ParseQueryString(); err != nil {
return results, err
}
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("photos: %+v", f)))
q := s.db.NewScope(nil).DB()
s := q.db.NewScope(nil).DB()
// q.LogMode(true)
// s.LogMode(true)
q = q.Table("photos").
s = s.Table("photos").
Select(`photos.*,
files.id AS file_id, files.file_uuid, files.file_primary, files.file_missing, files.file_name, files.file_hash,
files.file_type, files.file_mime, files.file_width, files.file_height, files.file_aspect_ratio,
@ -123,9 +123,9 @@ func (s *Query) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
Group("photos.id, files.id")
if f.ID != "" {
q = q.Where("photos.photo_uuid = ?", f.ID)
s = s.Where("photos.photo_uuid = ?", f.ID)
if result := q.Scan(&results); result.Error != nil {
if result := s.Scan(&results); result.Error != nil {
return results, result.Error
}
@ -137,27 +137,27 @@ func (s *Query) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
var labelIds []uint
if f.Label != "" {
if result := s.db.First(&label, "label_slug = ?", strings.ToLower(f.Label)); result.Error != nil {
if result := q.db.First(&label, "label_slug = ?", strings.ToLower(f.Label)); result.Error != nil {
log.Errorf("search: label \"%s\" not found", f.Label)
return results, fmt.Errorf("label \"%s\" not found", f.Label)
} else {
labelIds = append(labelIds, label.ID)
s.db.Where("category_id = ?", label.ID).Find(&categories)
q.db.Where("category_id = ?", label.ID).Find(&categories)
for _, category := range categories {
labelIds = append(labelIds, category.LabelID)
}
q = q.Where("photos_labels.label_id IN (?)", labelIds)
s = s.Where("photos_labels.label_id IN (?)", labelIds)
}
}
if f.Location == true {
q = q.Where("location_id > 0")
s = s.Where("location_id > 0")
if f.Query != "" {
q = q.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
s = s.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
Joins("LEFT JOIN keywords ON photos_keywords.keyword_id = keywords.id").
Where("keywords.keyword LIKE ?", strings.ToLower(f.Query)+"%")
}
@ -170,17 +170,17 @@ func (s *Query) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
lowerString := strings.ToLower(f.Query)
likeString := lowerString + "%"
q = q.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
s = s.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
Joins("LEFT JOIN keywords ON photos_keywords.keyword_id = keywords.id")
if result := s.db.First(&label, "label_slug = ?", slugString); result.Error != nil {
if result := q.db.First(&label, "label_slug = ?", slugString); result.Error != nil {
log.Infof("search: label \"%s\" not found, using fuzzy search", f.Query)
q = q.Where("keywords.keyword LIKE ?", likeString)
s = s.Where("keywords.keyword LIKE ?", likeString)
} else {
labelIds = append(labelIds, label.ID)
s.db.Where("category_id = ?", label.ID).Find(&categories)
q.db.Where("category_id = ?", label.ID).Find(&categories)
for _, category := range categories {
labelIds = append(labelIds, category.LabelID)
@ -188,98 +188,98 @@ func (s *Query) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
log.Infof("search: label \"%s\" includes %d categories", label.LabelName, len(labelIds))
q = q.Where("photos_labels.label_id IN (?) OR keywords.keyword LIKE ?", labelIds, likeString)
s = s.Where("photos_labels.label_id IN (?) OR keywords.keyword LIKE ?", labelIds, likeString)
}
}
if f.Archived {
q = q.Where("photos.deleted_at IS NOT NULL")
s = s.Where("photos.deleted_at IS NOT NULL")
} else {
q = q.Where("photos.deleted_at IS NULL")
s = s.Where("photos.deleted_at IS NULL")
}
if f.Error {
q = q.Where("files.file_error <> ''")
s = s.Where("files.file_error <> ''")
}
if f.Album != "" {
q = q.Joins("JOIN photos_albums ON photos_albums.photo_uuid = photos.photo_uuid").Where("photos_albums.album_uuid = ?", f.Album)
s = s.Joins("JOIN photos_albums ON photos_albums.photo_uuid = photos.photo_uuid").Where("photos_albums.album_uuid = ?", f.Album)
}
if f.Camera > 0 {
q = q.Where("photos.camera_id = ?", f.Camera)
s = s.Where("photos.camera_id = ?", f.Camera)
}
if f.Lens > 0 {
q = q.Where("photos.lens_id = ?", f.Lens)
s = s.Where("photos.lens_id = ?", f.Lens)
}
if f.Year > 0 {
q = q.Where("photos.photo_year = ?", f.Year)
s = s.Where("photos.photo_year = ?", f.Year)
}
if f.Month > 0 {
q = q.Where("photos.photo_month = ?", f.Month)
s = s.Where("photos.photo_month = ?", f.Month)
}
if f.Color != "" {
q = q.Where("files.file_main_color = ?", strings.ToLower(f.Color))
s = s.Where("files.file_main_color = ?", strings.ToLower(f.Color))
}
if f.Favorites {
q = q.Where("photos.photo_favorite = 1")
s = s.Where("photos.photo_favorite = 1")
}
if f.Public {
q = q.Where("photos.photo_private = 0")
s = s.Where("photos.photo_private = 0")
}
if f.Safe {
q = q.Where("photos.photo_nsfw = 0")
s = s.Where("photos.photo_nsfw = 0")
}
if f.Nsfw {
q = q.Where("photos.photo_nsfw = 1")
s = s.Where("photos.photo_nsfw = 1")
}
if f.Story {
q = q.Where("photos.photo_story = 1")
s = s.Where("photos.photo_story = 1")
}
if f.Country != "" {
q = q.Where("photos.photo_country = ?", f.Country)
s = s.Where("photos.photo_country = ?", f.Country)
}
if f.Title != "" {
q = q.Where("LOWER(photos.photo_title) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(f.Title)))
s = s.Where("LOWER(photos.photo_title) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(f.Title)))
}
if f.Hash != "" {
q = q.Where("files.file_hash = ?", f.Hash)
s = s.Where("files.file_hash = ?", f.Hash)
}
if f.Duplicate {
q = q.Where("files.file_duplicate = 1")
s = s.Where("files.file_duplicate = 1")
}
if f.Portrait {
q = q.Where("files.file_portrait = 1")
s = s.Where("files.file_portrait = 1")
}
if f.Mono {
q = q.Where("files.file_chroma = 0")
s = s.Where("files.file_chroma = 0")
} else if f.Chroma > 9 {
q = q.Where("files.file_chroma > ?", f.Chroma)
s = s.Where("files.file_chroma > ?", f.Chroma)
} else if f.Chroma > 0 {
q = q.Where("files.file_chroma > 0 AND files.file_chroma <= ?", f.Chroma)
s = s.Where("files.file_chroma > 0 AND files.file_chroma <= ?", f.Chroma)
}
if f.Fmin > 0 {
q = q.Where("photos.photo_f_number >= ?", f.Fmin)
s = s.Where("photos.photo_f_number >= ?", f.Fmin)
}
if f.Fmax > 0 {
q = q.Where("photos.photo_f_number <= ?", f.Fmax)
s = s.Where("photos.photo_f_number <= ?", f.Fmax)
}
if f.Dist == 0 {
@ -292,43 +292,43 @@ func (s *Query) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
if f.Lat > 0 {
latMin := f.Lat - SearchRadius*float64(f.Dist)
latMax := f.Lat + SearchRadius*float64(f.Dist)
q = q.Where("photos.photo_lat BETWEEN ? AND ?", latMin, latMax)
s = s.Where("photos.photo_lat BETWEEN ? AND ?", latMin, latMax)
}
if f.Lng > 0 {
lngMin := f.Lng - SearchRadius*float64(f.Dist)
lngMax := f.Lng + SearchRadius*float64(f.Dist)
q = q.Where("photos.photo_lng BETWEEN ? AND ?", lngMin, lngMax)
s = s.Where("photos.photo_lng BETWEEN ? AND ?", lngMin, lngMax)
}
if !f.Before.IsZero() {
q = q.Where("photos.taken_at <= ?", f.Before.Format("2006-01-02"))
s = s.Where("photos.taken_at <= ?", f.Before.Format("2006-01-02"))
}
if !f.After.IsZero() {
q = q.Where("photos.taken_at >= ?", f.After.Format("2006-01-02"))
s = s.Where("photos.taken_at >= ?", f.After.Format("2006-01-02"))
}
switch f.Order {
case "relevance":
q = q.Order("photo_story DESC, photo_favorite DESC, taken_at DESC")
s = s.Order("photo_story DESC, photo_favorite DESC, taken_at DESC")
case "newest":
q = q.Order("taken_at DESC, photos.photo_uuid")
s = s.Order("taken_at DESC, photos.photo_uuid")
case "oldest":
q = q.Order("taken_at, photos.photo_uuid")
s = s.Order("taken_at, photos.photo_uuid")
case "imported":
q = q.Order("photos.id DESC")
s = s.Order("photos.id DESC")
default:
q = q.Order("taken_at DESC, photos.photo_uuid")
s = s.Order("taken_at DESC, photos.photo_uuid")
}
if f.Count > 0 && f.Count <= 1000 {
q = q.Limit(f.Count).Offset(f.Offset)
s = s.Limit(f.Count).Offset(f.Offset)
} else {
q = q.Limit(100).Offset(0)
s = s.Limit(100).Offset(0)
}
if result := q.Scan(&results); result.Error != nil {
if result := s.Scan(&results); result.Error != nil {
return results, result.Error
}
@ -336,8 +336,8 @@ func (s *Query) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
}
// PhotoByID returns a Photo based on the ID.
func (s *Query) PhotoByID(photoID uint64) (photo entity.Photo, err error) {
if err := s.db.Where("id = ?", photoID).Preload("Description").First(&photo).Error; err != nil {
func (q *Query) PhotoByID(photoID uint64) (photo entity.Photo, err error) {
if err := q.db.Where("id = ?", photoID).Preload("Description").First(&photo).Error; err != nil {
return photo, err
}
@ -345,8 +345,8 @@ func (s *Query) PhotoByID(photoID uint64) (photo entity.Photo, err error) {
}
// PhotoByUUID returns a Photo based on the UUID.
func (s *Query) PhotoByUUID(photoUUID string) (photo entity.Photo, err error) {
if err := s.db.Where("photo_uuid = ?", photoUUID).Preload("Description").First(&photo).Error; err != nil {
func (q *Query) PhotoByUUID(photoUUID string) (photo entity.Photo, err error) {
if err := q.db.Where("photo_uuid = ?", photoUUID).Preload("Description").First(&photo).Error; err != nil {
return photo, err
}
@ -354,8 +354,8 @@ func (s *Query) PhotoByUUID(photoUUID string) (photo entity.Photo, err error) {
}
// PreloadPhotoByUUID returns a Photo based on the UUID with all dependencies preloaded.
func (s *Query) PreloadPhotoByUUID(photoUUID string) (photo entity.Photo, err error) {
if err := s.db.Where("photo_uuid = ?", photoUUID).
func (q *Query) PreloadPhotoByUUID(photoUUID string) (photo entity.Photo, err error) {
if err := q.db.Where("photo_uuid = ?", photoUUID).
Preload("Labels", func(db *gorm.DB) *gorm.DB {
return db.Order("photos_labels.label_uncertainty ASC, photos_labels.label_id DESC")
}).
@ -367,7 +367,7 @@ func (s *Query) PreloadPhotoByUUID(photoUUID string) (photo entity.Photo, err er
return photo, err
}
photo.PreloadMany(s.db)
photo.PreloadMany(q.db)
return photo, nil
}

View file

@ -9,7 +9,7 @@ import (
"github.com/photoprism/photoprism/internal/form"
)
/*func TestRepo_Photos(t *testing.T) {
/*func TestQuery_Photos(t *testing.T) {
conf := config.TestConfig()
search := New(conf.OriginalsPath(), conf.Db())
@ -28,7 +28,7 @@ import (
})
}*/
func TestRepo_FindPhotoByID(t *testing.T) {
func TestQuery_PhotoByID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
@ -46,7 +46,7 @@ func TestRepo_FindPhotoByID(t *testing.T) {
})
}
func TestRepo_FindPhotoByUUID(t *testing.T) {
func TestQuery_PhotoByUUID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
@ -64,7 +64,7 @@ func TestRepo_FindPhotoByUUID(t *testing.T) {
})
}
func TestRepo_PreloadPhotoByUUID(t *testing.T) {
func TestQuery_PreloadPhotoByUUID(t *testing.T) {
conf := config.TestConfig()
search := New(conf.Db())
@ -81,7 +81,8 @@ func TestRepo_PreloadPhotoByUUID(t *testing.T) {
t.Log(result)
})
}
func TestSearch_Photos_Query(t *testing.T) {
func TestSearch_Photos(t *testing.T) {
conf := config.TestConfig()
conf.CreateDirectories()

View file

@ -8,23 +8,23 @@ import (
)
// PhotoSelection returns all selected photos.
func (s *Query) PhotoSelection(f form.Selection) (results []entity.Photo, err error) {
func (q *Query) PhotoSelection(f form.Selection) (results []entity.Photo, err error) {
if f.Empty() {
return results, errors.New("no photos selected")
}
q := s.db.NewScope(nil).DB()
s := q.db.NewScope(nil).DB()
q = q.Table("photos").
s = s.Table("photos").
Select("photos.*").
Joins("LEFT JOIN photos_labels ON photos_labels.photo_id = photos.id").
Joins("LEFT JOIN labels ON photos_labels.label_id = labels.id AND labels.deleted_at IS NULL").
Where("photos.deleted_at IS NULL").
Group("photos.id")
q = q.Where("photos.photo_uuid IN (?) OR labels.label_uuid IN (?)", f.Photos, f.Labels)
s = s.Where("photos.photo_uuid IN (?) OR labels.label_uuid IN (?)", f.Photos, f.Labels)
if result := q.Scan(&results); result.Error != nil {
if result := s.Scan(&results); result.Error != nil {
return results, result.Error
}