Places: Refactor GeoJSON API endpoint

This commit is contained in:
Michael Mayer 2021-11-26 13:59:10 +01:00
parent 34e1773595
commit 3e6c7052bb
25 changed files with 412 additions and 387 deletions

View file

@ -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 {

View file

@ -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)
})
}

View file

@ -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)
})
}

View file

@ -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)

View file

@ -35,7 +35,7 @@ func SearchPhotos(router *gin.RouterGroup) {
return
}
var f form.PhotoSearch
var f form.SearchPhotos
err := c.MustBindWith(&f, binding.Form)

View file

@ -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

View file

@ -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,

View file

@ -159,7 +159,7 @@ func (m *Folder) Create() error {
return nil
}
f := form.PhotoSearch{
f := form.SearchPhotos{
Path: m.Path,
Public: true,
}

View file

@ -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"`

View file

@ -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}
}

View file

@ -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)
}

View file

@ -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}
}

View file

@ -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,

View file

@ -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,
}

View file

@ -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

View file

@ -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,

View file

@ -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 {

View file

@ -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()
}

View file

@ -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)

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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)