Add API pagination for faces

This commit is contained in:
viktorstrate
2021-02-25 18:48:59 +01:00
parent f31afb8339
commit 7fab8287a2
5 changed files with 158 additions and 91 deletions

View File

@@ -46,6 +46,9 @@ models:
model: github.com/photoview/photoview/api/graphql/models.ShareToken
FaceGroup:
model: github.com/photoview/photoview/api/graphql/models.FaceGroup
fields:
imageFaces:
resolver: true
ImageFace:
model: github.com/photoview/photoview/api/graphql/models.ImageFace
fields:

View File

@@ -39,6 +39,7 @@ type Config struct {
type ResolverRoot interface {
Album() AlbumResolver
FaceGroup() FaceGroupResolver
ImageFace() ImageFaceResolver
Media() MediaResolver
Mutation() MutationResolver
@@ -73,9 +74,10 @@ type ComplexityRoot struct {
}
FaceGroup struct {
ID func(childComplexity int) int
ImageFaces func(childComplexity int) int
Label func(childComplexity int) int
ID func(childComplexity int) int
ImageFaceCount func(childComplexity int) int
ImageFaces func(childComplexity int, paginate *models.Pagination) int
Label func(childComplexity int) int
}
FaceRectangle struct {
@@ -261,6 +263,10 @@ type AlbumResolver interface {
Path(ctx context.Context, obj *models.Album) ([]*models.Album, error)
Shares(ctx context.Context, obj *models.Album) ([]*models.ShareToken, error)
}
type FaceGroupResolver interface {
ImageFaces(ctx context.Context, obj *models.FaceGroup, paginate *models.Pagination) ([]*models.ImageFace, error)
ImageFaceCount(ctx context.Context, obj *models.FaceGroup) (int, error)
}
type ImageFaceResolver interface {
FaceGroup(ctx context.Context, obj *models.ImageFace) (*models.FaceGroup, error)
}
@@ -451,12 +457,24 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.FaceGroup.ID(childComplexity), true
case "FaceGroup.imageFaceCount":
if e.complexity.FaceGroup.ImageFaceCount == nil {
break
}
return e.complexity.FaceGroup.ImageFaceCount(childComplexity), true
case "FaceGroup.imageFaces":
if e.complexity.FaceGroup.ImageFaces == nil {
break
}
return e.complexity.FaceGroup.ImageFaces(childComplexity), true
args, err := ec.field_FaceGroup_imageFaces_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.FaceGroup.ImageFaces(childComplexity, args["paginate"].(*models.Pagination)), true
case "FaceGroup.label":
if e.complexity.FaceGroup.Label == nil {
@@ -1878,7 +1896,8 @@ type TimelineGroup {
type FaceGroup {
id: ID!
label: String
imageFaces: [ImageFace!]!
imageFaces(paginate: Pagination): [ImageFace!]!
imageFaceCount: Int!
}
type ImageFace {
@@ -1959,6 +1978,21 @@ func (ec *executionContext) field_Album_subAlbums_args(ctx context.Context, rawA
return args, nil
}
func (ec *executionContext) field_FaceGroup_imageFaces_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 *models.Pagination
if tmp, ok := rawArgs["paginate"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("paginate"))
arg0, err = ec.unmarshalOPagination2ᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐPagination(ctx, tmp)
if err != nil {
return nil, err
}
}
args["paginate"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_authorizeUser_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@@ -3267,14 +3301,21 @@ func (ec *executionContext) _FaceGroup_imageFaces(ctx context.Context, field gra
Object: "FaceGroup",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_FaceGroup_imageFaces_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.ImageFaces, nil
return ec.resolvers.FaceGroup().ImageFaces(rctx, obj, args["paginate"].(*models.Pagination))
})
if err != nil {
ec.Error(ctx, err)
@@ -3286,9 +3327,44 @@ func (ec *executionContext) _FaceGroup_imageFaces(ctx context.Context, field gra
}
return graphql.Null
}
res := resTmp.([]models.ImageFace)
res := resTmp.([]*models.ImageFace)
fc.Result = res
return ec.marshalNImageFace2ᚕgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐImageFaceᚄ(ctx, field.Selections, res)
return ec.marshalNImageFace2ᚕgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐImageFaceᚄ(ctx, field.Selections, res)
}
func (ec *executionContext) _FaceGroup_imageFaceCount(ctx context.Context, field graphql.CollectedField, obj *models.FaceGroup) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "FaceGroup",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.FaceGroup().ImageFaceCount(rctx, obj)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(int)
fc.Result = res
return ec.marshalNInt2int(ctx, field.Selections, res)
}
func (ec *executionContext) _FaceRectangle_minX(ctx context.Context, field graphql.CollectedField, obj *models.FaceRectangle) (ret graphql.Marshaler) {
@@ -9233,15 +9309,38 @@ func (ec *executionContext) _FaceGroup(ctx context.Context, sel ast.SelectionSet
case "id":
out.Values[i] = ec._FaceGroup_id(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
atomic.AddUint32(&invalids, 1)
}
case "label":
out.Values[i] = ec._FaceGroup_label(ctx, field, obj)
case "imageFaces":
out.Values[i] = ec._FaceGroup_imageFaces(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
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._FaceGroup_imageFaces(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "imageFaceCount":
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._FaceGroup_imageFaceCount(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@@ -10817,47 +10916,6 @@ func (ec *executionContext) marshalNID2ᚕintᚄ(ctx context.Context, sel ast.Se
return ret
}
func (ec *executionContext) marshalNImageFace2githubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐImageFace(ctx context.Context, sel ast.SelectionSet, v models.ImageFace) graphql.Marshaler {
return ec._ImageFace(ctx, sel, &v)
}
func (ec *executionContext) marshalNImageFace2ᚕgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐImageFaceᚄ(ctx context.Context, sel ast.SelectionSet, v []models.ImageFace) graphql.Marshaler {
ret := make(graphql.Array, len(v))
var wg sync.WaitGroup
isLen1 := len(v) == 1
if !isLen1 {
wg.Add(len(v))
}
for i := range v {
i := i
fc := &graphql.FieldContext{
Index: &i,
Result: &v[i],
}
ctx := graphql.WithFieldContext(ctx, fc)
f := func(i int) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = nil
}
}()
if !isLen1 {
defer wg.Done()
}
ret[i] = ec.marshalNImageFace2githubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐImageFace(ctx, sel, v[i])
}
if isLen1 {
f(i)
} else {
go f(i)
}
}
wg.Wait()
return ret
}
func (ec *executionContext) marshalNImageFace2ᚕᚖgithubᚗcomᚋphotoviewᚋphotoviewᚋapiᚋgraphqlᚋmodelsᚐImageFaceᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.ImageFace) graphql.Marshaler {
ret := make(graphql.Array, len(v))
var wg sync.WaitGroup

View File

@@ -15,10 +15,18 @@ type imageFaceResolver struct {
*Resolver
}
type faceGroupResolver struct {
*Resolver
}
func (r *Resolver) ImageFace() api.ImageFaceResolver {
return imageFaceResolver{r}
}
func (r *Resolver) FaceGroup() api.FaceGroupResolver {
return faceGroupResolver{r}
}
func (r imageFaceResolver) FaceGroup(ctx context.Context, obj *models.ImageFace) (*models.FaceGroup, error) {
if obj.FaceGroup != nil {
return obj.FaceGroup, nil
@@ -34,6 +42,27 @@ func (r imageFaceResolver) FaceGroup(ctx context.Context, obj *models.ImageFace)
return &faceGroup, nil
}
func (r faceGroupResolver) ImageFaces(ctx context.Context, obj *models.FaceGroup, paginate *models.Pagination) ([]*models.ImageFace, error) {
query := r.Database.Joins("Media").Where("face_group_id = ?", obj.ID)
query = models.FormatSQL(query, nil, paginate)
var imageFaces []*models.ImageFace
if err := query.Find(&imageFaces).Error; err != nil {
return nil, err
}
return imageFaces, nil
}
func (r faceGroupResolver) ImageFaceCount(ctx context.Context, obj *models.FaceGroup) (int, error) {
var count int64
if err := r.Database.Model(&models.ImageFace{}).Where("face_group_id = ?", obj.ID).Count(&count).Error; err != nil {
return -1, err
}
return int(count), nil
}
func (r *queryResolver) MyFaceGroups(ctx context.Context, paginate *models.Pagination) ([]*models.FaceGroup, error) {
user := auth.UserFromContext(ctx)
if user == nil {
@@ -49,47 +78,22 @@ func (r *queryResolver) MyFaceGroups(ctx context.Context, paginate *models.Pagin
userAlbumIDs[i] = album.ID
}
imageFaceQuery := r.Database.
Joins("Media").
Where("Media.album_id IN (?)", userAlbumIDs)
var imageFaces []*models.ImageFace
if err := imageFaceQuery.Find(&imageFaces).Error; err != nil {
return nil, err
}
faceGroupMap := make(map[int][]models.ImageFace)
for _, face := range imageFaces {
_, found := faceGroupMap[face.FaceGroupID]
if found {
faceGroupMap[face.FaceGroupID] = append(faceGroupMap[face.FaceGroupID], *face)
} else {
faceGroupMap[face.FaceGroupID] = make([]models.ImageFace, 1)
faceGroupMap[face.FaceGroupID][0] = *face
}
}
faceGroupIDs := make([]int, len(faceGroupMap))
i := 0
for groupID := range faceGroupMap {
faceGroupIDs[i] = groupID
i++
}
faceGroupQuery := r.Database.
Joins("LEFT JOIN image_faces ON image_faces.id = face_groups.id").
Where("face_groups.id IN (?)", faceGroupIDs).
// Where("face_groups.id IN (?)", faceGroupIDs).
Where("image_faces.media_id IN (?)", r.Database.Select("media_id").Table("media").Where("media.album_id IN (?)", userAlbumIDs)).
Order("CASE WHEN label IS NULL THEN 1 ELSE 0 END")
faceGroupQuery = models.FormatSQL(faceGroupQuery, nil, paginate)
var faceGroups []*models.FaceGroup
if err := faceGroupQuery.Find(&faceGroups).Error; err != nil {
return nil, err
}
for _, faceGroup := range faceGroups {
faceGroup.ImageFaces = faceGroupMap[faceGroup.ID]
}
// for _, faceGroup := range faceGroups {
// faceGroup.ImageFaces = faceGroupMap[faceGroup.ID]
// }
return faceGroups, nil
}

View File

@@ -333,7 +333,8 @@ type TimelineGroup {
type FaceGroup {
id: ID!
label: String
imageFaces: [ImageFace!]!
imageFaces(paginate: Pagination): [ImageFace!]!
imageFaceCount: Int!
}
type ImageFace {

View File

@@ -13,7 +13,8 @@ export const MY_FACES_QUERY = gql`
myFaceGroups {
id
label
imageFaces {
imageFaceCount
imageFaces(paginate: { limit: 1 }) {
id
rectangle {
minX
@@ -125,7 +126,7 @@ const FaceDetails = ({ group }) => {
labeled={!!group.label}
onClick={() => setEditLabel(true)}
>
<FaceImagesCount>{group.imageFaces.length}</FaceImagesCount>
<FaceImagesCount>{group.imageFaceCount}</FaceImagesCount>
<FaceLabel>{group.label ?? 'Unlabeled'}</FaceLabel>
<EditIcon name="pencil" />
</FaceDetailsButton>