diff --git a/frontend/src/model/face.js b/frontend/src/model/face.js index aa742184b..8b96535fd 100644 --- a/frontend/src/model/face.js +++ b/frontend/src/model/face.js @@ -65,7 +65,7 @@ export class Face extends RestModel { } classes(selected) { - let classes = ["is-face", "id-" + this.ID]; + let classes = ["is-face", "uid-" + this.ID]; if (this.Hidden) classes.push("is-hidden"); if (selected) classes.push("is-selected"); diff --git a/frontend/src/pages/people/faces.vue b/frontend/src/pages/people/faces.vue index d330665a9..8768cdd94 100644 --- a/frontend/src/pages/people/faces.vue +++ b/frontend/src/pages/people/faces.vue @@ -40,7 +40,7 @@ xs12 sm6 md4 lg3 xl2 xxl1 d-flex > @@ -216,20 +216,13 @@ export default { }, methods: { searchCount() { - const offset = parseInt(window.localStorage.getItem("faces_offset")); - - if(this.offset > 0 || !offset) { - return this.batchSize; - } - - return offset + this.batchSize; + return this.batchSize; }, sortOrder() { - return "relevance"; + return "samples"; }, setOffset(offset) { this.offset = offset; - window.localStorage.setItem("faces_offset", offset); }, toggleLike(ev, index) { const inputType = this.input.eval(ev, index); @@ -376,6 +369,9 @@ export default { this.scrollDisabled = true; this.listen = false; + // Always refresh all faces for now. + this.dirty = true; + const count = this.dirty ? (this.page + 2) * this.batchSize : this.batchSize; const offset = this.dirty ? 0 : this.offset; diff --git a/frontend/tests/unit/model/face_test.js b/frontend/tests/unit/model/face_test.js index 79650c48c..273a606ce 100644 --- a/frontend/tests/unit/model/face_test.js +++ b/frontend/tests/unit/model/face_test.js @@ -26,19 +26,19 @@ describe("model/face", () => { const face = new Face(values); const result = face.classes(true); assert.include(result, "is-face"); - assert.include(result, "id-f123ghytrfggd"); + assert.include(result, "uid-f123ghytrfggd"); assert.include(result, "is-selected"); assert.notInclude(result, "is-hidden"); const result2 = face.classes(false); assert.include(result2, "is-face"); - assert.include(result2, "id-f123ghytrfggd"); + assert.include(result2, "uid-f123ghytrfggd"); assert.notInclude(result2, "is-selected"); assert.notInclude(result2, "is-hidden"); const values2 = { ID: "f123ghytrfggd", Samples: 5, Hidden: true }; const face2 = new Face(values2); const result3 = face2.classes(true); assert.include(result3, "is-face"); - assert.include(result3, "id-f123ghytrfggd"); + assert.include(result3, "uid-f123ghytrfggd"); assert.include(result3, "is-selected"); assert.include(result3, "is-hidden"); }); diff --git a/internal/search/faces.go b/internal/search/faces.go index e6bd69a4f..100408392 100644 --- a/internal/search/faces.go +++ b/internal/search/faces.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" + "github.com/jinzhu/gorm" + "github.com/photoprism/photoprism/internal/entity" "github.com/photoprism/photoprism/internal/form" "github.com/photoprism/photoprism/pkg/txt" @@ -31,10 +33,35 @@ func Faces(f form.FaceSearch) (results FaceResults, err error) { s = s.Order("subj_uid") case "added": s = s.Order(fmt.Sprintf("%s.created_at DESC", entity.Face{}.TableName())) + case "samples": + s = s.Order("samples DESC") default: s = s.Order("samples DESC") } + // Make sure at least one marker exists. + if f.Markers || txt.Yes(f.Unknown) { + s = s.Where("id IN (SELECT face_id FROM ? WHERE "+ + "face_id IS NOT NULL AND face_id <> '' AND marker_type = ? AND marker_src = ? AND marker_invalid = 0)", + gorm.Expr(entity.Marker{}.TableName()), entity.MarkerFace, entity.SrcImage) + } + + // Adds markers to search results if requested. + addMarkers := func(results FaceResults) FaceResults { + r := make(FaceResults, 0, len(results)) + + // Add markers to results. + for i := range results { + if marker := entity.FindFaceMarker(results[i].ID); marker != nil { + m := results[i] + m.Marker = marker + r = append(r, m) + } + } + + return r + } + // Find specific IDs? if f.ID != "" { s = s.Where(fmt.Sprintf("%s.id IN (?)", entity.Face{}.TableName()), strings.Split(strings.ToUpper(f.ID), txt.Or)) @@ -42,10 +69,7 @@ func Faces(f form.FaceSearch) (results FaceResults, err error) { if result := s.Scan(&results); result.Error != nil { return results, result.Error } else if f.Markers { - // Add markers to results. - for i := range results { - results[i].Marker = entity.FindFaceMarker(results[i].ID) - } + return addMarkers(results), nil } return results, nil @@ -69,10 +93,7 @@ func Faces(f form.FaceSearch) (results FaceResults, err error) { if res := s.Scan(&results); res.Error != nil { return results, res.Error } else if f.Markers { - // Add markers to results. - for i := range results { - results[i].Marker = entity.FindFaceMarker(results[i].ID) - } + return addMarkers(results), nil } return results, nil diff --git a/internal/search/faces_test.go b/internal/search/faces_test.go index f34c4a1fe..1ff34925a 100644 --- a/internal/search/faces_test.go +++ b/internal/search/faces_test.go @@ -13,7 +13,13 @@ func TestFaces(t *testing.T) { results, err := Faces(form.FaceSearch{Unknown: "yes", Order: "added", Markers: true}) assert.NoError(t, err) t.Logf("Faces: %#v", results) - assert.LessOrEqual(t, 1, len(results)) + if len(results) == 0 { + t.Fatal("results are empty") + } else if results[0].Marker == nil { + t.Fatal("marker is nil") + } else if results[0].Marker.MarkerUID == "" { + t.Fatal("marker uid is empty") + } }) t.Run("Search with limit", func(t *testing.T) { results, err := Faces(form.FaceSearch{Offset: 3, Order: "subject", Markers: true})