From eedaae8f9105a9e05962b843353bf69d50729d1f Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Thu, 26 Mar 2020 09:41:33 +0100 Subject: [PATCH] Backend: Move photo description to separate table Signed-off-by: Michael Mayer --- frontend/src/dialog/photo-edit/details.vue | 80 ++++++++++------ frontend/src/model/abstract.js | 9 +- frontend/src/model/photo.js | 29 +++--- internal/config/db.go | 1 + internal/entity/description.go | 57 +++++++++++ internal/entity/photo.go | 105 ++++++++------------- internal/form/photo.go | 47 ++++----- internal/form/photo_search.go | 3 - internal/form/photo_search_test.go | 8 +- internal/photoprism/index_mediafile.go | 73 +++++++------- internal/query/photo.go | 18 +--- internal/query/photo_test.go | 28 ------ 12 files changed, 246 insertions(+), 212 deletions(-) create mode 100644 internal/entity/description.go diff --git a/frontend/src/dialog/photo-edit/details.vue b/frontend/src/dialog/photo-edit/details.vue index c599b07d2..62d97a23f 100644 --- a/frontend/src/dialog/photo-edit/details.vue +++ b/frontend/src/dialog/photo-edit/details.vue @@ -228,11 +228,11 @@ placeholder="" :rows="1" color="secondary-dark" - v-model="model.PhotoDescription" + v-model="model.Description.PhotoDescription" > - + - - - - - - - - + + + + + + + + + + + + + + + + @@ -309,9 +333,7 @@ return { config: this.$config.values, all: { - countries: [{code: "", name: this.$gettext("Unknown")}], - cameras: [{ID: 0, CameraModel: this.$gettext("Unknown")}], - lenses: [{ID: 0, LensModel: "Unknown"}], + countries: [{code: "", name: ""}], colors: [{label: "Unknown", name: ""}], }, readonly: this.$config.getValue("readonly"), @@ -380,10 +402,10 @@ return this.all.countries.concat(this.config.countries); }, cameraOptions() { - return this.all.cameras.concat(this.config.cameras); + return this.config.cameras; }, lensOptions() { - return this.all.lenses.concat(this.config.lenses); + return this.config.lenses; }, colorOptions() { return this.all.colors.concat(this.config.colors); diff --git a/frontend/src/model/abstract.js b/frontend/src/model/abstract.js index 2a318abe8..934453598 100644 --- a/frontend/src/model/abstract.js +++ b/frontend/src/model/abstract.js @@ -18,7 +18,12 @@ class Abstract { for (let key in values) { if (values.hasOwnProperty(key) && key !== "__originalValues") { this[key] = values[key]; - this.__originalValues[key] = values[key]; + if(typeof values[key] === "object") { + this.__originalValues[key] = JSON.parse(JSON.stringify(values[key])); + } else { + this.__originalValues[key] = values[key]; + } + } } @@ -48,7 +53,7 @@ class Abstract { val = this[key]; } - if(!changed || val !== this.__originalValues[key]) { + if(!changed || JSON.stringify(val) !== JSON.stringify(this.__originalValues[key])) { result[key] = val; } } diff --git a/frontend/src/model/photo.js b/frontend/src/model/photo.js index cc53d3537..ded28a274 100644 --- a/frontend/src/model/photo.js +++ b/frontend/src/model/photo.js @@ -11,11 +11,6 @@ class Photo extends Abstract { PhotoPath: "", PhotoName: "", PhotoTitle: "", - PhotoDescription: "", - PhotoNotes: "", - PhotoKeywords: "", - PhotoArtist: "", - PhotoCopyright: "", PhotoFavorite: false, PhotoPrivate: false, PhotoNSFW: false, @@ -42,11 +37,21 @@ class Photo extends Abstract { PhotoYear: 0, PhotoMonth: 0, TakenAtLocal: "", - ModifiedDate: false, ModifiedTitle: false, - ModifiedDetails: false, + ModifiedDescription: false, + ModifiedDate: false, ModifiedLocation: false, + ModifiedCamera: false, TimeZone: "", + Description: { + PhotoDescription: "", + PhotoKeywords: "", + PhotoNotes: "", + PhotoSubject: "", + PhotoArtist: "", + PhotoCopyright: "", + PhotoLicense: "", + }, Files: [], Labels: [], Keywords: [], @@ -228,11 +233,11 @@ class Photo extends Abstract { values.ModifiedTitle = true } - if(values.PhotoKeywords) { - values.ModifiedKeywords = true + if(values.Description) { + values.ModifiedDescription = true } - if(values.PhotoLat || values.PhotoLng || values.PhotoAltitude) { + if(values.PhotoLat || values.PhotoLng || values.PhotoAltitude || values.PhotoCountry) { values.ModifiedLocation = true } @@ -240,8 +245,8 @@ class Photo extends Abstract { values.ModifiedDate = true } - if(values.CameraID || values.LensID || values.PhotoCountry) { - values.ModifiedDetails = true + if(values.CameraID || values.LensID) { + values.ModifiedCamera = true } return Api.put(this.getEntityResource(), values).then((response) => Promise.resolve(this.setValues(response.data))); diff --git a/internal/config/db.go b/internal/config/db.go index 473351cfe..f94fc2836 100644 --- a/internal/config/db.go +++ b/internal/config/db.go @@ -63,6 +63,7 @@ func (c *Config) MigrateDb() { &entity.Account{}, &entity.File{}, &entity.Photo{}, + &entity.Description{}, &entity.Event{}, &entity.Place{}, &entity.Location{}, diff --git a/internal/entity/description.go b/internal/entity/description.go new file mode 100644 index 000000000..8268f17ee --- /dev/null +++ b/internal/entity/description.go @@ -0,0 +1,57 @@ +package entity + +import ( + "github.com/jinzhu/gorm" + "github.com/photoprism/photoprism/internal/mutex" +) + +// Description stores additional metadata fields for each photo to improve search performance. +type Description struct { + PhotoID uint `gorm:"primary_key;auto_increment:false"` + PhotoDescription string `gorm:"type:text;" json:"PhotoDescription"` + PhotoKeywords string `gorm:"type:text;" json:"PhotoKeywords"` + PhotoNotes string `gorm:"type:text;" json:"PhotoNotes"` + PhotoSubject string `json:"PhotoSubject"` + PhotoArtist string `json:"PhotoArtist"` + PhotoCopyright string `json:"PhotoCopyright"` + PhotoLicense string `json:"PhotoLicense"` +} + +// FirstOrCreate returns the matching entity or creates a new one. +func (m *Description) FirstOrCreate(db *gorm.DB) error { + mutex.Db.Lock() + defer mutex.Db.Unlock() + + return db.FirstOrCreate(m, "photo_id = ?", m.PhotoID).Error +} + +// NoDescription checks if the photo has no Description +func (m *Description) NoDescription() bool { + return m.PhotoDescription == "" +} + +// NoKeywords checks if the photo has no Keywords +func (m *Description) NoKeywords() bool { + return m.PhotoKeywords == "" +} + +// NoSubject checks if the photo has no Subject +func (m *Description) NoSubject() bool { + return m.PhotoSubject == "" +} + +// NoNotes checks if the photo has no Notes +func (m *Description) NoNotes() bool { + return m.PhotoNotes == "" +} + +// NoArtist checks if the photo has no Artist +func (m *Description) NoArtist() bool { + return m.PhotoArtist == "" +} + +// NoCopyright checks if the photo has no Copyright +func (m *Description) NoCopyright() bool { + return m.PhotoCopyright == "" +} + diff --git a/internal/entity/photo.go b/internal/entity/photo.go index 7cec447ed..82fd5f93f 100644 --- a/internal/entity/photo.go +++ b/internal/entity/photo.go @@ -1,6 +1,7 @@ package entity import ( + "strings" "time" "github.com/jinzhu/gorm" @@ -18,13 +19,6 @@ type Photo struct { PhotoPath string `gorm:"type:varbinary(512);index;"` PhotoName string `gorm:"type:varbinary(256);"` PhotoTitle string `json:"PhotoTitle"` - PhotoSubject string `json:"PhotoSubject"` - PhotoKeywords string `json:"PhotoKeywords"` - PhotoDescription string `gorm:"type:text;" json:"PhotoDescription"` - PhotoNotes string `gorm:"type:text;" json:"PhotoNotes"` - PhotoArtist string `json:"PhotoArtist"` - PhotoCopyright string `json:"PhotoCopyright"` - PhotoLicense string `json:"PhotoLicense"` PhotoFavorite bool `json:"PhotoFavorite"` PhotoPrivate bool `json:"PhotoPrivate"` PhotoNSFW bool `json:"PhotoNSFW"` @@ -39,30 +33,31 @@ type Photo struct { CameraID uint `gorm:"index:idx_photos_camera_lens;" json:"CameraID"` CameraSerial string `gorm:"type:varbinary(128);" json:"CameraSerial"` LensID uint `gorm:"index:idx_photos_camera_lens;" json:"LensID"` - AccountID uint `json:"AccountID"` - PlaceID string `gorm:"type:varbinary(16);index;" json:"PlaceID"` - LocationID string `gorm:"type:varbinary(16);index;" json:"LocationID"` - LocationEstimated bool `json:"LocationEstimated"` - PhotoCountry string `gorm:"index:idx_photos_country_year_month;" json:"PhotoCountry"` - PhotoYear int `gorm:"index:idx_photos_country_year_month;"` - PhotoMonth int `gorm:"index:idx_photos_country_year_month;"` - TimeZone string `gorm:"type:varbinary(64);" json:"TimeZone"` - TakenAtLocal time.Time `gorm:"type:datetime;"` - ModifiedTitle bool `json:"ModifiedTitle"` - ModifiedDetails bool `json:"ModifiedDetails"` - ModifiedLocation bool `json:"ModifiedLocation"` - ModifiedKeywords bool `json:"ModifiedKeywords"` - ModifiedDate bool `json:"ModifiedDate"` - Camera *Camera `json:"Camera"` - Lens *Lens `json:"Lens"` - Location *Location `json:"-"` - Place *Place `json:"-"` - Account *Account `json:"-"` - Files []File - Labels []PhotoLabel - Keywords []Keyword `json:"-"` - Albums []Album `json:"-"` - CreatedAt time.Time + AccountID uint `json:"AccountID"` + PlaceID string `gorm:"type:varbinary(16);index;" json:"PlaceID"` + LocationID string `gorm:"type:varbinary(16);index;" json:"LocationID"` + LocationEstimated bool `json:"LocationEstimated"` + PhotoCountry string `gorm:"index:idx_photos_country_year_month;" json:"PhotoCountry"` + PhotoYear int `gorm:"index:idx_photos_country_year_month;"` + PhotoMonth int `gorm:"index:idx_photos_country_year_month;"` + TimeZone string `gorm:"type:varbinary(64);" json:"TimeZone"` + TakenAtLocal time.Time `gorm:"type:datetime;"` + ModifiedTitle bool `json:"ModifiedTitle"` + ModifiedDescription bool `json:"ModifiedDescription"` + ModifiedDate bool `json:"ModifiedDate"` + ModifiedLocation bool `json:"ModifiedLocation"` + ModifiedCamera bool `json:"ModifiedCamera"` + Description Description `json:"Description"` + Camera *Camera `json:"Camera"` + Lens *Lens `json:"Lens"` + Location *Location `json:"-"` + Place *Place `json:"-"` + Account *Account `json:"-"` + Files []File + Labels []PhotoLabel + Keywords []Keyword `json:"-"` + Albums []Album `json:"-"` + CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time `sql:"index"` } @@ -73,6 +68,16 @@ func SavePhoto(model Photo, form form.Photo, db *gorm.DB) error { return err } + if form.Description.PhotoID == model.ID { + if err := deepcopier.Copy(&model.Description).From(form.Description); err != nil { + return err + } + + model.Description.PhotoKeywords = strings.Join(txt.UniqueKeywords(model.Description.PhotoKeywords), ", ") + } + + log.Debugf("model: %+v", model) + model.IndexKeywords(db) return db.Save(&model).Error @@ -123,10 +128,10 @@ func (m *Photo) IndexKeywords(db *gorm.DB) { // Add title, description and other keywords keywords = append(keywords, txt.Keywords(m.PhotoTitle)...) - keywords = append(keywords, txt.Keywords(m.PhotoKeywords)...) - keywords = append(keywords, txt.Keywords(m.PhotoSubject)...) - keywords = append(keywords, txt.Keywords(m.PhotoArtist)...) - keywords = append(keywords, txt.Keywords(m.PhotoDescription)...) + keywords = append(keywords, txt.Keywords(m.Description.PhotoDescription)...) + keywords = append(keywords, txt.Keywords(m.Description.PhotoKeywords)...) + keywords = append(keywords, txt.Keywords(m.Description.PhotoSubject)...) + keywords = append(keywords, txt.Keywords(m.Description.PhotoArtist)...) keywords = txt.UniqueWords(keywords) @@ -223,36 +228,6 @@ func (m *Photo) NoTitle() bool { return m.PhotoTitle == "" } -// NoDescription checks if the photo has no Description -func (m *Photo) NoDescription() bool { - return m.PhotoDescription == "" -} - -// NoNotes checks if the photo has no Notes -func (m *Photo) NoNotes() bool { - return m.PhotoNotes == "" -} - -// NoArtist checks if the photo has no Artist -func (m *Photo) NoArtist() bool { - return m.PhotoArtist == "" -} - -// NoCopyright checks if the photo has no Copyright -func (m *Photo) NoCopyright() bool { - return m.PhotoCopyright == "" -} - -// NoSubject checks if the photo has no Subject -func (m *Photo) NoSubject() bool { - return m.PhotoSubject == "" -} - -// NoKeywords checks if the photo has no Keywords -func (m *Photo) NoKeywords() bool { - return m.PhotoKeywords == "" -} - // NoCameraSerial checks if the photo has no CameraSerial func (m *Photo) NoCameraSerial() bool { return m.CameraSerial == "" diff --git a/internal/form/photo.go b/internal/form/photo.go index fdebb0058..8596f68a9 100644 --- a/internal/form/photo.go +++ b/internal/form/photo.go @@ -8,13 +8,18 @@ import ( // Photo represents a photo edit form. type Photo struct { - TakenAt time.Time `json:"TakenAt"` - PhotoTitle string `json:"PhotoTitle"` - PhotoDescription string `json:"PhotoDescription"` - PhotoNotes string `json:"PhotoNotes"` - PhotoArtist string `json:"PhotoArtist"` - PhotoKeywords string `json:"PhotoKeywords"` - PhotoCopyright string `json:"PhotoCopyright"` + TakenAt time.Time `json:"TakenAt"` + PhotoTitle string `json:"PhotoTitle"` + Description struct { + PhotoID uint `json:"PhotoID" deepcopier:"skip"` + PhotoDescription string `json:"PhotoDescription"` + PhotoKeywords string `json:"PhotoKeywords"` + PhotoNotes string `json:"PhotoNotes"` + PhotoSubject string `json:"PhotoSubject"` + PhotoArtist string `json:"PhotoArtist"` + PhotoCopyright string `json:"PhotoCopyright"` + PhotoLicense string `json:"PhotoLicense"` + } `json:"Description"` PhotoFavorite bool `json:"PhotoFavorite"` PhotoPrivate bool `json:"PhotoPrivate"` PhotoNSFW bool `json:"PhotoNSFW"` @@ -24,20 +29,20 @@ type Photo struct { PhotoAltitude int `json:"PhotoAltitude"` PhotoFocalLength int `json:"PhotoFocalLength"` PhotoIso int `json:"PhotoIso"` - PhotoFNumber float64 `json:"PhotoFNumber"` - PhotoExposure string `json:"PhotoExposure"` - CameraID uint `json:"CameraID"` - LensID uint `json:"LensID"` - LocationID string `json:"LocationID"` - PlaceID string `json:"PlaceID"` - PhotoCountry string `json:"PhotoCountry"` - TimeZone string `json:"TimeZone"` - TakenAtLocal time.Time `json:"TakenAtLocal"` - ModifiedTitle bool `json:"ModifiedTitle"` - ModifiedKeywords bool `json:"ModifiedKeywords"` - ModifiedDetails bool `json:"ModifiedDetails"` - ModifiedLocation bool `json:"ModifiedLocation"` - ModifiedDate bool `json:"ModifiedDate"` + PhotoFNumber float64 `json:"PhotoFNumber"` + PhotoExposure string `json:"PhotoExposure"` + CameraID uint `json:"CameraID"` + LensID uint `json:"LensID"` + LocationID string `json:"LocationID"` + PlaceID string `json:"PlaceID"` + PhotoCountry string `json:"PhotoCountry"` + TimeZone string `json:"TimeZone"` + TakenAtLocal time.Time `json:"TakenAtLocal"` + ModifiedTitle bool `json:"ModifiedTitle"` + ModifiedDescription bool `json:"ModifiedDescription"` + ModifiedDate bool `json:"ModifiedDate"` + ModifiedLocation bool `json:"ModifiedLocation"` + ModifiedCamera bool `json:"ModifiedCamera"` } func NewPhoto(m interface{}) (f Photo, err error) { diff --git a/internal/form/photo_search.go b/internal/form/photo_search.go index f0452d2ff..8322ff774 100644 --- a/internal/form/photo_search.go +++ b/internal/form/photo_search.go @@ -9,9 +9,6 @@ type PhotoSearch struct { Query string `form:"q"` ID string `form:"id"` Title string `form:"title"` - Description string `form:"description"` - Notes string `form:"notes"` - Artist string `form:"artist"` Hash string `form:"hash"` Duplicate bool `form:"duplicate"` Archived bool `form:"archived"` diff --git a/internal/form/photo_search_test.go b/internal/form/photo_search_test.go index d5cbe203b..f1aac858e 100644 --- a/internal/form/photo_search_test.go +++ b/internal/form/photo_search_test.go @@ -37,7 +37,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, 33.45343166666667, form.Lat) }) t.Run("valid query 2", func(t *testing.T) { - form := &PhotoSearch{Query: "chroma:600 description:\"test\" after:2018-01-15 duplicate:false favorites:true lng:33.45343166666667"} + form := &PhotoSearch{Query: "chroma:600 title:\"test\" after:2018-01-15 duplicate:false favorites:true lng:33.45343166666667"} err := form.ParseQueryString() @@ -48,13 +48,13 @@ func TestParseQueryString(t *testing.T) { } assert.Equal(t, uint(0x258), form.Chroma) - assert.Equal(t, "test", form.Description) + assert.Equal(t, "test", form.Title) assert.Equal(t, time.Date(2018, 01, 15, 0, 0, 0, 0, time.UTC), form.After) assert.Equal(t, false, form.Duplicate) assert.Equal(t, 33.45343166666667, form.Lng) }) t.Run("valid query with umlauts", func(t *testing.T) { - form := &PhotoSearch{Query: "description:\"tübingen\""} + form := &PhotoSearch{Query: "title:\"tübingen\""} err := form.ParseQueryString() @@ -64,7 +64,7 @@ func TestParseQueryString(t *testing.T) { t.Fatal("err should be nil") } - assert.Equal(t, "tübingen", form.Description) + assert.Equal(t, "tübingen", form.Title) }) t.Run("query for invalid filter", func(t *testing.T) { form := &PhotoSearch{Query: "xxx:false"} diff --git a/internal/photoprism/index_mediafile.go b/internal/photoprism/index_mediafile.go index 4a95e66ea..bddacf674 100644 --- a/internal/photoprism/index_mediafile.go +++ b/internal/photoprism/index_mediafile.go @@ -33,6 +33,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) I start := time.Now() var photo entity.Photo + var description entity.Description var file, primaryFile entity.File var metaData meta.Data var photoQuery, fileQuery *gorm.DB @@ -87,6 +88,10 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) I return indexResultSkipped } + if photoExists { + ind.db.Model(&photo).Related(&description) + } + if fileHash == "" { fileHash = m.Hash() } @@ -132,28 +137,28 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) I photo.PhotoTitle = metaData.Title } - if photo.NoDescription() { - photo.PhotoDescription = metaData.Description + if photo.Description.NoDescription() { + photo.Description.PhotoDescription = metaData.Description } - if photo.NoNotes() { - photo.PhotoNotes = metaData.Comment + if photo.Description.NoNotes() { + photo.Description.PhotoNotes = metaData.Comment } - if photo.NoSubject() { - photo.PhotoSubject = metaData.Subject + if photo.Description.NoSubject() { + photo.Description.PhotoSubject = metaData.Subject } - if photo.NoKeywords() { - photo.PhotoKeywords = metaData.Keywords + if photo.Description.NoKeywords() { + photo.Description.PhotoKeywords = metaData.Keywords } - if photo.NoArtist() && metaData.Artist != "" { - photo.PhotoArtist = metaData.Artist + if photo.Description.NoArtist() && metaData.Artist != "" { + photo.Description.PhotoArtist = metaData.Artist } - if photo.NoArtist() && metaData.CameraOwner != "" { - photo.PhotoArtist = metaData.CameraOwner + if photo.Description.NoArtist() && metaData.CameraOwner != "" { + photo.Description.PhotoArtist = metaData.CameraOwner } if photo.NoCameraSerial() { @@ -168,7 +173,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) I } } - if !photo.ModifiedDetails && (fileChanged || o.UpdateCamera) { + if !photo.ModifiedCamera && (fileChanged || o.UpdateCamera) { // Set UpdateCamera, Lens, Focal Length and F Number photo.Camera = entity.NewCamera(m.CameraModel(), m.CameraMake()).FirstOrCreate(ind.db) photo.Lens = entity.NewLens(m.LensModel(), m.LensMake()).FirstOrCreate(ind.db) @@ -219,16 +224,20 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) I photo.PhotoTitle = data.Title } - if photo.NoCopyright() && data.Copyright != "" { - photo.PhotoCopyright = data.Copyright + if photo.Description.NoCopyright() && data.Copyright != "" { + photo.Description.PhotoCopyright = data.Copyright } - if photo.NoArtist() && data.Artist != "" { - photo.PhotoArtist = data.Artist + if photo.Description.NoArtist() && data.Artist != "" { + photo.Description.PhotoArtist = data.Artist } - if photo.NoDescription() && data.Description != "" { - photo.PhotoDescription = data.Description + if photo.Description.NoDescription() && data.Description != "" { + photo.Description.PhotoDescription = data.Description + } + + if photo.Description.NoNotes() && data.Comment != "" { + photo.Description.PhotoNotes = data.Comment } } } @@ -271,24 +280,22 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) I } if file.FilePrimary && (fileChanged || o.UpdateKeywords) { - w := txt.Keywords(photo.PhotoKeywords) + w := txt.Keywords(photo.Description.PhotoKeywords) - if !photo.ModifiedKeywords { - if NonCanonical(fileBase) { - w = append(w, txt.Keywords(filePath)...) - w = append(w, txt.Keywords(fileBase)...) - } - - w = append(w, locKeywords...) - w = append(w, txt.Keywords(file.OriginalName)...) - w = append(w, file.FileMainColor) - w = append(w, labels.Keywords()...) + if NonCanonical(fileBase) { + w = append(w, txt.Keywords(filePath)...) + w = append(w, txt.Keywords(fileBase)...) } - photo.PhotoKeywords = strings.Join(txt.UniqueWords(w), ", ") + w = append(w, locKeywords...) + w = append(w, txt.Keywords(file.OriginalName)...) + w = append(w, file.FileMainColor) + w = append(w, labels.Keywords()...) - if photo.PhotoKeywords != "" { - log.Debugf("index: updated photo keywords (%s)", photo.PhotoKeywords) + photo.Description.PhotoKeywords = strings.Join(txt.UniqueWords(w), ", ") + + if photo.Description.PhotoKeywords != "" { + log.Debugf("index: updated photo keywords (%s)", photo.Description.PhotoKeywords) } else { log.Debug("index: no photo keywords") } diff --git a/internal/query/photo.go b/internal/query/photo.go index a21a570a4..570f87f9c 100644 --- a/internal/query/photo.go +++ b/internal/query/photo.go @@ -26,14 +26,9 @@ type PhotoResult struct { PhotoPath string PhotoName string PhotoTitle string - PhotoDescription string PhotoYear int PhotoMonth int PhotoCountry string - PhotoArtist string - PhotoKeywords string - PhotoColors string - PhotoColor string PhotoFavorite bool PhotoPrivate bool PhotoSensitive bool @@ -259,14 +254,6 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) { q = q.Where("LOWER(photos.photo_title) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(f.Title))) } - if f.Description != "" { - q = q.Where("LOWER(photos.photo_description) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(f.Description))) - } - - if f.Notes != "" { - q = q.Where("LOWER(photos.photo_notes) LIKE ?", fmt.Sprintf("%%%s%%", strings.ToLower(f.Notes))) - } - if f.Hash != "" { q = q.Where("files.file_hash = ?", f.Hash) } @@ -350,7 +337,7 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) { // FindPhotoByID returns a Photo based on the ID. func (s *Repo) FindPhotoByID(photoID uint64) (photo entity.Photo, err error) { - if err := s.db.Where("id = ?", photoID).First(&photo).Error; err != nil { + if err := s.db.Where("id = ?", photoID).Preload("Description").First(&photo).Error; err != nil { return photo, err } @@ -359,7 +346,7 @@ func (s *Repo) FindPhotoByID(photoID uint64) (photo entity.Photo, err error) { // FindPhotoByUUID returns a Photo based on the UUID. func (s *Repo) FindPhotoByUUID(photoUUID string) (photo entity.Photo, err error) { - if err := s.db.Where("photo_uuid = ?", photoUUID).First(&photo).Error; err != nil { + if err := s.db.Where("photo_uuid = ?", photoUUID).Preload("Description").First(&photo).Error; err != nil { return photo, err } @@ -375,6 +362,7 @@ func (s *Repo) PreloadPhotoByUUID(photoUUID string) (photo entity.Photo, err err Preload("Labels.Label"). Preload("Camera"). Preload("Lens"). + Preload("Description"). First(&photo).Error; err != nil { return photo, err } diff --git a/internal/query/photo_test.go b/internal/query/photo_test.go index 1cfc9270b..59fdf43fe 100644 --- a/internal/query/photo_test.go +++ b/internal/query/photo_test.go @@ -216,34 +216,6 @@ func TestSearch_Photos_Query(t *testing.T) { t.Logf("results: %+v", photos) }) - t.Run("form.description", func(t *testing.T) { - var f form.PhotoSearch - f.Query = "description:xxx" - f.Count = 3 - f.Offset = 0 - - photos, err := search.Photos(f) - - if err != nil { - t.Fatal(err) - } - - t.Logf("results: %+v", photos) - }) - t.Run("form.notes", func(t *testing.T) { - var f form.PhotoSearch - f.Query = "notes:xxx" - f.Count = 3 - f.Offset = 0 - - photos, err := search.Photos(f) - - if err != nil { - t.Fatal(err) - } - - t.Logf("results: %+v", photos) - }) t.Run("form.hash", func(t *testing.T) { var f form.PhotoSearch f.Query = "hash:xxx"