package query import ( "time" "github.com/jinzhu/gorm" "github.com/photoprism/photoprism/internal/entity" ) // PhotoByID returns a Photo based on the ID. func PhotoByID(photoID uint64) (photo entity.Photo, err error) { if err := UnscopedDb().Where("id = ?", photoID). Preload("Labels", func(db *gorm.DB) *gorm.DB { return db.Order("photos_labels.uncertainty ASC, photos_labels.label_id DESC") }). Preload("Labels.Label"). Preload("Camera"). Preload("Lens"). Preload("Details"). Preload("Place"). Preload("Cell"). Preload("Cell.Place"). First(&photo).Error; err != nil { return photo, err } return photo, nil } // PhotoByUID returns a Photo based on the UID. func PhotoByUID(photoUID string) (photo entity.Photo, err error) { if err := UnscopedDb().Where("photo_uid = ?", photoUID). Preload("Labels", func(db *gorm.DB) *gorm.DB { return db.Order("photos_labels.uncertainty ASC, photos_labels.label_id DESC") }). Preload("Labels.Label"). Preload("Camera"). Preload("Lens"). Preload("Details"). Preload("Place"). Preload("Cell"). Preload("Cell.Place"). First(&photo).Error; err != nil { return photo, err } return photo, nil } // PhotoPreloadByUID returns a Photo based on the UID with all dependencies preloaded. func PhotoPreloadByUID(photoUID string) (photo entity.Photo, err error) { if err := UnscopedDb().Where("photo_uid = ?", photoUID). Preload("Labels", func(db *gorm.DB) *gorm.DB { return db.Order("photos_labels.uncertainty ASC, photos_labels.label_id DESC") }). Preload("Labels.Label"). Preload("Camera"). Preload("Lens"). Preload("Details"). Preload("Place"). Preload("Cell"). Preload("Cell.Place"). First(&photo).Error; err != nil { return photo, err } photo.PreloadMany() return photo, nil } // PhotosMissing returns photo entities without existing files. func PhotosMissing(limit int, offset int) (entities entity.Photos, err error) { err = Db(). Select("photos.*"). Joins("JOIN files a ON photos.id = a.photo_id "). Joins("LEFT JOIN files b ON a.photo_id = b.photo_id AND a.id != b.id AND b.file_missing = 0 AND b.file_root = '/'"). Where("a.file_missing = 1 AND b.id IS NULL"). Where("photos.photo_type <> ?", entity.TypeText). Group("photos.id"). Limit(limit).Offset(offset).Find(&entities).Error return entities, err } // ResetPhotoQuality resets the quality of photos without primary file to -1. func ResetPhotoQuality() error { return Db().Table("photos"). Where("id IN (SELECT id FROM (SELECT photos.id FROM photos LEFT JOIN files ON photos.id = files.photo_id AND files.file_primary = 1 WHERE files.id IS NULL GROUP BY photos.id) AS tmp)"). Update("photo_quality", -1).Error } // PhotosCheck returns photos selected for maintenance. func PhotosCheck(limit int, offset int) (entities entity.Photos, err error) { err = Db(). Preload("Labels", func(db *gorm.DB) *gorm.DB { return db.Order("photos_labels.uncertainty ASC, photos_labels.label_id DESC") }). Preload("Labels.Label"). Preload("Camera"). Preload("Lens"). Preload("Details"). Preload("Place"). Preload("Cell"). Preload("Cell.Place"). Where("checked_at IS NULL OR checked_at < ?", time.Now().Add(-1*time.Hour*24*3)). Where("updated_at < ? OR (cell_id = 'zz' AND photo_lat <> 0)", time.Now().Add(-1*time.Minute*10)). Limit(limit).Offset(offset).Find(&entities).Error return entities, err } // MatchingPhotos returns photos sharing the same exact time and location, or unique image id. func MatchingPhotos(meta, uuid bool) (entities entity.Photos, err error) { if !meta && !uuid { return entities, nil } stmt := UnscopedDb().Table("photos"). Select("photos.*") switch { case meta && uuid: stmt = stmt.Joins(`JOIN photos dup ON photos.id < dup.id AND photos.photo_single = 0 AND dup.photo_single = 0 AND photos.deleted_at IS NULL AND dup.deleted_at IS NULL AND ((photos.taken_src = 'meta' AND dup.taken_src = 'meta' AND photos.photo_lat = dup.photo_lat AND photos.photo_lng = dup.photo_lng AND photos.taken_at = dup.taken_at AND photos.camera_id = dup.camera_id AND photos.camera_serial = dup.camera_serial) OR (photos.uuid <> '' AND photos.uuid = dup.uuid))`) case meta: stmt = stmt.Joins(`JOIN photos dup ON photos.id < dup.id AND photos.photo_single = 0 AND dup.photo_single = 0 AND photos.deleted_at IS NULL AND dup.deleted_at IS NULL AND ((photos.taken_src = 'meta' AND dup.taken_src = 'meta' AND photos.photo_lat = dup.photo_lat AND photos.photo_lng = dup.photo_lng AND photos.taken_at = dup.taken_at AND photos.camera_id = dup.camera_id AND photos.camera_serial = dup.camera_serial))`) case uuid: stmt = stmt.Joins(`JOIN photos dup ON photos.id < dup.id AND photos.photo_single = 0 AND dup.photo_single = 0 AND photos.deleted_at IS NULL AND dup.deleted_at IS NULL AND (photos.uuid <> '' AND photos.uuid = dup.uuid)`) } err = stmt.Group("photos.id").Find(&entities).Error return entities, err }