From 3e6c7052bbc82c320928e4ac0f56228bf33193fa Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Fri, 26 Nov 2021 13:59:10 +0100 Subject: [PATCH] Places: Refactor GeoJSON API endpoint --- internal/api/event.go | 2 +- internal/api/photo_search_geo.go | 109 ---------- internal/api/search_geojson.go | 70 +++++++ ...rch_geo_test.go => search_geojson_test.go} | 4 +- .../api/{photo_search.go => search_photos.go} | 2 +- ...o_search_test.go => search_photos_test.go} | 0 internal/api/share_preview.go | 2 +- internal/entity/album.go | 2 +- internal/entity/folder.go | 2 +- internal/form/label_search.go | 2 +- ...{photo_search_geo.go => search_geojson.go} | 18 +- ...rch_geo_test.go => search_geojson_test.go} | 16 +- .../{photo_search.go => search_photos.go} | 18 +- ...o_search_test.go => search_photos_test.go} | 42 ++-- internal/photoprism/moments.go | 8 +- internal/query/albums.go | 2 +- internal/search/albums_photos.go | 2 +- internal/search/{photos_geo.go => geojson.go} | 4 +- internal/search/geojson_result.go | 94 +++++++++ ..._result_test.go => geojson_result_test.go} | 0 .../{photos_geo_test.go => geojson_test.go} | 194 +++++++++--------- internal/search/photos.go | 3 +- internal/search/photos_geo_result.go | 31 --- internal/search/photos_test.go | 170 +++++++-------- internal/server/routes.go | 2 +- 25 files changed, 412 insertions(+), 387 deletions(-) delete mode 100644 internal/api/photo_search_geo.go create mode 100644 internal/api/search_geojson.go rename internal/api/{photo_search_geo_test.go => search_geojson_test.go} (79%) rename internal/api/{photo_search.go => search_photos.go} (98%) rename internal/api/{photo_search_test.go => search_photos_test.go} (100%) rename internal/form/{photo_search_geo.go => search_geojson.go} (85%) rename internal/form/{photo_search_geo_test.go => search_geojson_test.go} (76%) rename internal/form/{photo_search.go => search_photos.go} (89%) rename internal/form/{photo_search_test.go => search_photos_test.go} (80%) rename internal/search/{photos_geo.go => geojson.go} (98%) create mode 100644 internal/search/geojson_result.go rename internal/search/{photos_geo_result_test.go => geojson_result_test.go} (100%) rename internal/search/{photos_geo_test.go => geojson_test.go} (79%) delete mode 100644 internal/search/photos_geo_result.go diff --git a/internal/api/event.go b/internal/api/event.go index 3179c7c23..f3ab93f09 100644 --- a/internal/api/event.go +++ b/internal/api/event.go @@ -16,7 +16,7 @@ const ( ) func PublishPhotoEvent(e EntityEvent, uid string, c *gin.Context) { - f := form.PhotoSearch{ID: uid, Merged: true} + f := form.SearchPhotos{ID: uid, Merged: true} result, _, err := search.Photos(f) if err != nil { diff --git a/internal/api/photo_search_geo.go b/internal/api/photo_search_geo.go deleted file mode 100644 index b93dbcea1..000000000 --- a/internal/api/photo_search_geo.go +++ /dev/null @@ -1,109 +0,0 @@ -package api - -import ( - "net/http" - - "github.com/photoprism/photoprism/internal/acl" - "github.com/photoprism/photoprism/internal/entity" - "github.com/photoprism/photoprism/internal/form" - "github.com/photoprism/photoprism/internal/search" - "github.com/photoprism/photoprism/pkg/txt" - - "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/binding" - - geojson "github.com/paulmach/go.geojson" -) - -// SearchPhotosGeo performs a geo search with reduced metadata details for improved performance. -// -// GET /api/v1/geo -func SearchPhotosGeo(router *gin.RouterGroup) { - router.GET("/geo", func(c *gin.Context) { - s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionSearch) - - if s.Invalid() { - AbortUnauthorized(c) - return - } - - var f form.PhotoSearchGeo - - err := c.MustBindWith(&f, binding.Form) - - if err != nil { - AbortBadRequest(c) - return - } - - photos, err := search.PhotosGeo(f) - - if err != nil { - log.Warnf("search: %s", err) - AbortBadRequest(c) - return - } - - fc := geojson.NewFeatureCollection() - - bbox := make([]float64, 4) - - bboxMin := func(pos int, val float64) { - if bbox[pos] == 0.0 || bbox[pos] > val { - bbox[pos] = val - } - } - - bboxMax := func(pos int, val float64) { - if bbox[pos] == 0.0 || bbox[pos] < val { - bbox[pos] = val - } - } - - for _, p := range photos { - bboxMin(0, p.Lng()) - bboxMin(1, p.Lat()) - bboxMax(2, p.Lng()) - bboxMax(3, p.Lat()) - - props := gin.H{ - "UID": p.PhotoUID, - "Hash": p.FileHash, - "Width": p.FileWidth, - "Height": p.FileHeight, - "TakenAt": p.TakenAt, - "Title": p.PhotoTitle, - } - - if p.PhotoDescription != "" { - props["Description"] = p.PhotoDescription - } - - if p.PhotoType != entity.TypeImage && p.PhotoType != entity.TypeDefault { - props["Type"] = p.PhotoType - } - - if p.PhotoFavorite { - props["Favorite"] = true - } - - feat := geojson.NewPointFeature([]float64{p.Lng(), p.Lat()}) - feat.ID = p.ID - feat.Properties = props - fc.AddFeature(feat) - } - - fc.BoundingBox = bbox - - resp, err := fc.MarshalJSON() - - if err != nil { - c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())}) - return - } - - AddTokenHeaders(c) - - c.Data(http.StatusOK, "application/json", resp) - }) -} diff --git a/internal/api/search_geojson.go b/internal/api/search_geojson.go new file mode 100644 index 000000000..f0fc93971 --- /dev/null +++ b/internal/api/search_geojson.go @@ -0,0 +1,70 @@ +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + + "github.com/photoprism/photoprism/internal/acl" + "github.com/photoprism/photoprism/internal/form" + "github.com/photoprism/photoprism/internal/search" + "github.com/photoprism/photoprism/pkg/txt" +) + +// SearchGeo finds photos and returns results as Geo, so they can be displayed on a map. +// +// GET /api/v1/geo +func SearchGeo(router *gin.RouterGroup) { + router.GET("/geo", func(c *gin.Context) { + s := Auth(SessionID(c), acl.ResourcePhotos, acl.ActionSearch) + + if s.Invalid() { + AbortUnauthorized(c) + return + } + + var f form.SearchGeo + + err := c.MustBindWith(&f, binding.Form) + + if err != nil { + AbortBadRequest(c) + return + } + + // Guests may only see public content. + if s.Guest() { + if f.Album == "" || !s.HasShare(f.Album) { + AbortUnauthorized(c) + return + } + + f.Public = true + f.Private = false + f.Archived = false + f.Review = false + } + + // Find matching pictures. + photos, err := search.Geo(f) + + if err != nil { + log.Warnf("search: %s", err) + AbortBadRequest(c) + return + } + + // Create GeoJSON from search results. + resp, err := photos.MarshalJSON() + + if err != nil { + c.AbortWithStatusJSON(400, gin.H{"error": txt.UcFirst(err.Error())}) + return + } + + AddTokenHeaders(c) + + c.Data(http.StatusOK, "application/json", resp) + }) +} diff --git a/internal/api/photo_search_geo_test.go b/internal/api/search_geojson_test.go similarity index 79% rename from internal/api/photo_search_geo_test.go rename to internal/api/search_geojson_test.go index 08f529b27..f62eaf444 100644 --- a/internal/api/photo_search_geo_test.go +++ b/internal/api/search_geojson_test.go @@ -7,11 +7,11 @@ import ( "github.com/stretchr/testify/assert" ) -func TestSearchPhotosGeo(t *testing.T) { +func TestSearchGeo(t *testing.T) { t.Run("Success", func(t *testing.T) { app, router, _ := NewApiTest() - SearchPhotosGeo(router) + SearchGeo(router) result := PerformRequest(app, "GET", "/api/v1/geo") assert.Equal(t, http.StatusOK, result.Code) diff --git a/internal/api/photo_search.go b/internal/api/search_photos.go similarity index 98% rename from internal/api/photo_search.go rename to internal/api/search_photos.go index 43016dbf3..13fdaaeb2 100644 --- a/internal/api/photo_search.go +++ b/internal/api/search_photos.go @@ -35,7 +35,7 @@ func SearchPhotos(router *gin.RouterGroup) { return } - var f form.PhotoSearch + var f form.SearchPhotos err := c.MustBindWith(&f, binding.Form) diff --git a/internal/api/photo_search_test.go b/internal/api/search_photos_test.go similarity index 100% rename from internal/api/photo_search_test.go rename to internal/api/search_photos_test.go diff --git a/internal/api/share_preview.go b/internal/api/share_preview.go index 7d438ea62..c3fb99c36 100644 --- a/internal/api/share_preview.go +++ b/internal/api/share_preview.go @@ -62,7 +62,7 @@ func SharePreview(router *gin.RouterGroup) { return } - var f form.PhotoSearch + var f form.SearchPhotos // Covers may only contain public content in shared albums. f.Album = share diff --git a/internal/entity/album.go b/internal/entity/album.go index 91ff7ec88..c4aa64db8 100644 --- a/internal/entity/album.go +++ b/internal/entity/album.go @@ -208,7 +208,7 @@ func NewMonthAlbum(albumTitle, albumSlug string, year, month int) *Album { return nil } - f := form.PhotoSearch{ + f := form.SearchPhotos{ Year: strconv.Itoa(year), Month: strconv.Itoa(month), Public: true, diff --git a/internal/entity/folder.go b/internal/entity/folder.go index dca87ffe5..2c4909ea4 100644 --- a/internal/entity/folder.go +++ b/internal/entity/folder.go @@ -159,7 +159,7 @@ func (m *Folder) Create() error { return nil } - f := form.PhotoSearch{ + f := form.SearchPhotos{ Path: m.Path, Public: true, } diff --git a/internal/form/label_search.go b/internal/form/label_search.go index 2c0c30eab..4863a15bd 100644 --- a/internal/form/label_search.go +++ b/internal/form/label_search.go @@ -1,6 +1,6 @@ package form -// PhotoSearch represents search form fields for "/api/v1/labels". +// LabelSearch represents search form fields for "/api/v1/labels". type LabelSearch struct { Query string `form:"q"` ID string `form:"id"` diff --git a/internal/form/photo_search_geo.go b/internal/form/search_geojson.go similarity index 85% rename from internal/form/photo_search_geo.go rename to internal/form/search_geojson.go index bd366250d..18a1fa79d 100644 --- a/internal/form/photo_search_geo.go +++ b/internal/form/search_geojson.go @@ -2,8 +2,8 @@ package form import "time" -// PhotoSearchGeo represents search form fields for "/api/v1/geo". -type PhotoSearchGeo struct { +// SearchGeo represents search form fields for "/api/v1/geo". +type SearchGeo struct { Query string `form:"q"` Type string `form:"type"` Path string `form:"path"` @@ -48,17 +48,17 @@ type PhotoSearchGeo struct { } // GetQuery returns the query parameter as string. -func (f *PhotoSearchGeo) GetQuery() string { +func (f *SearchGeo) GetQuery() string { return f.Query } // SetQuery sets the query parameter. -func (f *PhotoSearchGeo) SetQuery(q string) { +func (f *SearchGeo) SetQuery(q string) { f.Query = q } // ParseQueryString parses the query parameter if possible. -func (f *PhotoSearchGeo) ParseQueryString() error { +func (f *SearchGeo) ParseQueryString() error { err := ParseQueryString(f) if f.Path == "" && f.Folder != "" { @@ -80,15 +80,15 @@ func (f *PhotoSearchGeo) ParseQueryString() error { } // Serialize returns a string containing non-empty fields and values of a struct. -func (f *PhotoSearchGeo) Serialize() string { +func (f *SearchGeo) Serialize() string { return Serialize(f, false) } // SerializeAll returns a string containing all non-empty fields and values of a struct. -func (f *PhotoSearchGeo) SerializeAll() string { +func (f *SearchGeo) SerializeAll() string { return Serialize(f, true) } -func NewGeoSearch(query string) PhotoSearchGeo { - return PhotoSearchGeo{Query: query} +func NewGeoSearch(query string) SearchGeo { + return SearchGeo{Query: query} } diff --git a/internal/form/photo_search_geo_test.go b/internal/form/search_geojson_test.go similarity index 76% rename from internal/form/photo_search_geo_test.go rename to internal/form/search_geojson_test.go index cbf6107e0..57b2a6c1e 100644 --- a/internal/form/photo_search_geo_test.go +++ b/internal/form/search_geojson_test.go @@ -9,7 +9,7 @@ import ( func TestGeoSearch(t *testing.T) { t.Run("subjects", func(t *testing.T) { - form := &PhotoSearchGeo{Query: "subjects:\"Jens Mander\""} + form := &SearchGeo{Query: "subjects:\"Jens Mander\""} err := form.ParseQueryString() @@ -20,7 +20,7 @@ func TestGeoSearch(t *testing.T) { assert.Equal(t, "Jens Mander", form.Subjects) }) t.Run("aliases", func(t *testing.T) { - form := &PhotoSearchGeo{Query: "people:\"Jens & Mander\" folder:Foo person:Bar"} + form := &SearchGeo{Query: "people:\"Jens & Mander\" folder:Foo person:Bar"} err := form.ParseQueryString() @@ -35,7 +35,7 @@ func TestGeoSearch(t *testing.T) { assert.Equal(t, "Jens & Mander", form.Subjects) }) t.Run("keywords", func(t *testing.T) { - form := &PhotoSearchGeo{Query: "keywords:\"Foo Bar\""} + form := &SearchGeo{Query: "keywords:\"Foo Bar\""} err := form.ParseQueryString() @@ -46,7 +46,7 @@ func TestGeoSearch(t *testing.T) { assert.Equal(t, "Foo Bar", form.Keywords) }) t.Run("valid query", func(t *testing.T) { - form := &PhotoSearchGeo{Query: "query:\"fooBar baz\" before:2019-01-15 dist:25000 lat:33.45343166666667"} + form := &SearchGeo{Query: "query:\"fooBar baz\" before:2019-01-15 dist:25000 lat:33.45343166666667"} err := form.ParseQueryString() @@ -62,7 +62,7 @@ func TestGeoSearch(t *testing.T) { assert.Equal(t, float32(33.45343), form.Lat) }) t.Run("valid query path empty folder not empty", func(t *testing.T) { - form := &PhotoSearchGeo{Query: "query:\"fooBar baz\" before:2019-01-15 dist:25000 lat:33.45343166666667 folder:test"} + form := &SearchGeo{Query: "query:\"fooBar baz\" before:2019-01-15 dist:25000 lat:33.45343166666667 folder:test"} err := form.ParseQueryString() @@ -82,18 +82,18 @@ func TestGeoSearch(t *testing.T) { } func TestGeoSearch_Serialize(t *testing.T) { - form := &PhotoSearchGeo{Query: "query:\"fooBar baz\"", Favorite: true} + form := &SearchGeo{Query: "query:\"fooBar baz\"", Favorite: true} assert.Equal(t, "q:\"query:fooBar baz\" favorite:true", form.Serialize()) } func TestGeoSearch_SerializeAll(t *testing.T) { - form := &PhotoSearchGeo{Query: "query:\"fooBar baz\"", Favorite: true} + form := &SearchGeo{Query: "query:\"fooBar baz\"", Favorite: true} assert.Equal(t, "q:\"query:fooBar baz\" favorite:true", form.SerializeAll()) } func TestNewGeoSearch(t *testing.T) { r := NewGeoSearch("Berlin") - assert.IsType(t, PhotoSearchGeo{}, r) + assert.IsType(t, SearchGeo{}, r) } diff --git a/internal/form/photo_search.go b/internal/form/search_photos.go similarity index 89% rename from internal/form/photo_search.go rename to internal/form/search_photos.go index f62c2ae6f..8a7524e75 100644 --- a/internal/form/photo_search.go +++ b/internal/form/search_photos.go @@ -4,8 +4,8 @@ import ( "time" ) -// PhotoSearch represents search form fields for "/api/v1/photos". -type PhotoSearch struct { +// SearchPhotos represents search form fields for "/api/v1/photos". +type SearchPhotos struct { Query string `form:"q"` Filter string `form:"filter"` ID string `form:"id"` @@ -73,15 +73,15 @@ type PhotoSearch struct { Merged bool `form:"merged" serialize:"-"` } -func (f *PhotoSearch) GetQuery() string { +func (f *SearchPhotos) GetQuery() string { return f.Query } -func (f *PhotoSearch) SetQuery(q string) { +func (f *SearchPhotos) SetQuery(q string) { f.Query = q } -func (f *PhotoSearch) ParseQueryString() error { +func (f *SearchPhotos) ParseQueryString() error { if err := ParseQueryString(f); err != nil { return err } @@ -111,15 +111,15 @@ func (f *PhotoSearch) ParseQueryString() error { } // Serialize returns a string containing non-empty fields and values of a struct. -func (f *PhotoSearch) Serialize() string { +func (f *SearchPhotos) Serialize() string { return Serialize(f, false) } // SerializeAll returns a string containing all non-empty fields and values of a struct. -func (f *PhotoSearch) SerializeAll() string { +func (f *SearchPhotos) SerializeAll() string { return Serialize(f, true) } -func NewPhotoSearch(query string) PhotoSearch { - return PhotoSearch{Query: query} +func NewPhotoSearch(query string) SearchPhotos { + return SearchPhotos{Query: query} } diff --git a/internal/form/photo_search_test.go b/internal/form/search_photos_test.go similarity index 80% rename from internal/form/photo_search_test.go rename to internal/form/search_photos_test.go index bb2289618..7b185cc25 100644 --- a/internal/form/photo_search_test.go +++ b/internal/form/search_photos_test.go @@ -8,14 +8,14 @@ import ( ) func TestPhotoSearchForm(t *testing.T) { - form := &PhotoSearch{} + form := &SearchPhotos{} - assert.IsType(t, new(PhotoSearch), form) + assert.IsType(t, new(SearchPhotos), form) } func TestParseQueryString(t *testing.T) { t.Run("subjects", func(t *testing.T) { - form := &PhotoSearch{Query: "subjects:\"Jens & Mander\""} + form := &SearchPhotos{Query: "subjects:\"Jens & Mander\""} err := form.ParseQueryString() @@ -26,7 +26,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "Jens & Mander", form.Subjects) }) t.Run("aliases", func(t *testing.T) { - form := &PhotoSearch{Query: "people:\"Jens & Mander\" folder:Foo person:Bar"} + form := &SearchPhotos{Query: "people:\"Jens & Mander\" folder:Foo person:Bar"} err := form.ParseQueryString() @@ -41,7 +41,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "Jens & Mander", form.Subjects) }) t.Run("keywords", func(t *testing.T) { - form := &PhotoSearch{Query: "keywords:\"Foo Bar\""} + form := &SearchPhotos{Query: "keywords:\"Foo Bar\""} err := form.ParseQueryString() @@ -52,7 +52,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "Foo Bar", form.Keywords) }) t.Run("and query", func(t *testing.T) { - form := &PhotoSearch{Query: "\"Jens & Mander\" title:\"Tübingen\""} + form := &SearchPhotos{Query: "\"Jens & Mander\" title:\"Tübingen\""} err := form.ParseQueryString() @@ -65,7 +65,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "Tübingen", form.Title) }) t.Run("path", func(t *testing.T) { - form := &PhotoSearch{Query: "path:123abc/,EFG"} + form := &SearchPhotos{Query: "path:123abc/,EFG"} err := form.ParseQueryString() @@ -79,7 +79,7 @@ func TestParseQueryString(t *testing.T) { }) t.Run("folder", func(t *testing.T) { - form := &PhotoSearch{Query: "folder:123abc/,EFG"} + form := &SearchPhotos{Query: "folder:123abc/,EFG"} err := form.ParseQueryString() @@ -92,7 +92,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "123abc/,EFG", form.Path) }) t.Run("valid query", func(t *testing.T) { - form := &PhotoSearch{Query: "label:cat query:\"fooBar baz\" before:2019-01-15 camera:23 favorite:false dist:25000 lat:33.45343166666667"} + form := &SearchPhotos{Query: "label:cat query:\"fooBar baz\" before:2019-01-15 camera:23 favorite:false dist:25000 lat:33.45343166666667"} err := form.ParseQueryString() @@ -111,7 +111,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, float32(33.45343), form.Lat) }) t.Run("valid query 2", func(t *testing.T) { - form := &PhotoSearch{Query: "chroma:200 title:\"te:st\" after:2018-01-15 favorite:true lng:33.45343166666667"} + form := &SearchPhotos{Query: "chroma:200 title:\"te:st\" after:2018-01-15 favorite:true lng:33.45343166666667"} err := form.ParseQueryString() @@ -127,7 +127,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, float32(33.45343), form.Lng) }) t.Run("valid query with filter", func(t *testing.T) { - form := &PhotoSearch{Query: "label:cat title:\"fooBar baz\"", Filter: "label:dog"} + form := &SearchPhotos{Query: "label:cat title:\"fooBar baz\"", Filter: "label:dog"} err := form.ParseQueryString() @@ -142,7 +142,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "fooBar baz", form.Title) }) t.Run("valid query with umlauts", func(t *testing.T) { - form := &PhotoSearch{Query: "title:\"tübingen\""} + form := &SearchPhotos{Query: "title:\"tübingen\""} err := form.ParseQueryString() @@ -155,7 +155,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "tübingen", form.Title) }) t.Run("query for invalid filter", func(t *testing.T) { - form := &PhotoSearch{Query: "xxx:false"} + form := &SearchPhotos{Query: "xxx:false"} err := form.ParseQueryString() @@ -168,7 +168,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "unknown filter: Xxx", err.Error()) }) t.Run("query for favorites with uncommon bool value", func(t *testing.T) { - form := &PhotoSearch{Query: "favorite:cat"} + form := &SearchPhotos{Query: "favorite:cat"} err := form.ParseQueryString() @@ -179,7 +179,7 @@ func TestParseQueryString(t *testing.T) { assert.True(t, form.Favorite) }) t.Run("query for lat with invalid type", func(t *testing.T) { - form := &PhotoSearch{Query: "lat:cat"} + form := &SearchPhotos{Query: "lat:cat"} err := form.ParseQueryString() @@ -192,7 +192,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "strconv.ParseFloat: parsing \"cat\": invalid syntax", err.Error()) }) t.Run("query for dist with invalid type", func(t *testing.T) { - form := &PhotoSearch{Query: "dist:cat"} + form := &SearchPhotos{Query: "dist:cat"} err := form.ParseQueryString() @@ -205,7 +205,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "strconv.Atoi: parsing \"cat\": invalid syntax", err.Error()) }) t.Run("query for camera with invalid type", func(t *testing.T) { - form := &PhotoSearch{Query: "camera:cat"} + form := &SearchPhotos{Query: "camera:cat"} err := form.ParseQueryString() @@ -218,7 +218,7 @@ func TestParseQueryString(t *testing.T) { assert.Equal(t, "strconv.Atoi: parsing \"cat\": invalid syntax", err.Error()) }) t.Run("query for before with invalid type", func(t *testing.T) { - form := &PhotoSearch{Query: "before:cat"} + form := &SearchPhotos{Query: "before:cat"} err := form.ParseQueryString() @@ -234,11 +234,11 @@ func TestParseQueryString(t *testing.T) { func TestNewPhotoSearch(t *testing.T) { r := NewPhotoSearch("cat") - assert.IsType(t, PhotoSearch{}, r) + assert.IsType(t, SearchPhotos{}, r) } func TestPhotoSearch_Serialize(t *testing.T) { - form := PhotoSearch{ + form := SearchPhotos{ Query: "foo BAR", Private: true, Photo: false, @@ -258,7 +258,7 @@ func TestPhotoSearch_Serialize(t *testing.T) { } func TestPhotoSearch_SerializeAll(t *testing.T) { - form := PhotoSearch{ + form := SearchPhotos{ Query: "foo BAR", Private: true, Photo: false, diff --git a/internal/photoprism/moments.go b/internal/photoprism/moments.go index 5f217b0ad..d2903ac2c 100644 --- a/internal/photoprism/moments.go +++ b/internal/photoprism/moments.go @@ -87,7 +87,7 @@ func (w *Moments) Start() (err error) { log.Errorf("moments: %s", err.Error()) } else { for _, mom := range results { - f := form.PhotoSearch{ + f := form.SearchPhotos{ Path: mom.Path, Public: true, } @@ -148,7 +148,7 @@ func (w *Moments) Start() (err error) { log.Errorf("moments: %s", err.Error()) } else { for _, mom := range results { - f := form.PhotoSearch{ + f := form.SearchPhotos{ Country: mom.Country, Year: strconv.Itoa(mom.Year), Public: true, @@ -183,7 +183,7 @@ func (w *Moments) Start() (err error) { log.Errorf("moments: %s", err.Error()) } else { for _, mom := range results { - f := form.PhotoSearch{ + f := form.SearchPhotos{ Country: mom.Country, State: mom.State, Public: true, @@ -222,7 +222,7 @@ func (w *Moments) Start() (err error) { for _, mom := range results { w.MigrateSlug(mom, entity.AlbumMoment) - f := form.PhotoSearch{ + f := form.SearchPhotos{ Label: mom.Label, Public: true, } diff --git a/internal/query/albums.go b/internal/query/albums.go index 1bbae9fec..6c5b30225 100644 --- a/internal/query/albums.go +++ b/internal/query/albums.go @@ -32,7 +32,7 @@ func AlbumCoverByUID(uid string) (file entity.File, err error) { if err := UnscopedDb().Where("album_uid = ?", uid).First(&a).Error; err != nil { return file, err } else if a.AlbumType != entity.AlbumDefault { // TODO: Optimize - f := form.PhotoSearch{Album: a.AlbumUID, Filter: a.AlbumFilter, Order: entity.SortOrderRelevance, Count: 1, Offset: 0, Merged: false} + f := form.SearchPhotos{Album: a.AlbumUID, Filter: a.AlbumFilter, Order: entity.SortOrderRelevance, Count: 1, Offset: 0, Merged: false} if photos, _, err := search.Photos(f); err != nil { return file, err diff --git a/internal/search/albums_photos.go b/internal/search/albums_photos.go index 24214b0a6..a4f7262c9 100644 --- a/internal/search/albums_photos.go +++ b/internal/search/albums_photos.go @@ -7,7 +7,7 @@ import ( // AlbumPhotos returns up to count photos from an album. func AlbumPhotos(a entity.Album, count int) (results PhotoResults, err error) { - results, _, err = Photos(form.PhotoSearch{ + results, _, err = Photos(form.SearchPhotos{ Album: a.AlbumUID, Filter: a.AlbumFilter, Count: count, diff --git a/internal/search/photos_geo.go b/internal/search/geojson.go similarity index 98% rename from internal/search/photos_geo.go rename to internal/search/geojson.go index d30ff421b..015a9be07 100644 --- a/internal/search/photos_geo.go +++ b/internal/search/geojson.go @@ -18,8 +18,8 @@ import ( "github.com/photoprism/photoprism/pkg/txt" ) -// PhotosGeo searches for photos based on Form values and returns GeoResults ([]GeoResult). -func PhotosGeo(f form.PhotoSearchGeo) (results GeoResults, err error) { +// Geo searches for photos based on Form values and returns GeoResults ([]GeoResult). +func Geo(f form.SearchGeo) (results GeoResults, err error) { start := time.Now() if err := f.ParseQueryString(); err != nil { diff --git a/internal/search/geojson_result.go b/internal/search/geojson_result.go new file mode 100644 index 000000000..1c53ac968 --- /dev/null +++ b/internal/search/geojson_result.go @@ -0,0 +1,94 @@ +package search + +import ( + "time" + + "github.com/gin-gonic/gin" + geojson "github.com/paulmach/go.geojson" + "github.com/photoprism/photoprism/internal/entity" +) + +// GeoResult represents a photo geo search result. +type GeoResult struct { + ID string `json:"-"` + PhotoUID string `json:"UID"` + PhotoType string `json:"Type,omitempty"` + PhotoLat float32 `json:"Lat"` + PhotoLng float32 `json:"Lng"` + PhotoTitle string `json:"Title"` + PhotoDescription string `json:"Description,omitempty"` + PhotoFavorite bool `json:"Favorite,omitempty"` + FileHash string `json:"Hash"` + FileWidth int `json:"Width"` + FileHeight int `json:"Height"` + TakenAt time.Time `json:"TakenAt"` +} + +// Lat returns the position latitude. +func (photo GeoResult) Lat() float64 { + return float64(photo.PhotoLat) +} + +// Lng returns the position longitude. +func (photo GeoResult) Lng() float64 { + return float64(photo.PhotoLng) +} + +// GeoResults represents a list of geo search results. +type GeoResults []GeoResult + +// MarshalJSON returns results as Geo. +func (photos GeoResults) MarshalJSON() ([]byte, error) { + fc := geojson.NewFeatureCollection() + + bbox := make([]float64, 4) + + bboxMin := func(pos int, val float64) { + if bbox[pos] == 0.0 || bbox[pos] > val { + bbox[pos] = val + } + } + + bboxMax := func(pos int, val float64) { + if bbox[pos] == 0.0 || bbox[pos] < val { + bbox[pos] = val + } + } + + for _, p := range photos { + bboxMin(0, p.Lng()) + bboxMin(1, p.Lat()) + bboxMax(2, p.Lng()) + bboxMax(3, p.Lat()) + + props := gin.H{ + "UID": p.PhotoUID, + "Hash": p.FileHash, + "Width": p.FileWidth, + "Height": p.FileHeight, + "TakenAt": p.TakenAt, + "Title": p.PhotoTitle, + } + + if p.PhotoDescription != "" { + props["Description"] = p.PhotoDescription + } + + if p.PhotoType != entity.TypeImage && p.PhotoType != entity.TypeDefault { + props["Type"] = p.PhotoType + } + + if p.PhotoFavorite { + props["Favorite"] = true + } + + feat := geojson.NewPointFeature([]float64{p.Lng(), p.Lat()}) + feat.ID = p.ID + feat.Properties = props + fc.AddFeature(feat) + } + + fc.BoundingBox = bbox + + return fc.MarshalJSON() +} diff --git a/internal/search/photos_geo_result_test.go b/internal/search/geojson_result_test.go similarity index 100% rename from internal/search/photos_geo_result_test.go rename to internal/search/geojson_result_test.go diff --git a/internal/search/photos_geo_test.go b/internal/search/geojson_test.go similarity index 79% rename from internal/search/photos_geo_test.go rename to internal/search/geojson_test.go index 2563e6a20..e683fe769 100644 --- a/internal/search/photos_geo_test.go +++ b/internal/search/geojson_test.go @@ -14,7 +14,7 @@ func TestGeo(t *testing.T) { t.Run("UnknownFaces", func(t *testing.T) { query := form.NewGeoSearch("face:none") - if result, err := PhotosGeo(query); err != nil { + if result, err := Geo(query); err != nil { t.Fatal(err) } else { assert.Equal(t, 0, len(result)) @@ -23,7 +23,7 @@ func TestGeo(t *testing.T) { t.Run("form.keywords", func(t *testing.T) { query := form.NewGeoSearch("keywords:bridge") - if result, err := PhotosGeo(query); err != nil { + if result, err := Geo(query); err != nil { t.Fatal(err) } else { assert.GreaterOrEqual(t, len(result), 1) @@ -32,7 +32,7 @@ func TestGeo(t *testing.T) { t.Run("form.subjects", func(t *testing.T) { query := form.NewGeoSearch("subjects:John") - if result, err := PhotosGeo(query); err != nil { + if result, err := Geo(query); err != nil { t.Fatal(err) } else { assert.GreaterOrEqual(t, len(result), 0) @@ -41,7 +41,7 @@ func TestGeo(t *testing.T) { t.Run("find_all", func(t *testing.T) { query := form.NewGeoSearch("") - if result, err := PhotosGeo(query); err != nil { + if result, err := Geo(query); err != nil { t.Fatal(err) } else { assert.LessOrEqual(t, 4, len(result)) @@ -50,7 +50,7 @@ func TestGeo(t *testing.T) { t.Run("search for bridge", func(t *testing.T) { query := form.NewGeoSearch("Query:bridge Before:3006-01-02") - result, err := PhotosGeo(query) + result, err := Geo(query) t.Logf("RESULT: %+v", result) if err != nil { @@ -63,7 +63,7 @@ func TestGeo(t *testing.T) { t.Run("search for date range", func(t *testing.T) { query := form.NewGeoSearch("After:2014-12-02 Before:3006-01-02") - result, err := PhotosGeo(query) + result, err := Geo(query) // t.Logf("RESULT: %+v", result) @@ -75,7 +75,7 @@ func TestGeo(t *testing.T) { }) t.Run("search for review true, quality 0", func(t *testing.T) { - f := form.PhotoSearchGeo{ + f := form.SearchGeo{ Query: "", Before: time.Time{}, After: time.Time{}, @@ -89,7 +89,7 @@ func TestGeo(t *testing.T) { Review: true, } - result, err := PhotosGeo(f) + result, err := Geo(f) if err != nil { t.Fatal(err) @@ -103,7 +103,7 @@ func TestGeo(t *testing.T) { }) t.Run("search for review false, quality > 0", func(t *testing.T) { - f := form.PhotoSearchGeo{ + f := form.SearchGeo{ Query: "", Before: time.Time{}, After: time.Time{}, @@ -117,7 +117,7 @@ func TestGeo(t *testing.T) { Review: false, } - result, err := PhotosGeo(f) + result, err := Geo(f) if err != nil { t.Fatal(err) @@ -126,7 +126,7 @@ func TestGeo(t *testing.T) { assert.IsType(t, GeoResults{}, result) }) t.Run("search for s2", func(t *testing.T) { - f := form.PhotoSearchGeo{ + f := form.SearchGeo{ Query: "", Before: time.Time{}, After: time.Time{}, @@ -140,7 +140,7 @@ func TestGeo(t *testing.T) { Review: false, } - result, err := PhotosGeo(f) + result, err := Geo(f) if err != nil { t.Fatal(err) @@ -149,7 +149,7 @@ func TestGeo(t *testing.T) { assert.IsType(t, GeoResults{}, result) }) t.Run("search for Olc", func(t *testing.T) { - f := form.PhotoSearchGeo{ + f := form.SearchGeo{ Query: "", Before: time.Time{}, After: time.Time{}, @@ -163,7 +163,7 @@ func TestGeo(t *testing.T) { Review: false, } - result, err := PhotosGeo(f) + result, err := Geo(f) if err != nil { t.Fatal(err) @@ -171,11 +171,11 @@ func TestGeo(t *testing.T) { assert.IsType(t, GeoResults{}, result) }) t.Run("query for label flower", func(t *testing.T) { - f := form.PhotoSearchGeo{ + f := form.SearchGeo{ Query: "flower", } - result, err := PhotosGeo(f) + result, err := Geo(f) if err != nil { t.Fatal(err) } @@ -183,7 +183,7 @@ func TestGeo(t *testing.T) { assert.IsType(t, GeoResults{}, result) }) t.Run("query for label landscape", func(t *testing.T) { - f := form.PhotoSearchGeo{ + f := form.SearchGeo{ Query: "landscape", Album: "test", Camera: 123, @@ -200,7 +200,7 @@ func TestGeo(t *testing.T) { Private: true, } - result, err := PhotosGeo(f) + result, err := Geo(f) if err != nil { t.Fatal(err) } @@ -208,7 +208,7 @@ func TestGeo(t *testing.T) { assert.IsType(t, GeoResults{}, result) }) t.Run("search with multiple parameters", func(t *testing.T) { - f := form.PhotoSearchGeo{ + f := form.SearchGeo{ Query: "landscape", Photo: true, Path: "/xxx,xxx", @@ -218,7 +218,7 @@ func TestGeo(t *testing.T) { Public: true, } - result, err := PhotosGeo(f) + result, err := Geo(f) if err != nil { t.Fatal(err) } @@ -226,7 +226,7 @@ func TestGeo(t *testing.T) { assert.IsType(t, GeoResults{}, result) }) t.Run("search for archived true", func(t *testing.T) { - f := form.PhotoSearchGeo{ + f := form.SearchGeo{ Query: "landscape", Photo: true, Path: "/xxx/xxx/", @@ -234,7 +234,7 @@ func TestGeo(t *testing.T) { Archived: true, } - result, err := PhotosGeo(f) + result, err := Geo(f) if err != nil { t.Fatal(err) } @@ -242,10 +242,10 @@ func TestGeo(t *testing.T) { assert.IsType(t, GeoResults{}, result) }) t.Run("faces:true", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Query = "faces:true" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -254,10 +254,10 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 4) }) t.Run("faces:yes", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Faces = "Yes" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -266,10 +266,10 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 4) }) t.Run("face:yes", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Face = "Yes" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -278,11 +278,11 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 4) }) t.Run("f.Faces:new", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Faces = "New" f.Face = "" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -291,10 +291,10 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 3) }) t.Run("faces:no", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Faces = "No" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -303,10 +303,10 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 8) }) t.Run("faces:2", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Faces = "2" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -315,10 +315,10 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 1) }) t.Run("face: TOSCDXCS4VI3PGIUTCNIQCNI6HSFXQVZ", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Face = "TOSCDXCS4VI3PGIUTCNIQCNI6HSFXQVZ" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -327,11 +327,11 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 2) }) t.Run("day", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Day = "18" f.Month = "4" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -340,10 +340,10 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 1) }) t.Run("subject uid in query", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Query = "Actress" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -352,10 +352,10 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 1) }) t.Run("albums", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Albums = "2030" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -364,10 +364,10 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 10) }) t.Run("path or path", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Path = "1990/04" + "|" + "2015/11" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -376,10 +376,10 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 3) }) t.Run("name or name", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Name = "20151101_000000_51C501B5" + "|" + "Video" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -388,11 +388,11 @@ func TestGeo(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 2) }) t.Run("query: videos", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "videos" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -407,11 +407,11 @@ func TestGeo(t *testing.T) { } }) t.Run("query: video", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "video" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -426,11 +426,11 @@ func TestGeo(t *testing.T) { } }) t.Run("query: live", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "live" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -445,11 +445,11 @@ func TestGeo(t *testing.T) { } }) t.Run("query: raws", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "raws" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -464,11 +464,11 @@ func TestGeo(t *testing.T) { } }) t.Run("query: panoramas", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "panoramas" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -481,11 +481,11 @@ func TestGeo(t *testing.T) { } }) t.Run("query: scans", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "scans" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -498,11 +498,11 @@ func TestGeo(t *testing.T) { } }) t.Run("query: faces", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "faces" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -515,11 +515,11 @@ func TestGeo(t *testing.T) { } }) t.Run("query: people", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "people" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -533,11 +533,11 @@ func TestGeo(t *testing.T) { } }) t.Run("query: favorites", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "favorites" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -552,10 +552,10 @@ func TestGeo(t *testing.T) { } }) t.Run("keywords:kuh|bridge > keywords:bridge&kuh", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Query = "keywords:kuh|bridge" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -563,7 +563,7 @@ func TestGeo(t *testing.T) { f.Query = "keywords:bridge&kuh" - photos2, err2 := PhotosGeo(f) + photos2, err2 := Geo(f) if err2 != nil { t.Fatal(err2) @@ -572,10 +572,10 @@ func TestGeo(t *testing.T) { assert.Greater(t, len(photos), len(photos2)) }) t.Run("albums and and or search", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.Query = "albums:Holiday|Berlin" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -583,7 +583,7 @@ func TestGeo(t *testing.T) { f.Query = "albums:Berlin&Holiday" - photos2, err2 := PhotosGeo(f) + photos2, err2 := Geo(f) if err2 != nil { t.Fatal(err2) @@ -591,11 +591,11 @@ func TestGeo(t *testing.T) { assert.Greater(t, len(photos), len(photos2)) }) t.Run("f.Album = uid", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Album = "at9lxuqxpogaaba9" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -609,10 +609,10 @@ func TestGeo(t *testing.T) { } }) t.Run("people and and or search", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.People = "Actor A|Actress A" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) @@ -620,7 +620,7 @@ func TestGeo(t *testing.T) { f.People = "Actor A&Actress A" - photos2, err2 := PhotosGeo(f) + photos2, err2 := Geo(f) if err2 != nil { t.Fatal(err2) @@ -629,19 +629,19 @@ func TestGeo(t *testing.T) { assert.Greater(t, len(photos), len(photos2)) }) t.Run("people = subjects & person = subject", func(t *testing.T) { - var f form.PhotoSearchGeo + var f form.SearchGeo f.People = "Actor" - photos, err := PhotosGeo(f) + photos, err := Geo(f) if err != nil { t.Fatal(err) } - var f2 form.PhotoSearchGeo + var f2 form.SearchGeo f2.Subjects = "Actor" - photos2, err2 := PhotosGeo(f2) + photos2, err2 := Geo(f2) if err2 != nil { t.Fatal(err2) @@ -649,20 +649,20 @@ func TestGeo(t *testing.T) { assert.Equal(t, len(photos), len(photos2)) - var f3 form.PhotoSearchGeo + var f3 form.SearchGeo f3.Person = "Actor A" - photos3, err3 := PhotosGeo(f3) + photos3, err3 := Geo(f3) if err3 != nil { t.Fatal(err3) } - var f4 form.PhotoSearchGeo + var f4 form.SearchGeo f4.Subject = "Actor A" - photos4, err4 := PhotosGeo(f4) + photos4, err4 := Geo(f4) if err4 != nil { t.Fatal(err4) @@ -671,10 +671,10 @@ func TestGeo(t *testing.T) { assert.Equal(t, len(photos3), len(photos4)) assert.Equal(t, len(photos), len(photos4)) - var f5 form.PhotoSearchGeo + var f5 form.SearchGeo f5.Subject = "jqy1y111h1njaaad" - photos5, err5 := PhotosGeo(f5) + photos5, err5 := Geo(f5) if err5 != nil { t.Fatal(err5) @@ -684,11 +684,11 @@ func TestGeo(t *testing.T) { }) t.Run("f.Scan = true", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Scan = true - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -702,11 +702,11 @@ func TestGeo(t *testing.T) { } }) t.Run("f.Panorama = true", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Panorama = true - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -720,11 +720,11 @@ func TestGeo(t *testing.T) { } }) t.Run("f.Raw = true", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Raw = true - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -738,11 +738,11 @@ func TestGeo(t *testing.T) { } }) t.Run("f.Live = true", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Live = true - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -756,11 +756,11 @@ func TestGeo(t *testing.T) { } }) t.Run("f.Title = phototobebatchapproved2", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Title = "phototobebatchapproved2" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) @@ -774,11 +774,11 @@ func TestGeo(t *testing.T) { } }) t.Run("f.Query = p", func(t *testing.T) { - var frm form.PhotoSearchGeo + var frm form.SearchGeo frm.Query = "p" frm.Title = "" - photos, err := PhotosGeo(frm) + photos, err := Geo(frm) if err != nil { t.Fatal(err) diff --git a/internal/search/photos.go b/internal/search/photos.go index b0c31a062..561183826 100644 --- a/internal/search/photos.go +++ b/internal/search/photos.go @@ -17,7 +17,7 @@ import ( ) // Photos searches for photos based on a Form and returns PhotoResults ([]Photo). -func Photos(f form.PhotoSearch) (results PhotoResults, count int, err error) { +func Photos(f form.SearchPhotos) (results PhotoResults, count int, err error) { start := time.Now() if err := f.ParseQueryString(); err != nil { @@ -75,6 +75,7 @@ func Photos(f form.PhotoSearch) (results PhotoResults, count int, err error) { s = s.Order("taken_at DESC, photos.photo_uid, files.file_primary DESC") } + // Include hidden files? if !f.Hidden { s = s.Where("files.file_type = 'jpg' OR files.file_video = 1") diff --git a/internal/search/photos_geo_result.go b/internal/search/photos_geo_result.go deleted file mode 100644 index 7b8893194..000000000 --- a/internal/search/photos_geo_result.go +++ /dev/null @@ -1,31 +0,0 @@ -package search - -import ( - "time" -) - -// GeoResult represents a photo geo json search result. -type GeoResult struct { - ID string `json:"-"` - PhotoUID string `json:"UID"` - PhotoType string `json:"Type,omitempty"` - PhotoLat float32 `json:"Lat"` - PhotoLng float32 `json:"Lng"` - PhotoTitle string `json:"Title"` - PhotoDescription string `json:"Description,omitempty"` - PhotoFavorite bool `json:"Favorite,omitempty"` - FileHash string `json:"Hash"` - FileWidth int `json:"Width"` - FileHeight int `json:"Height"` - TakenAt time.Time `json:"TakenAt"` -} - -func (g GeoResult) Lat() float64 { - return float64(g.PhotoLat) -} - -func (g GeoResult) Lng() float64 { - return float64(g.PhotoLng) -} - -type GeoResults []GeoResult diff --git a/internal/search/photos_test.go b/internal/search/photos_test.go index 0b91d4072..027a46a2d 100644 --- a/internal/search/photos_test.go +++ b/internal/search/photos_test.go @@ -13,7 +13,7 @@ import ( func TestPhotos(t *testing.T) { t.Run("Chinese", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "张" frm.Count = 10 @@ -24,7 +24,7 @@ func TestPhotos(t *testing.T) { assert.NoError(t, err) }) t.Run("UnknownFaces", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "" frm.Face = "None" @@ -37,7 +37,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(results)) }) t.Run("search all", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "" frm.Count = 10 @@ -63,7 +63,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("search for ID and merged", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "" frm.Count = 5000 @@ -79,7 +79,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for ID with merged false", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "" frm.Count = 5000 @@ -95,7 +95,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("label query dog", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "label:dog" frm.Count = 10 @@ -108,7 +108,7 @@ func TestPhotos(t *testing.T) { assert.Equal(t, 0, count) }) t.Run("label query landscape", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "label:landscape Order:relevance" frm.Count = 10 @@ -122,7 +122,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 2, len(photos)) }) t.Run("invalid label query", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "label:xxx" frm.Count = 10 @@ -135,7 +135,7 @@ func TestPhotos(t *testing.T) { assert.Equal(t, 0, count) }) t.Run("form.location true", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "" frm.Count = 10 @@ -151,7 +151,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 2, len(photos)) }) t.Run("form.location true and keyword", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "bridge" frm.Count = 10 @@ -168,7 +168,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for keyword", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "bridge" frm.Count = 5000 @@ -183,7 +183,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 2, len(photos)) }) t.Run("search for label in query", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "flower" frm.Count = 5000 @@ -198,7 +198,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for archived", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Count = 5000 @@ -213,7 +213,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for private", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Count = 5000 @@ -229,7 +229,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for public", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Count = 5000 @@ -244,7 +244,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 3, len(photos)) }) t.Run("search for review", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Count = 5000 @@ -259,7 +259,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for quality", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Count = 5000 @@ -275,7 +275,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for file error", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Count = 5000 @@ -290,7 +290,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("form.camera", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Count = 10 @@ -306,7 +306,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 3, len(photos)) }) t.Run("form.color", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Count = 3 f.Offset = 0 @@ -321,7 +321,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("form.favorites", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "favorite:true" f.Count = 10 f.Offset = 0 @@ -335,7 +335,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("form.country", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "country:zz" f.Count = 10 f.Offset = 0 @@ -350,7 +350,7 @@ func TestPhotos(t *testing.T) { }) t.Run("form.title", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "title:Neckarbrücke" f.Count = 10 f.Offset = 0 @@ -366,7 +366,7 @@ func TestPhotos(t *testing.T) { }) t.Run("form.keywords", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "keywords:bridge" f.Count = 10 f.Offset = 0 @@ -381,7 +381,7 @@ func TestPhotos(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 4) }) t.Run("form.face", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "face:PN6QO5INYTUSAATOFL43LL2ABAV5ACZK" f.Count = 10 f.Offset = 0 @@ -396,7 +396,7 @@ func TestPhotos(t *testing.T) { assert.Equal(t, 1, len(photos)) }) t.Run("form.subject", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "subject:jqu0xs11qekk9jx8" f.Count = 10 f.Offset = 0 @@ -411,7 +411,7 @@ func TestPhotos(t *testing.T) { assert.Equal(t, 1, len(photos)) }) t.Run("form.subjects", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "subjects:John" f.Count = 10 f.Offset = 0 @@ -426,7 +426,7 @@ func TestPhotos(t *testing.T) { assert.Equal(t, 1, len(photos)) }) t.Run("form.people", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "people:John" f.Count = 10 f.Offset = 0 @@ -441,7 +441,7 @@ func TestPhotos(t *testing.T) { assert.Equal(t, 1, len(photos)) }) t.Run("form.hash", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "hash:2cad9168fa6acc5c5c2965ddf6ec465ca42fd818" f.Count = 3 f.Offset = 0 @@ -457,7 +457,7 @@ func TestPhotos(t *testing.T) { }) t.Run("form.portrait", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "portrait:true" f.Count = 10 f.Offset = 0 @@ -473,7 +473,7 @@ func TestPhotos(t *testing.T) { }) t.Run("form.mono", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "mono:true" f.Count = 10 f.Offset = 0 @@ -488,7 +488,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("form.chroma >9 Order:similar", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "chroma:25 Order:similar" f.Count = 3 f.Offset = 0 @@ -503,7 +503,7 @@ func TestPhotos(t *testing.T) { }) t.Run("form.chroma <9", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "chroma:4" f.Count = 3 f.Offset = 0 @@ -519,7 +519,7 @@ func TestPhotos(t *testing.T) { }) t.Run("form.fmin and Order:oldest", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "Fmin:5 Order:oldest" f.Count = 10 f.Offset = 0 @@ -533,7 +533,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("form.fmax and Order:newest", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "Fmax:2 Order:newest" f.Count = 10 f.Offset = 0 @@ -548,7 +548,7 @@ func TestPhotos(t *testing.T) { }) t.Run("form.Lat and form.Lng and Order:imported", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "Lat:33.45343166666667 Lng:25.764711666666667 Dist:2000 Order:imported" f.Count = 10 f.Offset = 0 @@ -562,7 +562,7 @@ func TestPhotos(t *testing.T) { }) t.Run("form.Lat and form.Lng and Order:imported Dist:6000", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "Lat:33.45343166666667 Lng:25.764711666666667 Dist:6000 Order:imported" f.Count = 10 f.Offset = 0 @@ -576,7 +576,7 @@ func TestPhotos(t *testing.T) { }) t.Run("form.Before and form.After Order:relevance", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "Before:2016-01-01 After:2013-01-01 Order:relevance" f.Count = 5000 f.Offset = 0 @@ -591,7 +591,7 @@ func TestPhotos(t *testing.T) { }) t.Run("search for diff", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "Diff:800" f.Count = 5000 f.Offset = 0 @@ -604,7 +604,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for lens, month, year, album", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Count = 5000 f.Offset = 0 @@ -621,7 +621,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("albums", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Albums = "Berlin" @@ -633,7 +633,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("f.album", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "" f.Album = "Berlin" @@ -645,7 +645,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for state", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.State = "KwaZulu-Natal" photos, _, err := Photos(f) @@ -657,7 +657,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("search for category", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Category = "botanical garden" photos, _, err := Photos(f) @@ -670,7 +670,7 @@ func TestPhotos(t *testing.T) { }) t.Run("search for labels", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Label = "botanical-garden|nature|landscape|park" photos, _, err := Photos(f) @@ -683,7 +683,7 @@ func TestPhotos(t *testing.T) { }) t.Run("search for primary files", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Primary = true photos, _, err := Photos(f) @@ -696,7 +696,7 @@ func TestPhotos(t *testing.T) { }) t.Run("search for landscape", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "landscape" photos, _, err := Photos(f) @@ -709,7 +709,7 @@ func TestPhotos(t *testing.T) { }) t.Run("search with multiple parameters", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Hidden = true f.Scan = true f.Year = "2010" @@ -729,7 +729,7 @@ func TestPhotos(t *testing.T) { assert.IsType(t, PhotoResults{}, photos) }) t.Run("search with multiple parameters", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Hidden = true f.Scan = true f.Year = strconv.Itoa(2010) @@ -753,7 +753,7 @@ func TestPhotos(t *testing.T) { assert.IsType(t, PhotoResults{}, photos) }) t.Run("search all recently edited", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "" frm.Count = 10 @@ -780,7 +780,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("search unstacked panoramas", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "" frm.Count = 10 @@ -809,7 +809,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("Or search", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "" frm.Count = 10 @@ -839,7 +839,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("faces", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "faces:true" f.Count = 10 f.Offset = 0 @@ -853,7 +853,7 @@ func TestPhotos(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 3) }) t.Run("faces:yes", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Faces = "Yes" photos, _, err := Photos(f) @@ -865,7 +865,7 @@ func TestPhotos(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 3) }) t.Run("faces:no", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Faces = "No" photos, _, err := Photos(f) @@ -877,7 +877,7 @@ func TestPhotos(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 9) }) t.Run("f.face yes", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Face = "yes" photos, _, err := Photos(f) @@ -889,7 +889,7 @@ func TestPhotos(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 9) }) t.Run("faces:2", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Faces = "2" photos, _, err := Photos(f) @@ -901,7 +901,7 @@ func TestPhotos(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 1) }) t.Run("Subject", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Subject = "jqu0xs11qekk9jx8" frm.Count = 10 @@ -927,7 +927,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("NewFaces", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Face = "new" frm.Count = 10 @@ -942,7 +942,7 @@ func TestPhotos(t *testing.T) { assert.LessOrEqual(t, 1, len(photos)) }) t.Run("query: videos", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "videos" frm.Count = 10 @@ -968,7 +968,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: video", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "video" frm.Count = 10 @@ -994,7 +994,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: live", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "live" frm.Count = 10 @@ -1020,7 +1020,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("f.live", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Live = true frm.Query = "" @@ -1047,7 +1047,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: raws", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "raws" frm.Count = 10 @@ -1073,7 +1073,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("f.Raw", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Raw = true frm.Query = "" @@ -1100,7 +1100,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: faces", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "faces" frm.Count = 10 @@ -1125,7 +1125,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: faces", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "faces:new" frm.Face = "" @@ -1151,7 +1151,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: people", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "people" frm.Count = 10 @@ -1177,7 +1177,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: favorites", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "favorites" frm.Count = 10 @@ -1203,7 +1203,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: stacks", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "stacks" frm.Count = 10 @@ -1228,7 +1228,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: panoramas", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "panoramas" frm.Count = 10 @@ -1254,7 +1254,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: scans", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "scans" frm.Count = 10 @@ -1280,7 +1280,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: monochrome", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "monochrome" frm.Count = 10 @@ -1305,7 +1305,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("query: mono", func(t *testing.T) { - var frm form.PhotoSearch + var frm form.SearchPhotos frm.Query = "mono" frm.Count = 10 @@ -1330,7 +1330,7 @@ func TestPhotos(t *testing.T) { } }) t.Run("filename", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Filename = "1990/04/Quality1FavoriteTrue.jpg" photos, _, err := Photos(f) @@ -1342,7 +1342,7 @@ func TestPhotos(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 1) }) t.Run("original name or original name", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Original = "my-videos/IMG_88888" + "|" + "Vacation/exampleFileNameOriginal" photos, _, err := Photos(f) @@ -1354,7 +1354,7 @@ func TestPhotos(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 2) }) t.Run("Stack", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Stack = true photos, _, err := Photos(f) @@ -1366,7 +1366,7 @@ func TestPhotos(t *testing.T) { assert.GreaterOrEqual(t, len(photos), 2) }) t.Run("keywords:kuh|bridge > keywords:bridge&kuh", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "keywords:kuh|bridge" photos, _, err := Photos(f) @@ -1386,7 +1386,7 @@ func TestPhotos(t *testing.T) { assert.Greater(t, len(photos), len(photos2)) }) t.Run("albums and and or search", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "albums:Holiday|Berlin" photos, _, err := Photos(f) @@ -1405,7 +1405,7 @@ func TestPhotos(t *testing.T) { assert.Greater(t, len(photos), len(photos2)) }) t.Run("people and and or search", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.People = "Actor A|Actress A" photos, _, err := Photos(f) @@ -1425,7 +1425,7 @@ func TestPhotos(t *testing.T) { assert.Greater(t, len(photos), len(photos2)) }) t.Run("people = subjects & person = subject", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.People = "Actor" photos, _, err := Photos(f) @@ -1433,7 +1433,7 @@ func TestPhotos(t *testing.T) { if err != nil { t.Fatal(err) } - var f2 form.PhotoSearch + var f2 form.SearchPhotos f2.Subjects = "Actor" @@ -1445,7 +1445,7 @@ func TestPhotos(t *testing.T) { assert.Equal(t, len(photos), len(photos2)) - var f3 form.PhotoSearch + var f3 form.SearchPhotos f3.Person = "Actor A" @@ -1455,7 +1455,7 @@ func TestPhotos(t *testing.T) { t.Fatal(err3) } - var f4 form.PhotoSearch + var f4 form.SearchPhotos f4.Subject = "Actor A" photos4, _, err4 := Photos(f4) @@ -1469,7 +1469,7 @@ func TestPhotos(t *testing.T) { }) t.Run("Search in Title", func(t *testing.T) { - var f form.PhotoSearch + var f form.SearchPhotos f.Query = "N" f.Title = "" f.Count = 10 diff --git a/internal/server/routes.go b/internal/server/routes.go index 32a78518e..86bbbe9a6 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -72,7 +72,7 @@ func registerRoutes(router *gin.Engine, conf *config.Config) { // Photos. api.SearchPhotos(v1) - api.SearchPhotosGeo(v1) + api.SearchGeo(v1) api.GetPhoto(v1) api.GetPhotoYaml(v1) api.UpdatePhoto(v1)