2021-08-12 04:54:20 +02:00
|
|
|
package query
|
|
|
|
|
|
|
|
import (
|
2021-08-19 23:12:51 +02:00
|
|
|
"fmt"
|
2021-08-30 11:26:57 +02:00
|
|
|
"strings"
|
2021-08-19 23:12:51 +02:00
|
|
|
|
2021-08-30 11:26:57 +02:00
|
|
|
"github.com/jinzhu/gorm"
|
2021-08-24 14:27:34 +02:00
|
|
|
"github.com/photoprism/photoprism/pkg/txt"
|
|
|
|
|
2021-08-12 04:54:20 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/entity"
|
|
|
|
)
|
|
|
|
|
2021-08-16 00:29:36 +02:00
|
|
|
// Subjects returns subjects from the index.
|
|
|
|
func Subjects(limit, offset int) (result entity.Subjects, err error) {
|
2021-08-12 04:54:20 +02:00
|
|
|
stmt := Db()
|
|
|
|
|
2021-08-24 14:27:34 +02:00
|
|
|
stmt = stmt.Order("subject_name").Limit(limit).Offset(offset)
|
2021-08-12 04:54:20 +02:00
|
|
|
err = stmt.Find(&result).Error
|
|
|
|
|
|
|
|
return result, err
|
|
|
|
}
|
2021-08-19 23:12:51 +02:00
|
|
|
|
2021-08-28 15:26:26 +02:00
|
|
|
// SubjectMap returns a map of subjects indexed by UID.
|
|
|
|
func SubjectMap() (result map[string]entity.Subject, err error) {
|
|
|
|
result = make(map[string]entity.Subject)
|
|
|
|
|
|
|
|
var subj entity.Subjects
|
|
|
|
|
|
|
|
stmt := Db().Where("subject_src <> ?", entity.SrcDefault)
|
|
|
|
|
|
|
|
if err = stmt.Find(&subj).Error; err != nil {
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, s := range subj {
|
|
|
|
result[s.SubjectUID] = s
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, err
|
|
|
|
}
|
|
|
|
|
2021-08-22 16:14:34 +02:00
|
|
|
// RemoveDanglingMarkerSubjects permanently deletes dangling marker subjects from the index.
|
|
|
|
func RemoveDanglingMarkerSubjects() (removed int64, err error) {
|
|
|
|
res := UnscopedDb().
|
2021-08-19 23:12:51 +02:00
|
|
|
Where("subject_src = ?", entity.SrcMarker).
|
|
|
|
Where(fmt.Sprintf("subject_uid NOT IN (SELECT subject_uid FROM %s)", entity.Face{}.TableName())).
|
2021-08-22 16:14:34 +02:00
|
|
|
Where(fmt.Sprintf("subject_uid NOT IN (SELECT subject_uid FROM %s)", entity.Marker{}.TableName())).
|
|
|
|
Delete(&entity.Subject{})
|
|
|
|
|
|
|
|
return res.RowsAffected, res.Error
|
2021-08-19 23:12:51 +02:00
|
|
|
}
|
2021-08-24 14:27:34 +02:00
|
|
|
|
2021-08-29 13:26:05 +02:00
|
|
|
// CreateMarkerSubjects adds and references known marker subjects.
|
|
|
|
func CreateMarkerSubjects() (affected int64, err error) {
|
2021-08-24 14:27:34 +02:00
|
|
|
var markers entity.Markers
|
|
|
|
|
|
|
|
if err := Db().
|
2021-09-01 12:48:17 +02:00
|
|
|
Where("subject_uid = '' AND marker_name <> '' AND subject_src <> ?", entity.SrcAuto).
|
2021-08-24 14:27:34 +02:00
|
|
|
Where("marker_invalid = 0 AND marker_type = ?", entity.MarkerFace).
|
|
|
|
Order("marker_name").
|
|
|
|
Find(&markers).Error; err != nil {
|
|
|
|
return affected, err
|
|
|
|
} else if len(markers) == 0 {
|
|
|
|
return affected, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var name string
|
|
|
|
var subj *entity.Subject
|
|
|
|
|
|
|
|
for _, m := range markers {
|
|
|
|
if name == m.MarkerName && subj != nil {
|
|
|
|
// Do nothing.
|
|
|
|
} else if subj = entity.NewSubject(m.MarkerName, entity.SubjectPerson, entity.SrcMarker); subj == nil {
|
|
|
|
log.Errorf("faces: subject should not be nil - bug?")
|
|
|
|
continue
|
|
|
|
} else if subj = entity.FirstOrCreateSubject(subj); subj == nil {
|
|
|
|
log.Errorf("faces: failed adding subject %s", txt.Quote(m.MarkerName))
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
affected++
|
|
|
|
}
|
|
|
|
|
|
|
|
name = m.MarkerName
|
|
|
|
|
|
|
|
if err := m.Updates(entity.Values{"SubjectUID": subj.SubjectUID}); err != nil {
|
|
|
|
return affected, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if m.FaceID == "" {
|
|
|
|
continue
|
|
|
|
} else if err := Db().Model(&entity.Face{}).Where("id = ? AND subject_uid = ''", m.FaceID).Update("SubjectUID", subj.SubjectUID).Error; err != nil {
|
|
|
|
return affected, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return affected, err
|
|
|
|
}
|
2021-08-30 11:26:57 +02:00
|
|
|
|
2021-08-30 11:56:34 +02:00
|
|
|
// SearchSubjectUIDs finds subject UIDs matching the search string, and removes names from the remaining query.
|
|
|
|
func SearchSubjectUIDs(s string) (result []string, names []string, remaining string) {
|
2021-08-30 11:26:57 +02:00
|
|
|
if s == "" {
|
2021-08-30 11:56:34 +02:00
|
|
|
return result, names, s
|
2021-08-30 11:26:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type Matches struct {
|
2021-09-01 12:48:17 +02:00
|
|
|
SubjectUID string
|
|
|
|
SubjectName string
|
|
|
|
SubjectAlias string
|
2021-08-30 11:26:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var matches []Matches
|
|
|
|
|
|
|
|
stmt := Db().Model(entity.Subject{})
|
|
|
|
stmt = stmt.Where("subject_src <> ?", entity.SrcDefault)
|
|
|
|
|
2021-09-01 12:48:17 +02:00
|
|
|
if where := LikeAllNames(Cols{"subject_name", "subject_alias"}, s); len(where) == 0 {
|
2021-08-30 11:56:34 +02:00
|
|
|
return result, names, s
|
2021-08-30 11:26:57 +02:00
|
|
|
} else {
|
|
|
|
stmt = stmt.Where("?", gorm.Expr(strings.Join(where, " OR ")))
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := stmt.Scan(&matches).Error; err != nil {
|
|
|
|
log.Errorf("search: %s while finding subjects", err)
|
|
|
|
} else if len(matches) == 0 {
|
2021-08-30 11:56:34 +02:00
|
|
|
return result, names, s
|
2021-08-30 11:26:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, m := range matches {
|
|
|
|
result = append(result, m.SubjectUID)
|
2021-08-30 11:56:34 +02:00
|
|
|
names = append(names, m.SubjectName)
|
2021-08-30 11:26:57 +02:00
|
|
|
|
2021-09-01 12:48:17 +02:00
|
|
|
for _, r := range txt.Words(strings.ToLower(m.SubjectName)) {
|
|
|
|
if len(r) > 1 {
|
|
|
|
s = strings.ReplaceAll(s, r, "")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, r := range txt.Words(strings.ToLower(m.SubjectAlias)) {
|
|
|
|
if len(r) > 1 {
|
|
|
|
s = strings.ReplaceAll(s, r, "")
|
|
|
|
}
|
2021-08-30 11:26:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s = strings.Trim(s, "&| ")
|
|
|
|
|
2021-08-30 11:56:34 +02:00
|
|
|
return result, names, s
|
2021-08-30 11:26:57 +02:00
|
|
|
}
|