photoprism/internal/entity/photo_counts.go

198 lines
6.2 KiB
Go

package entity
import (
"fmt"
"strings"
"time"
"github.com/jinzhu/gorm"
)
type LabelPhotoCount struct {
LabelID int
PhotoCount int
}
type LabelPhotoCounts []LabelPhotoCount
// LabelCounts returns the number of photos for each label ID.
func LabelCounts() LabelPhotoCounts {
result := LabelPhotoCounts{}
if err := UnscopedDb().Raw(`
SELECT label_id, SUM(photo_count) AS photo_count FROM (
SELECT l.id AS label_id, COUNT(*) AS photo_count FROM labels l
JOIN photos_labels pl ON pl.label_id = l.id
JOIN photos ph ON pl.photo_id = ph.id
WHERE pl.uncertainty < 100
AND ph.photo_quality >= 0
AND ph.photo_private = 0
AND ph.deleted_at IS NULL GROUP BY l.id
UNION ALL
SELECT l.id AS label_id, COUNT(*) AS photo_count FROM labels l
JOIN categories c ON c.category_id = l.id
JOIN photos_labels pl ON pl.label_id = c.label_id
JOIN photos ph ON pl.photo_id = ph.id
WHERE pl.uncertainty < 100
AND ph.photo_quality >= 0
AND ph.photo_private = 0
AND ph.deleted_at IS NULL GROUP BY l.id) counts GROUP BY label_id
`).Scan(&result).Error; err != nil {
log.Errorf("label-count: %s", err.Error())
}
return result
}
// UpdatePlacesPhotoCounts updates the places photo counts.
func UpdatePlacesPhotoCounts() (err error) {
start := time.Now()
// Update places.
if err = Db().Table("places").
UpdateColumn("photo_count", gorm.Expr("(SELECT COUNT(*) FROM photos p "+
"WHERE places.id = p.place_id "+
"AND p.photo_quality >= 0 "+
"AND p.photo_private = 0 "+
"AND p.deleted_at IS NULL)")).Error; err != nil {
return err
}
log.Debugf("counts: updated places [%s]", time.Since(start))
return nil
}
// UpdateSubjectFileCounts updates the subject file counts.
func UpdateSubjectFileCounts() (err error) {
start := time.Now()
// Update subjects.
if err = Db().Table(Subject{}.TableName()).
UpdateColumn("file_count", gorm.Expr("(SELECT COUNT(*) FROM files f "+
fmt.Sprintf(
"JOIN %s m ON f.file_uid = m.file_uid AND m.subj_uid = %s.subj_uid ",
Marker{}.TableName(),
Subject{}.TableName())+
" WHERE m.marker_invalid = 0 AND f.deleted_at IS NULL)")).Error; err != nil {
return err
}
log.Debugf("counts: updated subjects [%s]", time.Since(start))
return nil
}
// UpdateLabelPhotoCounts updates the label photo counts.
func UpdateLabelPhotoCounts() (err error) {
start := time.Now()
if IsDialect(MySQL) {
if err = Db().
Table("labels").
UpdateColumn("photo_count",
gorm.Expr(`(SELECT photo_count FROM (
SELECT label_id, SUM(photo_count) AS photo_count FROM (
(SELECT l.id AS label_id, COUNT(*) AS photo_count FROM labels l
JOIN photos_labels pl ON pl.label_id = l.id
JOIN photos ph ON pl.photo_id = ph.id
WHERE pl.uncertainty < 100
AND ph.photo_quality >= 0
AND ph.photo_private = 0
AND ph.deleted_at IS NULL GROUP BY l.id)
UNION ALL
(SELECT l.id AS label_id, COUNT(*) AS photo_count FROM labels l
JOIN categories c ON c.category_id = l.id
JOIN photos_labels pl ON pl.label_id = c.label_id
JOIN photos ph ON pl.photo_id = ph.id
WHERE pl.uncertainty < 100
AND ph.photo_quality >= 0
AND ph.photo_private = 0
AND ph.deleted_at IS NULL GROUP BY l.id)) counts GROUP BY label_id
) label_counts WHERE label_id = labels.id)`)).Error; err != nil {
return err
}
} else if IsDialect(SQLite) {
if err = Db().
Table("labels").
UpdateColumn("photo_count",
gorm.Expr(`(SELECT photo_count FROM (SELECT label_id, SUM(photo_count) AS photo_count FROM (
SELECT l.id AS label_id, COUNT(*) AS photo_count FROM labels l
JOIN photos_labels pl ON pl.label_id = l.id
JOIN photos ph ON pl.photo_id = ph.id
WHERE pl.uncertainty < 100
AND ph.photo_quality >= 0
AND ph.photo_private = 0
AND ph.deleted_at IS NULL GROUP BY l.id
UNION ALL
SELECT l.id AS label_id, COUNT(*) AS photo_count FROM labels l
JOIN categories c ON c.category_id = l.id
JOIN photos_labels pl ON pl.label_id = c.label_id
JOIN photos ph ON pl.photo_id = ph.id
WHERE pl.uncertainty < 100
AND ph.photo_quality >= 0
AND ph.photo_private = 0
AND ph.deleted_at IS NULL GROUP BY l.id) counts GROUP BY label_id) label_counts WHERE label_id = labels.id)`)).Error; err != nil {
return err
}
} else {
return fmt.Errorf("unknown sql dialect %s", DbDialect())
}
log.Debugf("counts: updated labels [%s]", time.Since(start))
return nil
}
// UpdatePhotoCounts updates precalculated photo and file counts.
func UpdatePhotoCounts() (err error) {
if err = UpdatePlacesPhotoCounts(); err != nil {
if strings.Contains(err.Error(), "Error 1054") {
log.Errorf("counts: failed updating places, incompatible database version")
log.Errorf("%s see https://jira.mariadb.org/browse/MDEV-25362", err)
return nil
}
return err
}
if err = UpdateSubjectFileCounts(); err != nil {
if strings.Contains(err.Error(), "Error 1054") {
log.Errorf("counts: failed updating subjects, incompatible database version")
log.Errorf("%s see https://jira.mariadb.org/browse/MDEV-25362", err)
return nil
}
return err
}
if err = UpdateLabelPhotoCounts(); err != nil {
return err
}
/* TODO: Slow with many photos due to missing index.
start = time.Now()
// Update calendar album visibility.
switch DbDialect() {
default:
if err = UnscopedDb().Exec(`UPDATE albums SET deleted_at = ? WHERE album_type=? AND id NOT IN (
SELECT a.id FROM albums a JOIN photos p ON a.album_month = MONTH(p.taken_at) AND a.album_year = YEAR(p.taken_at)
AND p.deleted_at IS NULL AND p.photo_quality > -1 AND p.photo_private = 0 WHERE album_type=? GROUP BY a.id)`,
TimeStamp(), AlbumMonth, AlbumMonth).Error; err != nil {
return err
}
if err = UnscopedDb().Exec(`UPDATE albums SET deleted_at = NULL WHERE album_type=? AND id IN (
SELECT a.id FROM albums a JOIN photos p ON a.album_month = MONTH(p.taken_at) AND a.album_year = YEAR(p.taken_at)
AND p.deleted_at IS NULL AND p.photo_quality > -1 AND p.photo_private = 0 WHERE album_type=? GROUP BY a.id)`,
AlbumMonth, AlbumMonth).Error; err != nil {
return err
}
}
log.Debugf("calendar: updating visibility completed [%s]", time.Since(start))
*/
return nil
}