Search: Add "reverse" query param to sort results in reverse order #683

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2025-07-14 18:08:05 +02:00
parent 66cd55461f
commit 88126e3e48
22 changed files with 212 additions and 68 deletions

View file

@ -262,10 +262,13 @@ export default {
return "cards";
},
getSortOrder() {
sortOrder() {
const query = this.$route.query;
return query["order"] ? query["order"] : this.model?.Order;
},
sortReverse() {
return !!this.$route?.query["reverse"] && this.$route.query["reverse"] === "true";
},
openDate(index) {
if (!this.canEdit) {
return this.openPhoto(index);
@ -325,7 +328,7 @@ export default {
if (showMerged) {
this.$lightbox.openModels(Thumb.fromFiles([selected]), 0, this.model);
} else if (this.getSortOrder() === "random") {
} else if (this.sortOrder() === "random") {
this.$lightbox.openModels(Thumb.fromPhotos(this.results), index, this.model);
} else {
this.$lightbox.openView(this, index);
@ -353,7 +356,8 @@ export default {
offset: offset,
s: this.uid,
merged: true,
order: this.getSortOrder(),
order: this.sortOrder(),
reverse: this.sortReverse(),
};
Object.assign(params, this.lastFilter);
@ -474,7 +478,8 @@ export default {
offset: this.offset,
s: this.uid,
merged: true,
order: this.getSortOrder(),
order: this.sortOrder(),
reverse: this.sortReverse(),
};
Object.assign(params, this.filter);

View file

@ -396,10 +396,11 @@ export default {
const query = this.$route.query;
const routeName = this.$route.name;
const order = this.sortOrder();
const reverse = this.sortReverse();
const q = query["q"] ? query["q"] : "";
const category = query["category"] ? query["category"] : "";
const year = query["year"] ? parseInt(query["year"]) : "";
const filter = { q, category, order, year };
const filter = { q, category, order, reverse, year };
const settings = {};
const features = this.$config.getSettings().features;
@ -503,6 +504,7 @@ export default {
this.filter.category = query["category"] ? query["category"] : "";
this.filter.year = query["year"] ? parseInt(query["year"]) : "";
this.filter.order = this.sortOrder();
this.filter.reverse = this.sortReverse();
this.search();
},
@ -627,6 +629,9 @@ export default {
return this.defaultOrder;
},
sortReverse() {
return !!this.$route?.query["reverse"] && this.$route.query["reverse"] === "true";
},
searchCount() {
const offset = parseInt(window.localStorage.getItem("albums.offset"));

View file

@ -117,7 +117,7 @@ export default {
const label = query["label"] ? query["label"] : "";
const latlng = query["latlng"] ? query["latlng"] : "";
const view = this.getViewType();
const order = this.getSortOrder();
const order = this.sortOrder();
const filter = {
country: country,
camera: camera,
@ -128,6 +128,7 @@ export default {
month: month,
color: color,
order: order,
reverse: this.sortReverse(),
q: q,
};
@ -217,7 +218,8 @@ export default {
this.filter.color = query["color"] ? query["color"] : "";
this.filter.label = query["label"] ? query["label"] : "";
this.filter.latlng = query["latlng"] ? query["latlng"] : "";
this.filter.order = this.getSortOrder();
this.filter.order = this.sortOrder();
this.filter.reverse = this.sortReverse();
this.settings.view = this.getViewType();
@ -354,7 +356,7 @@ export default {
return "";
},
getSortOrder() {
sortOrder() {
if (this.embedded) {
return "newest";
}
@ -396,6 +398,9 @@ export default {
return defaultOrder;
},
sortReverse() {
return !!this.$route?.query["reverse"] && this.$route.query["reverse"] === "true";
},
openDate(index) {
const photo = this.results[index];

View file

@ -83,7 +83,7 @@ func AlbumCoverByUID(uid string, public bool) (file entity.File, err error) {
// Automatically hide empty months.
switch a.AlbumType {
case entity.AlbumMonth, entity.AlbumState:
if err := a.Delete(); err != nil {
if err = a.Delete(); err != nil {
log.Errorf("%s: %s (hide)", a.AlbumType, err)
} else {
log.Infof("%s: %s hidden", a.AlbumType, clean.Log(a.AlbumTitle))

View file

@ -78,57 +78,57 @@ func UserAlbums(frm form.SearchAlbums, sess *entity.Session) (results AlbumResul
// Set sort order.
switch frm.Order {
case sortby.Count:
s = s.Order("photo_count DESC, albums.album_title, albums.album_uid DESC")
s = s.Order(OrderExpr("photo_count DESC, albums.album_title, albums.album_uid DESC", frm.Reverse))
case sortby.Moment, sortby.Newest:
if frm.Type == entity.AlbumManual || frm.Type == entity.AlbumState {
s = s.Order("albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_uid DESC", frm.Reverse))
} else if frm.Type == entity.AlbumMoment {
s = s.Order("has_year, albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.album_uid DESC")
s = s.Order(OrderExpr("has_year, albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.album_uid DESC", frm.Reverse))
} else {
s = s.Order("albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.album_uid DESC", frm.Reverse))
}
case sortby.Oldest:
if frm.Type == entity.AlbumManual || frm.Type == entity.AlbumState {
s = s.Order("albums.album_uid ASC")
s = s.Order(OrderExpr("albums.album_uid ASC", frm.Reverse))
} else if frm.Type == entity.AlbumMoment {
s = s.Order("has_year, albums.album_year ASC, albums.album_month ASC, albums.album_day ASC, albums.album_title, albums.album_uid ASC")
s = s.Order(OrderExpr("has_year, albums.album_year ASC, albums.album_month ASC, albums.album_day ASC, albums.album_title, albums.album_uid ASC", frm.Reverse))
} else {
s = s.Order("albums.album_year ASC, albums.album_month ASC, albums.album_day ASC, albums.album_title, albums.album_uid ASC")
s = s.Order(OrderExpr("albums.album_year ASC, albums.album_month ASC, albums.album_day ASC, albums.album_title, albums.album_uid ASC", frm.Reverse))
}
case sortby.Added:
s = s.Order("albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_uid DESC", frm.Reverse))
case sortby.Edited:
s = s.Order("albums.updated_at DESC, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.updated_at DESC, albums.album_uid DESC", frm.Reverse))
case sortby.Place:
s = s.Order("no_location, albums.album_location, has_year, albums.album_year DESC, albums.album_month ASC, albums.album_day ASC, albums.album_title, albums.album_uid DESC")
s = s.Order(OrderExpr("no_location, albums.album_location, has_year, albums.album_year DESC, albums.album_month ASC, albums.album_day ASC, albums.album_title, albums.album_uid DESC", frm.Reverse))
case sortby.Path:
s = s.Order("albums.album_path, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_path, albums.album_uid DESC", frm.Reverse))
case sortby.Category:
s = s.Order("albums.album_category, albums.album_title, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_category, albums.album_title, albums.album_uid DESC", frm.Reverse))
case sortby.Slug:
s = s.Order("albums.album_slug ASC, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_slug ASC, albums.album_uid DESC", frm.Reverse))
case sortby.Favorites:
if frm.Type == entity.AlbumFolder {
s = s.Order("albums.album_favorite DESC, albums.album_path ASC, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_favorite DESC, albums.album_path ASC, albums.album_uid DESC", frm.Reverse))
} else if frm.Type == entity.AlbumMonth {
s = s.Order("albums.album_favorite DESC, albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_favorite DESC, albums.album_year DESC, albums.album_month DESC, albums.album_day DESC, albums.album_title, albums.album_uid DESC", frm.Reverse))
} else {
s = s.Order("albums.album_favorite DESC, albums.album_title ASC, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_favorite DESC, albums.album_title ASC, albums.album_uid DESC", frm.Reverse))
}
case sortby.Name:
if frm.Type == entity.AlbumFolder {
s = s.Order("albums.album_path ASC, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_path ASC, albums.album_uid DESC", frm.Reverse))
} else {
s = s.Order("albums.album_title ASC, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_title ASC, albums.album_uid DESC", frm.Reverse))
}
case sortby.NameReverse:
if frm.Type == entity.AlbumFolder {
s = s.Order("albums.album_path DESC, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_path DESC, albums.album_uid DESC", frm.Reverse))
} else {
s = s.Order("albums.album_title DESC, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_title DESC, albums.album_uid DESC", frm.Reverse))
}
default:
s = s.Order("albums.album_favorite DESC, albums.album_title ASC, albums.album_uid DESC")
s = s.Order(OrderExpr("albums.album_favorite DESC, albums.album_title ASC, albums.album_uid DESC", frm.Reverse))
}
// Find specific UIDs only?

View file

@ -62,13 +62,13 @@ func Faces(frm form.SearchFaces) (results FaceResults, err error) {
// Set sort order.
switch frm.Order {
case "subject":
s = s.Order(fmt.Sprintf("%s.subj_uid", facesTable))
s = s.Order(OrderExpr(fmt.Sprintf("%s.subj_uid ASC", facesTable), frm.Reverse))
case "added":
s = s.Order(fmt.Sprintf("%s.created_at DESC", facesTable))
s = s.Order(OrderExpr(fmt.Sprintf("%s.created_at DESC", facesTable), frm.Reverse))
case "samples":
s = s.Order(fmt.Sprintf("%s.samples DESC, %s.id", facesTable, facesTable))
s = s.Order(OrderExpr(fmt.Sprintf("%s.samples DESC, %s.id", facesTable, facesTable), frm.Reverse))
default:
s = s.Order(fmt.Sprintf("%s.samples DESC, %s.id", facesTable, facesTable))
s = s.Order(OrderExpr(fmt.Sprintf("%s.samples DESC, %s.id", facesTable, facesTable), frm.Reverse))
}
// Find specific IDs?

View file

@ -0,0 +1,10 @@
package search
import (
"github.com/photoprism/photoprism/internal/entity/sortby"
)
// OrderExpr replaces "ASC" with "DESC" and "DESC" with "ASC" in the specified query order string if reverse is true.
func OrderExpr(s string, reverse bool) string {
return sortby.OrderExpr(s, reverse)
}

View file

@ -0,0 +1,24 @@
package search
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestOrderExpr(t *testing.T) {
t.Run("Default", func(t *testing.T) {
assert.Equal(t, "", OrderExpr("", false))
assert.Equal(t, "photos.edited_at", OrderExpr("photos.edited_at", false))
assert.Equal(t, "photos.edited_at ASC", OrderExpr("photos.edited_at ASC", false))
assert.Equal(t, "photos.edited_at DESC, files.media_id", OrderExpr("photos.edited_at DESC, files.media_id", false))
assert.Equal(t, "photos.edited_at DESC, files.media_id ASC", OrderExpr("photos.edited_at DESC, files.media_id ASC", false))
})
t.Run("Reverse", func(t *testing.T) {
assert.Equal(t, "", OrderExpr("", true))
assert.Equal(t, "photos.edited_at", OrderExpr("photos.edited_at", true))
assert.Equal(t, "photos.edited_at DESC", OrderExpr("photos.edited_at ASC", true))
assert.Equal(t, "photos.edited_at ASC, files.media_id", OrderExpr("photos.edited_at DESC, files.media_id", true))
assert.Equal(t, "photos.edited_at ASC, files.media_id DESC", OrderExpr("photos.edited_at DESC, files.media_id ASC", true))
})
}

View file

@ -180,36 +180,36 @@ func searchPhotos(frm form.SearchPhotos, sess *entity.Session, resultCols string
// Set sort order.
switch frm.Order {
case sortby.Edited:
s = s.Where("photos.edited_at IS NOT NULL").Order("photos.edited_at DESC, files.media_id")
s = s.Where("photos.edited_at IS NOT NULL").Order(OrderExpr("photos.edited_at DESC, files.media_id", frm.Reverse))
case sortby.Updated, sortby.UpdatedAt:
s = s.Where("photos.updated_at > photos.created_at").Order("photos.updated_at DESC, files.media_id")
s = s.Where("photos.updated_at > photos.created_at").Order(OrderExpr("photos.updated_at DESC, files.media_id", frm.Reverse))
case sortby.Archived:
s = s.Order("photos.deleted_at DESC, files.media_id")
s = s.Order(OrderExpr("photos.deleted_at DESC, files.media_id", frm.Reverse))
case sortby.Relevance:
if frm.Label != "" {
s = s.Order("photos.photo_quality DESC, photos_labels.uncertainty ASC, files.time_index")
s = s.Order(OrderExpr("photos.photo_quality DESC, photos_labels.uncertainty ASC, files.time_index", frm.Reverse))
} else {
s = s.Order("photos.photo_quality DESC, files.time_index")
s = s.Order(OrderExpr("photos.photo_quality DESC, files.time_index", frm.Reverse))
}
case sortby.Duration:
s = s.Order("photos.photo_duration DESC, files.time_index")
s = s.Order(OrderExpr("photos.photo_duration DESC, files.time_index", frm.Reverse))
case sortby.Size:
s = s.Order("files.file_size DESC, files.time_index")
s = s.Order(OrderExpr("files.file_size DESC, files.time_index", frm.Reverse))
case sortby.Newest:
s = s.Order("files.time_index")
s = s.Order(OrderExpr("files.time_index", frm.Reverse))
case sortby.Oldest:
s = s.Order("files.photo_taken_at, files.media_id")
s = s.Order(OrderExpr("files.photo_taken_at ASC, files.media_id", frm.Reverse))
case sortby.Similar:
s = s.Where("files.file_diff > 0")
s = s.Order("photos.photo_color, photos.cell_id, files.file_diff, files.photo_id, files.time_index")
s = s.Order(OrderExpr("photos.photo_color ASC, photos.cell_id ASC, files.file_diff, files.photo_id, files.time_index", frm.Reverse))
case sortby.Name:
s = s.Order("photos.photo_path, photos.photo_name, files.time_index")
s = s.Order(OrderExpr("photos.photo_path ASC, photos.photo_name ASC, files.time_index", frm.Reverse))
case sortby.Title:
s = s.Order("photos.photo_title, photos.photo_name, files.time_index")
s = s.Order(OrderExpr("photos.photo_title ASC, photos.photo_name ASC, files.time_index", frm.Reverse))
case sortby.Random:
s = s.Order(sortby.RandomExpr(s.Dialect()))
case sortby.Default, sortby.Imported, sortby.Added:
s = s.Order("files.media_id")
s = s.Order(OrderExpr("files.media_id", frm.Reverse))
default:
return PhotoResults{}, 0, ErrBadSortOrder
}

View file

@ -57,17 +57,19 @@ func Sessions(frm form.SearchSessions) (result entity.Sessions, err error) {
// Sort results?
switch order {
case sortby.LastActive:
stmt = stmt.Order("last_active DESC, user_name, client_name, id")
stmt = stmt.Order(OrderExpr("last_active DESC, user_name, client_name, id", frm.Reverse))
case sortby.SessExpires:
stmt = stmt.Order("sess_expires DESC, user_name, client_name, id")
stmt = stmt.Order(OrderExpr("sess_expires DESC, user_name, client_name, id", frm.Reverse))
case sortby.ClientName:
stmt = stmt.Where("client_name <> '' AND client_name IS NOT NULL").Order("client_name, created_at, id")
stmt = stmt.
Where("client_name <> '' AND client_name IS NOT NULL").
Order(OrderExpr("client_name, created_at, id", frm.Reverse))
case sortby.Login, sortby.LoginAt:
stmt = stmt.Order("login_at DESC, user_name, client_name, id")
stmt = stmt.Order(OrderExpr("login_at DESC, user_name, client_name, id", frm.Reverse))
case sortby.Created, sortby.CreatedAt:
stmt = stmt.Order("created_at ASC, user_name, client_name, id")
stmt = stmt.Order(OrderExpr("created_at ASC, user_name, client_name, id", frm.Reverse))
case sortby.Updated, sortby.UpdatedAt:
stmt = stmt.Order("updated_at DESC, user_name, client_name, id")
stmt = stmt.Order(OrderExpr("updated_at DESC, user_name, client_name, id", frm.Reverse))
default:
return result, fmt.Errorf("invalid sort order %s", order)
}

View file

@ -34,15 +34,15 @@ func Subjects(frm form.SearchSubjects) (results SubjectResults, err error) {
// Set sort order.
switch frm.Order {
case "name":
s = s.Order("subj_name")
s = s.Order(OrderExpr("subj_name ASC", frm.Reverse))
case "count":
s = s.Order("file_count DESC")
s = s.Order(OrderExpr("file_count DESC", frm.Reverse))
case "added":
s = s.Order(fmt.Sprintf("%s.created_at DESC", subjTable))
s = s.Order(OrderExpr(fmt.Sprintf("%s.created_at DESC", subjTable), frm.Reverse))
case "relevance":
s = s.Order("subj_favorite DESC, photo_count DESC")
s = s.Order(OrderExpr("subj_favorite DESC, photo_count DESC", frm.Reverse))
default:
s = s.Order("subj_favorite DESC, subj_name")
s = s.Order(OrderExpr("subj_favorite DESC, subj_name ASC", frm.Reverse))
}
if frm.UID != "" {

View file

@ -60,21 +60,21 @@ func Users(frm form.SearchUsers) (result entity.Users, err error) {
switch sortOrder {
case sortby.Name:
sortOrder = "user_name, id"
sortOrder = OrderExpr("user_name ASC, id ASC", frm.Reverse)
case sortby.DisplayName:
sortOrder = "display_name, id"
sortOrder = OrderExpr("display_name ASC, id ASC", frm.Reverse)
case sortby.Login, sortby.LoginAt:
sortOrder = "login_at DESC, id"
sortOrder = OrderExpr("login_at DESC, id ASC", frm.Reverse)
case sortby.Created, sortby.CreatedAt:
sortOrder = "created_at ASC, id"
sortOrder = OrderExpr("created_at ASC, id ASC", frm.Reverse)
case sortby.Updated, sortby.UpdatedAt:
sortOrder = "updated_at DESC, id"
sortOrder = OrderExpr("updated_at DESC, id ASC", frm.Reverse)
case sortby.Deleted, sortby.DeletedAt:
sortOrder = "deleted_at DESC, created_at DESC, id"
sortOrder = OrderExpr("deleted_at DESC, created_at DESC, id ASC", frm.Reverse)
case sortby.Email:
sortOrder = "user_email, id"
sortOrder = OrderExpr("user_email ASC, id ASC", frm.Reverse)
default:
sortOrder = "user_name, id"
sortOrder = OrderExpr("user_name ASC, id ASC", frm.Reverse)
}
if limit > 0 {

View file

@ -0,0 +1,44 @@
package sortby
import (
"strings"
)
// Sort direction strings.
const (
DirAsc = "ASC"
DirDesc = "DESC"
)
// OrderReplacer replaces "ASC" with "DESC" and "DESC" with "ASC"
var OrderReplacer = strings.NewReplacer(DirAsc, DirDesc, DirDesc, DirAsc)
// OrderExpr replaces "ASC" with "DESC" and "DESC" with "ASC" in the specified query order string if reverse is true.
func OrderExpr(s string, reverse bool) string {
if s == "" {
return ""
} else if reverse {
return OrderReplacer.Replace(s)
}
return s
}
// OrderAsc returns the expression used for sorting in ascending order.
func OrderAsc(reverse bool) string {
if reverse {
return DirDesc
}
return DirAsc
}
// OrderDesc returns the expression used for sorting in descending order.
func OrderDesc(reverse bool) string {
if reverse {
return DirAsc
}
return DirDesc
}

View file

@ -0,0 +1,42 @@
package sortby
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestOrderExpr(t *testing.T) {
t.Run("Default", func(t *testing.T) {
assert.Equal(t, "", OrderExpr("", false))
assert.Equal(t, "photos.edited_at", OrderExpr("photos.edited_at", false))
assert.Equal(t, "photos.edited_at ASC", OrderExpr("photos.edited_at ASC", false))
assert.Equal(t, "photos.edited_at DESC, files.media_id", OrderExpr("photos.edited_at DESC, files.media_id", false))
assert.Equal(t, "photos.edited_at DESC, files.media_id ASC", OrderExpr("photos.edited_at DESC, files.media_id ASC", false))
})
t.Run("Reverse", func(t *testing.T) {
assert.Equal(t, "", OrderExpr("", true))
assert.Equal(t, "photos.edited_at", OrderExpr("photos.edited_at", true))
assert.Equal(t, "photos.edited_at DESC", OrderExpr("photos.edited_at ASC", true))
assert.Equal(t, "photos.edited_at ASC, files.media_id", OrderExpr("photos.edited_at DESC, files.media_id", true))
assert.Equal(t, "photos.edited_at ASC, files.media_id DESC", OrderExpr("photos.edited_at DESC, files.media_id ASC", true))
})
}
func TestOrderAsc(t *testing.T) {
t.Run("Default", func(t *testing.T) {
assert.Equal(t, DirAsc, OrderAsc(false))
})
t.Run("Reverse", func(t *testing.T) {
assert.Equal(t, DirDesc, OrderAsc(true))
})
}
func TestOrderDesc(t *testing.T) {
t.Run("Default", func(t *testing.T) {
assert.Equal(t, DirDesc, OrderDesc(false))
})
t.Run("Reverse", func(t *testing.T) {
assert.Equal(t, DirAsc, OrderDesc(true))
})
}

View file

@ -19,6 +19,7 @@ type SearchAlbums struct {
Count int `form:"count" binding:"required" serialize:"-"`
Offset int `form:"offset" serialize:"-"`
Order string `form:"order" serialize:"-"`
Reverse bool `form:"reverse" serialize:"-"`
}
func (f *SearchAlbums) GetQuery() string {

View file

@ -11,6 +11,7 @@ type SearchFaces struct {
Count int `form:"count" binding:"required" serialize:"-"`
Offset int `form:"offset" serialize:"-"`
Order string `form:"order" serialize:"-"`
Reverse bool `form:"reverse" serialize:"-"`
}
func (f *SearchFaces) GetQuery() string {

View file

@ -11,6 +11,7 @@ type SearchLabels struct {
Count int `form:"count" binding:"required" serialize:"-"`
Offset int `form:"offset" serialize:"-"`
Order string `form:"order" serialize:"-"`
Reverse bool `form:"reverse" serialize:"-"`
}
func (f *SearchLabels) GetQuery() string {

View file

@ -98,6 +98,7 @@ type SearchPhotos struct {
Count int `form:"count" binding:"required" serialize:"-"` // Result FILE limit
Offset int `form:"offset" serialize:"-"` // Result FILE offset
Order string `form:"order" serialize:"-"` // Sort order
Reverse bool `form:"reverse" serialize:"-"` // Merge FILES in response
Merged bool `form:"merged" serialize:"-"` // Merge FILES in response
Details bool `form:"-" serialize:"-"` // Include additional information from details table
}

View file

@ -8,9 +8,10 @@ type SearchSessions struct {
UID string `form:"uid"`
Provider string `form:"provider"`
Method string `form:"method"`
Order string `form:"order" serialize:"-"`
Count int `form:"count" binding:"required" serialize:"-"`
Offset int `form:"offset" serialize:"-"`
Order string `form:"order" serialize:"-"`
Reverse bool `form:"reverse" serialize:"-"`
}
// AuthProviders returns the normalized authentication provider types.

View file

@ -16,6 +16,7 @@ type SearchSubjects struct {
Count int `form:"count" binding:"required" serialize:"-"`
Offset int `form:"offset" serialize:"-"`
Order string `form:"order" serialize:"-"`
Reverse bool `form:"reverse" serialize:"-"`
}
func (f *SearchSubjects) GetQuery() string {

View file

@ -8,9 +8,10 @@ type SearchUsers struct {
Email string `form:"email"`
All bool `form:"all"`
Deleted bool `form:"deleted"`
Order string `form:"order" serialize:"-"`
Count int `form:"count" binding:"required" serialize:"-"`
Offset int `form:"offset" serialize:"-"`
Order string `form:"order" serialize:"-"`
Reverse bool `form:"reverse" serialize:"-"`
}
func (f *SearchUsers) GetQuery() string {