mirror of https://github.com/stashapp/stash.git
Handle large and all entity queries (#3544)
* Remove upper page size limit * Batch GetMany function * Remove upper query limit from UI
This commit is contained in:
parent
ac67d640db
commit
58852f86fe
|
@ -96,15 +96,15 @@ func (ff FindFilterType) GetPage() int {
|
|||
func (ff FindFilterType) GetPageSize() int {
|
||||
const defaultPerPage = 25
|
||||
const minPerPage = 0
|
||||
const maxPerPage = 1000
|
||||
|
||||
if ff.PerPage == nil {
|
||||
return defaultPerPage
|
||||
}
|
||||
|
||||
if *ff.PerPage > maxPerPage {
|
||||
return maxPerPage
|
||||
} else if *ff.PerPage < minPerPage {
|
||||
// removed the maxPerPage check. We already all -1 to indicate all results
|
||||
// so there is no conceivable reason we should limit the page size
|
||||
|
||||
if *ff.PerPage < minPerPage {
|
||||
// negative page sizes should return all results
|
||||
// this is a sanity check in case GetPageSize is
|
||||
// called with a negative page size.
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package sqlite
|
||||
|
||||
const defaultBatchSize = 1000
|
||||
|
||||
// batchExec executes the provided function in batches of the provided size.
|
||||
func batchExec(ids []int, batchSize int, fn func(batch []int) error) error {
|
||||
for i := 0; i < len(ids); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(ids) {
|
||||
end = len(ids)
|
||||
}
|
||||
|
||||
batch := ids[i:end]
|
||||
if err := fn(batch); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -363,17 +363,23 @@ func (qb *GalleryStore) Find(ctx context.Context, id int) (*models.Gallery, erro
|
|||
}
|
||||
|
||||
func (qb *GalleryStore) FindMany(ctx context.Context, ids []int) ([]*models.Gallery, error) {
|
||||
q := qb.selectDataset().Prepared(true).Where(qb.table().Col(idColumn).In(ids))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
galleries := make([]*models.Gallery, len(ids))
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
galleries[i] = s
|
||||
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
|
||||
q := qb.selectDataset().Prepared(true).Where(qb.table().Col(idColumn).In(batch))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
galleries[i] = s
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range galleries {
|
||||
|
|
|
@ -260,17 +260,23 @@ func (qb *ImageStore) Find(ctx context.Context, id int) (*models.Image, error) {
|
|||
}
|
||||
|
||||
func (qb *ImageStore) FindMany(ctx context.Context, ids []int) ([]*models.Image, error) {
|
||||
q := qb.selectDataset().Prepared(true).Where(qb.table().Col(idColumn).In(ids))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
images := make([]*models.Image, len(ids))
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
images[i] = s
|
||||
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
|
||||
q := qb.selectDataset().Prepared(true).Where(qb.table().Col(idColumn).In(batch))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
images[i] = s
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range images {
|
||||
|
|
|
@ -70,17 +70,23 @@ func (qb *movieQueryBuilder) Find(ctx context.Context, id int) (*models.Movie, e
|
|||
|
||||
func (qb *movieQueryBuilder) FindMany(ctx context.Context, ids []int) ([]*models.Movie, error) {
|
||||
tableMgr := movieTableMgr
|
||||
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(ids...))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]*models.Movie, len(ids))
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
ret[i] = s
|
||||
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
|
||||
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(batch...))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
ret[i] = s
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range ret {
|
||||
|
|
|
@ -299,17 +299,23 @@ func (qb *PerformerStore) Find(ctx context.Context, id int) (*models.Performer,
|
|||
|
||||
func (qb *PerformerStore) FindMany(ctx context.Context, ids []int) ([]*models.Performer, error) {
|
||||
tableMgr := performerTableMgr
|
||||
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(ids...))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]*models.Performer, len(ids))
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
ret[i] = s
|
||||
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
|
||||
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(batch...))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
ret[i] = s
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range ret {
|
||||
|
|
|
@ -364,18 +364,24 @@ func (qb *SceneStore) Find(ctx context.Context, id int) (*models.Scene, error) {
|
|||
}
|
||||
|
||||
func (qb *SceneStore) FindMany(ctx context.Context, ids []int) ([]*models.Scene, error) {
|
||||
table := qb.table()
|
||||
q := qb.selectDataset().Prepared(true).Where(table.Col(idColumn).In(ids))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scenes := make([]*models.Scene, len(ids))
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
scenes[i] = s
|
||||
table := qb.table()
|
||||
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
|
||||
q := qb.selectDataset().Prepared(true).Where(table.Col(idColumn).In(batch))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
scenes[i] = s
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range scenes {
|
||||
|
|
|
@ -80,17 +80,23 @@ func (qb *studioQueryBuilder) Find(ctx context.Context, id int) (*models.Studio,
|
|||
|
||||
func (qb *studioQueryBuilder) FindMany(ctx context.Context, ids []int) ([]*models.Studio, error) {
|
||||
tableMgr := studioTableMgr
|
||||
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(ids...))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]*models.Studio, len(ids))
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
ret[i] = s
|
||||
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
|
||||
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(batch...))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
ret[i] = s
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range ret {
|
||||
|
|
|
@ -98,17 +98,23 @@ func (qb *tagQueryBuilder) Find(ctx context.Context, id int) (*models.Tag, error
|
|||
|
||||
func (qb *tagQueryBuilder) FindMany(ctx context.Context, ids []int) ([]*models.Tag, error) {
|
||||
tableMgr := tagTableMgr
|
||||
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(ids...))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]*models.Tag, len(ids))
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
ret[i] = s
|
||||
if err := batchExec(ids, defaultBatchSize, func(batch []int) error {
|
||||
q := goqu.Select("*").From(tableMgr.table).Where(tableMgr.byIDInts(batch...))
|
||||
unsorted, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range unsorted {
|
||||
i := intslice.IntIndex(ids, s.ID)
|
||||
ret[i] = s
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range ret {
|
||||
|
|
|
@ -40,7 +40,6 @@ import {
|
|||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { useDebounce } from "src/hooks/debounce";
|
||||
|
||||
const maxPageSize = 1000;
|
||||
interface IListFilterProps {
|
||||
onFilterUpdate: (newFilter: ListFilterModel) => void;
|
||||
filter: ListFilterModel;
|
||||
|
@ -134,11 +133,6 @@ export const ListFilter: React.FC<IListFilterProps> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
// don't allow page sizes over 1000
|
||||
if (pp > maxPageSize) {
|
||||
pp = maxPageSize;
|
||||
}
|
||||
|
||||
const newFilter = cloneDeep(filter);
|
||||
newFilter.itemsPerPage = pp;
|
||||
newFilter.currentPage = 1;
|
||||
|
@ -377,7 +371,6 @@ export const ListFilter: React.FC<IListFilterProps> = ({
|
|||
<Form.Control
|
||||
type="number"
|
||||
min={1}
|
||||
max={maxPageSize}
|
||||
className="text-input"
|
||||
ref={perPageInput}
|
||||
onKeyPress={(e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
|
|
Loading…
Reference in New Issue