Add photo_count column to labels table
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
3aad02501f
commit
252e67ce03
17 changed files with 95 additions and 43 deletions
|
@ -1,4 +1,4 @@
|
|||
FROM photoprism/development:20200427
|
||||
FROM photoprism/development:20200509
|
||||
|
||||
# Set up project directory
|
||||
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
||||
|
|
4
Makefile
4
Makefile
|
@ -14,7 +14,7 @@ all: dep build
|
|||
dep: dep-tensorflow dep-js dep-go
|
||||
build: generate build-js build-go
|
||||
install: install-bin install-assets
|
||||
test: test-js test-go
|
||||
test: reset-test-db test-js test-go
|
||||
acceptance-all: start acceptance acceptance-firefox stop
|
||||
test-all: test acceptance-all
|
||||
fmt: fmt-js fmt-go
|
||||
|
@ -90,6 +90,8 @@ acceptance:
|
|||
acceptance-firefox:
|
||||
$(info Running JS acceptance tests in Firefox...)
|
||||
(cd frontend && npm run acceptance-firefox)
|
||||
reset-test-db:
|
||||
mysql < scripts/reset-test-db.sql
|
||||
test-go:
|
||||
$(info Running all Go unit tests...)
|
||||
$(GOTEST) -parallel 1 -count 1 -cpu 1 -tags slow -timeout 20m ./pkg/... ./internal/...
|
||||
|
|
|
@ -48,7 +48,7 @@ services:
|
|||
expose:
|
||||
- "4001"
|
||||
volumes:
|
||||
- "./scripts/test-db.sql:/docker-entrypoint-initdb.d/test-db.sql"
|
||||
- "./scripts/reset-test-db.sql:/docker-entrypoint-initdb.d/reset-test-db.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
|
|
|
@ -49,7 +49,7 @@ services:
|
|||
ports:
|
||||
- "4001:4001" # MySQL (for tests)
|
||||
volumes:
|
||||
- "./scripts/test-db.sql:/docker-entrypoint-initdb.d/test-db.sql"
|
||||
- "./scripts/reset-test-db.sql:/docker-entrypoint-initdb.d/reset-test-db.sql"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: photoprism
|
||||
MYSQL_USER: photoprism
|
||||
|
|
5
docker/development/.my.cnf
Normal file
5
docker/development/.my.cnf
Normal file
|
@ -0,0 +1,5 @@
|
|||
[client]
|
||||
user=root
|
||||
password=photoprism
|
||||
host=photoprism-db
|
||||
port=4001
|
|
@ -116,6 +116,9 @@ RUN env GO111MODULE=off /usr/local/go/bin/go get -u github.com/tsliwowicz/go-wrk
|
|||
RUN env GO111MODULE=off /usr/local/go/bin/go get -u github.com/kyoh86/richgo
|
||||
RUN echo "alias go=richgo" > /root/.bash_aliases
|
||||
|
||||
# MariaDB test database settings
|
||||
COPY /docker/development/.my.cnf /root/.my.cnf
|
||||
|
||||
# Set up project directory
|
||||
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM photoprism/development:20200427 as build
|
||||
FROM photoprism/development:20200509 as build
|
||||
|
||||
# Set up project directory
|
||||
WORKDIR "/go/src/github.com/photoprism/photoprism"
|
||||
|
@ -85,7 +85,7 @@ RUN chmod -R 777 /photoprism
|
|||
RUN photoprism -v
|
||||
|
||||
# Expose http and database ports
|
||||
EXPOSE 2342 2343
|
||||
EXPOSE 2342 2343 4000
|
||||
|
||||
# Run server
|
||||
CMD photoprism start
|
||||
|
|
|
@ -169,7 +169,7 @@ RUN chmod -R 777 /photoprism
|
|||
RUN photoprism -v
|
||||
|
||||
# Expose http and database ports
|
||||
EXPOSE 2342 2343
|
||||
EXPOSE 2342 2343 4000
|
||||
|
||||
# Run server
|
||||
CMD photoprism start
|
||||
|
|
|
@ -41,7 +41,12 @@ func BatchPhotosArchive(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
log.Infof("photos: archiving %#v", f.Photos)
|
||||
|
||||
entity.Db().Where("photo_uuid IN (?)", f.Photos).Delete(&entity.Photo{})
|
||||
err := entity.Db().Where("photo_uuid IN (?)", f.Photos).Delete(&entity.Photo{}).Error
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
return
|
||||
}
|
||||
|
||||
if err := query.UpdatePhotoCounts(); err != nil {
|
||||
log.Errorf("photos: %s", err)
|
||||
|
@ -82,8 +87,13 @@ func BatchPhotosRestore(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
log.Infof("restoring photos: %#v", f.Photos)
|
||||
|
||||
entity.Db().Unscoped().Model(&entity.Photo{}).Where("photo_uuid IN (?)", f.Photos).
|
||||
UpdateColumn("deleted_at", gorm.Expr("NULL"))
|
||||
err := entity.Db().Unscoped().Model(&entity.Photo{}).Where("photo_uuid IN (?)", f.Photos).
|
||||
UpdateColumn("deleted_at", gorm.Expr("NULL")).Error
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusInternalServerError, ErrSaveFailed)
|
||||
return
|
||||
}
|
||||
|
||||
if err := query.UpdatePhotoCounts(); err != nil {
|
||||
log.Errorf("photos: %s", err)
|
||||
|
@ -165,6 +175,10 @@ func BatchPhotosPrivate(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := query.UpdatePhotoCounts(); err != nil {
|
||||
log.Errorf("photos: %s", err)
|
||||
}
|
||||
|
||||
if entities, err := query.PhotoSelection(f); err == nil {
|
||||
event.EntitiesUpdated("photos", entities)
|
||||
}
|
||||
|
|
|
@ -71,6 +71,10 @@ func AddPhotoLabel(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := query.UpdatePhotoCounts(); err != nil {
|
||||
log.Errorf("photo: %s", err)
|
||||
}
|
||||
|
||||
PublishPhotoEvent(EntityUpdated, c.Param("uuid"), c)
|
||||
|
||||
event.Success("label updated")
|
||||
|
@ -131,6 +135,10 @@ func RemovePhotoLabel(router *gin.RouterGroup, conf *config.Config) {
|
|||
return
|
||||
}
|
||||
|
||||
if err := query.UpdatePhotoCounts(); err != nil {
|
||||
log.Errorf("photo: %s", err)
|
||||
}
|
||||
|
||||
PublishPhotoEvent(EntityUpdated, c.Param("uuid"), c)
|
||||
|
||||
event.Success("label removed")
|
||||
|
|
|
@ -57,10 +57,10 @@ func (list Types) WaitForMigration() {
|
|||
for name := range list {
|
||||
for i := 0; i <= attempts; i++ {
|
||||
if err := Db().Raw(fmt.Sprintf("DESCRIBE `%s`", name)).Scan(&struct{}{}).Error; err == nil {
|
||||
log.Debugf("entity: table %s migrated", name)
|
||||
// log.Debugf("entity: table %s migrated", name)
|
||||
break
|
||||
} else {
|
||||
log.Debugf("entity: %s", err.Error())
|
||||
log.Debugf("entity: wait for migration %s (%s)", err.Error(), name)
|
||||
}
|
||||
|
||||
if i == attempts {
|
||||
|
@ -78,8 +78,8 @@ func (list Types) Truncate() {
|
|||
if err := Db().Raw(fmt.Sprintf("TRUNCATE TABLE `%s`", name)).Scan(&struct{}{}).Error; err == nil {
|
||||
log.Debugf("entity: removed all data from %s", name)
|
||||
break
|
||||
} else {
|
||||
log.Debugf("entity: %s", err.Error())
|
||||
} else if err.Error() != "record not found" {
|
||||
log.Debugf("entity: truncate %s (%s)", err.Error(), name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ func (list Types) Truncate() {
|
|||
func (list Types) Migrate() {
|
||||
for _, entity := range list {
|
||||
if err := UnscopedDb().AutoMigrate(entity).Error; err != nil {
|
||||
log.Debugf("entity: %s (waiting 1s)", err.Error())
|
||||
log.Debugf("entity: migrate %s (waiting 1s)", err.Error())
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ type Label struct {
|
|||
LabelNotes string `gorm:"type:text;"`
|
||||
LabelCategories []*Label `gorm:"many2many:categories;association_jointable_foreignkey:category_id"`
|
||||
Links []Link `gorm:"foreignkey:ShareUUID;association_foreignkey:LabelUUID"`
|
||||
PhotoCount int
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt *time.Time `sql:"index"`
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package query
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
|
||||
// UpdatePhotoCounts updates photos count in related tables as needed.
|
||||
func UpdatePhotoCounts() error {
|
||||
/*
|
||||
UPDATE places
|
||||
SET
|
||||
photo_count = (SELECT
|
||||
COUNT(*) FROM
|
||||
photos ph
|
||||
WHERE places.id = ph.place_id AND ph.photo_quality >= 0 AND ph.deleted_at IS NULL)
|
||||
*/
|
||||
|
||||
return Db().Table("places").
|
||||
UpdateColumn("photo_count", gorm.Expr("(SELECT COUNT(*) FROM photos ph WHERE places.id = ph.place_id AND ph.photo_quality >= 0 AND ph.deleted_at IS NULL)")).Error
|
||||
}
|
|
@ -43,7 +43,7 @@ func LabelByUUID(labelUUID string) (label entity.Label, err error) {
|
|||
func LabelThumbBySlug(labelSlug string) (file entity.File, err error) {
|
||||
if err := 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").
|
||||
Joins("JOIN photos_labels ON photos_labels.label_id = labels.id AND photos_labels.photo_id = files.photo_id AND photos_labels.uncertainty < 100").
|
||||
Joins("JOIN photos ON photos.id = files.photo_id AND photos.photo_private = 0 AND photos.deleted_at IS NULL").
|
||||
Order("photos.photo_quality DESC, photos_labels.uncertainty ASC").
|
||||
First(&file).Error; err != nil {
|
||||
|
@ -58,7 +58,7 @@ func LabelThumbByUUID(labelUUID string) (file entity.File, err error) {
|
|||
// Search matching label
|
||||
err = 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").
|
||||
Joins("JOIN photos_labels ON photos_labels.label_id = labels.id AND photos_labels.photo_id = files.photo_id AND photos_labels.uncertainty < 100").
|
||||
Joins("JOIN photos ON photos.id = files.photo_id AND photos.photo_private = 0 AND photos.deleted_at IS NULL").
|
||||
Order("photos.photo_quality DESC, photos_labels.uncertainty ASC").
|
||||
First(&file).Error
|
||||
|
@ -69,7 +69,7 @@ func LabelThumbByUUID(labelUUID string) (file entity.File, err error) {
|
|||
|
||||
// If failed, search for category instead
|
||||
err = 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 photos_labels ON photos_labels.photo_id = files.photo_id AND photos_labels.uncertainty < 100").
|
||||
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).
|
||||
Joins("JOIN photos ON photos.id = files.photo_id AND photos.photo_private = 0 AND photos.deleted_at IS NULL").
|
||||
|
|
28
internal/query/photo_counts.go
Normal file
28
internal/query/photo_counts.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package query
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
|
||||
// UpdatePhotoCounts updates photos count in related tables as needed.
|
||||
func UpdatePhotoCounts() error {
|
||||
if err := Db().Table("places").
|
||||
UpdateColumn("photo_count", gorm.Expr("(SELECT COUNT(*) FROM photos ph " +
|
||||
"WHERE places.id = ph.place_id " +
|
||||
"AND ph.photo_quality >= 0 " +
|
||||
"AND ph.photo_private = 0 " +
|
||||
"AND ph.deleted_at IS NULL)")).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := Db().Table("labels").
|
||||
UpdateColumn("photo_count", gorm.Expr("(SELECT COUNT(*) FROM photos_labels " +
|
||||
"JOIN photos ph ON photos_labels.photo_id = ph.id " +
|
||||
"WHERE photos_labels.label_id = labels.id " +
|
||||
"AND photos_labels.uncertainty < 100 " +
|
||||
"AND ph.photo_quality >= 0 " +
|
||||
"AND ph.photo_private = 0 " +
|
||||
"AND ph.deleted_at IS NULL)")).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,9 +1,18 @@
|
|||
CREATE DATABASE IF NOT EXISTS api;
|
||||
CREATE DATABASE IF NOT EXISTS config;
|
||||
CREATE DATABASE IF NOT EXISTS entity;
|
||||
DROP DATABASE IF EXISTS photoprism;
|
||||
CREATE DATABASE IF NOT EXISTS photoprism;
|
||||
CREATE DATABASE IF NOT EXISTS query;
|
||||
CREATE DATABASE IF NOT EXISTS remote;
|
||||
CREATE DATABASE IF NOT EXISTS service;
|
||||
CREATE DATABASE IF NOT EXISTS workers;
|
||||
DROP DATABASE IF EXISTS acceptance;
|
||||
CREATE DATABASE IF NOT EXISTS acceptance;
|
||||
DROP DATABASE IF EXISTS api;
|
||||
CREATE DATABASE IF NOT EXISTS api;
|
||||
DROP DATABASE IF EXISTS config;
|
||||
CREATE DATABASE IF NOT EXISTS config;
|
||||
DROP DATABASE IF EXISTS entity;
|
||||
CREATE DATABASE IF NOT EXISTS entity;
|
||||
DROP DATABASE IF EXISTS query;
|
||||
CREATE DATABASE IF NOT EXISTS query;
|
||||
DROP DATABASE IF EXISTS remote;
|
||||
CREATE DATABASE IF NOT EXISTS remote;
|
||||
DROP DATABASE IF EXISTS service;
|
||||
CREATE DATABASE IF NOT EXISTS service;
|
||||
DROP DATABASE IF EXISTS workers;
|
||||
CREATE DATABASE IF NOT EXISTS workers;
|
Loading…
Reference in a new issue