Search: Improve "year", "month", and "day" filters
This commit is contained in:
parent
a66de2e822
commit
070efcbc66
12 changed files with 102 additions and 35 deletions
|
@ -3,6 +3,7 @@ package entity
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -199,8 +200,8 @@ func NewMonthAlbum(albumTitle, albumSlug string, year, month int) *Album {
|
|||
}
|
||||
|
||||
f := form.PhotoSearch{
|
||||
Year: year,
|
||||
Month: month,
|
||||
Year: strconv.Itoa(year),
|
||||
Month: strconv.Itoa(month),
|
||||
Public: true,
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,9 @@ type PhotoSearch struct {
|
|||
Category string `form:"category"` // Moments
|
||||
Country string `form:"country"` // Moments
|
||||
State string `form:"state"` // Moments
|
||||
Year int `form:"year"` // Moments
|
||||
Month int `form:"month"` // Moments
|
||||
Day int `form:"day"` // Moments
|
||||
Year string `form:"year"` // Moments
|
||||
Month string `form:"month"` // Moments
|
||||
Day string `form:"day"` // Moments
|
||||
Face string `form:"face"` // UIDs
|
||||
Subject string `form:"subject"` // UIDs
|
||||
Person string `form:"person"` // Alias for Subject
|
||||
|
|
|
@ -34,9 +34,9 @@ type PhotoSearchGeo struct {
|
|||
Album string `form:"album"`
|
||||
Albums string `form:"albums"`
|
||||
Country string `form:"country"`
|
||||
Year int `form:"year"` // Moments
|
||||
Month int `form:"month"` // Moments
|
||||
Day int `form:"day"` // Moments
|
||||
Year string `form:"year"` // Moments
|
||||
Month string `form:"month"` // Moments
|
||||
Day string `form:"day"` // Moments
|
||||
Color string `form:"color"`
|
||||
Camera int `form:"camera"`
|
||||
Lens int `form:"lens"`
|
||||
|
|
|
@ -244,7 +244,7 @@ func TestPhotoSearch_Serialize(t *testing.T) {
|
|||
Photo: false,
|
||||
Lat: 1.5,
|
||||
Lng: -10.33333,
|
||||
Year: 2002,
|
||||
Year: "2002",
|
||||
Chroma: 1,
|
||||
Diff: 424242,
|
||||
Before: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC),
|
||||
|
@ -264,7 +264,7 @@ func TestPhotoSearch_SerializeAll(t *testing.T) {
|
|||
Photo: false,
|
||||
Lat: 1.5,
|
||||
Lng: -10.33333,
|
||||
Year: 2002,
|
||||
Year: "2002|2003",
|
||||
Chroma: 1,
|
||||
Diff: 424242,
|
||||
Before: time.Date(2019, 01, 15, 0, 0, 0, 0, time.UTC),
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"math"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
|
@ -126,7 +127,7 @@ func (w *Moments) Start() (err error) {
|
|||
for _, mom := range results {
|
||||
f := form.PhotoSearch{
|
||||
Country: mom.Country,
|
||||
Year: mom.Year,
|
||||
Year: strconv.Itoa(mom.Year),
|
||||
Public: true,
|
||||
}
|
||||
|
||||
|
|
|
@ -201,3 +201,37 @@ func AnySlug(col, search, sep string) (where string) {
|
|||
|
||||
return strings.Join(wheres, " OR ")
|
||||
}
|
||||
|
||||
// AnyInt returns a where condition that matches any integer within a range.
|
||||
func AnyInt(col, numbers, sep string, min, max int) (where string) {
|
||||
if numbers == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
if sep == "" {
|
||||
sep = txt.Or
|
||||
}
|
||||
|
||||
var matches []int
|
||||
var wheres []string
|
||||
|
||||
for _, n := range strings.Split(numbers, sep) {
|
||||
i := txt.Int(n)
|
||||
|
||||
if i == 0 || i < min || i > max {
|
||||
continue
|
||||
}
|
||||
|
||||
matches = append(matches, i)
|
||||
}
|
||||
|
||||
if len(matches) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, n := range matches {
|
||||
wheres = append(wheres, fmt.Sprintf("%s = %d", col, n))
|
||||
}
|
||||
|
||||
return strings.Join(wheres, " OR ")
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package search
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -267,3 +269,30 @@ func TestAnySlug(t *testing.T) {
|
|||
assert.Equal(t, "custom_slug = '' OR custom_slug = ''", where)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAnyInt(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
where := AnyInt("photos.photo_month", "", txt.Or, entity.UnknownMonth, txt.MonthMax)
|
||||
assert.Equal(t, "", where)
|
||||
})
|
||||
|
||||
t.Run("Range", func(t *testing.T) {
|
||||
where := AnyInt("photos.photo_month", "-3|0|10|9|11|12|13", txt.Or, entity.UnknownMonth, txt.MonthMax)
|
||||
assert.Equal(t, "photos.photo_month = 10 OR photos.photo_month = 9 OR photos.photo_month = 11 OR photos.photo_month = 12", where)
|
||||
})
|
||||
|
||||
t.Run("Chars", func(t *testing.T) {
|
||||
where := AnyInt("photos.photo_month", "a|b|c", txt.Or, entity.UnknownMonth, txt.MonthMax)
|
||||
assert.Equal(t, "", where)
|
||||
})
|
||||
|
||||
t.Run("CommaSeparated", func(t *testing.T) {
|
||||
where := AnyInt("photos.photo_month", "-3,10,9,11,12,13", ",", entity.UnknownMonth, txt.MonthMax)
|
||||
assert.Equal(t, "photos.photo_month = 10 OR photos.photo_month = 9 OR photos.photo_month = 11 OR photos.photo_month = 12", where)
|
||||
})
|
||||
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
where := AnyInt("photos.photo_month", " , | ", ",", entity.UnknownMonth, txt.MonthMax)
|
||||
assert.Equal(t, "", where)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -284,18 +284,18 @@ func Photos(f form.PhotoSearch) (results PhotoResults, count int, err error) {
|
|||
}
|
||||
|
||||
// Filter by year?
|
||||
if (f.Year > 0 && f.Year <= txt.YearMax) || f.Year == entity.UnknownYear {
|
||||
s = s.Where("photos.photo_year = ?", f.Year)
|
||||
if f.Year != "" {
|
||||
s = s.Where(AnyInt("photos.photo_year", f.Year, txt.Or, entity.UnknownYear, txt.YearMax))
|
||||
}
|
||||
|
||||
// Filter by month?
|
||||
if (f.Month >= txt.MonthMin && f.Month <= txt.MonthMax) || f.Month == entity.UnknownMonth {
|
||||
s = s.Where("photos.photo_month = ?", f.Month)
|
||||
if f.Month != "" {
|
||||
s = s.Where(AnyInt("photos.photo_month", f.Month, txt.Or, entity.UnknownMonth, txt.MonthMax))
|
||||
}
|
||||
|
||||
// Filter by day?
|
||||
if (f.Day >= txt.DayMin && f.Month <= txt.DayMax) || f.Day == entity.UnknownDay {
|
||||
s = s.Where("photos.photo_day = ?", f.Day)
|
||||
if f.Day != "" {
|
||||
s = s.Where(AnyInt("photos.photo_day", f.Day, txt.Or, entity.UnknownDay, txt.DayMax))
|
||||
}
|
||||
|
||||
// Find or exclude people if detected.
|
||||
|
|
|
@ -165,18 +165,18 @@ func PhotosGeo(f form.PhotoSearchGeo) (results GeoResults, err error) {
|
|||
}
|
||||
|
||||
// Filter by year?
|
||||
if (f.Year > 0 && f.Year <= txt.YearMax) || f.Year == entity.UnknownYear {
|
||||
s = s.Where("photos.photo_year = ?", f.Year)
|
||||
if f.Year != "" {
|
||||
s = s.Where(AnyInt("photos.photo_year", f.Year, txt.Or, entity.UnknownYear, txt.YearMax))
|
||||
}
|
||||
|
||||
// Filter by month?
|
||||
if (f.Month >= txt.MonthMin && f.Month <= txt.MonthMax) || f.Month == entity.UnknownMonth {
|
||||
s = s.Where("photos.photo_month = ?", f.Month)
|
||||
if f.Month != "" {
|
||||
s = s.Where(AnyInt("photos.photo_month", f.Month, txt.Or, entity.UnknownMonth, txt.MonthMax))
|
||||
}
|
||||
|
||||
// Filter by day?
|
||||
if (f.Day >= txt.DayMin && f.Month <= txt.DayMax) || f.Day == entity.UnknownDay {
|
||||
s = s.Where("photos.photo_day = ?", f.Day)
|
||||
if f.Day != "" {
|
||||
s = s.Where(AnyInt("photos.photo_day", f.Day, txt.Or, entity.UnknownDay, txt.DayMax))
|
||||
}
|
||||
|
||||
// Find or exclude people if detected.
|
||||
|
|
|
@ -179,8 +179,8 @@ func TestGeo(t *testing.T) {
|
|||
Album: "test",
|
||||
Camera: 123,
|
||||
Lens: 123,
|
||||
Year: 2010,
|
||||
Month: 12,
|
||||
Year: "2010",
|
||||
Month: "12",
|
||||
Color: "red",
|
||||
Country: entity.UnknownID,
|
||||
Type: "jpg",
|
||||
|
@ -282,8 +282,8 @@ func TestGeo(t *testing.T) {
|
|||
})
|
||||
t.Run("day", func(t *testing.T) {
|
||||
var f form.PhotoSearchGeo
|
||||
f.Day = 18
|
||||
f.Month = 4
|
||||
f.Day = "18"
|
||||
f.Month = "4"
|
||||
|
||||
photos, err := PhotosGeo(f)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package search
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
|
@ -597,8 +598,8 @@ func TestPhotos(t *testing.T) {
|
|||
f.Count = 5000
|
||||
f.Offset = 0
|
||||
f.Lens = 1000000
|
||||
f.Month = 7
|
||||
f.Year = 2790
|
||||
f.Month = strconv.Itoa(7)
|
||||
f.Year = strconv.Itoa(2790)
|
||||
f.Album = "at9lxuqxpogaaba8"
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
@ -688,8 +689,8 @@ func TestPhotos(t *testing.T) {
|
|||
var f form.PhotoSearch
|
||||
f.Hidden = true
|
||||
f.Scan = true
|
||||
f.Year = 2010
|
||||
f.Day = 1
|
||||
f.Year = "2010"
|
||||
f.Day = "1"
|
||||
f.Photo = true
|
||||
f.Name = "xxx"
|
||||
f.Original = "xxyy"
|
||||
|
@ -708,8 +709,8 @@ func TestPhotos(t *testing.T) {
|
|||
var f form.PhotoSearch
|
||||
f.Hidden = true
|
||||
f.Scan = true
|
||||
f.Year = 2010
|
||||
f.Day = 1
|
||||
f.Year = strconv.Itoa(2010)
|
||||
f.Day = strconv.Itoa(1)
|
||||
f.Video = true
|
||||
f.Name = "xxx"
|
||||
f.Original = "xxyy"
|
||||
|
|
|
@ -2,6 +2,7 @@ package txt
|
|||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Int converts a string to a signed integer or 0 if invalid.
|
||||
|
@ -10,7 +11,7 @@ func Int(s string) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
result, err := strconv.ParseInt(s, 10, 32)
|
||||
result, err := strconv.ParseInt(strings.TrimSpace(s), 10, 32)
|
||||
|
||||
if err != nil {
|
||||
return 0
|
||||
|
@ -25,7 +26,7 @@ func UInt(s string) uint {
|
|||
return 0
|
||||
}
|
||||
|
||||
result, err := strconv.ParseInt(s, 10, 32)
|
||||
result, err := strconv.ParseInt(strings.TrimSpace(s), 10, 32)
|
||||
|
||||
if err != nil || result < 0 {
|
||||
return 0
|
||||
|
|
Loading…
Reference in a new issue