Write tests for searching
This commit is contained in:
parent
0ae34829e2
commit
adfcf33e79
|
@ -0,0 +1,73 @@
|
||||||
|
package actions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/photoview/photoview/api/graphql/models"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Search(db *gorm.DB, query string, userID int, _limitMedia *int, _limitAlbums *int) (*models.SearchResult, error) {
|
||||||
|
limitMedia := 10
|
||||||
|
limitAlbums := 10
|
||||||
|
|
||||||
|
if _limitMedia != nil {
|
||||||
|
limitMedia = *_limitMedia
|
||||||
|
}
|
||||||
|
|
||||||
|
if _limitAlbums != nil {
|
||||||
|
limitAlbums = *_limitAlbums
|
||||||
|
}
|
||||||
|
|
||||||
|
wildQuery := "%" + query + "%"
|
||||||
|
|
||||||
|
var media []*models.Media
|
||||||
|
|
||||||
|
userSubquery := db.Table("user_albums").Where("user_id = ?", userID)
|
||||||
|
if db.Dialector.Name() == "postgres" {
|
||||||
|
userSubquery = userSubquery.Where("album_id = \"Album\".id")
|
||||||
|
} else {
|
||||||
|
userSubquery = userSubquery.Where("album_id = Album.id")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.Joins("Album").
|
||||||
|
Where("EXISTS (?)", userSubquery).
|
||||||
|
Where("media.title LIKE ? OR media.path LIKE ?", wildQuery, wildQuery).
|
||||||
|
Clauses(clause.OrderBy{
|
||||||
|
Expression: clause.Expr{
|
||||||
|
SQL: "(CASE WHEN media.title LIKE ? THEN 2 WHEN media.path LIKE ? THEN 1 END) DESC",
|
||||||
|
Vars: []interface{}{wildQuery, wildQuery},
|
||||||
|
WithoutParentheses: true},
|
||||||
|
}).
|
||||||
|
Limit(limitMedia).Find(&media).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "searching media")
|
||||||
|
}
|
||||||
|
|
||||||
|
var albums []*models.Album
|
||||||
|
|
||||||
|
err = db.
|
||||||
|
Where("EXISTS (?)", db.Table("user_albums").Where("user_id = ?", userID).Where("album_id = albums.id")).
|
||||||
|
Where("albums.title LIKE ? OR albums.path LIKE ?", wildQuery, wildQuery).
|
||||||
|
Clauses(clause.OrderBy{
|
||||||
|
Expression: clause.Expr{
|
||||||
|
SQL: "(CASE WHEN albums.title LIKE ? THEN 2 WHEN albums.path LIKE ? THEN 1 END) DESC",
|
||||||
|
Vars: []interface{}{wildQuery, wildQuery},
|
||||||
|
WithoutParentheses: true},
|
||||||
|
}).
|
||||||
|
Limit(limitAlbums).
|
||||||
|
Find(&albums).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "searching albums")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := models.SearchResult{
|
||||||
|
Query: query,
|
||||||
|
Media: media,
|
||||||
|
Albums: albums,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result, nil
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package actions_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/photoview/photoview/api/graphql/models"
|
||||||
|
"github.com/photoview/photoview/api/graphql/models/actions"
|
||||||
|
"github.com/photoview/photoview/api/test_utils"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSearch(t *testing.T) {
|
||||||
|
db := test_utils.DatabaseTest(t)
|
||||||
|
|
||||||
|
user, err := models.RegisterUser(db, "user", nil, true)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
rootAlbum := models.Album{
|
||||||
|
Title: "root_album",
|
||||||
|
Path: "/media/",
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, db.Create(&rootAlbum).Error)
|
||||||
|
assert.NoError(t, db.Model(&rootAlbum).Association("Owners").Append(user))
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
ID int
|
||||||
|
UserID int
|
||||||
|
AlbumID int
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaTitles := []string{
|
||||||
|
"SOME_IMAGE.jpg",
|
||||||
|
"imageA.jpg",
|
||||||
|
"imageB.jpg",
|
||||||
|
"imageC.jpg",
|
||||||
|
"movie.mp4",
|
||||||
|
"person.png",
|
||||||
|
"123.png",
|
||||||
|
"ABC.gif",
|
||||||
|
"dog.mov",
|
||||||
|
"cat.mov",
|
||||||
|
"IMG_3255.JPG",
|
||||||
|
"IMG_5532.JPG",
|
||||||
|
"IMG_5533.JPG",
|
||||||
|
"IMG_5534.JPG",
|
||||||
|
"IMG_5535.JPG",
|
||||||
|
"IMG_5536.JPG",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mediaTitle := range mediaTitles {
|
||||||
|
image := models.Media{
|
||||||
|
Title: mediaTitle,
|
||||||
|
Path: fmt.Sprintf("/media/%s", mediaTitle),
|
||||||
|
AlbumID: rootAlbum.ID,
|
||||||
|
}
|
||||||
|
assert.NoError(t, db.Create(&image).Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchTest = struct {
|
||||||
|
query string
|
||||||
|
userID int
|
||||||
|
limitMedia *int
|
||||||
|
limitAlbum *int
|
||||||
|
|
||||||
|
expectedMediaCount int
|
||||||
|
expectedAlbumCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
searchTests := []SearchTest{
|
||||||
|
{
|
||||||
|
query: "image",
|
||||||
|
userID: user.ID,
|
||||||
|
expectedMediaCount: 4,
|
||||||
|
expectedAlbumCount: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
query: "g",
|
||||||
|
userID: user.ID,
|
||||||
|
expectedMediaCount: 10,
|
||||||
|
expectedAlbumCount: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
query: "media",
|
||||||
|
userID: user.ID,
|
||||||
|
expectedMediaCount: 10,
|
||||||
|
expectedAlbumCount: 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range searchTests {
|
||||||
|
t.Run(fmt.Sprintf("Search query: '%s'", test.query), func(t *testing.T) {
|
||||||
|
result, err := actions.Search(db, test.query, test.userID, test.limitMedia, test.limitAlbum)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, result.Query, test.query)
|
||||||
|
assert.Len(t, result.Albums, test.expectedAlbumCount)
|
||||||
|
assert.Len(t, result.Media, test.expectedMediaCount)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,7 +71,7 @@ type MediaURL struct {
|
||||||
Model
|
Model
|
||||||
MediaID int `gorm:"not null;index"`
|
MediaID int `gorm:"not null;index"`
|
||||||
Media *Media `gorm:"constraint:OnDelete:CASCADE;"`
|
Media *Media `gorm:"constraint:OnDelete:CASCADE;"`
|
||||||
MediaName string `gorm:"not null"`
|
MediaName string `gorm:"not null;index"`
|
||||||
Width int `gorm:"not null"`
|
Width int `gorm:"not null"`
|
||||||
Height int `gorm:"not null"`
|
Height int `gorm:"not null"`
|
||||||
Purpose MediaPurpose `gorm:"not null;index"`
|
Purpose MediaPurpose `gorm:"not null;index"`
|
||||||
|
|
|
@ -4,78 +4,16 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/photoview/photoview/api/graphql/auth"
|
"github.com/photoview/photoview/api/graphql/auth"
|
||||||
"github.com/pkg/errors"
|
"github.com/photoview/photoview/api/graphql/models/actions"
|
||||||
"gorm.io/gorm/clause"
|
|
||||||
|
|
||||||
"github.com/photoview/photoview/api/graphql/models"
|
"github.com/photoview/photoview/api/graphql/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *Resolver) Search(ctx context.Context, query string, _limitMedia *int, _limitAlbums *int) (*models.SearchResult, error) {
|
func (r *Resolver) Search(ctx context.Context, query string, limitMedia *int, limitAlbums *int) (*models.SearchResult, error) {
|
||||||
user := auth.UserFromContext(ctx)
|
user := auth.UserFromContext(ctx)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return nil, auth.ErrUnauthorized
|
return nil, auth.ErrUnauthorized
|
||||||
}
|
}
|
||||||
|
|
||||||
limitMedia := 10
|
return actions.Search(r.Database, query, user.ID, limitMedia, limitAlbums)
|
||||||
limitAlbums := 10
|
|
||||||
|
|
||||||
if _limitMedia != nil {
|
|
||||||
limitMedia = *_limitMedia
|
|
||||||
}
|
|
||||||
|
|
||||||
if _limitAlbums != nil {
|
|
||||||
limitAlbums = *_limitAlbums
|
|
||||||
}
|
|
||||||
|
|
||||||
wildQuery := "%" + query + "%"
|
|
||||||
|
|
||||||
var media []*models.Media
|
|
||||||
|
|
||||||
userSubquery := r.Database.Table("user_albums").Where("user_id = ?", user.ID)
|
|
||||||
if r.Database.Dialector.Name() == "postgres" {
|
|
||||||
userSubquery = userSubquery.Where("album_id = \"Album\".id")
|
|
||||||
} else {
|
|
||||||
userSubquery = userSubquery.Where("album_id = Album.id")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := r.Database.Joins("Album").
|
|
||||||
Where("EXISTS (?)", userSubquery).
|
|
||||||
Where("media.title LIKE ? OR media.path LIKE ?", wildQuery, wildQuery).
|
|
||||||
Clauses(clause.OrderBy{
|
|
||||||
Expression: clause.Expr{
|
|
||||||
SQL: "(CASE WHEN media.title LIKE ? THEN 2 WHEN media.path LIKE ? THEN 1 END) DESC",
|
|
||||||
Vars: []interface{}{wildQuery, wildQuery},
|
|
||||||
WithoutParentheses: true},
|
|
||||||
}).
|
|
||||||
Limit(limitMedia).Find(&media).Error
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "searching media")
|
|
||||||
}
|
|
||||||
|
|
||||||
var albums []*models.Album
|
|
||||||
|
|
||||||
err = r.Database.
|
|
||||||
Where("EXISTS (?)", r.Database.Table("user_albums").Where("user_id = ?", user.ID).Where("album_id = albums.id")).
|
|
||||||
Where("albums.title LIKE ? OR albums.path LIKE ?", wildQuery, wildQuery).
|
|
||||||
Clauses(clause.OrderBy{
|
|
||||||
Expression: clause.Expr{
|
|
||||||
SQL: "(CASE WHEN albums.title LIKE ? THEN 2 WHEN albums.path LIKE ? THEN 1 END) DESC",
|
|
||||||
Vars: []interface{}{wildQuery, wildQuery},
|
|
||||||
WithoutParentheses: true},
|
|
||||||
}).
|
|
||||||
Limit(limitAlbums).
|
|
||||||
Find(&albums).Error
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "searching albums")
|
|
||||||
}
|
|
||||||
|
|
||||||
result := models.SearchResult{
|
|
||||||
Query: query,
|
|
||||||
Media: media,
|
|
||||||
Albums: albums,
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result, nil
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue