2021-09-18 15:32:39 +02:00
package search
2020-05-08 15:41:01 +02:00
import (
"fmt"
"strings"
"time"
2020-12-15 20:14:06 +01:00
"github.com/photoprism/photoprism/pkg/fs"
2020-05-11 14:49:00 +02:00
"github.com/jinzhu/gorm"
2020-05-08 15:41:01 +02:00
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/pkg/txt"
)
2021-09-18 15:32:39 +02:00
// Photos searches for photos based on a Form and returns PhotoResults ([]Photo).
func Photos ( f form . PhotoSearch ) ( results PhotoResults , count int , err error ) {
2020-05-23 20:58:58 +02:00
start := time . Now ( )
2020-05-08 15:41:01 +02:00
if err := f . ParseQueryString ( ) ; err != nil {
return results , 0 , err
}
s := UnscopedDb ( )
// s.LogMode(true)
2021-01-20 12:08:48 +01:00
// Base query.
2020-05-08 15:41:01 +02:00
s = s . Table ( "photos" ) .
2021-01-10 15:30:12 +01:00
Select ( ` photos . * , photos . id AS composite_id ,
2021-01-27 21:30:10 +01:00
files . id AS file_id , files . file_uid , files . instance_id , files . file_primary , files . file_sidecar ,
files . file_portrait , files . file_video , files . file_missing , files . file_name , files . file_root , files . file_hash ,
files . file_codec , files . file_type , files . file_mime , files . file_width , files . file_height ,
files . file_aspect_ratio , files . file_orientation , files . file_main_color , files . file_colors , files . file_luminance ,
files . file_chroma , files . file_projection , files . file_diff , files . file_duration , files . file_size ,
2020-05-08 15:41:01 +02:00
cameras . camera_make , cameras . camera_model ,
lenses . lens_make , lenses . lens_model ,
2020-07-12 08:27:05 +02:00
places . place_label , places . place_city , places . place_state , places . place_country ` ) .
2020-05-14 19:03:12 +02:00
Joins ( "JOIN files ON photos.id = files.photo_id AND files.file_missing = 0 AND files.deleted_at IS NULL" ) .
2021-01-10 12:38:51 +01:00
Joins ( "LEFT JOIN cameras ON photos.camera_id = cameras.id" ) .
Joins ( "LEFT JOIN lenses ON photos.lens_id = lenses.id" ) .
Joins ( "LEFT JOIN places ON photos.place_id = places.id" )
2020-06-04 17:06:42 +02:00
2021-01-20 12:08:48 +01:00
// Limit result count.
if f . Count > 0 && f . Count <= MaxResults {
s = s . Limit ( f . Count ) . Offset ( f . Offset )
} else {
s = s . Limit ( MaxResults ) . Offset ( f . Offset )
}
// Set sort order.
switch f . Order {
case entity . SortOrderEdited :
s = s . Where ( "edited_at IS NOT NULL" ) . Order ( "edited_at DESC, photos.photo_uid, files.file_primary DESC" )
case entity . SortOrderRelevance :
if f . Label != "" {
s = s . Order ( "photo_quality DESC, photos_labels.uncertainty ASC, taken_at DESC, files.file_primary DESC" )
} else {
s = s . Order ( "photo_quality DESC, taken_at DESC, files.file_primary DESC" )
}
case entity . SortOrderNewest :
s = s . Order ( "taken_at DESC, photos.photo_uid, files.file_primary DESC" )
case entity . SortOrderOldest :
s = s . Order ( "taken_at, photos.photo_uid, files.file_primary DESC" )
case entity . SortOrderAdded :
s = s . Order ( "photos.id DESC, files.file_primary DESC" )
case entity . SortOrderSimilar :
s = s . Where ( "files.file_diff > 0" )
s = s . Order ( "photos.photo_color, photos.cell_id, files.file_diff, taken_at DESC, files.file_primary DESC" )
case entity . SortOrderName :
s = s . Order ( "photos.photo_path, photos.photo_name, files.file_primary DESC" )
default :
s = s . Order ( "taken_at DESC, photos.photo_uid, files.file_primary DESC" )
}
2020-06-04 17:06:42 +02:00
if ! f . Hidden {
s = s . Where ( "files.file_type = 'jpg' OR files.file_video = 1" )
if f . Error {
s = s . Where ( "files.file_error <> ''" )
} else {
s = s . Where ( "files.file_error = ''" )
}
}
2020-05-08 15:41:01 +02:00
2020-06-28 15:23:15 +02:00
// Return primary files only.
if f . Primary {
s = s . Where ( "files.file_primary = 1" )
}
2020-05-14 19:03:12 +02:00
// Shortcut for known photo ids.
2020-05-08 15:41:01 +02:00
if f . ID != "" {
2021-09-18 15:32:39 +02:00
s = s . Where ( "photos.photo_uid IN (?)" , strings . Split ( f . ID , txt . Or ) )
2020-05-08 15:41:01 +02:00
s = s . Order ( "files.file_primary DESC" )
if result := s . Scan ( & results ) ; result . Error != nil {
return results , 0 , result . Error
}
2020-05-23 20:58:58 +02:00
log . Infof ( "photos: found %d results for %s [%s]" , len ( results ) , f . SerializeAll ( ) , time . Since ( start ) )
2020-05-08 15:41:01 +02:00
if f . Merged {
return results . Merged ( )
}
return results , len ( results ) , nil
}
2020-05-14 19:03:12 +02:00
// Filter by label, label category and keywords.
2020-05-08 15:41:01 +02:00
var categories [ ] entity . Category
2020-05-11 14:49:00 +02:00
var labels [ ] entity . Label
2020-05-08 15:41:01 +02:00
var labelIds [ ] uint
if f . Label != "" {
2021-09-18 15:32:39 +02:00
if err := Db ( ) . Where ( AnySlug ( "label_slug" , f . Label , txt . Or ) ) . Or ( AnySlug ( "custom_slug" , f . Label , txt . Or ) ) . Find ( & labels ) . Error ; len ( labels ) == 0 || err != nil {
2020-05-29 18:04:30 +02:00
log . Errorf ( "search: labels %s not found" , txt . Quote ( f . Label ) )
return results , 0 , fmt . Errorf ( "%s not found" , txt . Quote ( f . Label ) )
2020-05-08 15:41:01 +02:00
} else {
2020-05-29 18:04:30 +02:00
for _ , l := range labels {
labelIds = append ( labelIds , l . ID )
Db ( ) . Where ( "category_id = ?" , l . ID ) . Find ( & categories )
2020-05-08 15:41:01 +02:00
2020-05-29 18:04:30 +02:00
log . Infof ( "search: label %s includes %d categories" , txt . Quote ( l . LabelName ) , len ( categories ) )
2020-05-08 15:41:01 +02:00
2020-05-29 18:04:30 +02:00
for _ , category := range categories {
labelIds = append ( labelIds , category . LabelID )
}
2020-05-08 15:41:01 +02:00
}
2020-05-29 18:04:30 +02:00
s = s . Joins ( "JOIN photos_labels ON photos_labels.photo_id = photos.id AND photos_labels.uncertainty < 100 AND photos_labels.label_id IN (?)" , labelIds ) .
Group ( "photos.id, files.id" )
2020-05-08 15:41:01 +02:00
}
}
2021-09-06 14:16:46 +02:00
// Clip to reasonable size and normalize operators.
2021-09-18 15:32:39 +02:00
f . Query = txt . NormalizeQuery ( f . Query )
2021-08-30 11:26:57 +02:00
// Modify query if it contains subject names.
if f . Query != "" && f . Subject == "" {
2021-09-18 15:32:39 +02:00
if subj , names , remaining := SubjectUIDs ( f . Query ) ; len ( subj ) > 0 {
f . Subject = strings . Join ( subj , txt . And )
2021-09-18 21:40:57 +02:00
log . Debugf ( "people: searching for %s" , txt . Quote ( txt . JoinNames ( names , false ) ) )
2021-08-30 11:26:57 +02:00
f . Query = remaining
}
}
2021-09-03 20:14:11 +02:00
// Set search filters based on search terms.
2021-09-17 15:52:25 +02:00
if terms := txt . SearchTerms ( f . Query ) ; f . Query != "" && len ( terms ) == 0 {
f . Name = fs . StripKnownExt ( f . Query ) + "*"
f . Query = ""
} else if len ( terms ) > 0 {
2021-09-03 20:14:11 +02:00
switch {
case terms [ "faces" ] :
f . Query = strings . ReplaceAll ( f . Query , "faces" , "" )
f . Faces = "true"
2021-09-06 15:42:30 +02:00
case terms [ "people" ] :
f . Query = strings . ReplaceAll ( f . Query , "people" , "" )
f . Faces = "true"
2021-09-03 20:14:11 +02:00
case terms [ "videos" ] :
f . Query = strings . ReplaceAll ( f . Query , "videos" , "" )
f . Video = true
case terms [ "favorites" ] :
f . Query = strings . ReplaceAll ( f . Query , "favorites" , "" )
f . Favorite = true
case terms [ "stacks" ] :
f . Query = strings . ReplaceAll ( f . Query , "stacks" , "" )
f . Stack = true
case terms [ "panoramas" ] :
f . Query = strings . ReplaceAll ( f . Query , "panoramas" , "" )
f . Panorama = true
case terms [ "scans" ] :
f . Query = strings . ReplaceAll ( f . Query , "scans" , "" )
f . Scan = true
case terms [ "monochrome" ] :
f . Query = strings . ReplaceAll ( f . Query , "monochrome" , "" )
f . Mono = true
}
}
2021-08-30 11:26:57 +02:00
// Filter by location?
2020-07-11 23:43:29 +02:00
if f . Geo == true {
2020-07-12 08:27:05 +02:00
s = s . Where ( "photos.cell_id <> 'zz'" )
2020-05-08 15:41:01 +02:00
2021-08-29 19:19:54 +02:00
for _ , where := range LikeAnyKeyword ( "k.keyword" , f . Query ) {
2021-08-29 16:16:49 +02:00
s = s . Where ( "photos.id IN (SELECT pk.photo_id FROM keywords k JOIN photos_keywords pk ON k.id = pk.keyword_id WHERE (?))" , gorm . Expr ( where ) )
2020-05-08 15:41:01 +02:00
}
} else if f . Query != "" {
2020-05-29 18:04:30 +02:00
if err := Db ( ) . Where ( AnySlug ( "custom_slug" , f . Query , " " ) ) . Find ( & labels ) . Error ; len ( labels ) == 0 || err != nil {
2021-08-30 11:26:57 +02:00
log . Debugf ( "search: label %s not found, using fuzzy search" , txt . Quote ( f . Query ) )
2020-05-08 15:41:01 +02:00
2021-08-29 19:19:54 +02:00
for _ , where := range LikeAnyKeyword ( "k.keyword" , f . Query ) {
2021-08-29 16:16:49 +02:00
s = s . Where ( "photos.id IN (SELECT pk.photo_id FROM keywords k JOIN photos_keywords pk ON k.id = pk.keyword_id WHERE (?))" , gorm . Expr ( where ) )
2020-05-11 14:49:00 +02:00
}
2020-05-08 15:41:01 +02:00
} else {
2020-05-11 14:49:00 +02:00
for _ , l := range labels {
labelIds = append ( labelIds , l . ID )
2020-05-08 15:41:01 +02:00
2020-05-11 14:49:00 +02:00
Db ( ) . Where ( "category_id = ?" , l . ID ) . Find ( & categories )
2020-05-08 15:41:01 +02:00
2021-08-30 11:26:57 +02:00
log . Debugf ( "search: label %s includes %d categories" , txt . Quote ( l . LabelName ) , len ( categories ) )
2020-05-08 15:41:01 +02:00
2020-05-11 14:49:00 +02:00
for _ , category := range categories {
labelIds = append ( labelIds , category . LabelID )
}
}
2020-05-08 15:41:01 +02:00
2021-08-29 19:19:54 +02:00
if wheres := LikeAnyKeyword ( "k.keyword" , f . Query ) ; len ( wheres ) > 0 {
2021-08-29 16:16:49 +02:00
for _ , where := range wheres {
s = s . Where ( "photos.id IN (SELECT pk.photo_id FROM keywords k JOIN photos_keywords pk ON k.id = pk.keyword_id WHERE (?)) OR " +
"photos.id IN (SELECT pl.photo_id FROM photos_labels pl WHERE pl.uncertainty < 100 AND pl.label_id IN (?))" , gorm . Expr ( where ) , labelIds )
}
2020-05-11 14:49:00 +02:00
} else {
s = s . Where ( "photos.id IN (SELECT pl.photo_id FROM photos_labels pl WHERE pl.uncertainty < 100 AND pl.label_id IN (?))" , labelIds )
}
2020-05-08 15:41:01 +02:00
}
}
2021-08-29 16:16:49 +02:00
// Search for one or more keywords?
if f . Keywords != "" {
2021-08-29 19:19:54 +02:00
for _ , where := range LikeAllKeywords ( "k.keyword" , f . Keywords ) {
2021-08-29 16:16:49 +02:00
s = s . Where ( "photos.id IN (SELECT pk.photo_id FROM keywords k JOIN photos_keywords pk ON k.id = pk.keyword_id WHERE (?))" , gorm . Expr ( where ) )
}
}
2021-09-18 15:32:39 +02:00
// Filter for one or more faces?
if f . Face != "" {
for _ , f := range strings . Split ( strings . ToUpper ( f . Face ) , txt . And ) {
s = s . Where ( fmt . Sprintf ( "photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 WHERE face_id IN (?))" ,
entity . Marker { } . TableName ( ) ) , strings . Split ( f , txt . Or ) )
}
}
2021-08-29 16:16:49 +02:00
// Filter for one or more subjects?
if f . Subject != "" {
2021-09-18 15:32:39 +02:00
for _ , subj := range strings . Split ( strings . ToLower ( f . Subject ) , txt . And ) {
2021-09-17 14:26:12 +02:00
s = s . Where ( fmt . Sprintf ( "photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 WHERE subj_uid IN (?))" ,
2021-09-18 15:32:39 +02:00
entity . Marker { } . TableName ( ) ) , strings . Split ( subj , txt . Or ) )
2021-08-30 11:56:34 +02:00
}
2021-08-29 16:16:49 +02:00
} else if f . Subjects != "" {
2021-09-17 14:26:12 +02:00
for _ , where := range LikeAnyWord ( "s.subj_name" , f . Subjects ) {
s = s . Where ( fmt . Sprintf ( "photos.id IN (SELECT photo_id FROM files f JOIN %s m ON f.file_uid = m.file_uid AND m.marker_invalid = 0 JOIN %s s ON s.subj_uid = m.subj_uid WHERE (?))" ,
2021-08-29 16:16:49 +02:00
entity . Marker { } . TableName ( ) , entity . Subject { } . TableName ( ) ) , gorm . Expr ( where ) )
}
}
// Filter by status?
2020-06-04 14:56:27 +02:00
if f . Hidden {
s = s . Where ( "photos.photo_quality = -1" )
s = s . Where ( "photos.deleted_at IS NULL" )
} else if f . Archived {
2020-12-15 20:14:06 +01:00
s = s . Where ( "photos.photo_quality > -1" )
2020-05-08 15:41:01 +02:00
s = s . Where ( "photos.deleted_at IS NOT NULL" )
} else {
s = s . Where ( "photos.deleted_at IS NULL" )
if f . Private {
s = s . Where ( "photos.photo_private = 1" )
} else if f . Public {
s = s . Where ( "photos.photo_private = 0" )
}
if f . Review {
s = s . Where ( "photos.photo_quality < 3" )
} else if f . Quality != 0 && f . Private == false {
s = s . Where ( "photos.photo_quality >= ?" , f . Quality )
}
}
2021-08-29 16:16:49 +02:00
// Filter by camera?
2020-05-08 15:41:01 +02:00
if f . Camera > 0 {
s = s . Where ( "photos.camera_id = ?" , f . Camera )
}
2021-08-29 16:16:49 +02:00
// Filter by camera lens?
2020-05-08 15:41:01 +02:00
if f . Lens > 0 {
s = s . Where ( "photos.lens_id = ?" , f . Lens )
}
2021-08-29 16:16:49 +02:00
// Filter by year?
2021-08-16 00:29:36 +02:00
if ( f . Year > 0 && f . Year <= txt . YearMax ) || f . Year == entity . UnknownYear {
2020-05-08 15:41:01 +02:00
s = s . Where ( "photos.photo_year = ?" , f . Year )
}
2021-08-29 16:16:49 +02:00
// Filter by month?
2021-08-16 00:29:36 +02:00
if ( f . Month >= txt . MonthMin && f . Month <= txt . MonthMax ) || f . Month == entity . UnknownMonth {
2020-05-08 15:41:01 +02:00
s = s . Where ( "photos.photo_month = ?" , f . Month )
}
2021-08-29 16:16:49 +02:00
// Filter by day?
2021-08-16 00:29:36 +02:00
if ( f . Day >= txt . DayMin && f . Month <= txt . DayMax ) || f . Day == entity . UnknownDay {
2020-07-06 07:41:33 +02:00
s = s . Where ( "photos.photo_day = ?" , f . Day )
}
2021-05-26 09:51:00 +02:00
// Find or exclude people if detected.
2021-05-26 10:46:32 +02:00
if txt . IsUInt ( f . Faces ) {
s = s . Where ( "photos.photo_faces >= ?" , txt . Int ( f . Faces ) )
} else if txt . Yes ( f . Faces ) {
2021-05-26 09:51:00 +02:00
s = s . Where ( "photos.photo_faces > 0" )
2021-05-26 10:46:32 +02:00
} else if txt . No ( f . Faces ) {
2021-05-26 09:51:00 +02:00
s = s . Where ( "photos.photo_faces = 0" )
2021-05-25 11:55:44 +02:00
}
2020-05-08 15:41:01 +02:00
if f . Color != "" {
2021-09-18 15:32:39 +02:00
s = s . Where ( "files.file_main_color IN (?)" , strings . Split ( strings . ToLower ( f . Color ) , txt . Or ) )
2020-05-08 15:41:01 +02:00
}
2020-05-14 19:03:12 +02:00
if f . Favorite {
2020-05-08 15:41:01 +02:00
s = s . Where ( "photos.photo_favorite = 1" )
}
2020-07-06 14:35:25 +02:00
if f . Scan {
s = s . Where ( "photos.photo_scan = 1" )
2020-07-05 14:48:49 +02:00
}
2020-07-16 13:02:48 +02:00
if f . Panorama {
s = s . Where ( "photos.photo_panorama = 1" )
}
2020-12-19 19:15:32 +01:00
if f . Stackable {
s = s . Where ( "photos.photo_stack > -1" )
} else if f . Unstacked {
s = s . Where ( "photos.photo_stack = -1" )
2020-12-11 17:21:13 +01:00
}
2020-05-08 15:41:01 +02:00
if f . Country != "" {
2021-09-18 15:32:39 +02:00
s = s . Where ( "photos.photo_country IN (?)" , strings . Split ( strings . ToLower ( f . Country ) , txt . Or ) )
2020-05-08 15:41:01 +02:00
}
2020-05-29 18:04:30 +02:00
if f . State != "" {
2021-09-18 15:32:39 +02:00
s = s . Where ( "places.place_state IN (?)" , strings . Split ( f . State , txt . Or ) )
2020-05-29 18:04:30 +02:00
}
if f . Category != "" {
2020-07-12 08:27:05 +02:00
s = s . Joins ( "JOIN cells ON photos.cell_id = cells.id" ) .
2021-09-18 15:32:39 +02:00
Where ( "cells.cell_category IN (?)" , strings . Split ( strings . ToLower ( f . Category ) , txt . Or ) )
2020-05-29 18:04:30 +02:00
}
2020-05-21 13:26:28 +02:00
// Filter by media type.
2020-05-21 10:03:56 +02:00
if f . Type != "" {
2021-09-18 15:32:39 +02:00
s = s . Where ( "photos.photo_type IN (?)" , strings . Split ( strings . ToLower ( f . Type ) , txt . Or ) )
2020-05-21 13:26:28 +02:00
}
if f . Video {
s = s . Where ( "photos.photo_type = 'video'" )
} else if f . Photo {
s = s . Where ( "photos.photo_type IN ('image','raw','live')" )
2020-05-21 10:03:56 +02:00
}
if f . Path != "" {
p := f . Path
if strings . HasPrefix ( p , "/" ) {
p = p [ 1 : ]
}
if strings . HasSuffix ( p , "/" ) {
s = s . Where ( "photos.photo_path = ?" , p [ : len ( p ) - 1 ] )
2021-09-18 15:32:39 +02:00
} else if strings . Contains ( p , txt . Or ) {
s = s . Where ( "photos.photo_path IN (?)" , strings . Split ( p , txt . Or ) )
2020-05-21 10:03:56 +02:00
} else {
s = s . Where ( "photos.photo_path LIKE ?" , strings . ReplaceAll ( p , "*" , "%" ) )
}
}
2021-09-18 15:32:39 +02:00
if strings . Contains ( f . Name , txt . Or ) {
s = s . Where ( "photos.photo_name IN (?)" , strings . Split ( f . Name , txt . Or ) )
2020-12-15 20:14:06 +01:00
} else if f . Name != "" {
s = s . Where ( "photos.photo_name LIKE ?" , strings . ReplaceAll ( fs . StripKnownExt ( f . Name ) , "*" , "%" ) )
2020-05-21 10:03:56 +02:00
}
2021-09-18 15:32:39 +02:00
if strings . Contains ( f . Filename , txt . Or ) {
s = s . Where ( "files.file_name IN (?)" , strings . Split ( f . Filename , txt . Or ) )
2020-12-15 20:14:06 +01:00
} else if f . Filename != "" {
2020-12-03 21:07:38 +01:00
s = s . Where ( "files.file_name LIKE ?" , strings . ReplaceAll ( f . Filename , "*" , "%" ) )
}
2021-09-18 15:32:39 +02:00
if strings . Contains ( f . Original , txt . Or ) {
s = s . Where ( "photos.original_name IN (?)" , strings . Split ( f . Original , txt . Or ) )
2020-12-15 20:14:06 +01:00
} else if f . Original != "" {
2020-06-03 10:33:09 +02:00
s = s . Where ( "photos.original_name LIKE ?" , strings . ReplaceAll ( f . Original , "*" , "%" ) )
}
2021-09-18 15:32:39 +02:00
if strings . Contains ( f . Title , txt . Or ) {
s = s . Where ( "photos.photo_title IN (?)" , strings . Split ( strings . ToLower ( f . Title ) , txt . Or ) )
2020-12-15 20:14:06 +01:00
} else if f . Title != "" {
s = s . Where ( "photos.photo_title LIKE ?" , strings . ReplaceAll ( strings . ToLower ( f . Title ) , "*" , "%" ) )
2020-05-08 15:41:01 +02:00
}
2021-09-18 15:32:39 +02:00
if strings . Contains ( f . Hash , txt . Or ) {
s = s . Where ( "files.file_hash IN (?)" , strings . Split ( strings . ToLower ( f . Hash ) , txt . Or ) )
2020-12-15 20:14:06 +01:00
} else if f . Hash != "" {
2021-09-18 15:32:39 +02:00
s = s . Where ( "files.file_hash IN (?)" , strings . Split ( strings . ToLower ( f . Hash ) , txt . Or ) )
2020-05-08 15:41:01 +02:00
}
if f . Portrait {
s = s . Where ( "files.file_portrait = 1" )
}
if f . Mono {
2020-12-19 01:56:00 +01:00
s = s . Where ( "files.file_chroma = 0 OR file_colors = '111111111'" )
2020-05-08 15:41:01 +02:00
} else if f . Chroma > 9 {
s = s . Where ( "files.file_chroma > ?" , f . Chroma )
} else if f . Chroma > 0 {
s = s . Where ( "files.file_chroma > 0 AND files.file_chroma <= ?" , f . Chroma )
}
if f . Diff != 0 {
s = s . Where ( "files.file_diff = ?" , f . Diff )
}
if f . Fmin > 0 {
s = s . Where ( "photos.photo_f_number >= ?" , f . Fmin )
}
if f . Fmax > 0 {
s = s . Where ( "photos.photo_f_number <= ?" , f . Fmax )
}
if f . Dist == 0 {
f . Dist = 20
} else if f . Dist > 5000 {
f . Dist = 5000
}
2021-02-11 20:06:23 +01:00
// Filter by approx distance to coordinates:
if f . Lat != 0 {
2021-09-18 15:32:39 +02:00
latMin := f . Lat - Radius * float32 ( f . Dist )
latMax := f . Lat + Radius * float32 ( f . Dist )
2020-05-08 15:41:01 +02:00
s = s . Where ( "photos.photo_lat BETWEEN ? AND ?" , latMin , latMax )
}
2021-02-11 20:06:23 +01:00
if f . Lng != 0 {
2021-09-18 15:32:39 +02:00
lngMin := f . Lng - Radius * float32 ( f . Dist )
lngMax := f . Lng + Radius * float32 ( f . Dist )
2020-05-08 15:41:01 +02:00
s = s . Where ( "photos.photo_lng BETWEEN ? AND ?" , lngMin , lngMax )
}
if ! f . Before . IsZero ( ) {
s = s . Where ( "photos.taken_at <= ?" , f . Before . Format ( "2006-01-02" ) )
}
if ! f . After . IsZero ( ) {
s = s . Where ( "photos.taken_at >= ?" , f . After . Format ( "2006-01-02" ) )
}
2021-08-29 16:16:49 +02:00
// Find stacks only?
2020-07-13 15:59:54 +02:00
if f . Stack {
2020-07-05 17:22:26 +02:00
s = s . Where ( "photos.id IN (SELECT a.photo_id FROM files a JOIN files b ON a.id != b.id AND a.photo_id = b.photo_id AND a.file_type = b.file_type WHERE a.file_type='jpg')" )
}
2021-08-29 16:16:49 +02:00
// Filter by album?
2020-05-30 01:41:47 +02:00
if f . Album != "" {
if f . Filter != "" {
s = s . Where ( "photos.photo_uid NOT IN (SELECT photo_uid FROM photos_albums pa WHERE pa.hidden = 1 AND pa.album_uid = ?)" , f . Album )
} else {
s = s . Joins ( "JOIN photos_albums ON photos_albums.photo_uid = photos.photo_uid" ) . Where ( "photos_albums.hidden = 0 AND photos_albums.album_uid = ?" , f . Album )
}
2020-07-05 14:48:49 +02:00
} else if f . Unsorted && f . Filter == "" {
s = s . Where ( "photos.photo_uid NOT IN (SELECT photo_uid FROM photos_albums pa WHERE pa.hidden = 0)" )
2021-08-29 16:16:49 +02:00
} else if f . Albums != "" {
2021-08-29 19:19:54 +02:00
for _ , where := range LikeAnyWord ( "a.album_title" , f . Albums ) {
2021-08-29 16:16:49 +02:00
s = s . Where ( "photos.photo_uid IN (SELECT pa.photo_uid FROM photos_albums pa JOIN albums a ON a.album_uid = pa.album_uid WHERE (?))" , gorm . Expr ( where ) )
}
2020-05-30 01:41:47 +02:00
}
2020-05-30 14:52:47 +02:00
if err := s . Scan ( & results ) . Error ; err != nil {
return results , 0 , err
2020-05-08 15:41:01 +02:00
}
2020-05-23 20:58:58 +02:00
log . Infof ( "photos: found %d results for %s [%s]" , len ( results ) , f . SerializeAll ( ) , time . Since ( start ) )
2020-05-08 15:41:01 +02:00
if f . Merged {
return results . Merged ( )
}
return results , len ( results ) , nil
}