1
Fork 0

Add search resolver to api

This commit is contained in:
viktorstrate 2020-03-05 11:53:42 +01:00
parent 5cee5d8dea
commit c30f5a833a
4 changed files with 388 additions and 0 deletions

View File

@ -140,6 +140,7 @@ type ComplexityRoot struct {
MyPhotos func(childComplexity int, filter *models.Filter) int
MyUser func(childComplexity int) int
Photo func(childComplexity int, id int) int
Search func(childComplexity int, query string, limitPhotos *int, limitAlbums *int) int
ShareToken func(childComplexity int, token string, password *string) int
SiteInfo func(childComplexity int) int
User func(childComplexity int, filter *models.Filter) int
@ -152,6 +153,12 @@ type ComplexityRoot struct {
Success func(childComplexity int) int
}
SearchResult struct {
Albums func(childComplexity int) int
Photos func(childComplexity int) int
Query func(childComplexity int) int
}
ShareToken struct {
Album func(childComplexity int) int
Expire func(childComplexity int) int
@ -216,6 +223,7 @@ type QueryResolver interface {
MyPhotos(ctx context.Context, filter *models.Filter) ([]*models.Photo, error)
Photo(ctx context.Context, id int) (*models.Photo, error)
ShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error)
Search(ctx context.Context, query string, limitPhotos *int, limitAlbums *int) (*models.SearchResult, error)
}
type ShareTokenResolver interface {
Owner(ctx context.Context, obj *models.ShareToken) (*models.User, error)
@ -770,6 +778,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Query.Photo(childComplexity, args["id"].(int)), true
case "Query.search":
if e.complexity.Query.Search == nil {
break
}
args, err := ec.field_Query_search_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Query.Search(childComplexity, args["query"].(string), args["limitPhotos"].(*int), args["limitAlbums"].(*int)), true
case "Query.shareToken":
if e.complexity.Query.ShareToken == nil {
break
@ -829,6 +849,27 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ScannerResult.Success(childComplexity), true
case "SearchResult.albums":
if e.complexity.SearchResult.Albums == nil {
break
}
return e.complexity.SearchResult.Albums(childComplexity), true
case "SearchResult.photos":
if e.complexity.SearchResult.Photos == nil {
break
}
return e.complexity.SearchResult.Photos(childComplexity), true
case "SearchResult.query":
if e.complexity.SearchResult.Query == nil {
break
}
return e.complexity.SearchResult.Query(childComplexity), true
case "ShareToken.album":
if e.complexity.ShareToken.Album == nil {
break
@ -1045,6 +1086,8 @@ type Query {
photo(id: Int!): Photo!
shareToken(token: String!, password: String): ShareToken!
search(query: String!, limitPhotos: Int, limitAlbums: Int): SearchResult!
}
type Mutation {
@ -1234,6 +1277,12 @@ type PhotoEXIF {
"An index describing the mode for adjusting the exposure of the image"
exposureProgram: Int
}
type SearchResult {
query: String!
albums: [Album!]!
photos: [Photo!]!
}
`},
)
@ -1623,6 +1672,36 @@ func (ec *executionContext) field_Query_photo_args(ctx context.Context, rawArgs
return args, nil
}
func (ec *executionContext) field_Query_search_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 string
if tmp, ok := rawArgs["query"]; ok {
arg0, err = ec.unmarshalNString2string(ctx, tmp)
if err != nil {
return nil, err
}
}
args["query"] = arg0
var arg1 *int
if tmp, ok := rawArgs["limitPhotos"]; ok {
arg1, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["limitPhotos"] = arg1
var arg2 *int
if tmp, ok := rawArgs["limitAlbums"]; ok {
arg2, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
if err != nil {
return nil, err
}
}
args["limitAlbums"] = arg2
return args, nil
}
func (ec *executionContext) field_Query_shareToken_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@ -4325,6 +4404,50 @@ func (ec *executionContext) _Query_shareToken(ctx context.Context, field graphql
return ec.marshalNShareToken2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_search(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
ec.Tracer.EndFieldExecution(ctx)
}()
rctx := &graphql.ResolverContext{
Object: "Query",
Field: field,
Args: nil,
IsMethod: true,
}
ctx = graphql.WithResolverContext(ctx, rctx)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Query_search_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
rctx.Args = args
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().Search(rctx, args["query"].(string), args["limitPhotos"].(*int), args["limitAlbums"].(*int))
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*models.SearchResult)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec.marshalNSearchResult2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐSearchResult(ctx, field.Selections, res)
}
func (ec *executionContext) _Query___type(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
@ -4542,6 +4665,117 @@ func (ec *executionContext) _ScannerResult_message(ctx context.Context, field gr
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) _SearchResult_query(ctx context.Context, field graphql.CollectedField, obj *models.SearchResult) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
ec.Tracer.EndFieldExecution(ctx)
}()
rctx := &graphql.ResolverContext{
Object: "SearchResult",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Query, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _SearchResult_albums(ctx context.Context, field graphql.CollectedField, obj *models.SearchResult) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
ec.Tracer.EndFieldExecution(ctx)
}()
rctx := &graphql.ResolverContext{
Object: "SearchResult",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Albums, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.([]*models.Album)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec.marshalNAlbum2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐAlbumᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _SearchResult_photos(ctx context.Context, field graphql.CollectedField, obj *models.SearchResult) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
ec.Tracer.EndFieldExecution(ctx)
}()
rctx := &graphql.ResolverContext{
Object: "SearchResult",
Field: field,
Args: nil,
IsMethod: false,
}
ctx = graphql.WithResolverContext(ctx, rctx)
ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Photos, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !ec.HasError(rctx) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.([]*models.Photo)
rctx.Result = res
ctx = ec.Tracer.StartFieldChildExecution(ctx)
return ec.marshalNPhoto2ᚕᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPhotoᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _ShareToken_id(ctx context.Context, field graphql.CollectedField, obj *models.ShareToken) (ret graphql.Marshaler) {
ctx = ec.Tracer.StartFieldExecution(ctx, field)
defer func() {
@ -6836,6 +7070,20 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
}
return res
})
case "search":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query_search(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "__type":
out.Values[i] = ec._Query___type(ctx, field)
case "__schema":
@ -6887,6 +7135,43 @@ func (ec *executionContext) _ScannerResult(ctx context.Context, sel ast.Selectio
return out
}
var searchResultImplementors = []string{"SearchResult"}
func (ec *executionContext) _SearchResult(ctx context.Context, sel ast.SelectionSet, obj *models.SearchResult) graphql.Marshaler {
fields := graphql.CollectFields(ec.RequestContext, sel, searchResultImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("SearchResult")
case "query":
out.Values[i] = ec._SearchResult_query(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "albums":
out.Values[i] = ec._SearchResult_albums(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "photos":
out.Values[i] = ec._SearchResult_photos(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var shareTokenImplementors = []string{"ShareToken"}
func (ec *executionContext) _ShareToken(ctx context.Context, sel ast.SelectionSet, obj *models.ShareToken) graphql.Marshaler {
@ -7537,6 +7822,20 @@ func (ec *executionContext) marshalNScannerResult2ᚖgithubᚗcomᚋviktorstrate
return ec._ScannerResult(ctx, sel, v)
}
func (ec *executionContext) marshalNSearchResult2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐSearchResult(ctx context.Context, sel ast.SelectionSet, v models.SearchResult) graphql.Marshaler {
return ec._SearchResult(ctx, sel, &v)
}
func (ec *executionContext) marshalNSearchResult2ᚖgithubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐSearchResult(ctx context.Context, sel ast.SelectionSet, v *models.SearchResult) graphql.Marshaler {
if v == nil {
if !ec.HasError(graphql.GetResolverContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._SearchResult(ctx, sel, v)
}
func (ec *executionContext) marshalNShareToken2githubᚗcomᚋviktorstrateᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐShareToken(ctx context.Context, sel ast.SelectionSet, v models.ShareToken) graphql.Marshaler {
return ec._ShareToken(ctx, sel, &v)
}

View File

@ -47,6 +47,12 @@ type ScannerResult struct {
Message *string `json:"message"`
}
type SearchResult struct {
Query string `json:"query"`
Albums []*Album `json:"albums"`
Photos []*Photo `json:"photos"`
}
// General public information about the site
type SiteInfo struct {
InitialSetup bool `json:"initialSetup"`

View File

@ -0,0 +1,75 @@
package resolvers
import (
"context"
"log"
"github.com/viktorstrate/photoview/api/graphql/auth"
"github.com/viktorstrate/photoview/api/graphql/models"
)
func (r *Resolver) Search(ctx context.Context, query string, _limitPhotos *int, _limitAlbums *int) (*models.SearchResult, error) {
user := auth.UserFromContext(ctx)
if user == nil {
return nil, auth.ErrUnauthorized
}
limitPhotos := 10
limitAlbums := 10
if _limitPhotos != nil {
limitPhotos = *_limitPhotos
}
if _limitAlbums != nil {
limitAlbums = *_limitAlbums
}
wildQuery := "%" + query + "%"
photoRows, err := r.Database.Query(`
SELECT photo.* FROM photo JOIN album ON photo.album_id = album.album_id
WHERE album.owner_id = ? AND photo.title LIKE ? OR photo.path LIKE ?
ORDER BY (
case when photo.title LIKE ? then 2
when photo.path LIKE ? then 1
end ) DESC
LIMIT ?
`, user.UserID, wildQuery, wildQuery, wildQuery, wildQuery, limitPhotos)
if err != nil {
log.Printf("ERROR: searching photos %s", err)
return nil, err
}
photos, err := models.NewPhotosFromRows(photoRows)
if err != nil {
return nil, err
}
albumRows, err := r.Database.Query(`
SELECT * FROM album
WHERE owner_id = ? AND title LIKE ? OR path LIKE ?
ORDER BY (
case when title LIKE ? then 2
when path LIKE ? then 1
end ) DESC
LIMIT ?
`, user.UserID, wildQuery, wildQuery, wildQuery, wildQuery, limitAlbums)
if err != nil {
log.Printf("ERROR: searching albums %s", err)
return nil, err
}
albums, err := models.NewAlbumsFromRows(albumRows)
if err != nil {
return nil, err
}
result := models.SearchResult{
Query: query,
Photos: photos,
Albums: albums,
}
return &result, nil
}

View File

@ -39,6 +39,8 @@ type Query {
photo(id: Int!): Photo!
shareToken(token: String!, password: String): ShareToken!
search(query: String!, limitPhotos: Int, limitAlbums: Int): SearchResult!
}
type Mutation {
@ -228,3 +230,9 @@ type PhotoEXIF {
"An index describing the mode for adjusting the exposure of the image"
exposureProgram: Int
}
type SearchResult {
query: String!
albums: [Album!]!
photos: [Photo!]!
}